• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

lightningnetwork / lnd / 15924767183

27 Jun 2025 11:04AM UTC coverage: 67.597% (-0.009%) from 67.606%
15924767183

Pull #9878

github

web-flow
Merge f0b6b70e2 into 6290edf14
Pull Request #9878: chainntfns: add option to send all confirmations

115 of 139 new or added lines in 3 files covered. (82.73%)

102 existing lines in 19 files now uncovered.

135139 of 199919 relevant lines covered (67.6%)

21914.19 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

77.51
/chainntnfs/test/test_interface.go
1
//go:build dev
2
// +build dev
3

4
package chainntnfstest
5

6
import (
7
        "bytes"
8
        "fmt"
9
        "log"
10
        "sync"
11
        "testing"
12
        "time"
13

14
        "github.com/btcsuite/btcd/btcutil"
15
        "github.com/btcsuite/btcd/chaincfg/chainhash"
16
        "github.com/btcsuite/btcd/integration/rpctest"
17
        "github.com/btcsuite/btcd/rpcclient"
18
        "github.com/btcsuite/btcd/wire"
19
        "github.com/btcsuite/btcwallet/chain"
20
        _ "github.com/btcsuite/btcwallet/walletdb/bdb" // Required to auto-register the boltdb walletdb implementation.
21
        "github.com/lightninglabs/neutrino"
22
        "github.com/lightningnetwork/lnd/blockcache"
23
        "github.com/lightningnetwork/lnd/chainntnfs"
24
        "github.com/lightningnetwork/lnd/chainntnfs/bitcoindnotify"
25
        "github.com/lightningnetwork/lnd/chainntnfs/btcdnotify"
26
        "github.com/lightningnetwork/lnd/chainntnfs/neutrinonotify"
27
        "github.com/lightningnetwork/lnd/channeldb"
28
        "github.com/lightningnetwork/lnd/lntest/unittest"
29
        "github.com/lightningnetwork/lnd/lnutils"
30
        "github.com/stretchr/testify/require"
31
)
32

33
// assertTxIndexInBlock verifies that the transaction at the given index in the
34
// block matches the expected txid.
35
func assertTxIndexInBlock(t *testing.T, miner *rpctest.Harness,
36
        blockHash *chainhash.Hash, txIndex uint32,
37
        expectedTxid *chainhash.Hash) {
88✔
38

88✔
39
        t.Helper()
88✔
40

88✔
41
        msgBlock, err := miner.Client.GetBlock(blockHash)
88✔
42
        if err != nil {
88✔
NEW
43
                t.Fatalf("unable to fetch block: %v", err)
×
NEW
44
        }
×
45
        block := btcutil.NewBlock(msgBlock)
88✔
46
        specifiedTxHash, err := block.TxHash(int(txIndex))
88✔
47
        if err != nil {
88✔
NEW
48
                t.Fatalf("unable to index into block: %v", err)
×
NEW
49
        }
×
50
        if !specifiedTxHash.IsEqual(expectedTxid) {
88✔
NEW
51
                t.Fatalf("mismatched tx indexes: expected %v, got %v",
×
NEW
52
                        expectedTxid, specifiedTxHash)
×
NEW
53
        }
×
54
}
55

56
func testSingleConfirmationNotification(miner *rpctest.Harness,
57
        notifier chainntnfs.TestChainNotifier, scriptDispatch bool, t *testing.T) {
8✔
58

8✔
59
        // We'd like to test the case of being notified once a txid reaches
8✔
60
        // a *single* confirmation.
8✔
61
        //
8✔
62
        // So first, let's send some coins to "ourself", obtaining a txid.
8✔
63
        // We're spending from a coinbase output here, so we use the dedicated
8✔
64
        // function.
8✔
65
        txid, pkScript, err := chainntnfs.GetTestTxidAndScript(miner)
8✔
66
        require.NoError(t, err, "unable to create test tx")
8✔
67
        err = chainntnfs.WaitForMempoolTx(miner, txid)
8✔
68
        require.NoError(t, err, "tx not relayed to miner")
8✔
69

8✔
70
        _, currentHeight, err := miner.Client.GetBestBlock()
8✔
71
        require.NoError(t, err, "unable to get current height")
8✔
72

8✔
73
        // Now that we have a txid, register a confirmation notification with
8✔
74
        // the chainntfn source.
8✔
75
        numConfs := uint32(1)
8✔
76
        var confIntent *chainntnfs.ConfirmationEvent
8✔
77
        if scriptDispatch {
12✔
78
                confIntent, err = notifier.RegisterConfirmationsNtfn(
4✔
79
                        nil, pkScript, numConfs, uint32(currentHeight),
4✔
80
                )
4✔
81
        } else {
8✔
82
                confIntent, err = notifier.RegisterConfirmationsNtfn(
4✔
83
                        txid, pkScript, numConfs, uint32(currentHeight),
4✔
84
                )
4✔
85
        }
4✔
86
        require.NoError(t, err, "unable to register ntfn")
8✔
87

8✔
88
        // Now generate a single block, the transaction should be included which
8✔
89
        // should trigger a notification event.
8✔
90
        blockHash, err := miner.Client.Generate(1)
8✔
91
        require.NoError(t, err, "unable to generate single block")
8✔
92

8✔
93
        // Assert the above tx is mined in the block.
8✔
94
        block, err := miner.Client.GetBlock(blockHash[0])
8✔
95
        require.NoError(t, err)
8✔
96
        require.Len(t, block.Transactions, 2, "block does not contain tx")
8✔
97

8✔
98
        select {
8✔
99
        case confInfo := <-confIntent.Confirmed:
8✔
100
                if !confInfo.BlockHash.IsEqual(blockHash[0]) {
8✔
101
                        t.Fatalf("mismatched block hashes: expected %v, got %v",
×
102
                                blockHash[0], confInfo.BlockHash)
×
103
                }
×
104

105
                // Finally, we'll verify that the tx index returned is the exact
106
                // same as the tx index of the transaction within the block
107
                // itself.
108
                assertTxIndexInBlock(
8✔
109
                        t, miner, blockHash[0], confInfo.TxIndex, txid,
8✔
110
                )
8✔
111
                require.Zero(t, confInfo.NumConfsLeft)
8✔
112

UNCOV
113
        case <-time.After(20 * time.Second):
×
114
                t.Fatalf("confirmation notification never received")
×
115
        }
116
}
117

118
func testMultiConfirmationNotification(miner *rpctest.Harness,
119
        notifier chainntnfs.TestChainNotifier, scriptDispatch bool, t *testing.T) {
8✔
120

8✔
121
        // We'd like to test the case of being notified once a txid reaches
8✔
122
        // N confirmations, where N > 1.
8✔
123
        //
8✔
124
        // Again, we'll begin by creating a fresh transaction, so we can obtain
8✔
125
        // a fresh txid.
8✔
126
        txid, pkScript, err := chainntnfs.GetTestTxidAndScript(miner)
8✔
127
        require.NoError(t, err, "unable to create test addr")
8✔
128
        if err := chainntnfs.WaitForMempoolTx(miner, txid); err != nil {
8✔
129
                t.Fatalf("tx not relayed to miner: %v", err)
×
130
        }
×
131

132
        _, currentHeight, err := miner.Client.GetBestBlock()
8✔
133
        require.NoError(t, err, "unable to get current height")
8✔
134

8✔
135
        numConfs := uint32(6)
8✔
136
        var confIntent *chainntnfs.ConfirmationEvent
8✔
137

8✔
138
        // We wish to receive all confirmations for the target transaction.
8✔
139
        if scriptDispatch {
12✔
140
                confIntent, err = notifier.RegisterConfirmationsNtfn(
4✔
141
                        nil, pkScript, numConfs, uint32(currentHeight),
4✔
142
                        chainntnfs.WithAllConfirmations(),
4✔
143
                )
4✔
144
        } else {
8✔
145
                confIntent, err = notifier.RegisterConfirmationsNtfn(
4✔
146
                        txid, pkScript, numConfs, uint32(currentHeight),
4✔
147
                        chainntnfs.WithAllConfirmations(),
4✔
148
                )
4✔
149
        }
4✔
150
        require.NoError(t, err, "unable to register ntfn")
8✔
151

8✔
152
        // Now generate a six blocks. The transaction should be included in the
8✔
153
        // first block, which will be built upon by the other 5 blocks.
8✔
154
        blockHash, err := miner.Client.Generate(6)
8✔
155
        if err != nil {
8✔
156
                t.Fatalf("unable to generate single block: %v", err)
×
157
        }
×
158

159
        // TODO(roasbeef): reduce all timeouts after neutrino sync tightended
160
        // up
161

162
        // Since we have registered for all confirmation events, we should
163
        // receive a confirmation notification for every confirmation of the
164
        // targeted transaction.
165
        for i := uint32(1); i <= numConfs; i++ {
56✔
166
                select {
48✔
167
                case txConf := <-confIntent.Confirmed:
48✔
168
                        // we'll verify that the tx index returned is the exact
48✔
169
                        // same as the tx index of the transaction within the
48✔
170
                        // block itself.
48✔
171
                        assertTxIndexInBlock(
48✔
172
                                t, miner, blockHash[0], txConf.TxIndex, txid,
48✔
173
                        )
48✔
174

48✔
175
                        // We'll also ensure that the block height has been set
48✔
176
                        // properly.
48✔
177
                        if txConf.BlockHeight != uint32(currentHeight+1) {
48✔
NEW
178
                                t.Fatalf("incorrect block height: expected "+
×
NEW
179
                                        "%v, got %v", txConf.BlockHeight,
×
NEW
180
                                        currentHeight)
×
NEW
181
                        }
×
182
                        require.Equal(t, numConfs-i, txConf.NumConfsLeft)
48✔
183

NEW
184
                case <-time.After(20 * time.Second):
×
NEW
185
                        t.Fatalf("confirmation notification never received")
×
186
                }
187
        }
188
}
189

190
func testBatchConfirmationNotification(miner *rpctest.Harness,
191
        notifier chainntnfs.TestChainNotifier, scriptDispatch bool, t *testing.T) {
8✔
192

8✔
193
        // We'd like to test a case of serving notifications to multiple
8✔
194
        // clients, each requesting to be notified once a txid receives
8✔
195
        // various numbers of confirmations.
8✔
196
        confSpread := [6]uint32{1, 2, 3, 6, 20, 22}
8✔
197
        confIntents := make([]*chainntnfs.ConfirmationEvent, len(confSpread))
8✔
198

8✔
199
        _, currentHeight, err := miner.Client.GetBestBlock()
8✔
200
        require.NoError(t, err, "unable to get current height")
8✔
201

8✔
202
        // Create a new txid spending miner coins for each confirmation entry
8✔
203
        // in confSpread, we collect each conf intent into a slice so we can
8✔
204
        // verify they're each notified at the proper number of confirmations
8✔
205
        // below.
8✔
206
        for i, numConfs := range confSpread {
56✔
207
                // All the clients with an even index will ask for the block
48✔
208
                // along side the conf ntfn.
48✔
209
                var opts []chainntnfs.NotifierOption
48✔
210
                if i%2 == 0 {
72✔
211
                        opts = append(opts, chainntnfs.WithIncludeBlock())
24✔
212
                }
24✔
213

214
                txid, pkScript, err := chainntnfs.GetTestTxidAndScript(miner)
48✔
215
                if err != nil {
48✔
216
                        t.Fatalf("unable to create test addr: %v", err)
×
217
                }
×
218
                var confIntent *chainntnfs.ConfirmationEvent
48✔
219
                if scriptDispatch {
72✔
220
                        confIntent, err = notifier.RegisterConfirmationsNtfn(
24✔
221
                                nil, pkScript, numConfs, uint32(currentHeight),
24✔
222
                                opts...,
24✔
223
                        )
24✔
224
                } else {
48✔
225
                        confIntent, err = notifier.RegisterConfirmationsNtfn(
24✔
226
                                txid, pkScript, numConfs, uint32(currentHeight),
24✔
227
                                opts...,
24✔
228
                        )
24✔
229
                }
24✔
230
                if err != nil {
48✔
231
                        t.Fatalf("unable to register ntfn: %v", err)
×
232
                }
×
233
                confIntents[i] = confIntent
48✔
234
                if err := chainntnfs.WaitForMempoolTx(miner, txid); err != nil {
48✔
235
                        t.Fatalf("tx not relayed to miner: %v", err)
×
236
                }
×
237

238
        }
239

240
        initialConfHeight := uint32(currentHeight + 1)
8✔
241

8✔
242
        // Now, for each confirmation intent, generate the delta number of blocks
8✔
243
        // needed to trigger the confirmation notification. A goroutine is
8✔
244
        // spawned in order to verify the proper notification is triggered.
8✔
245
        for i, numConfs := range confSpread {
56✔
246
                var blocksToGen uint32
48✔
247

48✔
248
                // If this is the last instance, manually index to generate the
48✔
249
                // proper block delta in order to avoid a panic.
48✔
250
                if i == len(confSpread)-1 {
56✔
251
                        blocksToGen = confSpread[len(confSpread)-1] - confSpread[len(confSpread)-2]
8✔
252
                } else {
48✔
253
                        blocksToGen = confSpread[i+1] - confSpread[i]
40✔
254
                }
40✔
255

256
                // Generate the number of blocks necessary to trigger this
257
                // current confirmation notification.
258
                if _, err := miner.Client.Generate(blocksToGen); err != nil {
48✔
259
                        t.Fatalf("unable to generate single block: %v", err)
×
260
                }
×
261

262
                select {
48✔
263
                case conf := <-confIntents[i].Confirmed:
48✔
264
                        // All of the notifications above were originally
48✔
265
                        // confirmed in the same block. The returned
48✔
266
                        // notification should list the initial confirmation
48✔
267
                        // height rather than the height they were _fully_
48✔
268
                        // confirmed.
48✔
269
                        if conf.BlockHeight != initialConfHeight {
48✔
270
                                t.Fatalf("notification has incorrect initial "+
×
271
                                        "conf height: expected %v, got %v",
×
272
                                        initialConfHeight, conf.BlockHeight)
×
273
                        }
×
274

275
                        // If this is an even client index, then we expect the
276
                        // block to be populated. Otherwise, it should be
277
                        // empty.
278
                        expectBlock := i%2 == 0
48✔
279
                        require.Equal(t, expectBlock, conf.Block != nil)
48✔
280
                        require.Zero(t, conf.NumConfsLeft)
48✔
281

282
                case <-time.After(20 * time.Second):
×
283
                        t.Fatalf("confirmation notification never received: %v", numConfs)
×
284
                }
285
        }
286
}
287

