Skip to content
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

sweepbatcher: add mixed batches as an option #791

Merged
merged 6 commits into from
Aug 21, 2024

Conversation

starius
Copy link
Collaborator

@starius starius commented Jul 12, 2024

Option WithMixedBatch instructs sweepbatcher to create mixed batches with regard to cooperativeness. Such a batch can include both sweeps signed both cooperatively and non-cooperatively. If cooperative signing fails for a sweep, transaction is updated to sign that sweep non-cooperatively and another round of cooperative signing runs on the remaining sweeps. The remaining sweeps are signed in non-cooperative (more expensive) way. If the whole procedure fails for whatever reason, the batch is signed non-cooperatively (the fallback).

Updated greedy batch selection algorithm to take into account whether mixed batches are enabled.

Pull Request Checklist

  • Update release_notes.md if your PR contains major features, breaking changes or bugfixes

@starius starius self-assigned this Jul 12, 2024
@starius starius force-pushed the sweepbatcher-mixed-batches branch from cce93ad to 9b82990 Compare July 12, 2024 18:57
@starius starius marked this pull request as ready for review July 13, 2024 02:25
Copy link
Collaborator

@hieblmi hieblmi left a comment

Choose a reason for hiding this comment

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

I appreciate your effort towards more flexible batcher features. On my first pass I just have a question regarding the leafWitnessSize. Great catch btw 🥇

