chainntfns: document the BtcdNotifier implementation of ChainNotifier

This commit is contained in:
Olaoluwa Osuntokun 2016-02-26 17:31:07 -08:00
parent 77d37298f4
commit 9b9f792f10
1 changed files with 45 additions and 27 deletions

View File

@ -14,10 +14,12 @@ import (
"github.com/lightningnetwork/lnd/chainntfs" "github.com/lightningnetwork/lnd/chainntfs"
) )
// BtcdNotifier... // BtcdNotifier implements the ChainNotifier interface using btcd's websockets
// notifications. Multiple concurrent clients are supported. All notifications
// are achieved via non-blocking sends on client channels.
type BtcdNotifier struct { type BtcdNotifier struct {
started int32 // To be used atomically started int32 // To be used atomically.
stopped int32 // To be used atomically stopped int32 // To be used atomically.
chainConn *btcrpcclient.Client chainConn *btcrpcclient.Client
@ -37,16 +39,14 @@ type BtcdNotifier struct {
quit chan struct{} quit chan struct{}
} }
// Ensure BtcdNotifier implements the ChainNotifier interface at compile time.
var _ chainntnfs.ChainNotifier = (*BtcdNotifier)(nil) var _ chainntnfs.ChainNotifier = (*BtcdNotifier)(nil)
// NewBtcdNotifier... // NewBtcdNotifier returns a new BtcdNotifier instance. This function assumes
// TODO(roasbeef): // the btcd node detailed in the passed configuration is already running, and
// * when asked for spent, request via client // willing to accept new websockets clients.
//func NewBtcdNotifier(ntfnSource *btcwallet.NotificationServer,
// chainConn *chain.RPCClient) (*BtcdNotifier, error) {
func NewBtcdNotifier(config *btcrpcclient.ConnConfig) (*BtcdNotifier, error) { func NewBtcdNotifier(config *btcrpcclient.ConnConfig) (*BtcdNotifier, error) {
notifier := &BtcdNotifier{ notifier := &BtcdNotifier{
notificationRegistry: make(chan interface{}), notificationRegistry: make(chan interface{}),
spendNotifications: make(map[wire.OutPoint]*spendNotification), spendNotifications: make(map[wire.OutPoint]*spendNotification),
@ -66,6 +66,8 @@ func NewBtcdNotifier(config *btcrpcclient.ConnConfig) (*BtcdNotifier, error) {
OnRedeemingTx: notifier.onRedeemingTx, OnRedeemingTx: notifier.onRedeemingTx,
} }
// Disable connecting to btcd within the btcrpcclient.New method. We defer
// establishing the connection to our .Start() method.
config.DisableConnectOnNew = true config.DisableConnectOnNew = true
config.DisableAutoReconnect = false config.DisableAutoReconnect = false
chainConn, err := btcrpcclient.New(config, ntfnCallbacks) chainConn, err := btcrpcclient.New(config, ntfnCallbacks)
@ -77,17 +79,19 @@ func NewBtcdNotifier(config *btcrpcclient.ConnConfig) (*BtcdNotifier, error) {
return notifier, nil return notifier, nil
} }
// Start... // Start connects to the running btcd node over websockets, registers for block
// notifications, and finally launches all related helper goroutines.
func (b *BtcdNotifier) Start() error { func (b *BtcdNotifier) Start() error {
// Already started? // Already started?
if atomic.AddInt32(&b.started, 1) != 1 { if atomic.AddInt32(&b.started, 1) != 1 {
return nil return nil
} }
// Connect to btcd, and register for notifications on connected, and
// disconnected blocks.
if err := b.chainConn.Connect(20); err != nil { if err := b.chainConn.Connect(20); err != nil {
return err return err
} }
if err := b.chainConn.NotifyBlocks(); err != nil { if err := b.chainConn.NotifyBlocks(); err != nil {
return err return err
} }
@ -98,13 +102,15 @@ func (b *BtcdNotifier) Start() error {
return nil return nil
} }
// Stop... // Stop shutsdown the BtcdNotifier.
func (b *BtcdNotifier) Stop() error { func (b *BtcdNotifier) Stop() error {
// Already shutting down? // Already shutting down?
if atomic.AddInt32(&b.stopped, 1) != 1 { if atomic.AddInt32(&b.stopped, 1) != 1 {
return nil return nil
} }
// Shutdown the rpc client, this gracefully disconnects from btcd, and
// cleans up all related resources.
b.chainConn.Shutdown() b.chainConn.Shutdown()
close(b.quit) close(b.quit)
@ -123,13 +129,14 @@ func (b *BtcdNotifier) Stop() error {
return nil return nil
} }
// connectedBlock... // blockNtfn packages a notification of a connected/disconnected block along
// with its height at the time.
type blockNtfn struct { type blockNtfn struct {
sha *wire.ShaHash sha *wire.ShaHash
height int32 height int32
} }
// onBlockConnected... // onBlockConnected implements on OnBlockConnected callback for btcrpcclient.
func (b *BtcdNotifier) onBlockConnected(hash *wire.ShaHash, height int32, t time.Time) { func (b *BtcdNotifier) onBlockConnected(hash *wire.ShaHash, height int32, t time.Time) {
select { select {
case b.connectedBlockHashes <- &blockNtfn{hash, height}: case b.connectedBlockHashes <- &blockNtfn{hash, height}:
@ -137,12 +144,12 @@ func (b *BtcdNotifier) onBlockConnected(hash *wire.ShaHash, height int32, t time
} }
} }
// onBlockDisconnected... // onBlockDisconnected implements on OnBlockDisconnected callback for btcrpcclient.
func (b *BtcdNotifier) onBlockDisconnected(hash *wire.ShaHash, height int32, t time.Time) { func (b *BtcdNotifier) onBlockDisconnected(hash *wire.ShaHash, height int32, t time.Time) {
b.onBlockDisconnected(hash, height, t) b.onBlockDisconnected(hash, height, t)
} }
// onRedeemingTx... // onRedeemingTx implements on OnRedeemingTx callback for btcrpcclient.
func (b *BtcdNotifier) onRedeemingTx(transaction *btcutil.Tx, details *btcjson.BlockDetails) { func (b *BtcdNotifier) onRedeemingTx(transaction *btcutil.Tx, details *btcjson.BlockDetails) {
select { select {
case b.relevantTxs <- transaction: case b.relevantTxs <- transaction:
@ -150,7 +157,8 @@ func (b *BtcdNotifier) onRedeemingTx(transaction *btcutil.Tx, details *btcjson.B
} }
} }
// notificationDispatcher... // notificationDispatcher is the primary goroutine which handles client
// notification registrations, as well as notification dispatches.
func (b *BtcdNotifier) notificationDispatcher() { func (b *BtcdNotifier) notificationDispatcher() {
out: out:
for { for {
@ -222,7 +230,9 @@ out:
b.wg.Done() b.wg.Done()
} }
// notifyConfs... // notifyConfs examines the current confirmation heap, sending off any
// notifications which have been triggered by the connection of a new block at
// newBlockHeight.
func (b *BtcdNotifier) notifyConfs(newBlockHeight int32) { func (b *BtcdNotifier) notifyConfs(newBlockHeight int32) {
// If the heap is empty, we have nothing to do. // If the heap is empty, we have nothing to do.
if b.confHeap.Len() == 0 { if b.confHeap.Len() == 0 {
@ -249,8 +259,11 @@ func (b *BtcdNotifier) notifyConfs(newBlockHeight int32) {
heap.Push(b.confHeap, nextConf) heap.Push(b.confHeap, nextConf)
} }
// checkConfirmationTrigger... // checkConfirmationTrigger determines if the passed txSha included at blockHeight
// TODO(roasbeef): perheps lookup, then track by inputs instead? // triggers any single confirmation notifications. In the event that the txid
// matches, yet needs additional confirmations, it is added to the confirmation
// heap to be triggered at a later time.
// TODO(roasbeef): perhaps lookup, then track by inputs instead?
func (b *BtcdNotifier) checkConfirmationTrigger(txSha *wire.ShaHash, blockHeight int32) { func (b *BtcdNotifier) checkConfirmationTrigger(txSha *wire.ShaHash, blockHeight int32) {
// If a confirmation notification has been registered // If a confirmation notification has been registered
// for this txid, then either trigger a notification // for this txid, then either trigger a notification
@ -280,15 +293,18 @@ func (b *BtcdNotifier) checkConfirmationTrigger(txSha *wire.ShaHash, blockHeight
} }
} }
// spendNotification.... // spendNotification couples a target outpoint along with the channel used for
// notifications once a spend of the outpoint has been detected.
type spendNotification struct { type spendNotification struct {
targetOutpoint *wire.OutPoint targetOutpoint *wire.OutPoint
spendChan chan *chainntnfs.SpendDetail spendChan chan *chainntnfs.SpendDetail
} }
// RegisterSpendNotification... // RegisterSpendNotification registers an intent to be notified once the target
// NOTE: eventChan MUST be buffered // outpoint has been spent by a transaction on-chain. Once a spend of the target
// outpoint has been detected, the details of the spending event will be sent
// across the 'Spend' channel.
func (b *BtcdNotifier) RegisterSpendNtfn(outpoint *wire.OutPoint) (*chainntnfs.SpendEvent, error) { func (b *BtcdNotifier) RegisterSpendNtfn(outpoint *wire.OutPoint) (*chainntnfs.SpendEvent, error) {
if err := b.chainConn.NotifySpent([]*wire.OutPoint{outpoint}); err != nil { if err := b.chainConn.NotifySpent([]*wire.OutPoint{outpoint}); err != nil {
return nil, err return nil, err
@ -304,8 +320,8 @@ func (b *BtcdNotifier) RegisterSpendNtfn(outpoint *wire.OutPoint) (*chainntnfs.S
return &chainntnfs.SpendEvent{ntfn.spendChan}, nil return &chainntnfs.SpendEvent{ntfn.spendChan}, nil
} }
// confirmationNotification... // confirmationNotification represents a client's intent to receive a
// TODO(roasbeef): re-org funny business // notification once the target txid reaches numConfirmations confirmations.
type confirmationsNotification struct { type confirmationsNotification struct {
txid *wire.ShaHash txid *wire.ShaHash
@ -313,10 +329,12 @@ type confirmationsNotification struct {
numConfirmations uint32 numConfirmations uint32
finConf chan struct{} finConf chan struct{}
negativeConf chan int32 negativeConf chan int32 // TODO(roasbeef): re-org funny business
} }
// RegisterConfirmationsNotification... // RegisterConfirmationsNotification registers a notification with BtcdNotifier
// which will be triggered once the txid reaches numConfs number of
// confirmations.
func (b *BtcdNotifier) RegisterConfirmationsNtfn(txid *wire.ShaHash, func (b *BtcdNotifier) RegisterConfirmationsNtfn(txid *wire.ShaHash,
numConfs uint32) (*chainntnfs.ConfirmationEvent, error) { numConfs uint32) (*chainntnfs.ConfirmationEvent, error) {