288
func checkNotificationFields(ntfn *chainntnfs.SpendDetail,
289
        outpoint *wire.OutPoint, spenderSha *chainhash.Hash,
290
        height int32, t *testing.T) {
96✔
291

96✔
292
        t.Helper()
96✔
293

96✔
294
        if *ntfn.SpentOutPoint != *outpoint {
96✔
295
                t.Fatalf("ntfn includes wrong output, reports "+
×
296
                        "%v instead of %v",
×
297
                        ntfn.SpentOutPoint, outpoint)
×
298
        }
×
299
        if !bytes.Equal(ntfn.SpenderTxHash[:], spenderSha[:]) {
96✔
300
                t.Fatalf("ntfn includes wrong spender tx sha, "+
×
301
                        "reports %v instead of %v",
×
302
                        ntfn.SpenderTxHash[:], spenderSha[:])
×
303
        }
×
304
        if ntfn.SpenderInputIndex != 0 {
96✔
305
                t.Fatalf("ntfn includes wrong spending input "+
×
306
                        "index, reports %v, should be %v",
×
307
                        ntfn.SpenderInputIndex, 0)
×
308
        }
×
309
        if ntfn.SpendingHeight != height {
96✔
310
                t.Fatalf("ntfn has wrong spending height: "+
×
311
                        "expected %v, got %v", height,
×
312
                        ntfn.SpendingHeight)
×
313
        }
×
314
}
315

316
func testSpendNotification(miner *rpctest.Harness,
317
        notifier chainntnfs.TestChainNotifier, scriptDispatch bool, t *testing.T) {
8✔
318

8✔
319
        // We'd like to test the spend notifications for all ChainNotifier
8✔
320
        // concrete implementations.
8✔
321
        //
8✔
322
        // To do so, we first create a new output to our test target address.
8✔
323
        outpoint, output, privKey := chainntnfs.CreateSpendableOutput(t, miner)
8✔
324

8✔
325
        _, currentHeight, err := miner.Client.GetBestBlock()
8✔
326
        require.NoError(t, err, "unable to get current height")
8✔
327

8✔
328
        // Now that we have an output index and the pkScript, register for a
8✔
329
        // spentness notification for the newly created output with multiple
8✔
330
        // clients in order to ensure the implementation can support
8✔
331
        // multi-client spend notifications.
8✔
332
        const numClients = 5
8✔
333
        spendClients := make([]*chainntnfs.SpendEvent, numClients)
8✔
334
        for i := 0; i < numClients; i++ {
48✔
335
                var spentIntent *chainntnfs.SpendEvent
40✔
336
                if scriptDispatch {
60✔
337
                        spentIntent, err = notifier.RegisterSpendNtfn(
20✔
338
                                nil, output.PkScript, uint32(currentHeight),
20✔
339
                        )
20✔
340
                } else {
40✔
341
                        spentIntent, err = notifier.RegisterSpendNtfn(
20✔
342
                                outpoint, output.PkScript, uint32(currentHeight),
20✔
343
                        )
20✔
344
                }
20✔
345
                if err != nil {
40✔
346
                        t.Fatalf("unable to register for spend ntfn: %v", err)
×
347
                }
×
348

349
                spendClients[i] = spentIntent
40✔
350
        }
351

352
        // Next, create a new transaction spending that output.
353
        spendingTx := chainntnfs.CreateSpendTx(t, outpoint, output, privKey)
8✔
354

8✔
355
        // Broadcast our spending transaction.
8✔
356
        spenderSha, err := miner.Client.SendRawTransaction(spendingTx, true)
8✔
357
        require.NoError(t, err, "unable to broadcast tx")
8✔
358

8✔
359
        if err := chainntnfs.WaitForMempoolTx(miner, spenderSha); err != nil {
8✔
360
                t.Fatalf("tx not relayed to miner: %v", err)
×
361
        }
×
362

363
        // Make sure notifications are not yet sent. We launch a go routine for
364
        // all the spend clients, such that we can wait for them all in
365
        // parallel.
366
        mempoolSpendTimeout := 2 * chainntnfs.TrickleInterval
8✔
367
        mempoolSpends := make(chan *chainntnfs.SpendDetail, numClients)
8✔
368
        for _, c := range spendClients {
48✔
369
                go func(client *chainntnfs.SpendEvent) {
80✔
370
                        select {
40✔
371
                        case s := <-client.Spend:
×
372
                                mempoolSpends <- s
×
373
                        case <-time.After(mempoolSpendTimeout):
40✔
374
                        }
375
                }(c)
376
        }
377

378
        select {
8✔
379
        case <-mempoolSpends:
×
380
                t.Fatalf("did not expect to get notification before " +
×
381
                        "block was mined")
×
382
        case <-time.After(mempoolSpendTimeout):
8✔
383
        }
384

385
        // Make sure registering a client after the tx is in the mempool still
386
        // doesn't trigger a notification.
387
        var spentIntent *chainntnfs.SpendEvent
8✔
388
        if scriptDispatch {
12✔
389
                spentIntent, err = notifier.RegisterSpendNtfn(
4✔
390
                        nil, output.PkScript, uint32(currentHeight),
4✔
391
                )
4✔
392
        } else {
8✔
393
                spentIntent, err = notifier.RegisterSpendNtfn(
4✔
394
                        outpoint, output.PkScript, uint32(currentHeight),
4✔
395
                )
4✔
396
        }
4✔
397
        require.NoError(t, err, "unable to register for spend ntfn")
8✔
398

8✔
399
        select {
8✔
400
        case <-spentIntent.Spend:
×
401
                t.Fatalf("did not expect to get notification before " +
×
402
                        "block was mined")
×
403
        case <-time.After(mempoolSpendTimeout):
8✔
404
        }
405
        spendClients = append(spendClients, spentIntent)
8✔
406

8✔
407
        // Now we mine a single block, which should include our spend. The
8✔
408
        // notification should also be sent off.
8✔
409
        if _, err := miner.Client.Generate(1); err != nil {
8✔
410
                t.Fatalf("unable to generate single block: %v", err)
×
411
        }
×
412

413
        _, currentHeight, err = miner.Client.GetBestBlock()
8✔
414
        require.NoError(t, err, "unable to get current height")
8✔
415

8✔
416
        for _, c := range spendClients {
56✔
417
                select {
48✔
418
                case ntfn := <-c.Spend:
48✔
419
                        // We've received the spend nftn. So now verify all the
48✔
420
                        // fields have been set properly.
48✔
421
                        checkNotificationFields(ntfn, outpoint, spenderSha,
48✔
422
                                currentHeight, t)
48✔
423
                case <-time.After(30 * time.Second):
×
424
                        t.Fatalf("spend ntfn never received")
×
425
                }
426
        }
427
}
428

429
func testBlockEpochNotification(miner *rpctest.Harness,
430
        notifier chainntnfs.TestChainNotifier, t *testing.T) {
4✔
431

4✔
432
        // We'd like to test the case of multiple registered clients receiving
4✔
433
        // block epoch notifications.
4✔
434
        const numBlocks = 10
4✔
435
        const numNtfns = numBlocks + 1
4✔
436
        const numClients = 5
4✔
437
        var wg sync.WaitGroup
4✔
438

4✔
439
        // Create numClients clients which will listen for block notifications. We
4✔
440
        // expect each client to receive 11 notifications, one for the current
4✔
441
        // tip of the chain, and one for each of the ten blocks we generate
4✔
442
        // below. So we'll use a WaitGroup to synchronize the test.
4✔
443
        clientErrors := make(chan error, numClients)
4✔
444
        for i := 0; i < numClients; i++ {
24✔
445
                epochClient, err := notifier.RegisterBlockEpochNtfn(nil)
20✔
446
                if err != nil {
20✔
447
                        t.Fatalf("unable to register for epoch notification")
×
448
                }
×
449

450
                wg.Add(numNtfns)
20✔
451
                go func() {
40✔
452
                        for i := 0; i < numNtfns; i++ {
240✔
453
                                // Ensure that each block epoch has a header,
220✔
454
                                // and that header matches the contained header
220✔
455
                                // hash.
220✔
456
                                blockEpoch := <-epochClient.Epochs
220✔
457
                                if blockEpoch.BlockHeader == nil {
220✔
458
                                        t.Logf("%d", i)
×
459
                                        clientErrors <- fmt.Errorf("block " +
×
460
                                                "header is nil")
×
461
                                        return
×
462
                                }
×
463
                                if blockEpoch.BlockHeader.BlockHash() !=
220✔
464
                                        *blockEpoch.Hash {
220✔
465

×
466
                                        clientErrors <- fmt.Errorf("block " +
×
467
                                                "header hash mismatch")
×
468
                                        return
×
469
                                }
×
470

471
                                wg.Done()
220✔
472
                        }
473
                }()
474
        }
475

476
        epochsSent := make(chan struct{})
4✔
477
        go func() {
8✔
478
                wg.Wait()
4✔
479
                close(epochsSent)
4✔
480
        }()
4✔
481

482
        // Now generate 10 blocks, the clients above should each receive 10
483
        // notifications, thereby unblocking the goroutine above.
484
        if _, err := miner.Client.Generate(numBlocks); err != nil {
4✔
485
                t.Fatalf("unable to generate blocks: %v", err)
×
486
        }
×
487

488
        select {
4✔
489
        case err := <-clientErrors:
×
490
                t.Fatalf("block epoch case failed: %v", err)
×
491
        case <-epochsSent:
4✔
492
        case <-time.After(30 * time.Second):
×
493
                t.Fatalf("all notifications not sent")
×
494
        }
495
}
496

497
func testMultiClientConfirmationNotification(miner *rpctest.Harness,
498
        notifier chainntnfs.TestChainNotifier, scriptDispatch bool, t *testing.T) {
8✔
499

8✔
500
        // We'd like to test the case of a multiple clients registered to
8✔
501
        // receive a confirmation notification for the same transaction.
8✔
502
        txid, pkScript, err := chainntnfs.GetTestTxidAndScript(miner)
8✔
503
        require.NoError(t, err, "unable to create test tx")
8✔
504
        if err := chainntnfs.WaitForMempoolTx(miner, txid); err != nil {
8✔
505
                t.Fatalf("tx not relayed to miner: %v", err)
×
506
        }
×
507

508
        var wg sync.WaitGroup
8✔
509
        const (
8✔
510
                numConfsClients = 5
8✔
511
                numConfs        = 1
8✔
512
        )
8✔
513

8✔
514
        _, currentHeight, err := miner.Client.GetBestBlock()
8✔
515
        require.NoError(t, err, "unable to get current height")
8✔
516

8✔
517
        // Register for a conf notification for the above generated txid with
8✔
518
        // numConfsClients distinct clients.
8✔
519
        for i := 0; i < numConfsClients; i++ {
48✔
520
                var confClient *chainntnfs.ConfirmationEvent
40✔
521
                if scriptDispatch {
60✔
522
                        confClient, err = notifier.RegisterConfirmationsNtfn(
20✔
523
                                nil, pkScript, numConfs, uint32(currentHeight),
20✔
524
                        )
20✔
525
                } else {
40✔
526
                        confClient, err = notifier.RegisterConfirmationsNtfn(
20✔
527
                                txid, pkScript, numConfs, uint32(currentHeight),
20✔
528
                        )
20✔
529
                }
20✔
530
                if err != nil {
40✔
531
                        t.Fatalf("unable to register for confirmation: %v", err)
×
532
                }
×
533

534
                wg.Add(1)
40✔
535
                go func() {
80✔
536
                        <-confClient.Confirmed
40✔
537
                        wg.Done()
40✔
538
                }()
40✔
539
        }
540

541
        confsSent := make(chan struct{})
8✔
542
        go func() {
16✔
543
                wg.Wait()
8✔
544
                close(confsSent)
8✔
545
        }()
8✔
546

547
        // Finally, generate a single block which should trigger the unblocking
548
        // of all numConfsClients blocked on the channel read above.
549
        if _, err := miner.Client.Generate(1); err != nil {
8✔
550
                t.Fatalf("unable to generate block: %v", err)
×
551
        }
×
552

553
        select {
8✔
554
        case <-confsSent:
8✔
555
        case <-time.After(30 * time.Second):
×
556
                t.Fatalf("all confirmation notifications not sent")
×
557
        }
558
}
559

560
// Tests the case in which a confirmation notification is requested for a
561
// transaction that has already been included in a block. In this case, the
562
// confirmation notification should be dispatched immediately.
563
func testTxConfirmedBeforeNtfnRegistration(miner *rpctest.Harness,
564
        notifier chainntnfs.TestChainNotifier, scriptDispatch bool, t *testing.T) {
8✔
565

8✔
566
        // First, let's send some coins to "ourself", obtaining a txid.  We're
8✔
567
        // spending from a coinbase output here, so we use the dedicated
8✔
568
        // function.
8✔
569
        txid3, pkScript3, err := chainntnfs.GetTestTxidAndScript(miner)
8✔
570
        require.NoError(t, err, "unable to create test tx")
8✔
571
        if err := chainntnfs.WaitForMempoolTx(miner, txid3); err != nil {
8✔
572
                t.Fatalf("tx not relayed to miner: %v", err)
×
573
        }
×
574

575
        // Generate another block containing tx 3, but we won't register conf
576
        // notifications for this tx until much later. The notifier must check
577
        // older blocks when the confirmation event is registered below to ensure
578
        // that the TXID hasn't already been included in the chain, otherwise the
579
        // notification will never be sent.
580
        _, err = miner.Client.Generate(1)
8✔
581
        require.NoError(t, err, "unable to generate block")
8✔
582

8✔
583
        txid1, pkScript1, err := chainntnfs.GetTestTxidAndScript(miner)
8✔
584
        require.NoError(t, err, "unable to create test tx")
8✔
585
        if err := chainntnfs.WaitForMempoolTx(miner, txid1); err != nil {
8✔
586
                t.Fatalf("tx not relayed to miner: %v", err)
×
587
        }
×
588

589
        txid2, pkScript2, err := chainntnfs.GetTestTxidAndScript(miner)
8✔
590
        require.NoError(t, err, "unable to create test tx")
8✔
591
        if err := chainntnfs.WaitForMempoolTx(miner, txid2); err != nil {
8✔
592
                t.Fatalf("tx not relayed to miner: %v", err)
×
593
        }
×
594

595
        _, currentHeight, err := miner.Client.GetBestBlock()
8✔
596
        require.NoError(t, err, "unable to get current height")
8✔
597

8✔
598
        // Now generate another block containing txs 1 & 2.
8✔
599
        blockHash, err := miner.Client.Generate(1)
8✔
600
        require.NoError(t, err, "unable to generate block")
8✔
601

8✔
602
        // Register a confirmation notification with the chainntfn source for tx2,
8✔
603
        // which is included in the last block. The height hint is the height before
8✔
604
        // the block is included. This notification should fire immediately since
8✔
605
        // only 1 confirmation is required.
8✔
606
        var ntfn1 *chainntnfs.ConfirmationEvent
8✔
607
        if scriptDispatch {
12✔
608
                ntfn1, err = notifier.RegisterConfirmationsNtfn(
4✔
609
                        nil, pkScript1, 1, uint32(currentHeight),
4✔
610
                        chainntnfs.WithIncludeBlock(),
4✔
611
                )
4✔
612
        } else {
8✔
613
                ntfn1, err = notifier.RegisterConfirmationsNtfn(
4✔
614
                        txid1, pkScript1, 1, uint32(currentHeight),
4✔
615
                )
4✔
616
        }
4✔
617
        require.NoError(t, err, "unable to register ntfn")
8✔
618

8✔
619
        select {
8✔
620
        case confInfo := <-ntfn1.Confirmed:
8✔
621
                // Finally, we'll verify that the tx index returned is the exact
8✔
622
                // same as the tx index of the transaction within the block
8✔
623
                // itself.
8✔
624
                assertTxIndexInBlock(
8✔
625
                        t, miner, blockHash[0], confInfo.TxIndex, txid1,
8✔
626
                )
8✔
627

8✔
628
                // We'll also ensure that the block height has been set
8✔
629
                // properly.
8✔
630
                if confInfo.BlockHeight != uint32(currentHeight+1) {
8✔
631
                        t.Fatalf("incorrect block height: expected %v, got %v",
×
632
                                confInfo.BlockHeight, currentHeight)
×
633
                }
×
634

635
                // Ensure that if this was a script dispatch, the block is set
636
                // as well.
637
                if scriptDispatch {
12✔
638
                        require.NotNil(t, confInfo.Block)
4✔
639
                }
4✔
640

641
                require.Zero(t, confInfo.NumConfsLeft)
8✔
642
                break
8✔
643

644
        case <-time.After(20 * time.Second):
×
645
                t.Fatalf("confirmation notification never received")
×
646
        }
647

648
        // Register a confirmation notification for tx2, requiring 3 confirmations.
649
        // This transaction is only partially confirmed, so the notification should
650
        // not fire yet.
651
        var ntfn2 *chainntnfs.ConfirmationEvent
8✔
652

8✔
653
        // We wish to receive all confirmations for tx2.
8✔
654
        if scriptDispatch {
12✔
655
                ntfn2, err = notifier.RegisterConfirmationsNtfn(
4✔
656
                        nil, pkScript2, 3, uint32(currentHeight),
4✔
657
                        chainntnfs.WithIncludeBlock(),
4✔
658
                        chainntnfs.WithAllConfirmations(),
4✔
659
                )
4✔
660
        } else {
8✔
661
                ntfn2, err = notifier.RegisterConfirmationsNtfn(
4✔
662
                        txid2, pkScript2, 3, uint32(currentHeight),
4✔
663
                        chainntnfs.WithAllConfirmations(),
4✔
664
                )
4✔
665
        }
4✔
666
        require.NoError(t, err, "unable to register ntfn")
8✔
667

8✔
668
        // Fully confirm tx3.
8✔
669
        _, err = miner.Client.Generate(2)
8✔
670
        require.NoError(t, err, "unable to generate block")
8✔
671

8✔
672
        // Since we have registered for all confirmation events, we should
8✔
673
        // receive a confirmation notification for every confirmation of tx2.
8✔
674
        for i := uint32(1); i <= 3; i++ {
32✔
675
                select {
24✔
676
                case txConf := <-ntfn2.Confirmed:
24✔
677
                        // we'll verify that the tx index returned is the exact
24✔
678
                        // same as the tx index of the transaction within the
24✔
679
                        // block itself.
24✔
680
                        assertTxIndexInBlock(
24✔
681
                                t, miner, blockHash[0], txConf.TxIndex, txid2,
24✔
682
                        )
24✔
683

24✔
684
                        // We'll also ensure that the block height has been set
24✔
685
                        // properly.
24✔
686
                        if txConf.BlockHeight != uint32(currentHeight+1) {
24✔
NEW
687
                                t.Fatalf("incorrect block height: expected "+
×
NEW
688
                                        "%v, got %v", txConf.BlockHeight,
×
NEW
689
                                        currentHeight)
×
NEW
690
                        }
×
691

692
                        // Ensure that if this was a script dispatch, the block
693
                        // is set as well.
694
                        if scriptDispatch {
36✔
695
                                require.NotNil(t, txConf.Block)
12✔
696
                        }
12✔
697

698
                        require.Equal(t, 3-i, txConf.NumConfsLeft)
24✔
699

24✔
700
                        continue
24✔
701

NEW
702
                case <-time.After(10 * time.Second):
×
NEW
703
                        t.Fatalf("confirmation notification never received")
×
704
                }
705
        }
706

707
        select {
8✔
708
        case <-ntfn1.Confirmed:
×
709
                t.Fatalf("received multiple confirmations for tx")
×
710
        case <-time.After(1 * time.Second):
8✔
711
        }
712

713
        // Finally register a confirmation notification for tx3, requiring 1
714
        // confirmation. Ensure that conf notifications do not refire on txs
715
        // 1 or 2.
716
        var ntfn3 *chainntnfs.ConfirmationEvent
8✔
717
        if scriptDispatch {
12✔
718
                ntfn3, err = notifier.RegisterConfirmationsNtfn(
4✔
719
                        nil, pkScript3, 1, uint32(currentHeight-1),
4✔
720
                        chainntnfs.WithIncludeBlock(),
4✔
721
                )
4✔
722
        } else {
8✔
723
                ntfn3, err = notifier.RegisterConfirmationsNtfn(
4✔
724
                        txid3, pkScript3, 1, uint32(currentHeight-1),
4✔
725
                )
4✔
726
        }
4✔
727
        require.NoError(t, err, "unable to register ntfn")
8✔
728

8✔
729
        // We'll also register for a confirmation notification with the pkscript
8✔
730
        // of a different transaction. This notification shouldn't fire since we
8✔
731
        // match on both txid and pkscript.
8✔
732
        var ntfn4 *chainntnfs.ConfirmationEvent
8✔
733
        ntfn4, err = notifier.RegisterConfirmationsNtfn(
8✔
734
                txid3, pkScript2, 1, uint32(currentHeight-1),
8✔
735
        )
8✔
736
        require.NoError(t, err, "unable to register ntfn")
8✔
737

8✔
738
        select {
8✔
739
        case confInfo := <-ntfn3.Confirmed:
8✔
740
                if scriptDispatch {
12✔
741
                        require.NotNil(t, confInfo.Block)
4✔
742
                }
4✔
743
                require.Zero(t, confInfo.NumConfsLeft)
8✔
744

745
        case <-time.After(10 * time.Second):
×
746
                t.Fatalf("confirmation notification never received")
×
747
        }
748

749
        select {
8✔
750
        case <-ntfn4.Confirmed:
×
751
                t.Fatalf("confirmation notification received")
×
752
        case <-time.After(5 * time.Second):
8✔
753
        }
754

755
        time.Sleep(1 * time.Second)
8✔
756

8✔
757
        select {
8✔
758
        case <-ntfn1.Confirmed:
×
759
                t.Fatalf("received multiple confirmations for tx")
×
760
        default:
8✔
761
        }
762

763
        select {
8✔
764
        case <-ntfn2.Confirmed:
×
765
                t.Fatalf("received multiple confirmations for tx")
×
766
        default:
8✔
767
        }
768
}
769

