Skip to content

sweepbatcher: batch change outputs #976

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 8 commits into from
Jul 29, 2025

Conversation

hieblmi
Copy link
Collaborator

@hieblmi hieblmi commented Jul 9, 2025

The batcher receives the ability to sign and publish batches with change outputs.

Change is created when the clients choose fractional swap amounts from the input values to be swapped. Usually the change is sent back to client's addresses.

If change is provided to ensurePresigned, presign and publishPresigned, it is expected to be placed within the passed []sweep.
Each sweep optionally holds a pointer to a change output. Only the first sweep in a sweep group called primary deposit will hold a pointer to the respective swap's change output.

  • unit test coverage for change outputs

@hieblmi hieblmi self-assigned this Jul 9, 2025
@hieblmi hieblmi marked this pull request as draft July 9, 2025 13:39
@hieblmi hieblmi requested a review from starius July 9, 2025 13:39
@hieblmi hieblmi force-pushed the batcher-change-outputs branch from 19b13cd to c7091de Compare July 9, 2025 13:46
@hieblmi
Copy link
Collaborator Author

hieblmi commented Jul 10, 2025

/gemini review

Copy link

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces the ability to handle change outputs in sweep batches, which is a significant feature. I've found a critical bug in fee calculation within CheckSignedTx that needs to be addressed. I've also pointed out a minor code simplification and a gap in test coverage for the new logic.

@hieblmi hieblmi force-pushed the batcher-change-outputs branch 4 times, most recently from c20504a to 0567051 Compare July 10, 2025 09:31
Copy link

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

The pull request introduces functionality to handle change outputs in sweep batcher, specifically for presigned transactions. The changes involve modifying the sweep struct, updating transaction construction to include change outputs, and adding a function to retrieve and de-duplicate change outputs. The code also includes unit tests for the new functionality. The changes look good overall, but there are a few areas that could be improved for robustness and clarity.

@hieblmi hieblmi force-pushed the batcher-change-outputs branch from 0567051 to 950fd4e Compare July 10, 2025 13:33
@hieblmi hieblmi marked this pull request as ready for review July 10, 2025 13:41
@hieblmi hieblmi force-pushed the batcher-change-outputs branch 4 times, most recently from 576270f to 35f5810 Compare July 14, 2025 07:04
@starius
Copy link
Collaborator

starius commented Jul 14, 2025

Multiple sweeps holding the same change reference will produce a single change output in the batch transaction.

This seems to be outdated.

@hieblmi hieblmi force-pushed the batcher-change-outputs branch from 35f5810 to 4c58f85 Compare July 15, 2025 18:34
@hieblmi hieblmi force-pushed the batcher-change-outputs branch 4 times, most recently from fe4ec68 to c0418e3 Compare July 16, 2025 10:14
@hieblmi hieblmi requested a review from starius July 16, 2025 11:54
Copy link
Collaborator

@starius starius left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM! 🥇

Added a couple of proposals.

@hieblmi hieblmi force-pushed the batcher-change-outputs branch from a728c80 to 1b31b88 Compare July 22, 2025 12:31
@hieblmi hieblmi requested a review from starius July 22, 2025 12:41
Copy link
Collaborator

@starius starius left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM! 💾

I sent PR hieblmi#1 with some changes that I propose, mostly for tests, but also most importantly to use minRelayFeeRate in presign and ensurePresigned as start value for presigning and when passing to SignTx. Please review the changes. You can squash the changes into proper commits or keep them as a separate commit.

Copy link
Collaborator

@starius starius left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM! 🍇
I think, it is ready. Great work!

Copy link
Member

@sputn1ck sputn1ck left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Left some comments re multiple change outputs

@@ -210,6 +210,13 @@ func estimateBatchWeight(batch *batch) (feeDetails, error) {
err)
}

