chainntnfs/txnotifier_test: add TestTxNotifierSpendDuringHistoricalRescan
This commit is contained in:
parent
e8b306b0ff
commit
f0048bb662
@ -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
|
// TestTxNotifierNtfnDone ensures that a notification is sent to registered
|
||||||
// clients through the Done channel once the notification request is no longer
|
// clients through the Done channel once the notification request is no longer
|
||||||
// under the risk of being reorged out of the chain.
|
// under the risk of being reorged out of the chain.
|
||||||
|
Loading…
Reference in New Issue
Block a user