770
// Test the case of a notification consumer having forget or being delayed in
771
// checking for a confirmation. This should not cause the notifier to stop
772
// working
773
func testLazyNtfnConsumer(miner *rpctest.Harness,
774
        notifier chainntnfs.TestChainNotifier, scriptDispatch bool, t *testing.T) {
8✔
775

8✔
776
        // Create a transaction to be notified about. We'll register for
8✔
777
        // notifications on this transaction but won't be prompt in checking them
8✔
778
        txid, pkScript, err := chainntnfs.GetTestTxidAndScript(miner)
8✔
779
        require.NoError(t, err, "unable to create test tx")
8✔
780
        if err := chainntnfs.WaitForMempoolTx(miner, txid); err != nil {
8✔
781
                t.Fatalf("tx not relayed to miner: %v", err)
×
782
        }
×
783

784
        _, currentHeight, err := miner.Client.GetBestBlock()
8✔
785
        require.NoError(t, err, "unable to get current height")
8✔
786

8✔
787
        numConfs := uint32(3)
8✔
788

8✔
789
        // Add a block right before registering, this makes race conditions
8✔
790
        // between the historical dispatcher and the normal dispatcher more obvious
8✔
791
        if _, err := miner.Client.Generate(1); err != nil {
8✔
792
                t.Fatalf("unable to generate blocks: %v", err)
×
793
        }
×
794

795
        var firstConfIntent *chainntnfs.ConfirmationEvent
8✔
796

8✔
797
        // We wish to receive all confirmations.
8✔
798
        if scriptDispatch {
12✔
799
                firstConfIntent, err = notifier.RegisterConfirmationsNtfn(
4✔
800
                        nil, pkScript, numConfs, uint32(currentHeight),
4✔
801
                        chainntnfs.WithAllConfirmations(),
4✔
802
                )
4✔
803
        } else {
8✔
804
                firstConfIntent, err = notifier.RegisterConfirmationsNtfn(
4✔
805
                        txid, pkScript, numConfs, uint32(currentHeight),
4✔
806
                        chainntnfs.WithAllConfirmations(),
4✔
807
                )
4✔
808
        }
4✔
809
        require.NoError(t, err, "unable to register ntfn")
8✔
810

8✔
811
        // Generate another 2 blocks, this should dispatch the confirm
8✔
812
        // notification twice for firstConfIntent since we're registered to
8✔
813
        // receive all confirmations.
8✔
814
        if _, err := miner.Client.Generate(2); err != nil {
8✔
815
                t.Fatalf("unable to generate blocks: %v", err)
×
816
        }
×
817

818
        // Now make another transaction, just because we haven't checked to see
819
        // if the first transaction has confirmed doesn't mean that we shouldn't
820
        // be able to see if this transaction confirms first
821
        txid, pkScript, err = chainntnfs.GetTestTxidAndScript(miner)
8✔
822
        require.NoError(t, err, "unable to create test tx")
8✔
823
        if err := chainntnfs.WaitForMempoolTx(miner, txid); err != nil {
8✔
824
                t.Fatalf("tx not relayed to miner: %v", err)
×
825
        }
×
826

827
        _, currentHeight, err = miner.Client.GetBestBlock()
8✔
828
        require.NoError(t, err, "unable to get current height")
8✔
829

8✔
830
        numConfs = 1
8✔
831
        var secondConfIntent *chainntnfs.ConfirmationEvent
8✔
832
        if scriptDispatch {
12✔
833
                secondConfIntent, err = notifier.RegisterConfirmationsNtfn(
4✔
834
                        nil, pkScript, numConfs, uint32(currentHeight),
4✔
835
                )
4✔
836
        } else {
8✔
837
                secondConfIntent, err = notifier.RegisterConfirmationsNtfn(
4✔
838
                        txid, pkScript, numConfs, uint32(currentHeight),
4✔
839
                )
4✔
840
        }
4✔
841
        require.NoError(t, err, "unable to register ntfn")
8✔
842

8✔
843
        if _, err := miner.Client.Generate(1); err != nil {
8✔
844
                t.Fatalf("unable to generate blocks: %v", err)
×
845
        }
×
846

847
        select {
8✔
848
        case txConf := <-secondConfIntent.Confirmed:
8✔
849
                // Successfully receive the second notification.
8✔
850
                require.Zero(t, txConf.NumConfsLeft)
8✔
851
                break
8✔
852

853
        case <-time.After(30 * time.Second):
×
854
                t.Fatalf("Second confirmation notification never received")
×
855
        }
856

857
        // Make sure the first tx confirmed successfully
858
        // Since we have registered for all confirmation events in
859
        // firstConfIntent, we should receive a confirmation notification for
860
        // every confirmation.
861
        for i := uint32(1); i <= 3; i++ {
32✔
862
                select {
24✔
863
                case txConf := <-firstConfIntent.Confirmed:
24✔
864
                        require.EqualValues(t, 3-i, txConf.NumConfsLeft)
24✔
865

NEW
866
                case <-time.After(30 * time.Second):
×
NEW
867
                        t.Fatalf("First confirmation notification never " +
×
NEW
868
                                "received")
×
869
                }
870
        }
871
}
872

873
// Tests the case in which a spend notification is requested for a spend that
874
// has already been included in a block. In this case, the spend notification
875
// should be dispatched immediately.
876
func testSpendBeforeNtfnRegistration(miner *rpctest.Harness,
877
        notifier chainntnfs.TestChainNotifier, scriptDispatch bool, t *testing.T) {
8✔
878

8✔
879
        // We'd like to test the spend notifications for all ChainNotifier
8✔
880
        // concrete implementations.
8✔
881
        //
8✔
882
        // To do so, we first create a new output to our test target address.
8✔
883
        outpoint, output, privKey := chainntnfs.CreateSpendableOutput(t, miner)
8✔
884

8✔
885
        _, heightHint, err := miner.Client.GetBestBlock()
8✔
886
        require.NoError(t, err, "unable to get current height")
8✔
887

8✔
888
        // We'll then spend this output and broadcast the spend transaction.
8✔
889
        spendingTx := chainntnfs.CreateSpendTx(t, outpoint, output, privKey)
8✔
890
        spenderSha, err := miner.Client.SendRawTransaction(spendingTx, true)
8✔
891
        require.NoError(t, err, "unable to broadcast tx")
8✔
892
        if err := chainntnfs.WaitForMempoolTx(miner, spenderSha); err != nil {
8✔
893
                t.Fatalf("tx not relayed to miner: %v", err)
×
894
        }
×
895

896
        // We create an epoch client we can use to make sure the notifier is
897
        // caught up to the mining node's chain.
898
        epochClient, err := notifier.RegisterBlockEpochNtfn(nil)
8✔
899
        require.NoError(t, err, "unable to register for block epoch")
8✔
900

8✔
901
        // Now we mine an additional block, which should include our spend.
8✔
902
        if _, err := miner.Client.Generate(1); err != nil {
8✔
903
                t.Fatalf("unable to generate single block: %v", err)
×
904
        }
×
905
        _, spendHeight, err := miner.Client.GetBestBlock()
8✔
906
        require.NoError(t, err, "unable to get current height")
8✔
907

8✔
908
        // checkSpends registers two clients to be notified of a spend that has
8✔
909
        // already happened. The notifier should dispatch a spend notification
8✔
910
        // immediately.
8✔
911
        checkSpends := func() {
24✔
912
                t.Helper()
16✔
913

16✔
914
                const numClients = 2
16✔
915
                spendClients := make([]*chainntnfs.SpendEvent, numClients)
16✔
916
                for i := 0; i < numClients; i++ {
48✔
917
                        var spentIntent *chainntnfs.SpendEvent
32✔
918
                        if scriptDispatch {
48✔
919
                                spentIntent, err = notifier.RegisterSpendNtfn(
16✔
920
                                        nil, output.PkScript, uint32(heightHint),
16✔
921
                                )
16✔
922
                        } else {
32✔
923
                                spentIntent, err = notifier.RegisterSpendNtfn(
16✔
924
                                        outpoint, output.PkScript,
16✔
925
                                        uint32(heightHint),
16✔
926
                                )
16✔
927
                        }
16✔
928
                        if err != nil {
32✔
929
                                t.Fatalf("unable to register for spend ntfn: %v",
×
930
                                        err)
×
931
                        }
×
932

933
                        spendClients[i] = spentIntent
32✔
934
                }
935

936
                for _, client := range spendClients {
48✔
937
                        select {
32✔
938
                        case ntfn := <-client.Spend:
32✔
939
                                // We've received the spend nftn. So now verify
32✔
940
                                // all the fields have been set properly.
32✔
941
                                checkNotificationFields(
32✔
942
                                        ntfn, outpoint, spenderSha, spendHeight, t,
32✔
943
                                )
32✔
944
                        case <-time.After(30 * time.Second):
×
945
                                t.Fatalf("spend ntfn never received")
×
946
                        }
947
                }
948
        }
949

950
        // Wait for the notifier to have caught up to the mined block.
951
        select {
8✔
952
        case _, ok := <-epochClient.Epochs:
8✔
953
                if !ok {
8✔
954
                        t.Fatalf("epoch channel was closed")
×
955
                }
×
956
        case <-time.After(15 * time.Second):
×
957
                t.Fatalf("did not receive block epoch")
×
958
        }
959

960
        // Check that the spend clients gets immediately notified for the spend
961
        // in the previous block.
962
        checkSpends()
8✔
963

8✔
964
        // Bury the spend even deeper, and do the same check.
8✔
965
        const numBlocks = 10
8✔
966
        if _, err := miner.Client.Generate(numBlocks); err != nil {
8✔
967
                t.Fatalf("unable to generate single block: %v", err)
×
968
        }
×
969

970
        // Wait for the notifier to have caught up with the new blocks.
971
        for i := 0; i < numBlocks; i++ {
88✔
972
                select {
80✔
973
                case _, ok := <-epochClient.Epochs:
80✔
974
                        if !ok {
80✔
975
                                t.Fatalf("epoch channel was closed")
×
976
                        }
×
977
                case <-time.After(15 * time.Second):
×
978
                        t.Fatalf("did not receive block epoch")
×
979
                }
980
        }
981

982
        // The clients should still be notified immediately.
983
        checkSpends()
8✔
984
}
985

986
func testCancelSpendNtfn(node *rpctest.Harness,
987
        notifier chainntnfs.TestChainNotifier, scriptDispatch bool, t *testing.T) {
8✔
988

8✔
989
        // We'd like to test that once a spend notification is registered, it
8✔
990
        // can be canceled before the notification is dispatched.
8✔
991

8✔
992
        // First, we'll start by creating a new output that we can spend
8✔
993
        // ourselves.
8✔
994
        outpoint, output, privKey := chainntnfs.CreateSpendableOutput(t, node)
8✔
995

8✔
996
        _, currentHeight, err := node.Client.GetBestBlock()
8✔
997
        require.NoError(t, err, "unable to get current height")
8✔
998

8✔
999
        // Create two clients that each registered to the spend notification.
8✔
1000
        // We'll cancel the notification for the first client and leave the
8✔
1001
        // notification for the second client enabled.
8✔
1002
        const numClients = 2
8✔
1003
        spendClients := make([]*chainntnfs.SpendEvent, numClients)
8✔
1004
        for i := 0; i < numClients; i++ {
24✔
1005
                var spentIntent *chainntnfs.SpendEvent
16✔
1006
                if scriptDispatch {
24✔
1007
                        spentIntent, err = notifier.RegisterSpendNtfn(
8✔
1008
                                nil, output.PkScript, uint32(currentHeight),
8✔
1009
                        )
8✔
1010
                } else {
16✔
1011
                        spentIntent, err = notifier.RegisterSpendNtfn(
8✔
1012
                                outpoint, output.PkScript, uint32(currentHeight),
8✔
1013
                        )
8✔
1014
                }
8✔
1015
                if err != nil {
16✔
1016
                        t.Fatalf("unable to register for spend ntfn: %v", err)
×
1017
                }
×
1018

1019
                spendClients[i] = spentIntent
16✔
1020
        }
1021

1022
        // Next, create a new transaction spending that output.
1023
        spendingTx := chainntnfs.CreateSpendTx(t, outpoint, output, privKey)
8✔
1024

8✔
1025
        // Before we broadcast the spending transaction, we'll cancel the
8✔
1026
        // notification of the first client.
8✔
1027
        spendClients[1].Cancel()
8✔
1028

8✔
1029
        // Broadcast our spending transaction.
8✔
1030
        spenderSha, err := node.Client.SendRawTransaction(spendingTx, true)
8✔
1031
        require.NoError(t, err, "unable to broadcast tx")
8✔
1032

8✔
1033
        if err := chainntnfs.WaitForMempoolTx(node, spenderSha); err != nil {
8✔
1034
                t.Fatalf("tx not relayed to miner: %v", err)
×
1035
        }
×
1036

1037
        // Now we mine a single block, which should include our spend. The
1038
        // notification should also be sent off.
1039
        if _, err := node.Client.Generate(1); err != nil {
8✔
1040
                t.Fatalf("unable to generate single block: %v", err)
×
1041
        }
×
1042

1043
        // The spend notification for the first client should have been
1044
        // dispatched.
1045
        select {
8✔
1046
        case ntfn := <-spendClients[0].Spend:
8✔
1047
                // We've received the spend nftn. So now verify all the
8✔
1048
                // fields have been set properly.
8✔
1049
                if *ntfn.SpentOutPoint != *outpoint {
8✔
1050
                        t.Fatalf("ntfn includes wrong output, reports "+
×
1051
                                "%v instead of %v",
×
1052
                                ntfn.SpentOutPoint, outpoint)
×
1053
                }
×
1054
                if !bytes.Equal(ntfn.SpenderTxHash[:], spenderSha[:]) {
8✔
1055
                        t.Fatalf("ntfn includes wrong spender tx sha, "+
×
1056
                                "reports %v instead of %v",
×
1057
                                ntfn.SpenderTxHash[:], spenderSha[:])
×
1058
                }
×
1059
                if ntfn.SpenderInputIndex != 0 {
8✔
1060
                        t.Fatalf("ntfn includes wrong spending input "+
×
1061
                                "index, reports %v, should be %v",
×
1062
                                ntfn.SpenderInputIndex, 0)
×
1063
                }
×
1064
        case <-time.After(20 * time.Second):
×
1065
                t.Fatalf("spend ntfn never received")
×
1066
        }
1067

1068
        // However, the spend notification of the second client should NOT have
1069
        // been dispatched.
1070
        select {
8✔
1071
        case _, ok := <-spendClients[1].Spend:
8✔
1072
                if ok {
8✔
1073
                        t.Fatalf("spend ntfn should have been canceled")
×
1074
                }
×
1075
        case <-time.After(20 * time.Second):
×
1076
                t.Fatalf("spend ntfn never canceled")
×
1077
        }
1078
}
1079

1080
func testCancelEpochNtfn(node *rpctest.Harness,
1081
        notifier chainntnfs.TestChainNotifier, t *testing.T) {
4✔
1082

4✔
1083
        // We'd like to ensure that once a client cancels their block epoch
4✔
1084
        // notifications, no further notifications are sent over the channel
4✔
1085
        // if/when new blocks come in.
4✔
1086
        const numClients = 2
4✔
1087

4✔
1088
        epochClients := make([]*chainntnfs.BlockEpochEvent, numClients)
4✔
1089
        for i := 0; i < numClients; i++ {
12✔
1090
                epochClient, err := notifier.RegisterBlockEpochNtfn(nil)
8✔
1091
                if err != nil {
8✔
1092
                        t.Fatalf("unable to register for epoch notification")
×
1093
                }
×
1094
                epochClients[i] = epochClient
8✔
1095
        }
1096

1097
        // Now before we mine any blocks, cancel the notification for the first
1098
        // epoch client.
1099
        epochClients[0].Cancel()
4✔
1100

4✔
1101
        // Now mine a single block, this should trigger the logic to dispatch
4✔
1102
        // epoch notifications.
4✔
1103
        if _, err := node.Client.Generate(1); err != nil {
4✔
1104
                t.Fatalf("unable to generate blocks: %v", err)
×
1105
        }
×
1106

1107
        // The epoch notification for the first client shouldn't have been
1108
        // dispatched.
1109
        select {
4✔
1110
        case _, ok := <-epochClients[0].Epochs:
4✔
1111
                if ok {
4✔
1112
                        t.Fatalf("epoch notification should have been canceled")
×
1113
                }
×
1114
        case <-time.After(2 * time.Second):
×
1115
                t.Fatalf("epoch notification not sent")
×
1116
        }
1117

1118
        // However, the epoch notification for the second client should have
1119
        // been dispatched as normal.
1120
        select {
4✔
1121
        case _, ok := <-epochClients[1].Epochs:
4✔
1122
                if !ok {
4✔
1123
                        t.Fatalf("epoch was canceled")
×
1124
                }
×
1125
        case <-time.After(20 * time.Second):
×
1126
                t.Fatalf("epoch notification not sent")
×
1127
        }
1128
}
1129

1130
func testReorgConf(miner *rpctest.Harness,
1131
        notifier chainntnfs.TestChainNotifier, scriptDispatch bool, t *testing.T) {
8✔
1132

8✔
1133
        // Set up a new miner that we can use to cause a reorg.
8✔
1134
        miner2 := unittest.NewMiner(
8✔
1135
                t, unittest.NetParams, []string{"--txindex"}, false, 0,
8✔
1136
        )
8✔
1137

8✔
1138
        // We start by connecting the new miner to our original miner,
8✔
1139
        // such that it will sync to our original chain.
8✔
1140
        if err := rpctest.ConnectNode(miner, miner2); err != nil {
8✔
1141
                t.Fatalf("unable to connect harnesses: %v", err)
×
1142
        }
×
1143
        nodeSlice := []*rpctest.Harness{miner, miner2}
8✔
1144
        if err := rpctest.JoinNodes(nodeSlice, rpctest.Blocks); err != nil {
8✔
1145
                t.Fatalf("unable to join node on blocks: %v", err)
×
1146
        }
×
1147

1148
        // The two should be on the same blockheight.
1149
        _, nodeHeight1, err := miner.Client.GetBestBlock()
8✔
1150
        if err != nil {
8✔
1151
                t.Fatalf("unable to get current blockheight %v", err)
×
1152
        }
×
1153

1154
        _, nodeHeight2, err := miner2.Client.GetBestBlock()
8✔
1155
        if err != nil {
8✔
1156
                t.Fatalf("unable to get current blockheight %v", err)
×
1157
        }
×
1158

1159
        if nodeHeight1 != nodeHeight2 {
8✔
1160
                t.Fatalf("expected both miners to be on the same height: %v vs %v",
×
1161
                        nodeHeight1, nodeHeight2)
×
1162
        }
×
1163

1164
        // We disconnect the two nodes, such that we can start mining on them
1165
        // individually without the other one learning about the new blocks.
1166
        err = miner.Client.AddNode(miner2.P2PAddress(), rpcclient.ANRemove)
8✔
1167
        require.NoError(t, err, "unable to remove node")
8✔
1168

8✔
1169
        txid, pkScript, err := chainntnfs.GetTestTxidAndScript(miner)
8✔
1170
        require.NoError(t, err, "unable to create test tx")
8✔
1171
        if err := chainntnfs.WaitForMempoolTx(miner, txid); err != nil {
8✔
1172
                t.Fatalf("tx not relayed to miner: %v", err)
×
1173
        }
×
1174

1175
        _, currentHeight, err := miner.Client.GetBestBlock()
8✔
1176
        require.NoError(t, err, "unable to get current height")
8✔
1177

8✔
1178
        // Now that we have a txid, register a confirmation notification with
8✔
1179
        // the chainntfn source.
8✔
1180
        numConfs := uint32(2)
8✔
1181
        var confIntent *chainntnfs.ConfirmationEvent
8✔
1182
        if scriptDispatch {
12✔
1183
                confIntent, err = notifier.RegisterConfirmationsNtfn(
4✔
1184
                        nil, pkScript, numConfs, uint32(currentHeight),
4✔
1185
                )
4✔
1186
        } else {
8✔
1187
                confIntent, err = notifier.RegisterConfirmationsNtfn(
4✔
1188
                        txid, pkScript, numConfs, uint32(currentHeight),
4✔
1189
                )
4✔
1190
        }
4✔
1191
        require.NoError(t, err, "unable to register ntfn")
8✔
1192

8✔
1193
        // Now generate a single block, the transaction should be included.
8✔
1194
        _, err = miner.Client.Generate(1)
8✔
1195
        require.NoError(t, err, "unable to generate single block")
8✔
1196

8✔
1197
        // Transaction only has one confirmation, and the notification is registered
8✔
1198
        // with 2 confirmations, so we should not be notified yet.
8✔
1199
        select {
8✔
1200
        case <-confIntent.Confirmed:
×
1201
                t.Fatal("tx was confirmed unexpectedly")
×
1202
        case <-time.After(1 * time.Second):
8✔
1203
        }
1204

1205
        // Reorganize transaction out of the chain by generating a longer fork
1206
        // from the other miner. The transaction is not included in this fork.
1207
        _, err = miner2.Client.Generate(2)
8✔
1208
        require.NoError(t, err)
8✔
1209

8✔
1210
        // Reconnect nodes to reach consensus on the longest chain. miner2's chain
8✔
1211
        // should win and become active on miner1.
8✔
1212
        if err := rpctest.ConnectNode(miner, miner2); err != nil {
8✔
1213
                t.Fatalf("unable to connect harnesses: %v", err)
×
1214
        }
×
1215
        nodeSlice = []*rpctest.Harness{miner, miner2}
8✔
1216
        if err := rpctest.JoinNodes(nodeSlice, rpctest.Blocks); err != nil {
8✔
1217
                t.Fatalf("unable to join node on blocks: %v", err)
×
1218
        }
×
1219

1220
        _, nodeHeight1, err = miner.Client.GetBestBlock()
8✔
1221
        if err != nil {
8✔
1222
                t.Fatalf("unable to get current blockheight %v", err)
×
1223
        }
×
1224

1225
        _, nodeHeight2, err = miner2.Client.GetBestBlock()
8✔
1226
        if err != nil {
8✔
1227
                t.Fatalf("unable to get current blockheight %v", err)
×
1228
        }
×
1229

1230
        if nodeHeight1 != nodeHeight2 {
8✔
1231
                t.Fatalf("expected both miners to be on the same height: %v vs %v",
×
1232
                        nodeHeight1, nodeHeight2)
×
1233
        }
×
1234

1235
        // Even though there is one block above the height of the block that the
1236
        // transaction was included in, it is not the active chain so the
1237
        // notification should not be sent.
1238
        select {
8✔
1239
        case <-confIntent.Confirmed:
×
1240
                t.Fatal("tx was confirmed unexpectedly")
×
1241
        case <-time.After(1 * time.Second):
8✔
1242
        }
1243

1244
        // Now confirm the transaction on the longest chain and verify that we
1245
        // receive the notification.
1246
        tx, err := miner.Client.GetRawTransaction(txid)
8✔
1247
        require.NoError(t, err, "unable to get raw tx")
8✔
1248

8✔
1249
        txid, err = miner2.Client.SendRawTransaction(tx.MsgTx(), false)
8✔
1250
        require.NoError(t, err, "unable to get send tx")
8✔
1251
        if err := chainntnfs.WaitForMempoolTx(miner, txid); err != nil {
8✔
1252
                t.Fatalf("tx not relayed to miner: %v", err)
×
1253
        }
×
1254

1255
        _, err = miner.Client.Generate(3)
8✔
1256
        require.NoError(t, err, "unable to generate single block")
8✔
1257

8✔
1258
        // Since confIntent did not request all confirmations, make sure that
8✔
1259
        // it only receives the final confirmation with num confs left as 0.
8✔
1260
        select {
8✔
1261
        case txConf := <-confIntent.Confirmed:
8✔
1262
                require.Zero(t, txConf.NumConfsLeft)
8✔
1263

1264
        case <-time.After(20 * time.Second):
×
1265
                t.Fatalf("confirmation notification never received")
×
1266
        }
1267
}
1268

1269
// testReorgSpend ensures that the different ChainNotifier implementations
1270
// correctly handle outpoints whose spending transaction has been reorged out of
1271
// the chain.
1272
func testReorgSpend(miner *rpctest.Harness,
1273
        notifier chainntnfs.TestChainNotifier, scriptDispatch bool, t *testing.T) {
8✔
1274

8✔
1275
        // We'll start by creating an output and registering a spend
8✔
1276
        // notification for it.
8✔
1277
        outpoint, output, privKey := chainntnfs.CreateSpendableOutput(t, miner)
8✔
1278
        _, heightHint, err := miner.Client.GetBestBlock()
8✔
1279
        require.NoError(t, err, "unable to retrieve current height")
8✔
1280

8✔
1281
        var spendIntent *chainntnfs.SpendEvent
8✔
1282
        if scriptDispatch {
12✔
1283
                spendIntent, err = notifier.RegisterSpendNtfn(
4✔
1284
                        nil, output.PkScript, uint32(heightHint),
4✔
1285
                )
4✔
1286
        } else {
8✔
1287
                spendIntent, err = notifier.RegisterSpendNtfn(
4✔
1288
                        outpoint, output.PkScript, uint32(heightHint),
4✔
1289
                )
4✔
1290
        }
4✔
1291
        require.NoError(t, err, "unable to register for spend")
8✔
1292

8✔
1293
        // Set up a new miner that we can use to cause a reorg.
8✔
1294
        miner2 := unittest.NewMiner(
8✔
1295
                t, unittest.NetParams, []string{"--txindex"}, false, 0,
8✔
1296
        )
8✔
1297

8✔
1298
        // We start by connecting the new miner to our original miner, in order
8✔
1299
        // to have a consistent view of the chain from both miners. They should
8✔
1300
        // be on the same block height.
8✔
1301
        if err := rpctest.ConnectNode(miner, miner2); err != nil {
8✔
1302
                t.Fatalf("unable to connect miners: %v", err)
×
1303
        }
×
1304
        nodeSlice := []*rpctest.Harness{miner, miner2}
8✔
1305
        if err := rpctest.JoinNodes(nodeSlice, rpctest.Blocks); err != nil {
8✔
1306
                t.Fatalf("unable to sync miners: %v", err)
×
1307
        }
×
1308
        _, minerHeight1, err := miner.Client.GetBestBlock()
8✔
1309
        require.NoError(t, err, "unable to get miner1's current height")
8✔
1310
        _, minerHeight2, err := miner2.Client.GetBestBlock()
8✔
1311
        require.NoError(t, err, "unable to get miner2's current height")
8✔
1312
        if minerHeight1 != minerHeight2 {
8✔
1313
                t.Fatalf("expected both miners to be on the same height: "+
×
1314
                        "%v vs %v", minerHeight1, minerHeight2)
×
1315
        }
×
1316

1317
        // We disconnect the two nodes, such that we can start mining on them
1318
        // individually without the other one learning about the new blocks.
1319
        err = miner.Client.AddNode(miner2.P2PAddress(), rpcclient.ANRemove)
8✔
1320
        require.NoError(t, err, "unable to disconnect miners")
8✔
1321

8✔
1322
        // Craft the spending transaction for the outpoint created above and
8✔
1323
        // confirm it under the chain of the original miner.
8✔
1324
        spendTx := chainntnfs.CreateSpendTx(t, outpoint, output, privKey)
8✔
1325
        spendTxHash, err := miner.Client.SendRawTransaction(spendTx, true)
8✔
1326
        require.NoError(t, err, "unable to broadcast spend tx")
8✔
1327
        if err := chainntnfs.WaitForMempoolTx(miner, spendTxHash); err != nil {
8✔
1328
                t.Fatalf("spend tx not relayed to miner: %v", err)
×
1329
        }
×
1330
        const numBlocks = 1
8✔
1331
        if _, err := miner.Client.Generate(numBlocks); err != nil {
8✔
1332
                t.Fatalf("unable to generate blocks: %v", err)
×
1333
        }
×
1334
        _, spendHeight, err := miner.Client.GetBestBlock()
8✔
1335
        require.NoError(t, err, "unable to get spend height")
8✔
1336

8✔
1337
        // We should see a spend notification dispatched with the correct spend
8✔
1338
        // details.
8✔
1339
        select {
8✔
1340
        case spendDetails := <-spendIntent.Spend:
8✔
1341
                checkNotificationFields(
8✔
1342
                        spendDetails, outpoint, spendTxHash, spendHeight, t,
8✔
1343
                )
8✔
1344
        case <-time.After(5 * time.Second):
×
1345
                t.Fatal("expected spend notification to be dispatched")
×
1346
        }
1347

1348
        // Now, with the other miner, we'll generate one more block than the
1349
        // other miner and connect them to cause a reorg.
1350
        if _, err := miner2.Client.Generate(numBlocks + 1); err != nil {
8✔
1351
                t.Fatalf("unable to generate blocks: %v", err)
×
1352
        }
×
1353
        if err := rpctest.ConnectNode(miner, miner2); err != nil {
8✔
1354
                t.Fatalf("unable to connect miners: %v", err)
×
1355
        }
×
1356
        nodeSlice = []*rpctest.Harness{miner2, miner}
8✔
1357
        if err := rpctest.JoinNodes(nodeSlice, rpctest.Blocks); err != nil {
8✔
1358
                t.Fatalf("unable to sync miners: %v", err)
×
1359
        }
×
1360
        _, minerHeight1, err = miner.Client.GetBestBlock()
8✔
1361
        require.NoError(t, err, "unable to get miner1's current height")
8✔
1362
        _, minerHeight2, err = miner2.Client.GetBestBlock()
8✔
1363
        require.NoError(t, err, "unable to get miner2's current height")
8✔
1364
        if minerHeight1 != minerHeight2 {
8✔
1365
                t.Fatalf("expected both miners to be on the same height: "+
×
1366
                        "%v vs %v", minerHeight1, minerHeight2)
×
1367
        }
×
1368

1369
        // We should receive a reorg notification.
1370
        select {
8✔
1371
        case _, ok := <-spendIntent.Reorg:
8✔
1372
                if !ok {
8✔
1373
                        t.Fatal("unexpected reorg channel closed")
×
1374
                }
×
1375
        case <-time.After(5 * time.Second):
×
1376
                t.Fatal("expected to receive reorg notification")
×
1377
        }
1378

1379
        // Now that both miners are on the same chain, we'll confirm the
1380
        // spending transaction of the outpoint and receive a notification for
1381
        // it.
1382
        if _, err = miner2.Client.SendRawTransaction(spendTx, true); err != nil {
8✔
1383
                t.Fatalf("unable to broadcast spend tx: %v", err)
×
1384
        }
×
1385
        if err := chainntnfs.WaitForMempoolTx(miner, spendTxHash); err != nil {
8✔
1386
                t.Fatalf("tx not relayed to miner: %v", err)
×
1387
        }
×
1388
        if _, err := miner.Client.Generate(numBlocks); err != nil {
8✔
1389
                t.Fatalf("unable to generate single block: %v", err)
×
1390
        }
×
1391
        _, spendHeight, err = miner.Client.GetBestBlock()
8✔
1392
        require.NoError(t, err, "unable to retrieve current height")
8✔
1393

8✔
1394
        select {
8✔
1395
        case spendDetails := <-spendIntent.Spend:
8✔
1396
                checkNotificationFields(
8✔
1397
                        spendDetails, outpoint, spendTxHash, spendHeight, t,
8✔
1398
                )
8✔
1399
        case <-time.After(5 * time.Second):
×
1400
                t.Fatal("expected spend notification to be dispatched")
×
1401
        }
1402
}
1403

1404
// testCatchUpClientOnMissedBlocks tests the case of multiple registered client
1405
// receiving historical block epoch notifications due to their best known block
1406
// being out of date.
1407
func testCatchUpClientOnMissedBlocks(miner *rpctest.Harness,
1408
        notifier chainntnfs.TestChainNotifier, t *testing.T) {
4✔
1409

4✔
1410
        const numBlocks = 10
4✔
1411
        const numClients = 5
4✔
1412
        var wg sync.WaitGroup
4✔
1413

4✔
1414
        outdatedHash, outdatedHeight, err := miner.Client.GetBestBlock()
4✔
1415
        require.NoError(t, err, "unable to retrieve current height")
4✔
1416

4✔
1417
        // This function is used by UnsafeStart to ensure all notifications
4✔
1418
        // are fully drained before clients register for notifications.
4✔
1419
        generateBlocks := func() error {
8✔
1420
                _, err = miner.Client.Generate(numBlocks)
4✔
1421
                return err
4✔
1422
        }
4✔
1423

1424
        // We want to ensure that when a client registers for block notifications,
1425
        // the notifier's best block is at the tip of the chain. If it isn't, the
1426
        // client may not receive all historical notifications.
1427
        bestHeight := outdatedHeight + numBlocks
4✔
1428
        err = notifier.UnsafeStart(bestHeight, nil, bestHeight, generateBlocks)
4✔
1429
        require.NoError(t, err, "unable to unsafe start the notifier")
4✔
1430
        defer notifier.Stop()
4✔
1431

4✔
1432
        // Create numClients clients whose best known block is 10 blocks behind
4✔
1433
        // the tip of the chain. We expect each client to receive numBlocks
4✔
1434
        // notifications, 1 for each block  they're behind.
4✔
1435
        clients := make([]*chainntnfs.BlockEpochEvent, 0, numClients)
4✔
1436
        outdatedBlock := &chainntnfs.BlockEpoch{
4✔
1437
                Height: outdatedHeight, Hash: outdatedHash,
4✔
1438
        }
4✔
1439
        for i := 0; i < numClients; i++ {
24✔
1440
                epochClient, err := notifier.RegisterBlockEpochNtfn(outdatedBlock)
20✔
1441
                if err != nil {
20✔
1442
                        t.Fatalf("unable to register for epoch notification: %v", err)
×
1443
                }
×
1444
                clients = append(clients, epochClient)
20✔
1445
        }
1446
        for expectedHeight := outdatedHeight + 1; expectedHeight <=
4✔
1447
                bestHeight; expectedHeight++ {
44✔
1448

40✔
1449
                for _, epochClient := range clients {
240✔
1450
                        select {
200✔
1451
                        case block := <-epochClient.Epochs:
200✔
1452
                                if block.Height != expectedHeight {
200✔
1453
                                        t.Fatalf("received block of height: %d, "+
×
1454
                                                "expected: %d", block.Height,
×
1455
                                                expectedHeight)
×
1456
                                }
×
1457
                        case <-time.After(20 * time.Second):
×
1458
                                t.Fatalf("did not receive historical notification "+
×
1459
                                        "for height %d", expectedHeight)
×
1460
                        }
1461

1462
                }
1463
        }
1464

1465
        // Finally, ensure that an extra block notification wasn't received.
1466
        anyExtras := make(chan struct{}, len(clients))
4✔
1467
        for _, epochClient := range clients {
24✔
1468
                wg.Add(1)
20✔
1469
                go func(epochClient *chainntnfs.BlockEpochEvent) {
40✔
1470
                        defer wg.Done()
20✔
1471
                        select {
20✔
1472
                        case <-epochClient.Epochs:
×
1473
                                anyExtras <- struct{}{}
×
1474
                        case <-time.After(5 * time.Second):
20✔
1475
                        }
1476
                }(epochClient)
1477
        }
1478

1479
        wg.Wait()
4✔
1480
        close(anyExtras)
4✔
1481

4✔
1482
        var extraCount int
4✔
1483
        for range anyExtras {
4✔
1484
                extraCount++
×
1485
        }
×
1486

1487
        if extraCount > 0 {
4✔
1488
                t.Fatalf("received %d unexpected block notification", extraCount)
×
1489
        }
×
1490
}
1491

1492
// testCatchUpOnMissedBlocks the case of multiple registered clients receiving
1493
// historical block epoch notifications due to the notifier's best known block
1494
// being out of date.
1495
func testCatchUpOnMissedBlocks(miner *rpctest.Harness,
1496
        notifier chainntnfs.TestChainNotifier, t *testing.T) {
4✔
1497

4✔
1498
        const numBlocks = 10
4✔
1499
        const numClients = 5
4✔
1500
        var wg sync.WaitGroup
4✔
1501

4✔
1502
        _, bestHeight, err := miner.Client.GetBestBlock()
4✔
1503
        if err != nil {
4✔
1504
                t.Fatalf("unable to get current blockheight %v", err)
×
1505
        }
×
1506

1507
        // This function is used by UnsafeStart to ensure all notifications
1508
        // are fully drained before clients register for notifications.
1509
        generateBlocks := func() error {
8✔
1510
                _, err = miner.Client.Generate(numBlocks)
4✔
1511
                return err
4✔
1512
        }
4✔
1513

1514
        // Next, start the notifier with outdated best block information.
1515
        err = notifier.UnsafeStart(
4✔
1516
                bestHeight, nil, bestHeight+numBlocks, generateBlocks,
4✔
1517
        )
4✔
1518
        require.NoError(t, err, "unable to unsafe start the notifier")
4✔
1519
        defer notifier.Stop()
4✔
1520

4✔
1521
        // Create numClients clients who will listen for block notifications.
4✔
1522
        clients := make([]*chainntnfs.BlockEpochEvent, 0, numClients)
4✔
1523
        for i := 0; i < numClients; i++ {
24✔
1524
                epochClient, err := notifier.RegisterBlockEpochNtfn(nil)
20✔
1525
                if err != nil {
20✔
1526
                        t.Fatalf("unable to register for epoch notification: %v", err)
×
1527
                }
×
1528

1529
                // Drain the notification dispatched upon registration as we're
1530
                // not interested in it.
1531
                select {
20✔
1532
                case <-epochClient.Epochs:
20✔
1533
                case <-time.After(5 * time.Second):
×
1534
                        t.Fatal("expected to receive epoch for current block " +
×
1535
                                "upon registration")
×
1536
                }
1537

1538
                clients = append(clients, epochClient)
20✔
1539
        }
1540

1541
        // Generate a single block to trigger the backlog of historical
1542
        // notifications for the previously mined blocks.
1543
        if _, err := miner.Client.Generate(1); err != nil {
4✔
1544
                t.Fatalf("unable to generate blocks: %v", err)
×
1545
        }
×
1546

1547
        // We expect each client to receive numBlocks + 1 notifications, 1 for
1548
        // each block that the notifier has missed out on.
1549
        for expectedHeight := bestHeight + 1; expectedHeight <=
4✔
1550
                bestHeight+numBlocks+1; expectedHeight++ {
48✔
1551

44✔
1552
                for _, epochClient := range clients {
264✔
1553
                        select {
220✔
1554
                        case block := <-epochClient.Epochs:
220✔
1555
                                if block.Height != expectedHeight {
220✔
1556
                                        t.Fatalf("received block of height: %d, "+
×
1557
                                                "expected: %d", block.Height,
×
1558
                                                expectedHeight)
×
1559
                                }
×
1560
                        case <-time.After(20 * time.Second):
×
1561
                                t.Fatalf("did not receive historical notification "+
×
1562
                                        "for height %d", expectedHeight)
×
1563
                        }
1564
                }
1565
        }
1566

1567
        // Finally, ensure that an extra block notification wasn't received.
1568
        anyExtras := make(chan struct{}, len(clients))
4✔
1569
        for _, epochClient := range clients {
24✔
1570
                wg.Add(1)
20✔
1571
                go func(epochClient *chainntnfs.BlockEpochEvent) {
40✔
1572
                        defer wg.Done()
20✔
1573
                        select {
20✔
1574
                        case <-epochClient.Epochs:
×
1575
                                anyExtras <- struct{}{}
×
1576
                        case <-time.After(5 * time.Second):
20✔
1577
                        }
1578
                }(epochClient)
1579
        }
1580

1581
        wg.Wait()
4✔
1582
        close(anyExtras)
4✔
1583

4✔
1584
        var extraCount int
4✔
1585
        for range anyExtras {
4✔
1586
                extraCount++
×
1587
        }
×
1588

1589
        if extraCount > 0 {
4✔
1590
                t.Fatalf("received %d unexpected block notification", extraCount)
×
1591
        }
×
1592
}
1593

1594
// testCatchUpOnMissedBlocks tests that a client will still receive all valid
1595
// block notifications in the case where a notifier's best block has been reorged
1596
// out of the chain.
1597
func testCatchUpOnMissedBlocksWithReorg(miner1 *rpctest.Harness,
1598
        notifier chainntnfs.TestChainNotifier, t *testing.T) {
4✔
1599

4✔
1600
        // If this is the neutrino notifier, then we'll skip this test for now
4✔
1601
        // as we're missing functionality required to ensure the test passes
4✔
1602
        // reliably.
4✔
1603
        if _, ok := notifier.(*neutrinonotify.NeutrinoNotifier); ok {
5✔
1604
                t.Skip("skipping re-org test for neutrino")
1✔
1605
        }
1✔
1606

1607
        const numBlocks = 10
3✔
1608
        const numClients = 5
3✔
1609
        var wg sync.WaitGroup
3✔
1610

3✔
1611
        // Set up a new miner that we can use to cause a reorg.
3✔
1612
        miner2 := unittest.NewMiner(
3✔
1613
                t, unittest.NetParams, []string{"--txindex"}, false, 0,
3✔
1614
        )
3✔
1615

3✔
1616
        // We start by connecting the new miner to our original miner,
3✔
1617
        // such that it will sync to our original chain.
3✔
1618
        if err := rpctest.ConnectNode(miner1, miner2); err != nil {
3✔
1619
                t.Fatalf("unable to connect harnesses: %v", err)
×
1620
        }
×
1621
        nodeSlice := []*rpctest.Harness{miner1, miner2}
3✔
1622
        if err := rpctest.JoinNodes(nodeSlice, rpctest.Blocks); err != nil {
3✔
1623
                t.Fatalf("unable to join node on blocks: %v", err)
×
1624
        }
×
1625

1626
        // The two should be on the same blockheight.
1627
        _, nodeHeight1, err := miner1.Client.GetBestBlock()
3✔
1628
        if err != nil {
3✔
1629
                t.Fatalf("unable to get current blockheight %v", err)
×
1630
        }
×
1631

1632
        _, nodeHeight2, err := miner2.Client.GetBestBlock()
3✔
1633
        if err != nil {
3✔
1634
                t.Fatalf("unable to get current blockheight %v", err)
×
1635
        }
×
1636

1637
        if nodeHeight1 != nodeHeight2 {
3✔
1638
                t.Fatalf("expected both miners to be on the same height: %v vs %v",
×
1639
                        nodeHeight1, nodeHeight2)
×
1640
        }
×
1641

1642
        // We disconnect the two nodes, such that we can start mining on them
1643
        // individually without the other one learning about the new blocks.
1644
        err = miner1.Client.AddNode(miner2.P2PAddress(), rpcclient.ANRemove)
3✔
1645
        require.NoError(t, err, "unable to remove node")
3✔
1646

3✔
1647
        // Now mine on each chain separately
3✔
1648
        blocks, err := miner1.Client.Generate(numBlocks)
3✔
1649
        require.NoError(t, err, "unable to generate single block")
3✔
1650

3✔
1651
        // We generate an extra block on miner 2's chain to ensure it is the
3✔
1652
        // longer chain.
3✔
1653
        _, err = miner2.Client.Generate(numBlocks + 1)
3✔
1654
        require.NoError(t, err, "unable to generate single block")
3✔
1655

3✔
1656
        // Sync the two chains to ensure they will sync to miner2's chain.
3✔
1657
        if err := rpctest.ConnectNode(miner1, miner2); err != nil {
3✔
1658
                t.Fatalf("unable to connect harnesses: %v", err)
×
1659
        }
×
1660
        nodeSlice = []*rpctest.Harness{miner1, miner2}
3✔
1661
        if err := rpctest.JoinNodes(nodeSlice, rpctest.Blocks); err != nil {
3✔
1662
                t.Fatalf("unable to join node on blocks: %v", err)
×
1663
        }
×
1664

1665
        // The two should be on the same block hash.
1666
        timeout := time.After(10 * time.Second)
3✔
1667
        for {
6✔
1668
                nodeHash1, _, err := miner1.Client.GetBestBlock()
3✔
1669
                if err != nil {
3✔
1670
                        t.Fatalf("unable to get current block hash: %v", err)
×
1671
                }
×
1672

1673
                nodeHash2, _, err := miner2.Client.GetBestBlock()
3✔
1674
                if err != nil {
3✔
1675
                        t.Fatalf("unable to get current block hash: %v", err)
×
1676
                }
×
1677

1678
                if *nodeHash1 == *nodeHash2 {
6✔
1679
                        break
3✔
1680
                }
1681
                select {
×
1682
                case <-timeout:
×
1683
                        t.Fatalf("Unable to sync two chains")
×
1684
                case <-time.After(50 * time.Millisecond):
×
1685
                        continue
×
1686
                }
1687
        }
1688

1689
        // Next, start the notifier with outdated best block information.
1690
        // We set the notifier's best block to be the last block mined on the
1691
        // shorter chain, to test that the notifier correctly rewinds to
1692
        // the common ancestor between the two chains.
1693
        syncHeight := nodeHeight1 + numBlocks + 1
3✔
1694
        err = notifier.UnsafeStart(
3✔
1695
                nodeHeight1+numBlocks, blocks[numBlocks-1], syncHeight, nil,
3✔
1696
        )
3✔
1697
        require.NoError(t, err, "Unable to unsafe start the notifier")
3✔
1698
        defer notifier.Stop()
3✔
1699

3✔
1700
        // Create numClients clients who will listen for block notifications.
3✔
1701
        clients := make([]*chainntnfs.BlockEpochEvent, 0, numClients)
3✔
1702
        for i := 0; i < numClients; i++ {
18✔
1703
                epochClient, err := notifier.RegisterBlockEpochNtfn(nil)
15✔
1704
                if err != nil {
15✔
1705
                        t.Fatalf("unable to register for epoch notification: %v", err)
×
1706
                }
×
1707

1708
                // Drain the notification dispatched upon registration as we're
1709
                // not interested in it.
1710
                select {
15✔
1711
                case <-epochClient.Epochs:
15✔
1712
                case <-time.After(5 * time.Second):
×
1713
                        t.Fatal("expected to receive epoch for current block " +
×
1714
                                "upon registration")
×
1715
                }
1716

1717
                clients = append(clients, epochClient)
15✔
1718
        }
1719

1720
        // Generate a single block, which should trigger the notifier to rewind
1721
        // to the common ancestor and dispatch notifications from there.
1722
        _, err = miner2.Client.Generate(1)
3✔
1723
        require.NoError(t, err, "unable to generate single block")
3✔
1724

3✔
1725
        // If the chain backend to the notifier stores information about reorged
3✔
1726
        // blocks, the notifier is able to rewind the chain to the common
3✔
1727
        // ancestor between the chain tip and its outdated best known block.
3✔
1728
        // In this case, the client is expected to receive numBlocks + 2
3✔
1729
        // notifications, 1 for each block the notifier has missed out on from
3✔
1730
        // the longer chain.
3✔
1731
        //
3✔
1732
        // If the chain backend does not store information about reorged blocks,
3✔
1733
        // the notifier has no way of knowing where to rewind to and therefore
3✔
1734
        // the client is only expected to receive notifications for blocks
3✔
1735
        // whose height is greater than the notifier's best known height: 2
3✔
1736
        // notifications, in this case.
3✔
1737
        var startingHeight int32
3✔
1738
        switch notifier.(type) {
3✔
1739
        case *neutrinonotify.NeutrinoNotifier:
×
1740
                startingHeight = nodeHeight1 + numBlocks + 1
×
1741
        default:
3✔
1742
                startingHeight = nodeHeight1 + 1
3✔
1743
        }
1744

1745
        for expectedHeight := startingHeight; expectedHeight <=
3✔
1746
                nodeHeight1+numBlocks+2; expectedHeight++ {
39✔
1747

36✔
1748
                for _, epochClient := range clients {
216✔
1749
                        select {
180✔
1750
                        case block := <-epochClient.Epochs:
180✔
1751
                                if block.Height != expectedHeight {
180✔
1752
                                        t.Fatalf("received block of height: %d, "+
×
1753
                                                "expected: %d", block.Height,
×
1754
                                                expectedHeight)
×
1755
                                }
×
1756
                        case <-time.After(20 * time.Second):
×
1757
                                t.Fatalf("did not receive historical notification "+
×
1758
                                        "for height %d", expectedHeight)
×
1759
                        }
1760
                }
1761
        }
1762

1763
        // Finally, ensure that an extra block notification wasn't received.
1764
        anyExtras := make(chan struct{}, len(clients))
3✔
1765
        for _, epochClient := range clients {
18✔
1766
                wg.Add(1)
15✔
1767
                go func(epochClient *chainntnfs.BlockEpochEvent) {
30✔
1768
                        defer wg.Done()
15✔
1769
                        select {
15✔
1770
                        case <-epochClient.Epochs:
×
1771
                                anyExtras <- struct{}{}
×
1772
                        case <-time.After(5 * time.Second):
15✔
1773
                        }
1774
                }(epochClient)
1775
        }
1776

1777
        wg.Wait()
3✔
1778
        close(anyExtras)
3✔
1779

3✔
1780
        var extraCount int
3✔
1781
        for range anyExtras {
3✔
1782
                extraCount++
×
1783
        }
×
1784

1785
        if extraCount > 0 {
3✔
1786
                t.Fatalf("received %d unexpected block notification", extraCount)
×
1787
        }
×
1788
}
1789

1790
// testIncludeBlockAsymmetry tests that if the same output is registered for a
1791
// notification by two callers, one is able to get a notification with the
1792
// block and the other one without it.
1793
func testIncludeBlockAsymmetry(miner *rpctest.Harness,
1794
        notifier chainntnfs.TestChainNotifier, scriptDispatch bool,
1795
        t *testing.T) {
8✔
1796

8✔
1797
        // We'll start by creating a new test transaction, waiting long enough
8✔
1798
        // for it to get into the mempool.
8✔
1799
        txid, pkScript, err := chainntnfs.GetTestTxidAndScript(miner)
8✔
1800
        require.NoError(t, err, "unable to create test tx")
8✔
1801
        if err := chainntnfs.WaitForMempoolTx(miner, txid); err != nil {
8✔
1802
                t.Fatalf("tx not relayed to miner: %v", err)
×
1803
        }
×
1804

1805
        _, currentHeight, err := miner.Client.GetBestBlock()
8✔
1806
        require.NoError(t, err, "unable to get current height")
8✔
1807

8✔
1808
        var (
8✔
1809
                confIntentNoBlock *chainntnfs.ConfirmationEvent
8✔
1810
                confIntentBlock   *chainntnfs.ConfirmationEvent
8✔
1811

8✔
1812
                numConfsLong  = uint32(6)
8✔
1813
                numConfsShort = uint32(1)
8✔
1814
        )
8✔
1815
        dispatchClients := func() {
24✔
1816
                dispatchTxid := txid
16✔
1817
                if scriptDispatch {
24✔
1818
                        dispatchTxid = nil
8✔
1819
                }
8✔
1820

1821
                confIntentNoBlock, err = notifier.RegisterConfirmationsNtfn(
16✔
1822
                        dispatchTxid, pkScript, numConfsLong,
16✔
1823
                        uint32(currentHeight),
16✔
1824
                )
16✔
1825
                require.NoError(t, err)
16✔
1826

16✔
1827
                confIntentBlock, err = notifier.RegisterConfirmationsNtfn(
16✔
1828
                        dispatchTxid, pkScript, numConfsShort,
16✔
1829
                        uint32(currentHeight), chainntnfs.WithIncludeBlock(),
16✔
1830
                )
16✔
1831
                require.NoError(t, err)
16✔
1832
        }
1833
        assertNtfns := func() {
24✔
1834
                // Make sure the long confirmation client receives the
16✔
1835
                // notification but not the block.
16✔
1836
                confNtfnNoBlock, err := lnutils.RecvOrTimeout(
16✔
1837
                        confIntentNoBlock.Confirmed, time.Second*5,
16✔
1838
                )
16✔
1839
                require.NoError(t, err)
16✔
1840
                require.Nil(t, (*confNtfnNoBlock).Block, "block not included")
16✔
1841

16✔
1842
                // And the short confirmation client receives the notification
16✔
1843
                // and the block.
16✔
1844
                confNtfnBlock, err := lnutils.RecvOrTimeout(
16✔
1845
                        confIntentBlock.Confirmed, time.Second*5,
16✔
1846
                )
16✔
1847
                require.NoError(t, err)
16✔
1848
                require.NotNil(t, (*confNtfnBlock).Block, "block included")
16✔
1849
        }
16✔
1850

1851
        // First, we start off by registering two clients for the same txid and
1852
        // pkScript. One of them will require 6 confirmations but not request
1853
        // the block, the other will just require a single confirmation and
1854
        // request the block.
1855
        dispatchClients()
8✔
1856

8✔
1857
        // Next, we'll generate a few blocks, which should confirm the
8✔
1858
        // transaction created above and trigger the notifications, as it should
8✔
1859
        // be confirmed enough for both clients.
8✔
1860
        _, err = miner.Client.Generate(6)
8✔
1861
        require.NoError(t, err, "unable to generate single block")
8✔
1862

8✔
1863
        // Make sure we got the notifications we expected.
8✔
1864
        assertNtfns()
8✔
1865

8✔
1866
        // Now dispatch the same clients again, which should hit the same
8✔
1867
        // conditions as above and use the cached rescan details.
8✔
1868
        dispatchClients()
8✔
1869

8✔
1870
        // And again, the notifications should be triggered as expected.
8✔
1871
        assertNtfns()
8✔
1872
}
1873

1874
type txNtfnTestCase struct {
1875
        name string
1876
        test func(node *rpctest.Harness, notifier chainntnfs.TestChainNotifier,
1877
                scriptDispatch bool, t *testing.T)
1878
}
1879

1880
type blockNtfnTestCase struct {
1881
        name string
1882
        test func(node *rpctest.Harness, notifier chainntnfs.TestChainNotifier,
1883
                t *testing.T)
1884
}
1885

1886
type blockCatchupTestCase struct {
1887
        name string
1888
        test func(node *rpctest.Harness, notifier chainntnfs.TestChainNotifier,
1889
                t *testing.T)
1890
}
1891

1892
var txNtfnTests = []txNtfnTestCase{
1893
        {
1894
                name: "single conf ntfn",
1895
                test: testSingleConfirmationNotification,
1896
        },
1897
        {
1898
                name: "multi conf ntfn",
1899
                test: testMultiConfirmationNotification,
1900
        },
1901
        {
1902
                name: "batch conf ntfn",
1903
                test: testBatchConfirmationNotification,
1904
        },
1905
        {
1906
                name: "multi client conf",
1907
                test: testMultiClientConfirmationNotification,
1908
        },
1909
        {
1910
                name: "lazy ntfn consumer",
1911
                test: testLazyNtfnConsumer,
1912
        },
1913
        {
1914
                name: "historical conf dispatch",
1915
                test: testTxConfirmedBeforeNtfnRegistration,
1916
        },
1917
        {
1918
                name: "reorg conf",
1919
                test: testReorgConf,
1920
        },
1921
        {
1922
                name: "spend ntfn",
1923
                test: testSpendNotification,
1924
        },
1925
        {
1926
                name: "historical spend dispatch",
1927
                test: testSpendBeforeNtfnRegistration,
1928
        },
1929
        {
1930
                name: "reorg spend",
1931
                test: testReorgSpend,
1932
        },
1933
        {
1934
                name: "cancel spend ntfn",
1935
                test: testCancelSpendNtfn,
1936
        },
1937
        {
1938
                name: "test include block asymmetry",
1939
                test: testIncludeBlockAsymmetry,
1940
        },
1941
}
1942

1943
var blockNtfnTests = []blockNtfnTestCase{
1944
        {
1945
                name: "block epoch",
1946
                test: testBlockEpochNotification,
1947
        },
1948
        {
1949
                name: "cancel epoch ntfn",
1950
                test: testCancelEpochNtfn,
1951
        },
1952
}
1953

1954
var blockCatchupTests = []blockCatchupTestCase{
1955
        {
1956
                name: "catch up client on historical block epoch ntfns",
1957
                test: testCatchUpClientOnMissedBlocks,
1958
        },
1959
        {
1960
                name: "test catch up on missed blocks",
1961
                test: testCatchUpOnMissedBlocks,
1962
        },
1963
        {
1964
                name: "test catch up on missed blocks w/ reorged best block",
1965
                test: testCatchUpOnMissedBlocksWithReorg,
1966
        },
1967
}
1968

1969
// TestInterfaces tests all registered interfaces with a unified set of tests
1970
// which exercise each of the required methods found within the ChainNotifier
1971
// interface.
1972
//
1973
// NOTE: In the future, when additional implementations of the ChainNotifier
1974
// interface have been implemented, in order to ensure the new concrete
1975
// implementation is automatically tested, two steps must be undertaken. First,
1976
// one needs add a "non-captured" (_) import from the new sub-package. This
1977
// import should trigger an init() method within the package which registers
1978
// the interface. Second, an additional case in the switch within the main loop
1979
// below needs to be added which properly initializes the interface.
1980
func TestInterfaces(t *testing.T, targetBackEnd string) {
4✔
1981
        // Initialize the harness around a btcd node which will serve as our
4✔
1982
        // dedicated miner to generate blocks, cause re-orgs, etc. We'll set up
4✔
1983
        // this node with a chain length of 125, so we have plenty of BTC to
4✔
1984
        // play around with.
4✔
1985
        miner := unittest.NewMiner(t, unittest.NetParams, nil, true, 25)
4✔
1986

4✔
1987
        p2pAddr := miner.P2PAddress()
4✔
1988

4✔
1989
        log.Printf("Running %v ChainNotifier interface tests",
4✔
1990
                2*len(txNtfnTests)+len(blockNtfnTests)+len(blockCatchupTests))
4✔
1991

4✔
1992
        for _, notifierDriver := range chainntnfs.RegisteredNotifiers() {
20✔
1993
                notifierType := notifierDriver.NotifierType
16✔
1994
                if notifierType != targetBackEnd {
28✔
1995
                        continue
12✔
1996
                }
1997

1998
                // Initialize a height hint cache for each notifier.
1999
                tempDir := t.TempDir()
4✔
2000
                db := channeldb.OpenForTesting(t, tempDir)
4✔
2001

4✔
2002
                testCfg := channeldb.CacheConfig{
4✔
2003
                        QueryDisable: false,
4✔
2004
                }
4✔
2005
                hintCache, err := channeldb.NewHeightHintCache(
4✔
2006
                        testCfg, db.Backend,
4✔
2007
                )
4✔
2008
                if err != nil {
4✔
2009
                        t.Fatalf("unable to create height hint cache: %v", err)
×
2010
                }
×
2011

2012
                blockCache := blockcache.NewBlockCache(10000)
4✔
2013

4✔
2014
                var (
4✔
2015
                        newNotifier func() (chainntnfs.TestChainNotifier, error)
4✔
2016
                )
4✔
2017

4✔
2018
                switch notifierType {
4✔
2019
                case "bitcoind":
1✔
2020
                        var bitcoindConn *chain.BitcoindConn
1✔
2021
                        bitcoindConn = unittest.NewBitcoindBackend(
1✔
2022
                                t, unittest.NetParams, miner, true, false,
1✔
2023
                        )
1✔
2024
                        newNotifier = func() (chainntnfs.TestChainNotifier, error) {
5✔
2025
                                return bitcoindnotify.New(
4✔
2026
                                        bitcoindConn, unittest.NetParams,
4✔
2027
                                        hintCache, hintCache, blockCache,
4✔
2028
                                ), nil
4✔
2029
                        }
4✔
2030

2031
                case "bitcoind-rpc-polling":
1✔
2032
                        var bitcoindConn *chain.BitcoindConn
1✔
2033
                        bitcoindConn = unittest.NewBitcoindBackend(
1✔
2034
                                t, unittest.NetParams, miner, true, true,
1✔
2035
                        )
1✔
2036
                        newNotifier = func() (chainntnfs.TestChainNotifier, error) {
5✔
2037
                                return bitcoindnotify.New(
4✔
2038
                                        bitcoindConn, unittest.NetParams,
4✔
2039
                                        hintCache, hintCache, blockCache,
4✔
2040
                                ), nil
4✔
2041
                        }
4✔
2042

2043
                case "btcd":
1✔
2044
                        rpcConfig := miner.RPCConfig()
1✔
2045
                        newNotifier = func() (chainntnfs.TestChainNotifier, error) {
5✔
2046
                                return btcdnotify.New(
4✔
2047
                                        &rpcConfig, unittest.NetParams,
4✔
2048
                                        hintCache, hintCache, blockCache,
4✔
2049
                                )
4✔
2050
                        }
4✔
2051

2052
                case "neutrino":
1✔
2053
                        var spvNode *neutrino.ChainService
1✔
2054
                        spvNode = unittest.NewNeutrinoBackend(
1✔
2055
                                t, unittest.NetParams, p2pAddr,
1✔
2056
                        )
1✔
2057
                        newNotifier = func() (chainntnfs.TestChainNotifier, error) {
5✔
2058
                                return neutrinonotify.New(
4✔
2059
                                        spvNode, hintCache, hintCache,
4✔
2060
                                        blockCache,
4✔
2061
                                ), nil
4✔
2062
                        }
4✔
2063
                }
2064

2065
                log.Printf("Running ChainNotifier interface tests for: %v",
4✔
2066
                        notifierType)
4✔
2067

4✔
2068
                notifier, err := newNotifier()
4✔
2069
                if err != nil {
4✔
2070
                        t.Fatalf("unable to create %v notifier: %v",
×
2071
                                notifierType, err)
×
2072
                }
×
2073
                if err := notifier.Start(); err != nil {
4✔
2074
                        t.Fatalf("unable to start notifier %v: %v",
×
2075
                                notifierType, err)
×
2076
                }
×
2077

2078
                for _, txNtfnTest := range txNtfnTests {
52✔
2079
                        for _, scriptDispatch := range []bool{false, true} {
144✔
2080
                                testName := fmt.Sprintf("%v %v", notifierType,
96✔
2081
                                        txNtfnTest.name)
96✔
2082
                                if scriptDispatch {
144✔
2083
                                        testName += " with script dispatch"
48✔
2084
                                }
48✔
2085
                                success := t.Run(testName, func(t *testing.T) {
192✔
2086
                                        txNtfnTest.test(
96✔
2087
                                                miner, notifier, scriptDispatch,
96✔
2088
                                                t,
96✔
2089
                                        )
96✔
2090
                                })
96✔
2091
                                if !success {
96✔
2092
                                        break
×
2093
                                }
2094
                        }
2095
                }
2096

2097
                for _, blockNtfnTest := range blockNtfnTests {
12✔
2098
                        testName := fmt.Sprintf("%v %v", notifierType,
8✔
2099
                                blockNtfnTest.name)
8✔
2100
                        success := t.Run(testName, func(t *testing.T) {
16✔
2101
                                blockNtfnTest.test(miner, notifier, t)
8✔
2102
                        })
8✔
2103
                        if !success {
8✔
2104
                                break
×
2105
                        }
2106
                }
2107

2108
                notifier.Stop()
4✔
2109

4✔
2110
                // Run catchup tests separately since they require restarting
4✔
2111
                // the notifier every time.
4✔
2112
                for _, blockCatchupTest := range blockCatchupTests {
16✔
2113
                        notifier, err = newNotifier()
12✔
2114
                        if err != nil {
12✔
2115
                                t.Fatalf("unable to create %v notifier: %v",
×
2116
                                        notifierType, err)
×
2117
                        }
×
2118

2119
                        testName := fmt.Sprintf("%v %v", notifierType,
12✔
2120
                                blockCatchupTest.name)
12✔
2121

12✔
2122
                        success := t.Run(testName, func(t *testing.T) {
24✔
2123
                                blockCatchupTest.test(miner, notifier, t)
12✔
2124
                        })
12✔
2125
                        if !success {
12✔
2126
                                break
×
2127
                        }
2128
                }
2129
        }
2130
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc