Merge pull request #3726 from halseth/chainntfs-test-spend-during-rescan
[test only] add TestTxNotifierSpendDuringHistoricalRescan
This commit is contained in:
commit
11a031c34b
@ -2155,6 +2155,210 @@ func TestTxNotifierSpendHintCache(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// TestTxNotifierSpendHinthistoricalRescan checks that the height hints and
|
||||
// spend notifications behave as expected when a spend is found at tip during a
|
||||
// historical rescan.
|
||||
func TestTxNotifierSpendDuringHistoricalRescan(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
const (
|
||||
startingHeight = 200
|
||||
reorgSafety = 10
|
||||
)
|
||||
|
||||
// Intiialize our TxNotifier instance backed by a height hint cache.
|
||||
hintCache := newMockHintCache()
|
||||
n := chainntnfs.NewTxNotifier(
|
||||
startingHeight, reorgSafety, hintCache, hintCache,
|
||||
)
|
||||
|
||||
// Create a test outpoint and register it for spend notifications.
|
||||
op1 := wire.OutPoint{Index: 1}
|
||||
ntfn1, err := n.RegisterSpend(&op1, testRawScript, 1)
|
||||
if err != nil {
|
||||
t.Fatalf("unable to register spend for op1: %v", err)
|
||||
}
|
||||
|
||||
// A historical rescan should be initiated from the height hint to the
|
||||
// current height.
|
||||
if ntfn1.HistoricalDispatch.StartHeight != 1 {
|
||||
t.Fatalf("expected historical dispatch to start at height hint")
|
||||
}
|
||||
|
||||
if ntfn1.HistoricalDispatch.EndHeight != startingHeight {
|
||||
t.Fatalf("expected historical dispatch to end at current height")
|
||||
}
|
||||
|
||||
// It should not have a spend hint set upon registration, as we must
|
||||
// first determine whether it has already been spent in the chain.
|
||||
_, err = hintCache.QuerySpendHint(ntfn1.HistoricalDispatch.SpendRequest)
|
||||
if err != chainntnfs.ErrSpendHintNotFound {
|
||||
t.Fatalf("unexpected error when querying for height hint "+
|
||||
"expected: %v, got %v", chainntnfs.ErrSpendHintNotFound,
|
||||
err)
|
||||
}
|
||||
|
||||
// Create a new empty block and extend the chain.
|
||||
height := uint32(startingHeight) + 1
|
||||
emptyBlock := btcutil.NewBlock(&wire.MsgBlock{})
|
||||
err = n.ConnectTip(
|
||||
emptyBlock.Hash(), height, emptyBlock.Transactions(),
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatalf("unable to connect block: %v", err)
|
||||
}
|
||||
if err := n.NotifyHeight(height); err != nil {
|
||||
t.Fatalf("unable to dispatch notifications: %v", err)
|
||||
}
|
||||
|
||||
// Since we haven't called UpdateSpendDetails yet, there should be no
|
||||
// spend hint found.
|
||||
_, err = hintCache.QuerySpendHint(ntfn1.HistoricalDispatch.SpendRequest)
|
||||
if err != chainntnfs.ErrSpendHintNotFound {
|
||||
t.Fatalf("unexpected error when querying for height hint "+
|
||||
"expected: %v, got %v", chainntnfs.ErrSpendHintNotFound,
|
||||
err)
|
||||
}
|
||||
|
||||
// Simulate a bunch of blocks being mined while the historical rescan
|
||||
// is still in progress. We make sure to not mine more than reorgSafety
|
||||
// blocks after the spend, since it will be forgotten then.
|
||||
var spendHeight uint32
|
||||
for i := 0; i < reorgSafety; i++ {
|
||||
height++
|
||||
|
||||
// Let the outpoint we are watching be spent midway.
|
||||
var block *btcutil.Block
|
||||
if i == 5 {
|
||||
// We'll create a new block that only contains the
|
||||
// spending transaction of the outpoint.
|
||||
spendTx1 := wire.NewMsgTx(2)
|
||||
spendTx1.AddTxIn(&wire.TxIn{
|
||||
PreviousOutPoint: op1,
|
||||
SignatureScript: testSigScript,
|
||||
})
|
||||
block = btcutil.NewBlock(&wire.MsgBlock{
|
||||
Transactions: []*wire.MsgTx{spendTx1},
|
||||
})
|
||||
spendHeight = height
|
||||
} else {
|
||||
// Otherwise we just create an empty block.
|
||||
block = btcutil.NewBlock(&wire.MsgBlock{})
|
||||
}
|
||||
|
||||
err = n.ConnectTip(
|
||||
block.Hash(), height, block.Transactions(),
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatalf("unable to connect block: %v", err)
|
||||
}
|
||||
if err := n.NotifyHeight(height); err != nil {
|
||||
t.Fatalf("unable to dispatch notifications: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
// Check that the height hint was set to the spending block.
|
||||
op1Hint, err := hintCache.QuerySpendHint(
|
||||
ntfn1.HistoricalDispatch.SpendRequest,
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatalf("unable to query for spend hint of op1: %v", err)
|
||||
}
|
||||
if op1Hint != spendHeight {
|
||||
t.Fatalf("expected hint %d, got %d", spendHeight, op1Hint)
|
||||
}
|
||||
|
||||
// We should be getting notified about the spend at this point.
|
||||
select {
|
||||
case <-ntfn1.Event.Spend:
|
||||
default:
|
||||
t.Fatal("expected to receive spend notification")
|
||||
}
|
||||
|
||||
// Now, we'll simulate that the historical rescan finished by
|
||||
// calling UpdateSpendDetails. Since a the spend actually happened at
|
||||
// tip while the rescan was in progress, the height hint should not be
|
||||
// updated to the latest height, but stay at the spend height.
|
||||
err = n.UpdateSpendDetails(ntfn1.HistoricalDispatch.SpendRequest, nil)
|
||||
if err != nil {
|
||||
t.Fatalf("unable to update spend details: %v", err)
|
||||
}
|
||||
|
||||
op1Hint, err = hintCache.QuerySpendHint(
|
||||
ntfn1.HistoricalDispatch.SpendRequest,
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatalf("unable to query for spend hint of op1: %v", err)
|
||||
}
|
||||
if op1Hint != spendHeight {
|
||||
t.Fatalf("expected hint %d, got %d", spendHeight, op1Hint)
|
||||
}
|
||||
|
||||
// Then, we'll create another block that spends a second outpoint.
|
||||
op2 := wire.OutPoint{Index: 2}
|
||||
spendTx2 := wire.NewMsgTx(2)
|
||||
spendTx2.AddTxIn(&wire.TxIn{
|
||||
PreviousOutPoint: op2,
|
||||
SignatureScript: testSigScript,
|
||||
})
|
||||
height++
|
||||
block2 := btcutil.NewBlock(&wire.MsgBlock{
|
||||
Transactions: []*wire.MsgTx{spendTx2},
|
||||
})
|
||||
err = n.ConnectTip(block2.Hash(), height, block2.Transactions())
|
||||
if err != nil {
|
||||
t.Fatalf("unable to connect block: %v", err)
|
||||
}
|
||||
if err := n.NotifyHeight(height); err != nil {
|
||||
t.Fatalf("unable to dispatch notifications: %v", err)
|
||||
}
|
||||
|
||||
// The outpoint's spend hint should remain the same as it's already
|
||||
// been spent before.
|
||||
op1Hint, err = hintCache.QuerySpendHint(ntfn1.HistoricalDispatch.SpendRequest)
|
||||
if err != nil {
|
||||
t.Fatalf("unable to query for spend hint of op1: %v", err)
|
||||
}
|
||||
if op1Hint != spendHeight {
|
||||
t.Fatalf("expected hint %d, got %d", spendHeight, op1Hint)
|
||||
}
|
||||
|
||||
// Now mine enough blocks for the spend notification to be forgotten.
|
||||
for i := 0; i < 2*reorgSafety; i++ {
|
||||
height++
|
||||
block := btcutil.NewBlock(&wire.MsgBlock{})
|
||||
|
||||
err := n.ConnectTip(
|
||||
block.Hash(), height, block.Transactions(),
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatalf("unable to connect block: %v", err)
|
||||
}
|
||||
if err := n.NotifyHeight(height); err != nil {
|
||||
t.Fatalf("unable to dispatch notifications: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
// Attempting to update spend details at this point should fail, since
|
||||
// the spend request should be removed. This is to ensure the height
|
||||
// hint won't be overwritten if the historical rescan finishes after
|
||||
// the spend request has been notified and removed because it has
|
||||
// matured.
|
||||
err = n.UpdateSpendDetails(ntfn1.HistoricalDispatch.SpendRequest, nil)
|
||||
if err == nil {
|
||||
t.Fatalf("expcted updating spend details to fail")
|
||||
}
|
||||
|
||||
// Finally, check that the height hint is still there, unchanged.
|
||||
op1Hint, err = hintCache.QuerySpendHint(ntfn1.HistoricalDispatch.SpendRequest)
|
||||
if err != nil {
|
||||
t.Fatalf("unable to query for spend hint of op1: %v", err)
|
||||
}
|
||||
if op1Hint != spendHeight {
|
||||
t.Fatalf("expected hint %d, got %d", spendHeight, op1Hint)
|
||||
}
|
||||
}
|
||||
|
||||
// TestTxNotifierNtfnDone ensures that a notification is sent to registered
|
||||
// clients through the Done channel once the notification request is no longer
|
||||
// under the risk of being reorged out of the chain.
|
||||
|
Loading…
Reference in New Issue
Block a user