// Add change output weights.
for _, s := range batch.sweeps {
if s.change != nil {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would it make sense to add a check for if a change pkscript repeats that we add to the other outputs value instead of adding a new output with the same pkscript? Is that something that can happen (e.g. on static loop in if change is the static addr change would be the same?)

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Very good suggestion!

@@ -1255,6 +1260,13 @@ func constructUnsignedTx(sweeps []sweep, address btcutil.Address,
LockTime: uint32(currentHeight),
}

var changeOutputs []*wire.TxOut
for _, sweep := range sweeps {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think here a similiar comment to my other comment, what happens if the pkscript is the same between multiple sweeps?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It totally makes sense to consolidate values of change outputs with identical pkscript. I've changed the workings to
do so here and in the greedy batch selection. I will also add unit tests for this.

},

{
name: "all sweeps different change outputs",
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think a testcase "all sweeps same change output" makes sense and the result should be a single change output right?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

added "all sweeps identical change pkscripts"

@hieblmi hieblmi requested review from starius and sputn1ck July 28, 2025 10:20
@hieblmi
Copy link
Collaborator Author

hieblmi commented Jul 28, 2025

I've added commit sweepbatcher: consolidate identical change pkscripts to tally up the values of identical change pkscripts.

@hieblmi hieblmi force-pushed the batcher-change-outputs branch from d2eee3f to fd4ee81 Compare July 28, 2025 10:32
Copy link
Member

@sputn1ck sputn1ck left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM! thanks for the additional change consolidation 🙏

Copy link
Collaborator

@starius starius left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM! 🌴
The code of last commit is great. I propose to add few tests to validate it.

}

weight.AddOutput(s.change.PkScript)
changeOutputs[pkScriptString] = struct{}{}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

Please add a test case to TestEstimateBatchWeight.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added "two sweeps regular batch with identical change".

@@ -395,6 +395,57 @@ func TestConstructUnsignedTx(t *testing.T) {
wantFee: 1248,
},

{
name: "all sweeps identical change pkscripts",
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

s/all sweeps identical change pkscripts/identical change pkscripts/

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

changed

wantWeight: 1076,
wantFeeForWeight: 1076,
wantFee: 1076,
},
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please add another test case with equal change pkscripts, but different satoshi values.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

added "identical change pkscripts different values".


// Mine a blocks to trigger republishing.
require.NoError(t, lnd.NotifyHeight(601))
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I propose to add a unit test with two swaps sharing the same change output pkscript.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

added testPresigned_presigned_group_with_identical_change_pkscript.

@hieblmi hieblmi force-pushed the batcher-change-outputs branch 2 times, most recently from 16ebc11 to c7b0428 Compare July 29, 2025 10:14
hieblmi and others added 6 commits July 29, 2025 12:33
This commit adds an optional change output to the sweep struct.
Presigning sweeps takes change outputs into account.
Each primary deposit id of a sweep group points to
an optional change output.
sweepbatcher.presign scans all passed sweeps for
change outputs and passes them to constructUnsignedTx.
Optional change of a swap is encoded in its sweeps
as a pointer to the same change output. This change
is taken into account when constructing the unsigned
batch transaction when it comes to tx weight and
outputs.
if constructUnsignedTx constructs a batch transaction that is
below the minimum relay fee, an error is returned.
 - ensurePresigned: use passed minRelayFeeRate instead of chainfee.FeePerKwFloor
 - presign: use minRelayFeeRate for start and minRelayFee
 - presign: make sure minRelayFeeRate is set
 - add tests for presign to test this new behavior; make sure the number of
   transactions is lower if minRelayFeeRate is higher
 - update error message in constructUnsignedTx: use <, not <= (more accurate)
 - use utils.DustLimitForPkScript instead of lnwallet.DustLimitForSize in tests
 - in tests adjust amounts to edge values, add controls
@hieblmi hieblmi force-pushed the batcher-change-outputs branch 2 times, most recently from 6679a5d to 29161bb Compare July 29, 2025 12:43
@hieblmi hieblmi requested a review from starius July 29, 2025 12:59
@hieblmi
Copy link
Collaborator Author

hieblmi commented Jul 29, 2025

@starius I had to sort the change outputs in constructUnsignedTx after they've been deduplicated in a map in order to make the order deterministic for the tests, see 29161bb#diff-355a81f8dab9aa0faaaa04740ca44f425ff0dd18dea7240e6a28e1697f222dd1R1441

Copy link
Collaborator

@starius starius left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM! 🥇
nit: I propose to use bip-0069 for outputs sorting.

}

// Sort the keys
sort.Strings(keys)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's use BIP-0069 for outputs sorting (change only, so main output is 0).
We have it implemented for inputs in utils/get_prevout_info.go

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've added a bip69 compatible utils method to sort tx outputs. I've added it in a separate commit 245a666.

hieblmi added 2 commits July 29, 2025 18:03
batch separate change outputs with identical pkscripts
are tallied up and consolidated to a single change output.
@hieblmi hieblmi force-pushed the batcher-change-outputs branch from 29161bb to d00b78e Compare July 29, 2025 19:22
@hieblmi hieblmi requested a review from starius July 29, 2025 19:42
Copy link
Collaborator

@starius starius left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM! 🏆

@hieblmi hieblmi merged commit dd85a61 into lightninglabs:master Jul 29, 2025
4 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants