mirror of
https://github.com/lightningnetwork/lnd.git
synced 2025-03-29 03:01:52 +01:00
chainntnfs: populate spendsByHeight during historical dispatch
This commit fixes a buggy scenario where: - a spend of the desired outpoint occurs - RegisterSpend is called, not immediately notifying - caller performs a historical dispatch, calling UpdateSpendDetails - caller is notified on the Spend channel - re-org occurs - caller is not notified of the re-org We fix this by correctly populating the spendsByHeight map when dispatchSpendDetails is called. This mirrors the confirmation case.
This commit is contained in:
parent
d263a01a2d
commit
2fbee31c6e
@ -1325,6 +1325,19 @@ func (n *TxNotifier) dispatchSpendDetails(ntfn *SpendNtfn, details *SpendDetail)
|
|||||||
return ErrTxNotifierExiting
|
return ErrTxNotifierExiting
|
||||||
}
|
}
|
||||||
|
|
||||||
|
spendHeight := uint32(details.SpendingHeight)
|
||||||
|
|
||||||
|
// We also add to spendsByHeight to notify on chain reorgs.
|
||||||
|
reorgSafeHeight := spendHeight + n.reorgSafetyLimit
|
||||||
|
if reorgSafeHeight > n.currentHeight {
|
||||||
|
txSet, exists := n.spendsByHeight[spendHeight]
|
||||||
|
if !exists {
|
||||||
|
txSet = make(map[SpendRequest]struct{})
|
||||||
|
n.spendsByHeight[spendHeight] = txSet
|
||||||
|
}
|
||||||
|
txSet[ntfn.SpendRequest] = struct{}{}
|
||||||
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -10,6 +10,7 @@ import (
|
|||||||
"github.com/btcsuite/btcd/wire"
|
"github.com/btcsuite/btcd/wire"
|
||||||
"github.com/btcsuite/btcutil"
|
"github.com/btcsuite/btcutil"
|
||||||
"github.com/lightningnetwork/lnd/chainntnfs"
|
"github.com/lightningnetwork/lnd/chainntnfs"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
@ -1843,6 +1844,97 @@ func TestTxNotifierSpendReorg(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TestTxNotifierUpdateSpendReorg tests that a call to RegisterSpend after the
|
||||||
|
// spend has been confirmed, and then UpdateSpendDetails (called by historical
|
||||||
|
// dispatch), followed by a chain re-org will notify on the Reorg channel. This
|
||||||
|
// was not always the case and has since been fixed.
|
||||||
|
func TestTxNotifierSpendReorgMissed(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
const startingHeight = 10
|
||||||
|
hintCache := newMockHintCache()
|
||||||
|
n := chainntnfs.NewTxNotifier(
|
||||||
|
startingHeight, chainntnfs.ReorgSafetyLimit, hintCache,
|
||||||
|
hintCache,
|
||||||
|
)
|
||||||
|
|
||||||
|
// We'll create a spending transaction that spends the outpoint we'll
|
||||||
|
// watch.
|
||||||
|
op := wire.OutPoint{Index: 1}
|
||||||
|
spendTx := wire.NewMsgTx(2)
|
||||||
|
spendTx.AddTxIn(&wire.TxIn{
|
||||||
|
PreviousOutPoint: op,
|
||||||
|
SignatureScript: testSigScript,
|
||||||
|
})
|
||||||
|
spendTxHash := spendTx.TxHash()
|
||||||
|
|
||||||
|
// Create the spend details that we'll call UpdateSpendDetails with.
|
||||||
|
spendDetails := &chainntnfs.SpendDetail{
|
||||||
|
SpentOutPoint: &op,
|
||||||
|
SpenderTxHash: &spendTxHash,
|
||||||
|
SpendingTx: spendTx,
|
||||||
|
SpenderInputIndex: 0,
|
||||||
|
SpendingHeight: startingHeight + 1,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Now confirm the spending transaction.
|
||||||
|
block := btcutil.NewBlock(&wire.MsgBlock{
|
||||||
|
Transactions: []*wire.MsgTx{spendTx},
|
||||||
|
})
|
||||||
|
err := n.ConnectTip(block.Hash(), startingHeight+1, block.Transactions())
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unable to connect block: %v", err)
|
||||||
|
}
|
||||||
|
if err := n.NotifyHeight(startingHeight + 1); err != nil {
|
||||||
|
t.Fatalf("unable to dispatch notifications: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// We register for the spend now and will not get a spend notification
|
||||||
|
// until we call UpdateSpendDetails.
|
||||||
|
ntfn, err := n.RegisterSpend(&op, testRawScript, 1)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unable to register spend: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Assert that the HistoricalDispatch variable is non-nil. We'll use
|
||||||
|
// the SpendRequest member to update the spend details.
|
||||||
|
require.NotEmpty(t, ntfn.HistoricalDispatch)
|
||||||
|
|
||||||
|
select {
|
||||||
|
case <-ntfn.Event.Spend:
|
||||||
|
t.Fatalf("did not expect to receive spend ntfn")
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
|
||||||
|
// We now call UpdateSpendDetails with our generated spend details to
|
||||||
|
// simulate a historical spend dispatch being performed. This should
|
||||||
|
// result in a notification being received on the Spend channel.
|
||||||
|
err = n.UpdateSpendDetails(
|
||||||
|
ntfn.HistoricalDispatch.SpendRequest, spendDetails,
|
||||||
|
)
|
||||||
|
require.Empty(t, err)
|
||||||
|
|
||||||
|
// Assert that we receive a Spend notification.
|
||||||
|
select {
|
||||||
|
case <-ntfn.Event.Spend:
|
||||||
|
default:
|
||||||
|
t.Fatalf("expected to receive spend ntfn")
|
||||||
|
}
|
||||||
|
|
||||||
|
// We will now re-org the spending transaction out of the chain, and we
|
||||||
|
// should receive a notification on the Reorg channel.
|
||||||
|
err = n.DisconnectTip(startingHeight + 1)
|
||||||
|
require.Empty(t, err)
|
||||||
|
|
||||||
|
select {
|
||||||
|
case <-ntfn.Event.Spend:
|
||||||
|
t.Fatalf("received unexpected spend ntfn")
|
||||||
|
case <-ntfn.Event.Reorg:
|
||||||
|
default:
|
||||||
|
t.Fatalf("expected spend reorg ntfn")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// TestTxNotifierConfirmHintCache ensures that the height hints for transactions
|
// TestTxNotifierConfirmHintCache ensures that the height hints for transactions
|
||||||
// are kept track of correctly with each new block connected/disconnected. This
|
// are kept track of correctly with each new block connected/disconnected. This
|
||||||
// test also asserts that the height hints are not updated until the simulated
|
// test also asserts that the height hints are not updated until the simulated
|
||||||
|
Loading…
x
Reference in New Issue
Block a user