Skip to content

Commit

Permalink
chainnotifier: add re-org chan option to RegisterConfirmationsNtfn
Browse files Browse the repository at this point in the history
With this commit we add the WithReOrgChan functional option to the
RegisterConfirmationsNtfn method that allows a caller to be notified
about a chain re-organization of a transaction.
Using WithReOrgChan() might be a breaking change to the caller, as the
behavior of the RegisterConfirmationsNtfn changes:
Before, the notification channel would not continue sending updates
after a confirmation event was received. When enabling WithReOrgChan(),
the notification channel will continue sending updates even after a
first confirmation, and will also send on the provided re-org channel if
a re-org happens. After a re-org a new confirmation event is sent on the
notification channel once the transaction is included in another block
again.

To allow a caller to reliably make sure a transaction reaches a certain
safe depth in the chain, it is recommended to use
RegisterConfirmationsNtfn with numConfs=1 but then keep the notification
channel open until the transaction reaches the desired number of
confirmations (or reac to re-org events if they happen).
  • Loading branch information
guggero committed Jul 26, 2023
1 parent 16208da commit 696cacd
Showing 1 changed file with 49 additions and 13 deletions.
62 changes: 49 additions & 13 deletions chainnotifier_client.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,14 @@ type notifierOptions struct {
// includeBlock if true, then the dispatched confirmation notification
// will include the block that mined the transaction.
includeBlock bool

// reOrgChan if set, will be sent on if the transaction is re-organized
// out of the chain. This channel being set will also imply that we
// don't cancel the notification listener after having received one
// confirmation event. That means the caller manually needs to cancel
// the passed in context to cancel being notified once the required
// number of confirmations have been reached.
reOrgChan chan struct{}
}

// defaultNotifierOptions returns the set of default options for the notifier.
Expand All @@ -38,15 +46,27 @@ func WithIncludeBlock() NotifierOption {
}
}

// WithReOrgChan configures a channel that will be sent on if the transaction is
// re-organized out of the chain. This channel being set will also imply that we
// don't cancel the notification listener after having received one confirmation
// event. That means the caller manually needs to cancel the passed in context
// to cancel being notified once the required number of confirmations have been
// reached.
func WithReOrgChan(reOrgChan chan struct{}) NotifierOption {
return func(o *notifierOptions) {
o.reOrgChan = reOrgChan
}
}

// ChainNotifierClient exposes base lightning functionality.
type ChainNotifierClient interface {
RegisterBlockEpochNtfn(ctx context.Context) (
chan int32, chan error, error)

RegisterConfirmationsNtfn(ctx context.Context, txid *chainhash.Hash,
pkScript []byte, numConfs, heightHint int32,
opts ...NotifierOption) (
chan *chainntnfs.TxConfirmation, chan error, error)
opts ...NotifierOption) (chan *chainntnfs.TxConfirmation,
chan error, error)

RegisterSpendNtfn(ctx context.Context,
outpoint *wire.OutPoint, pkScript []byte, heightHint int32) (
Expand Down Expand Up @@ -153,8 +173,8 @@ func (s *chainNotifierClient) RegisterSpendNtfn(ctx context.Context,

func (s *chainNotifierClient) RegisterConfirmationsNtfn(ctx context.Context,
txid *chainhash.Hash, pkScript []byte, numConfs, heightHint int32,
optFuncs ...NotifierOption) (
chan *chainntnfs.TxConfirmation, chan error, error) {
optFuncs ...NotifierOption) (chan *chainntnfs.TxConfirmation,
chan error, error) {

opts := defaultNotifierOptions()
for _, optFunc := range optFuncs {
Expand All @@ -166,8 +186,7 @@ func (s *chainNotifierClient) RegisterConfirmationsNtfn(ctx context.Context,
txidSlice = txid[:]
}
confStream, err := s.client.RegisterConfirmationsNtfn(
s.chainMac.WithMacaroonAuth(ctx),
&chainrpc.ConfRequest{
s.chainMac.WithMacaroonAuth(ctx), &chainrpc.ConfRequest{
Script: pkScript,
NumConfs: uint32(numConfs),
HeightHint: uint32(heightHint),
Expand Down Expand Up @@ -195,7 +214,7 @@ func (s *chainNotifierClient) RegisterConfirmationsNtfn(ctx context.Context,
}

switch c := confEvent.Event.(type) {
// Script confirmed
// Script confirmed.
case *chainrpc.ConfEvent_Conf:
tx, err := decodeTx(c.Conf.RawTx)
if err != nil {
Expand All @@ -205,7 +224,9 @@ func (s *chainNotifierClient) RegisterConfirmationsNtfn(ctx context.Context,

var block *wire.MsgBlock
if opts.includeBlock {
block, err = decodeBlock(c.Conf.RawBlock)
block, err = decodeBlock(
c.Conf.RawBlock,
)
if err != nil {
errChan <- err
return
Expand All @@ -227,10 +248,26 @@ func (s *chainNotifierClient) RegisterConfirmationsNtfn(ctx context.Context,
TxIndex: c.Conf.TxIndex,
Block: block,
}
return

// Ignore reorg events, not supported.
// If we're running in re-org aware mode, then
// we don't return here, since we might want to
// be informed about the new block we got
// confirmed in after a re-org.
if opts.reOrgChan == nil {
return
}

// On a re-org, we just need to signal, we don't have
// any additional information. But we only signal if the
// caller requested to be notified about re-orgs.
case *chainrpc.ConfEvent_Reorg:
if opts.reOrgChan != nil {
select {
case opts.reOrgChan <- struct{}{}:
case <-ctx.Done():
return
}
}
continue

// Nil event, should never happen.
Expand All @@ -240,9 +277,8 @@ func (s *chainNotifierClient) RegisterConfirmationsNtfn(ctx context.Context,

// Unexpected type.
default:
errChan <- fmt.Errorf(
"conf event has unexpected type",
)
errChan <- fmt.Errorf("conf event has " +
"unexpected type")
return
}
}
Expand Down

0 comments on commit 696cacd

Please sign in to comment.