mirror of
https://github.com/lightningnetwork/lnd.git
synced 2024-11-19 09:53:54 +01:00
chainntnfs: add ChainNotifier test interface for test methods
TestChainNotifier wraps the ChainNotifier interface to allow adding additional testing methods with access to private fields in the notifiers. These testing methods are only compiled when the build tag "debug" is set. UnsafeStart allows starting a notifier with a specified best block. UnsafeStart is useful for the purpose of testing cases where a notifier's best block is out of date when it receives a new block.
This commit is contained in:
parent
79cbea1c9c
commit
b3a5b3b576
77
chainntnfs/bitcoindnotify/bitcoind_debug.go
Normal file
77
chainntnfs/bitcoindnotify/bitcoind_debug.go
Normal file
@ -0,0 +1,77 @@
|
||||
package bitcoindnotify
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/btcsuite/btcd/chaincfg/chainhash"
|
||||
"github.com/btcsuite/btcwallet/chain"
|
||||
"github.com/lightningnetwork/lnd/chainntnfs"
|
||||
)
|
||||
|
||||
// UnsafeStart starts the notifier with a specified best height and optional
|
||||
// best hash. Its bestBlock and txConfNotifier are initialized with
|
||||
// bestHeight and optionally bestHash. The parameter generateBlocks is
|
||||
// necessary for the bitcoind notifier to ensure we drain all notifications up
|
||||
// to syncHeight, since if they are generated ahead of UnsafeStart the chainConn
|
||||
// may start up with an outdated best block and miss sending ntfns. Used for
|
||||
// testing.
|
||||
func (b *BitcoindNotifier) UnsafeStart(bestHeight int32, bestHash *chainhash.Hash,
|
||||
syncHeight int32, generateBlocks func() error) error {
|
||||
|
||||
// Connect to bitcoind, and register for notifications on connected,
|
||||
// and disconnected blocks.
|
||||
if err := b.chainConn.Start(); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := b.chainConn.NotifyBlocks(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
b.txConfNotifier = chainntnfs.NewTxConfNotifier(
|
||||
uint32(bestHeight), reorgSafetyLimit)
|
||||
|
||||
if generateBlocks != nil {
|
||||
// Ensure no block notifications are pending when we start the
|
||||
// notification dispatcher goroutine.
|
||||
|
||||
// First generate the blocks, then drain the notifications
|
||||
// for the generated blocks.
|
||||
if err := generateBlocks(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
timeout := time.After(60 * time.Second)
|
||||
loop:
|
||||
for {
|
||||
select {
|
||||
case ntfn := <-b.chainConn.Notifications():
|
||||
switch update := ntfn.(type) {
|
||||
case chain.BlockConnected:
|
||||
if update.Height >= syncHeight {
|
||||
break loop
|
||||
}
|
||||
}
|
||||
case <-timeout:
|
||||
return fmt.Errorf("unable to catch up to height %d",
|
||||
syncHeight)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Run notificationDispatcher after setting the notifier's best block
|
||||
// to avoid a race condition.
|
||||
b.bestBlock = chainntnfs.BlockEpoch{Height: bestHeight, Hash: bestHash}
|
||||
if bestHash == nil {
|
||||
hash, err := b.chainConn.GetBlockHash(int64(bestHeight))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
b.bestBlock.Hash = hash
|
||||
}
|
||||
|
||||
b.wg.Add(1)
|
||||
go b.notificationDispatcher()
|
||||
|
||||
return nil
|
||||
}
|
77
chainntnfs/btcdnotify/btcd_debug.go
Normal file
77
chainntnfs/btcdnotify/btcd_debug.go
Normal file
@ -0,0 +1,77 @@
|
||||
package btcdnotify
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/btcsuite/btcd/chaincfg/chainhash"
|
||||
"github.com/lightningnetwork/lnd/chainntnfs"
|
||||
)
|
||||
|
||||
// UnsafeStart starts the notifier with a specified best height and optional
|
||||
// best hash. Its bestBlock and txConfNotifier are initialized with
|
||||
// bestHeight and optionally bestHash. The parameter generateBlocks is
|
||||
// necessary for the bitcoind notifier to ensure we drain all notifications up
|
||||
// to syncHeight, since if they are generated ahead of UnsafeStart the chainConn
|
||||
// may start up with an outdated best block and miss sending ntfns. Used for
|
||||
// testing.
|
||||
func (b *BtcdNotifier) UnsafeStart(bestHeight int32, bestHash *chainhash.Hash,
|
||||
syncHeight int32, generateBlocks func() error) error {
|
||||
|
||||
// Connect to btcd, and register for notifications on connected, and
|
||||
// disconnected blocks.
|
||||
if err := b.chainConn.Connect(20); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := b.chainConn.NotifyBlocks(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
b.txConfNotifier = chainntnfs.NewTxConfNotifier(
|
||||
uint32(bestHeight), reorgSafetyLimit)
|
||||
|
||||
b.chainUpdates.Start()
|
||||
b.txUpdates.Start()
|
||||
|
||||
if generateBlocks != nil {
|
||||
// Ensure no block notifications are pending when we start the
|
||||
// notification dispatcher goroutine.
|
||||
|
||||
// First generate the blocks, then drain the notifications
|
||||
// for the generated blocks.
|
||||
if err := generateBlocks(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
timeout := time.After(60 * time.Second)
|
||||
loop:
|
||||
for {
|
||||
select {
|
||||
case ntfn := <-b.chainUpdates.ChanOut():
|
||||
lastReceivedNtfn := ntfn.(*chainUpdate)
|
||||
if lastReceivedNtfn.blockHeight >= syncHeight {
|
||||
break loop
|
||||
}
|
||||
case <-timeout:
|
||||
return fmt.Errorf("unable to catch up to height %d",
|
||||
syncHeight)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Run notificationDispatcher after setting the notifier's best block
|
||||
// to avoid a race condition.
|
||||
b.bestBlock = chainntnfs.BlockEpoch{Height: bestHeight, Hash: bestHash}
|
||||
if bestHash == nil {
|
||||
hash, err := b.chainConn.GetBlockHash(int64(bestHeight))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
b.bestBlock.Hash = hash
|
||||
}
|
||||
|
||||
b.wg.Add(1)
|
||||
go b.notificationDispatcher()
|
||||
|
||||
return nil
|
||||
}
|
13
chainntnfs/interface_debug.go
Normal file
13
chainntnfs/interface_debug.go
Normal file
@ -0,0 +1,13 @@
|
||||
package chainntnfs
|
||||
|
||||
import "github.com/btcsuite/btcd/chaincfg/chainhash"
|
||||
|
||||
// TestChainNotifier enables the use of methods that are only present during
|
||||
// testing for ChainNotifiers.
|
||||
type TestChainNotifier interface {
|
||||
ChainNotifier
|
||||
|
||||
// UnsafeStart enables notifiers to start up with a specific best block.
|
||||
// Used for testing.
|
||||
UnsafeStart(int32, *chainhash.Hash, int32, func() error) error
|
||||
}
|
99
chainntnfs/neutrinonotify/neutrino_debug.go
Normal file
99
chainntnfs/neutrinonotify/neutrino_debug.go
Normal file
@ -0,0 +1,99 @@
|
||||
package neutrinonotify
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/btcsuite/btcd/chaincfg/chainhash"
|
||||
"github.com/btcsuite/btcd/rpcclient"
|
||||
"github.com/btcsuite/btcwallet/waddrmgr"
|
||||
"github.com/lightninglabs/neutrino"
|
||||
"github.com/lightningnetwork/lnd/chainntnfs"
|
||||
)
|
||||
|
||||
// UnsafeStart starts the notifier with a specified best height and optional
|
||||
// best hash. Its bestHeight, txConfNotifier and neutrino node are initialized
|
||||
// with bestHeight. The parameter generateBlocks is necessary for the
|
||||
// bitcoind notifier to ensure we drain all notifications up to syncHeight,
|
||||
// since if they are generated ahead of UnsafeStart the chainConn may start
|
||||
// up with an outdated best block and miss sending ntfns. Used for testing.
|
||||
func (n *NeutrinoNotifier) UnsafeStart(bestHeight int32, bestHash *chainhash.Hash,
|
||||
syncHeight int32, generateBlocks func() error) error {
|
||||
|
||||
// We'll obtain the latest block height of the p2p node. We'll
|
||||
// start the auto-rescan from this point. Once a caller actually wishes
|
||||
// to register a chain view, the rescan state will be rewound
|
||||
// accordingly.
|
||||
header, height, err := n.p2pNode.BlockHeaders.ChainTip()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
startingPoint := &waddrmgr.BlockStamp{
|
||||
Height: int32(height),
|
||||
Hash: header.BlockHash(),
|
||||
}
|
||||
|
||||
// Next, we'll create our set of rescan options. Currently it's
|
||||
// required that a user MUST set an addr/outpoint/txid when creating a
|
||||
// rescan. To get around this, we'll add a "zero" outpoint, that won't
|
||||
// actually be matched.
|
||||
var zeroInput neutrino.InputWithScript
|
||||
rescanOptions := []neutrino.RescanOption{
|
||||
neutrino.StartBlock(startingPoint),
|
||||
neutrino.QuitChan(n.quit),
|
||||
neutrino.NotificationHandlers(
|
||||
rpcclient.NotificationHandlers{
|
||||
OnFilteredBlockConnected: n.onFilteredBlockConnected,
|
||||
OnFilteredBlockDisconnected: n.onFilteredBlockDisconnected,
|
||||
},
|
||||
),
|
||||
neutrino.WatchInputs(zeroInput),
|
||||
}
|
||||
|
||||
n.txConfNotifier = chainntnfs.NewTxConfNotifier(
|
||||
uint32(bestHeight), reorgSafetyLimit)
|
||||
|
||||
n.chainConn = &NeutrinoChainConn{n.p2pNode}
|
||||
|
||||
// Finally, we'll create our rescan struct, start it, and launch all
|
||||
// the goroutines we need to operate this ChainNotifier instance.
|
||||
n.chainView = n.p2pNode.NewRescan(rescanOptions...)
|
||||
n.rescanErr = n.chainView.Start()
|
||||
|
||||
n.chainUpdates.Start()
|
||||
|
||||
if generateBlocks != nil {
|
||||
// Ensure no block notifications are pending when we start the
|
||||
// notification dispatcher goroutine.
|
||||
|
||||
// First generate the blocks, then drain the notifications
|
||||
// for the generated blocks.
|
||||
if err := generateBlocks(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
timeout := time.After(60 * time.Second)
|
||||
loop:
|
||||
for {
|
||||
select {
|
||||
case ntfn := <-n.chainUpdates.ChanOut():
|
||||
lastReceivedNtfn := ntfn.(*filteredBlock)
|
||||
if lastReceivedNtfn.height >= uint32(syncHeight) {
|
||||
break loop
|
||||
}
|
||||
case <-timeout:
|
||||
return fmt.Errorf("unable to catch up to height %d",
|
||||
syncHeight)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Run notificationDispatcher after setting the notifier's best height
|
||||
// to avoid a race condition.
|
||||
n.bestHeight = uint32(bestHeight)
|
||||
|
||||
n.wg.Add(1)
|
||||
go n.notificationDispatcher()
|
||||
|
||||
return nil
|
||||
}
|
Loading…
Reference in New Issue
Block a user