Skip to content

Commit

Permalink
sweepbatcher: customize transaction labels
Browse files Browse the repository at this point in the history
Previously sweepbatcher used loop/labels.LoopOutBatchSweepSuccess to assign
transactions labels. The value is "BatchOutSweepSuccess -- $batch_id", which
does not fit use cases outside of loop-out. Now there is option WithTxLabeler
which sets a function used to generate the label.
  • Loading branch information
starius committed Aug 13, 2024
1 parent 6d3a488 commit a62b067
Show file tree
Hide file tree
Showing 3 changed files with 167 additions and 4 deletions.
9 changes: 6 additions & 3 deletions sweepbatcher/sweep_batch.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@ import (
"github.com/btcsuite/btcd/wire"
"github.com/btcsuite/btclog"
"github.com/lightninglabs/lndclient"
"github.com/lightninglabs/loop/labels"
"github.com/lightninglabs/loop/loopdb"
"github.com/lightninglabs/loop/swap"
sweeppkg "github.com/lightninglabs/loop/sweep"
Expand Down Expand Up @@ -142,6 +141,10 @@ type batchConfig struct {
// external source of fee rates (FeeRateProvider).
noBumping bool

// txLabeler is a function generating transaction label. It is called
// before publishing a batch transaction. Batch ID is passed to it.
txLabeler func(batchID int32) string

// customMuSig2Signer is a custom signer. If it is set, it is used to
// create musig2 signatures instead of musig2SignSweep and signerClient.
// Note that musig2SignSweep must be nil in this case, however signer
Expand Down Expand Up @@ -792,7 +795,7 @@ func (b *batch) publishBatch(ctx context.Context) (btcutil.Amount, error) {
b.debugLogTx("serialized non-coop sweep", batchTx)

err = b.wallet.PublishTransaction(
ctx, batchTx, labels.LoopOutBatchSweepSuccess(b.id),
ctx, batchTx, b.cfg.txLabeler(b.id),
)
if err != nil {
return fee, err
Expand Down Expand Up @@ -941,7 +944,7 @@ func (b *batch) publishBatchCoop(ctx context.Context) (btcutil.Amount,
b.debugLogTx("serialized coop sweep", batchTx)

err = b.wallet.PublishTransaction(
ctx, batchTx, labels.LoopOutBatchSweepSuccess(b.id),
ctx, batchTx, b.cfg.txLabeler(b.id),
)
if err != nil {
return fee, err, true
Expand Down
24 changes: 23 additions & 1 deletion sweepbatcher/sweep_batcher.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import (
"github.com/btcsuite/btcd/chaincfg/chainhash"
"github.com/btcsuite/btcd/wire"
"github.com/lightninglabs/lndclient"
"github.com/lightninglabs/loop/labels"
"github.com/lightninglabs/loop/loopdb"
"github.com/lightninglabs/loop/swap"
"github.com/lightninglabs/loop/utils"
Expand Down Expand Up @@ -258,6 +259,10 @@ type Batcher struct {
// ignored and fee bumping by sweepbatcher is disabled.
customFeeRate FeeRateProvider

// txLabeler is a function generating transaction label. It is called
// before publishing a batch transaction. Batch ID is passed to it.
txLabeler func(batchID int32) string

// customMuSig2Signer is a custom signer. If it is set, it is used to
// create musig2 signatures instead of musig2SignSweep and signerClient.
// Note that musig2SignSweep must be nil in this case, however signer
Expand All @@ -272,6 +277,10 @@ type BatcherConfig struct {
// ignored and fee bumping by sweepbatcher is disabled.
customFeeRate FeeRateProvider

// txLabeler is a function generating transaction label. It is called
// before publishing a batch transaction. Batch ID is passed to it.
txLabeler func(batchID int32) string

// customMuSig2Signer is a custom signer. If it is set, it is used to
// create musig2 signatures instead of musig2SignSweep and signerClient.
// Note that musig2SignSweep must be nil in this case, however signer
Expand All @@ -291,6 +300,15 @@ func WithCustomFeeRate(customFeeRate FeeRateProvider) BatcherOption {
}
}

// WithTxLabeler sets a function generating transaction label. It is called
// before publishing a batch transaction. Batch ID is passed to the function.
// By default, loop/labels.LoopOutBatchSweepSuccess is used.
func WithTxLabeler(txLabeler func(batchID int32) string) BatcherOption {
return func(cfg *BatcherConfig) {
cfg.txLabeler = txLabeler
}
}

// WithCustomSignMuSig2 instructs sweepbatcher to use a custom function to
// produce MuSig2 signatures. If it is set, it is used to create
// musig2 signatures instead of musig2SignSweep and signerClient. Note
Expand All @@ -310,7 +328,9 @@ func NewBatcher(wallet lndclient.WalletKitClient,
store BatcherStore, sweepStore SweepFetcher,
opts ...BatcherOption) *Batcher {

var cfg BatcherConfig
cfg := BatcherConfig{
txLabeler: labels.LoopOutBatchSweepSuccess,
}
for _, opt := range opts {
opt(&cfg)
}
Expand All @@ -335,6 +355,7 @@ func NewBatcher(wallet lndclient.WalletKitClient,
store: store,
sweepStore: sweepStore,
customFeeRate: cfg.customFeeRate,
txLabeler: cfg.txLabeler,
customMuSig2Signer: cfg.customMuSig2Signer,
}
}
Expand Down Expand Up @@ -933,6 +954,7 @@ func (b *Batcher) newBatchConfig(maxTimeoutDistance int32) batchConfig {
return batchConfig{
maxTimeoutDistance: maxTimeoutDistance,
noBumping: b.customFeeRate != nil,
txLabeler: b.txLabeler,
customMuSig2Signer: b.customMuSig2Signer,
}
}
Expand Down
138 changes: 138 additions & 0 deletions sweepbatcher/sweep_batcher_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import (
"github.com/btcsuite/btcd/chaincfg/chainhash"
"github.com/btcsuite/btcd/wire"
"github.com/btcsuite/btclog"
"github.com/lightninglabs/lndclient"
"github.com/lightninglabs/loop/loopdb"
"github.com/lightninglabs/loop/test"
"github.com/lightninglabs/loop/utils"
Expand Down Expand Up @@ -399,6 +400,138 @@ func testFeeBumping(t *testing.T, store testStore,
}
}

// walletKitWrapper wraps a wallet kit and memorizes label of the most recent
// published transaction.
type walletKitWrapper struct {
lndclient.WalletKitClient

lastLabel string
}

func (w *walletKitWrapper) PublishTransaction(ctx context.Context,
tx *wire.MsgTx, label string) error {

w.lastLabel = label

return w.WalletKitClient.PublishTransaction(ctx, tx, label)
}

// testTxLabeler tests transaction labels.
func testTxLabeler(t *testing.T, store testStore,
batcherStore testBatcherStore) {

defer test.Guard(t)()

lnd := test.NewMockLnd()
ctx, cancel := context.WithCancel(context.Background())

sweepStore, err := NewSweepFetcherFromSwapStore(store, lnd.ChainParams)
require.NoError(t, err)

walletKit := &walletKitWrapper{WalletKitClient: lnd.WalletKit}

batcher := NewBatcher(walletKit, lnd.ChainNotifier, lnd.Signer,
testMuSig2SignSweep, testVerifySchnorrSig, lnd.ChainParams,
batcherStore, sweepStore)
var runErr error
var wg sync.WaitGroup
wg.Add(1)
go func() {
defer wg.Done()
runErr = batcher.Run(ctx)
}()

// Create a sweep request.
sweepReq1 := SweepRequest{
SwapHash: lntypes.Hash{1, 1, 1},
Value: 111,
Outpoint: wire.OutPoint{
Hash: chainhash.Hash{1, 1},
Index: 1,
},
Notifier: &dummyNotifier,
}

swap1 := &loopdb.LoopOutContract{
SwapContract: loopdb.SwapContract{
CltvExpiry: 111,
AmountRequested: 111,
ProtocolVersion: loopdb.ProtocolVersionMuSig2,
HtlcKeys: htlcKeys,
},

DestAddr: destAddr,
SwapInvoice: swapInvoice,
SweepConfTarget: 111,
}

err = store.CreateLoopOut(ctx, sweepReq1.SwapHash, swap1)
require.NoError(t, err)
store.AssertLoopOutStored()

// Deliver sweep request to batcher.
require.NoError(t, batcher.AddSweep(&sweepReq1))

// Eventually request will be consumed and a new batch will spin up.
require.Eventually(t, func() bool {
return len(batcher.batches) == 1
}, test.Timeout, eventuallyCheckFrequency)

// When batch is successfully created it will execute it's first step,
// which leads to a spend monitor of the primary sweep.
<-lnd.RegisterSpendChannel

// Wait for tx to be published.
<-lnd.TxPublishChannel

// Find the batch and assign it to a local variable for easier access.
var theBatch *batch
for _, btch := range batcher.batches {
if btch.primarySweepID == sweepReq1.SwapHash {
theBatch = btch
}
}

// Now test the label.
wantLabel := fmt.Sprintf("BatchOutSweepSuccess -- %d", theBatch.id)
require.Equal(t, wantLabel, walletKit.lastLabel)

// Now make the batcher quit by canceling the context.
cancel()
wg.Wait()
checkBatcherError(t, runErr)

// Define dummy tx labeler, always returning "test".
txLabeler := func(batchID int32) string {
return "test"
}

// Now try it with option WithTxLabeler.
batcher = NewBatcher(walletKit, lnd.ChainNotifier, lnd.Signer,
testMuSig2SignSweep, testVerifySchnorrSig, lnd.ChainParams,
batcherStore, sweepStore, WithTxLabeler(txLabeler))
ctx, cancel = context.WithCancel(context.Background())
wg.Add(1)
go func() {
defer wg.Done()
runErr = batcher.Run(ctx)
}()

// Expect batch to register for spending.
<-lnd.RegisterSpendChannel

// Wait for tx to be published.
<-lnd.TxPublishChannel

// Now test the label.
require.Equal(t, "test", walletKit.lastLabel)

// Now make the batcher quit by canceling the context.
cancel()
wg.Wait()
checkBatcherError(t, runErr)
}

// testSweepBatcherSimpleLifecycle tests the simple lifecycle of the batches
// that are created and run by the batcher.
func testSweepBatcherSimpleLifecycle(t *testing.T, store testStore,
Expand Down Expand Up @@ -2278,6 +2411,11 @@ func TestFeeBumping(t *testing.T) {
})
}

// TestTxLabeler tests transaction labels.
func TestTxLabeler(t *testing.T) {
runTests(t, testTxLabeler)
}

// TestSweepBatcherSimpleLifecycle tests the simple lifecycle of the batches
// that are created and run by the batcher.
func TestSweepBatcherSimpleLifecycle(t *testing.T) {
Expand Down

0 comments on commit a62b067

Please sign in to comment.