@@ -962,21 +962,21 @@ func (b *batch) debugLogTx(msg string, tx *wire.MsgTx) {

// coopSignBatchTx collects the necessary signatures from the server in order
// to cooperatively sweep the funds.
func (b *batch) coopSignBatchTx(ctx context.Context, packet *psbt.Packet,
func (b *batch) coopSignBatchTx(ctx context.Context, tx *wire.MsgTx,
Copy link
Collaborator

Choose a reason for hiding this comment

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

👌

var psbtBuf bytes.Buffer
err = packet.Serialize(&psbtBuf)
// Create PSBT and prevOuts.
psbtBytes, prevOuts, err := b.createPsbt(batchTx, sweeps)
Copy link
Collaborator

Choose a reason for hiding this comment

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

👌

swap/htlc.go Outdated
@@ -283,10 +281,20 @@ func (h *Htlc) AddSuccessToEstimator(estimator *input.TxWeightEstimator) error {
trHtlc.InternalPubKey, successLeaf, timeoutLeafHash[:],
)

estimator.AddTapscriptInput(maxSuccessWitnessSize, tapscript)
// The leaf witness size must be calculated without the byte
Copy link
Collaborator

Choose a reason for hiding this comment

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

that's a very good catch fixing the fee overpayment 🥇

Copy link
Collaborator

Choose a reason for hiding this comment

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

The godoc says only the total size of all elements on the stack that are consumed by the revealed script should be counted. but we also count sigLength and preimg_length which I am unsure if they are pushed to the stack? I tought just the sig and the preimage are pushed?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

IIUC, sizes of elements also consume space in encoded transaction, even though they are not themselves pushed to stack. In sweepbatcher, I added comparison of real tx weight and the estimated weight, logging a warning in case of a discrepancy. The current value of leafWitnessSize (with elements' sizes) results in weight estimates equal to real tx size.

Copy link
Member

Choose a reason for hiding this comment

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

Oh that's a very nice catch!

@starius starius force-pushed the sweepbatcher-mixed-batches branch from 9b82990 to 2364192 Compare July 18, 2024 17:48
@starius starius marked this pull request as draft July 22, 2024 18:09
@starius
Copy link
Collaborator Author

starius commented Jul 22, 2024

Converting to draft for now.

Changes are needed in greedy batch selection algorithm, to adjust to the fact that batches can be mixed now.

Copy link
Member

@bhandras bhandras left a comment

Choose a reason for hiding this comment

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

Really nice work!! 🎉 Just a few comments, but overall looks very good already!

swap/htlc.go Outdated
@@ -283,10 +281,20 @@ func (h *Htlc) AddSuccessToEstimator(estimator *input.TxWeightEstimator) error {
trHtlc.InternalPubKey, successLeaf, timeoutLeafHash[:],
)

estimator.AddTapscriptInput(maxSuccessWitnessSize, tapscript)
// The leaf witness size must be calculated without the byte
Copy link
Member

Choose a reason for hiding this comment

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

Oh that's a very nice catch!

if ok {
// Preserve coopFailed value not to forget about cooperative
// spending failure in this sweep.
sweepValue := *sweep
Copy link
Member

Choose a reason for hiding this comment

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

nit: i think naming this variable as tmp could be more readable.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Done.

if err != nil {
b.log.Warnf("Mixed batch publish error: %v", err)
} else {
coopSuccess = true
Copy link
Member

Choose a reason for hiding this comment

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

Would this fail if it fails to replace the tx in the mempool?

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 changed publishMixedBatch interface: now it returns signSuccess bool result, as well as publishBatchCoop function. Also I renamed coopSuccess local variable here to signSuccess, since it indicates success of signing (and all preceding actions), which may be fully non-cooperative in case of publishMixedBatch.

txHash := tx.TxHash()
b.log.Infof("attempting to publish mixed tx=%v with feerate=%v, "+
"weight=%v, feeForWeight=%v, fee=%v, sweeps=%d (%d cooperative"+
" + %d non-cooperative), destAddr=%s",
Copy link
Member

Choose a reason for hiding this comment

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

nit: Would be nice to also log the swap hashes for additional debug info.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Done. Now log lines look like this:

attempting to publish mixed tx=xxx with feerate=10000 sat/kw, weight=559 wu, feeForWeight=0.00005590 BTC, fee=0.00005590 BTC, sweeps=1, 0 cooperative: () and 1 non-cooperative (01d0fabd251f), destAddr=xxx

attempting to publish mixed tx=xxxx with feerate=10000 sat/kw, weight=3377 wu, feeForWeight=0.00033770 BTC, fee=0.00033770 BTC, sweeps=9, 2 cooperative: (938834d6c636, 34ec81dbdaf9) and 7 non-cooperative (01d0fabd251f, 5778f985db75, 91d3827f052f, 4b78063b9c22, f5411ec7e51e, aae761377f3b, 067dc6a81018), destAddr=xxx

err := b.wallet.PublishTransaction(
ctx, tx, labels.LoopOutBatchSweepSuccess(b.id),
)
if err != nil {
Copy link
Member

Choose a reason for hiding this comment

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

Ideally I think we'd want to filter out the case where replace fails (but the tx itself is valid) so we don't end up ditching the coops sweeps in favor of revealing the scripts etc.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Done. See the comment above, about "signSuccess".

Copy link
Collaborator

@hieblmi hieblmi Aug 20, 2024

Choose a reason for hiding this comment

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

I think this requires checking for publishing errors like output already spent I think.

Discussed offline.

@starius starius force-pushed the sweepbatcher-mixed-batches branch 2 times, most recently from 1be3a90 to 3d26a70 Compare July 30, 2024 02:47
@starius starius marked this pull request as ready for review July 30, 2024 03:04
@starius
Copy link
Collaborator Author

starius commented Jul 30, 2024

Updated greedy batch selection algorithm to take into account whether mixed batches are enabled!

@lightninglabs-deploy
Copy link

@bhandras: review reminder
@hieblmi: review reminder
@GeorgeTsagk: review reminder

@starius starius requested a review from sputn1ck August 6, 2024 15:47
Copy link
Member

@bhandras bhandras left a comment

Choose a reason for hiding this comment

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

LGTM, awesome work! 🎉

@starius
Copy link
Collaborator Author

starius commented Aug 12, 2024

Rebased. Removed the commit already merged in #804

@starius
Copy link
Collaborator Author

starius commented Aug 13, 2024

NB: after #809 is merged, the custom transaction label generator should be used in mixed batch code as well.

@starius
Copy link
Collaborator Author

starius commented Aug 13, 2024

Rebased

@starius
Copy link
Collaborator Author

starius commented Aug 19, 2024

Pushed a fix for signing code. Real SignOutputRaw requires prevOuts for all inputs, not only the ones being signed. The commit will be squashed later.

Copy link
Collaborator

@hieblmi hieblmi left a comment

Choose a reason for hiding this comment

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

Great work and nice test coverage to back it up, including the integration test.

// mixedBatch instructs sweepbatcher to create mixed batches with regard
// to cooperativeness. Such a batch can include sweeps signed both
// cooperatively and non-cooperatively. If cooperative signing fails for
// a sweep, transaction is updated to sign that sweep non-cooperatively
Copy link
Collaborator

Choose a reason for hiding this comment

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

really cool system 👌

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Thanks! 😎

func (b *batch) constructUnsignedTx(sweeps []sweep,
address btcutil.Address) (*wire.MsgTx, lntypes.WeightUnit,
btcutil.Amount, btcutil.Amount, error) {

Copy link
Collaborator

Choose a reason for hiding this comment

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

nit: could return early if sweeps is empty.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Done

false
}

// Keep track of if any new sweep failed to sign cooperatively.
Copy link
Collaborator

Choose a reason for hiding this comment

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

nit: Keep track if any...

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Fixed

}

// Publish the transaction.
err := b.wallet.PublishTransaction(
Copy link
Collaborator

Choose a reason for hiding this comment

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

nit:

label := labels.LoopOutBatchSweepSuccess(b.id)
err := b.wallet.PublishTransaction(ctx, tx, label)
if err != nil {
      return 0, fmt.Errorf("publishing tx failed: %w", err), true
}

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 should be b.cfg.txLabeler(b.id), not labels.LoopOutBatchSweepSuccess(b.id), see #809

err := b.wallet.PublishTransaction(
ctx, tx, labels.LoopOutBatchSweepSuccess(b.id),
)
if err != nil {
Copy link
Collaborator

@hieblmi hieblmi Aug 20, 2024

Choose a reason for hiding this comment

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

I think this requires checking for publishing errors like output already spent I think.

Discussed offline.

... instead of psbt.Packet.

Make the code simpler. Function worked with packet.UnsignedTx only,
so it is easier to pass tx directly.
Unload method publishBatchCoop. Also this code will be reused in new method
for mixed batches soon.
Size of a signature affects the weight of transaction, which is verified
in tests.

Also method SignOutputRaw now returns the number of signatures matching
the number of signature descriptors to prevent crashes in tests where
multiple inputs are signed.
Option WithMixedBatch instructs sweepbatcher to create mixed batches with regard
to cooperativeness. Such a batch can include both sweeps signed both
cooperatively and non-cooperatively. If cooperative signing fails for a sweep,
transaction is updated to sign that sweep non-cooperatively and another round of
cooperative signing runs on the remaining sweeps. The remaining sweeps are
signed in non-cooperative (more expensive) way. If the whole procedure fails for
whatever reason, the batch is signed non-cooperatively (the fallback).
Treat coopFailed flag the same as nonCoopHint. The former is what we found in
previos signing attempts, the later is what the caller signalled to us.
@starius starius merged commit a6e9a2b into lightninglabs:master Aug 21, 2024
4 checks passed
@starius starius deleted the sweepbatcher-mixed-batches branch August 21, 2024 17:11
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.

4 participants