mirror of
https://github.com/lightninglabs/loop.git
synced 2026-08-13 12:33:03 +02:00
sweepbatcher: test spending notification and error
This commit is contained in:
parent
980c7d4e00
commit
8466e0f8eb
1 changed files with 220 additions and 24 deletions
|
|
@ -762,9 +762,9 @@ func testSweepBatcherSimpleLifecycle(t *testing.T, store testStore,
|
|||
batcher := NewBatcher(lnd.WalletKit, lnd.ChainNotifier, lnd.Signer,
|
||||
testMuSig2SignSweep, testVerifySchnorrSig, lnd.ChainParams,
|
||||
batcherStore, sweepStore)
|
||||
runErrChan := make(chan error)
|
||||
go func() {
|
||||
err := batcher.Run(ctx)
|
||||
checkBatcherError(t, err)
|
||||
runErrChan <- batcher.Run(ctx)
|
||||
}()
|
||||
|
||||
// Create a sweep request.
|
||||
|
|
@ -772,13 +772,24 @@ func testSweepBatcherSimpleLifecycle(t *testing.T, store testStore,
|
|||
Hash: chainhash.Hash{1, 1},
|
||||
Index: 1,
|
||||
}
|
||||
const (
|
||||
inputValue = 111
|
||||
outputValue = 50
|
||||
fee = inputValue - outputValue
|
||||
)
|
||||
spendErrChan := make(chan error, 1)
|
||||
notifier := &SpendNotifier{
|
||||
SpendChan: make(chan *SpendDetail, 1),
|
||||
SpendErrChan: spendErrChan,
|
||||
QuitChan: make(chan bool, 1),
|
||||
}
|
||||
sweepReq1 := SweepRequest{
|
||||
SwapHash: lntypes.Hash{1, 1, 1},
|
||||
Inputs: []Input{{
|
||||
Value: 111,
|
||||
Value: inputValue,
|
||||
Outpoint: op1,
|
||||
}},
|
||||
Notifier: &dummyNotifier,
|
||||
Notifier: notifier,
|
||||
}
|
||||
|
||||
const initiationHeight = 550
|
||||
|
|
@ -786,7 +797,7 @@ func testSweepBatcherSimpleLifecycle(t *testing.T, store testStore,
|
|||
swap1 := &loopdb.LoopOutContract{
|
||||
SwapContract: loopdb.SwapContract{
|
||||
CltvExpiry: 111,
|
||||
AmountRequested: 111,
|
||||
AmountRequested: inputValue,
|
||||
ProtocolVersion: loopdb.ProtocolVersionMuSig2,
|
||||
HtlcKeys: htlcKeys,
|
||||
InitiationHeight: initiationHeight,
|
||||
|
|
@ -806,33 +817,27 @@ func testSweepBatcherSimpleLifecycle(t *testing.T, store testStore,
|
|||
|
||||
// When batch is successfully created it will execute it's first step,
|
||||
// which leads to a spend monitor of the primary sweep.
|
||||
<-lnd.RegisterSpendChannel
|
||||
spendReg := <-lnd.RegisterSpendChannel
|
||||
|
||||
// Wait for tx to be published.
|
||||
<-lnd.TxPublishChannel
|
||||
|
||||
// Eventually request will be consumed and a new batch will spin up.
|
||||
var primarySweepID wire.OutPoint
|
||||
require.Eventually(t, func() bool {
|
||||
return batcher.numBatches(ctx) == 1
|
||||
}, test.Timeout, eventuallyCheckFrequency)
|
||||
batch := tryGetOnlyBatch(ctx, batcher)
|
||||
if batch == nil {
|
||||
return false
|
||||
}
|
||||
|
||||
// Find the batch and assign it to a local variable for easier access.
|
||||
batch := &batch{}
|
||||
for _, btch := range getBatches(ctx, batcher) {
|
||||
btch.testRunInEventLoop(ctx, func() {
|
||||
if btch.primarySweepID == op1 {
|
||||
batch = btch
|
||||
}
|
||||
})
|
||||
}
|
||||
primarySweepID = batch.snapshot(ctx).primarySweepID
|
||||
|
||||
require.Eventually(t, func() bool {
|
||||
// Batch should have the sweep stored.
|
||||
return batch.numSweeps(ctx) == 1
|
||||
}, test.Timeout, eventuallyCheckFrequency)
|
||||
|
||||
// The primary sweep id should be that of the first inserted sweep.
|
||||
require.Equal(t, batch.primarySweepID, op1)
|
||||
|
||||
// Wait for tx to be published.
|
||||
<-lnd.TxPublishChannel
|
||||
require.Equal(t, primarySweepID, op1)
|
||||
|
||||
err = lnd.NotifyHeight(601)
|
||||
require.NoError(t, err)
|
||||
|
|
@ -840,7 +845,11 @@ func testSweepBatcherSimpleLifecycle(t *testing.T, store testStore,
|
|||
// After receiving a height notification the batch will step again,
|
||||
// leading to a new spend monitoring.
|
||||
require.Eventually(t, func() bool {
|
||||
batch := batch.snapshot(ctx)
|
||||
batch := tryGetOnlyBatch(ctx, batcher)
|
||||
if batch == nil {
|
||||
return false
|
||||
}
|
||||
batch = batch.snapshot(ctx)
|
||||
|
||||
return batch.currentHeight == 601
|
||||
}, test.Timeout, eventuallyCheckFrequency)
|
||||
|
|
@ -848,6 +857,58 @@ func testSweepBatcherSimpleLifecycle(t *testing.T, store testStore,
|
|||
// Wait for tx to be published.
|
||||
<-lnd.TxPublishChannel
|
||||
|
||||
// Emulate spend error.
|
||||
testError := errors.New("test error")
|
||||
spendReg.ErrChan <- testError
|
||||
|
||||
// Make sure the caller of AddSweep got the spending error.
|
||||
notifierErr := <-spendErrChan
|
||||
require.Error(t, notifierErr)
|
||||
require.ErrorIs(t, notifierErr, testError)
|
||||
|
||||
// Wait for the batcher to crash because of the spending error.
|
||||
runErr := <-runErrChan
|
||||
require.ErrorIs(t, runErr, testError)
|
||||
|
||||
// Now launch the batcher again.
|
||||
batcher = NewBatcher(lnd.WalletKit, lnd.ChainNotifier, lnd.Signer,
|
||||
testMuSig2SignSweep, testVerifySchnorrSig, lnd.ChainParams,
|
||||
batcherStore, sweepStore)
|
||||
go func() {
|
||||
runErrChan <- batcher.Run(ctx)
|
||||
}()
|
||||
|
||||
// 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
|
||||
|
||||
// Deliver sweep request to batcher.
|
||||
spendChan := make(chan *SpendDetail, 1)
|
||||
notifier = &SpendNotifier{
|
||||
SpendChan: spendChan,
|
||||
SpendErrChan: make(chan error, 1),
|
||||
QuitChan: make(chan bool, 1),
|
||||
}
|
||||
sweepReq1.Notifier = notifier
|
||||
require.NoError(t, batcher.AddSweep(ctx, &sweepReq1))
|
||||
|
||||
// Wait for the notifier to be installed.
|
||||
require.Eventually(t, func() bool {
|
||||
batch := tryGetOnlyBatch(ctx, batcher)
|
||||
if batch == nil {
|
||||
return false
|
||||
}
|
||||
batch = batch.snapshot(ctx)
|
||||
|
||||
sweep := batch.sweeps[batch.primarySweepID]
|
||||
|
||||
return sweep.notifier != nil &&
|
||||
sweep.notifier.SpendChan == spendChan
|
||||
}, test.Timeout, eventuallyCheckFrequency)
|
||||
|
||||
// Create the spending tx that will trigger the spend monitor of the
|
||||
// batch.
|
||||
spendingTx := &wire.MsgTx{
|
||||
|
|
@ -861,6 +922,7 @@ func testSweepBatcherSimpleLifecycle(t *testing.T, store testStore,
|
|||
},
|
||||
TxOut: []*wire.TxOut{
|
||||
{
|
||||
Value: outputValue,
|
||||
PkScript: []byte{3, 2, 1},
|
||||
},
|
||||
},
|
||||
|
|
@ -879,6 +941,11 @@ func testSweepBatcherSimpleLifecycle(t *testing.T, store testStore,
|
|||
// We notify the spend.
|
||||
lnd.SpendChannel <- spendDetail
|
||||
|
||||
// Make sure the notifier got a proper spending notification.
|
||||
spending := <-spendChan
|
||||
require.Equal(t, spendingTxHash, spending.Tx.TxHash())
|
||||
require.Equal(t, btcutil.Amount(fee), spending.OnChainFeePortion)
|
||||
|
||||
// After receiving the spend, the batch is now monitoring for confs.
|
||||
confReg := <-lnd.RegisterConfChannel
|
||||
|
||||
|
|
@ -889,7 +956,84 @@ func testSweepBatcherSimpleLifecycle(t *testing.T, store testStore,
|
|||
// The batch should eventually read the spend notification and progress
|
||||
// its state to closed.
|
||||
require.Eventually(t, func() bool {
|
||||
batch := batch.snapshot(ctx)
|
||||
batch := tryGetOnlyBatch(ctx, batcher)
|
||||
if batch == nil {
|
||||
return false
|
||||
}
|
||||
batch = batch.snapshot(ctx)
|
||||
|
||||
return batch.state == Closed
|
||||
}, test.Timeout, eventuallyCheckFrequency)
|
||||
|
||||
// Emulate a confirmation error.
|
||||
confReg.ErrChan <- testError
|
||||
|
||||
// Wait for the batcher to crash because of the confirmation error.
|
||||
runErr = <-runErrChan
|
||||
require.ErrorIs(t, runErr, testError)
|
||||
|
||||
// Now launch the batcher again.
|
||||
batcher = NewBatcher(lnd.WalletKit, lnd.ChainNotifier, lnd.Signer,
|
||||
testMuSig2SignSweep, testVerifySchnorrSig, lnd.ChainParams,
|
||||
batcherStore, sweepStore)
|
||||
go func() {
|
||||
runErrChan <- batcher.Run(ctx)
|
||||
}()
|
||||
|
||||
// When batch is successfully created it will execute it's first step,
|
||||
// which leads to a spend monitor of the primary sweep.
|
||||
<-lnd.RegisterSpendChannel
|
||||
|
||||
// Deliver sweep request to batcher.
|
||||
spendChan = make(chan *SpendDetail, 1)
|
||||
notifier = &SpendNotifier{
|
||||
SpendChan: spendChan,
|
||||
SpendErrChan: make(chan error, 1),
|
||||
QuitChan: make(chan bool, 1),
|
||||
}
|
||||
sweepReq1.Notifier = notifier
|
||||
require.NoError(t, batcher.AddSweep(ctx, &sweepReq1))
|
||||
|
||||
// Wait for tx to be published. A closed batch is stored in DB as Open.
|
||||
<-lnd.TxPublishChannel
|
||||
|
||||
// Wait for the notifier to be installed.
|
||||
require.Eventually(t, func() bool {
|
||||
batch := tryGetOnlyBatch(ctx, batcher)
|
||||
if batch == nil {
|
||||
return false
|
||||
}
|
||||
batch = batch.snapshot(ctx)
|
||||
|
||||
sweep := batch.sweeps[batch.primarySweepID]
|
||||
|
||||
return sweep.notifier != nil &&
|
||||
sweep.notifier.SpendChan == spendChan
|
||||
}, test.Timeout, eventuallyCheckFrequency)
|
||||
|
||||
// We notify the spend.
|
||||
lnd.SpendChannel <- spendDetail
|
||||
|
||||
// Make sure the notifier got a proper spending notification.
|
||||
spending = <-spendChan
|
||||
require.Equal(t, spendingTxHash, spending.Tx.TxHash())
|
||||
require.Equal(t, btcutil.Amount(fee), spending.OnChainFeePortion)
|
||||
|
||||
// After receiving the spend, the batch is now monitoring for confs.
|
||||
confReg = <-lnd.RegisterConfChannel
|
||||
|
||||
// Make sure the confirmation has proper height hint. It should pass
|
||||
// the swap initiation height, not the current height.
|
||||
require.Equal(t, int32(initiationHeight), confReg.HeightHint)
|
||||
|
||||
// The batch should eventually read the spend notification and progress
|
||||
// its state to closed.
|
||||
require.Eventually(t, func() bool {
|
||||
batch := tryGetOnlyBatch(ctx, batcher)
|
||||
if batch == nil {
|
||||
return false
|
||||
}
|
||||
batch = batch.snapshot(ctx)
|
||||
|
||||
return batch.state == Closed
|
||||
}, test.Timeout, eventuallyCheckFrequency)
|
||||
|
|
@ -905,8 +1049,60 @@ func testSweepBatcherSimpleLifecycle(t *testing.T, store testStore,
|
|||
// Eventually the batch receives the confirmation notification and
|
||||
// confirms itself.
|
||||
require.Eventually(t, func() bool {
|
||||
batch := tryGetOnlyBatch(ctx, batcher)
|
||||
if batch == nil {
|
||||
return false
|
||||
}
|
||||
|
||||
return batch.isComplete()
|
||||
}, test.Timeout, eventuallyCheckFrequency)
|
||||
|
||||
// Now emulate adding the sweep again after it was fully confirmed.
|
||||
// This triggers another code path (monitorSpendAndNotify).
|
||||
spendChan = make(chan *SpendDetail, 1)
|
||||
notifier = &SpendNotifier{
|
||||
SpendChan: spendChan,
|
||||
SpendErrChan: make(chan error, 1),
|
||||
QuitChan: make(chan bool, 1),
|
||||
}
|
||||
sweepReq1.Notifier = notifier
|
||||
require.NoError(t, batcher.AddSweep(ctx, &sweepReq1))
|
||||
|
||||
// Expect a spending registration.
|
||||
<-lnd.RegisterSpendChannel
|
||||
|
||||
// We notify the spend.
|
||||
lnd.SpendChannel <- spendDetail
|
||||
|
||||
// Now expect the notifier to produce the spending details.
|
||||
spending = <-spendChan
|
||||
require.Equal(t, spendingTxHash, spending.Tx.TxHash())
|
||||
require.Equal(t, btcutil.Amount(fee), spending.OnChainFeePortion)
|
||||
|
||||
// Now check what happens in case of a spending error.
|
||||
spendErrChan = make(chan error, 1)
|
||||
notifier = &SpendNotifier{
|
||||
SpendChan: make(chan *SpendDetail, 1),
|
||||
SpendErrChan: spendErrChan,
|
||||
QuitChan: make(chan bool, 1),
|
||||
}
|
||||
sweepReq1.Notifier = notifier
|
||||
require.NoError(t, batcher.AddSweep(ctx, &sweepReq1))
|
||||
|
||||
// Expect a spending registration.
|
||||
spendReg = <-lnd.RegisterSpendChannel
|
||||
|
||||
// Emulate spend error.
|
||||
spendReg.ErrChan <- testError
|
||||
|
||||
// Make sure the caller of AddSweep got the spending error.
|
||||
notifierErr = <-spendErrChan
|
||||
require.Error(t, notifierErr)
|
||||
require.ErrorIs(t, notifierErr, testError)
|
||||
|
||||
// Wait for the batcher to crash because of the spending error.
|
||||
runErr = <-runErrChan
|
||||
require.ErrorIs(t, runErr, testError)
|
||||
}
|
||||
|
||||
// wrappedLogger implements btclog.Logger, recording last debug message format.
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue