From 4fff0d8401712f449b6e6355efc47859fba0f53c Mon Sep 17 00:00:00 2001 From: Olaoluwa Osuntokun Date: Tue, 5 Mar 2024 00:08:11 -0600 Subject: [PATCH] peer: create ErrorReporter implementation for rbf-fsm --- peer/brontide.go | 76 +++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 69 insertions(+), 7 deletions(-) diff --git a/peer/brontide.go b/peer/brontide.go index 816fd37d7..559d4fd5a 100644 --- a/peer/brontide.go +++ b/peer/brontide.go @@ -3432,6 +3432,70 @@ func chooseAddr(addr lnwire.DeliveryAddress) fn.Option[lnwire.DeliveryAddress] { return fn.Some(addr) } +// chanErrorReporter is a simple implementation of the +// chancloser.ErrorReporter. This is bound to a single channel by the channel +// ID. +type chanErrorReporter struct { + chanID lnwire.ChannelID + peer *Brontide +} + +// newChanErrorReporter creates a new instance of the chanErrorReporter. +func newChanErrorReporter(chanID lnwire.ChannelID, + peer *Brontide) *chanErrorReporter { + + return &chanErrorReporter{ + chanID: chanID, + peer: peer, + } +} + +// ReportError is a method that's used to report an error that occurred during +// state machine execution. This is used by the RBF close state machine to +// terminate the state machine and send an error to the remote peer. +// +// This is a part of the chancloser.ErrorReporter interface. +func (c *chanErrorReporter) ReportError(chanErr error) { + var errMsg []byte + if errors.Is(chanErr, chancloser.ErrInvalidStateTransition) { + errMsg = []byte("unexpected protocol message") + } else { + errMsg = []byte(chanErr.Error()) + } + + err := c.peer.SendMessageLazy(false, &lnwire.Error{ + ChanID: c.chanID, + Data: errMsg, + }) + if err != nil { + c.peer.log.Warnf("unable to send error message to peer: %v", + err) + } + + // After we send the error message to the peer, we'll re-initialize the + // coop close state machine as they may send a shutdown message to + // retry the coop close. + lnChan, ok := c.peer.activeChannels.Load(c.chanID) + if !ok { + return + } + + if lnChan == nil { + c.peer.log.Debugf("channel %v is pending, not "+ + "re-initializing coop close state machine", + c.chanID) + + return + } + + if _, err := c.peer.initRbfChanCloser(lnChan); err != nil { + delete(c.peer.activeChanCloses, c.chanID) + + c.peer.log.Errorf("unable to init RBF chan closer after "+ + "error case: %v", err) + } +} + // initRbfChanCloser initializes the channel closer for a channel that // is using the new RBF based co-op close protocol. This only creates the chan // closer, but doesn't attempt to trigger any manual state transitions. @@ -3503,9 +3567,6 @@ func (p *Brontide) initRbfChanCloser( ), } - // TODO(roasbeef): edge case here to re-enable a channel before both - // shutdown sent? - daemonAdapters := NewLndDaemonAdapters(LndAdapterCfg{ MsgSender: newPeerMsgSender(peerPub, p), TxBroadcaster: p.cfg.Wallet, @@ -3513,10 +3574,11 @@ func (p *Brontide) initRbfChanCloser( }) protoCfg := chancloser.RbfChanCloserCfg{ - Daemon: daemonAdapters, - InitialState: &initialState, - Env: &env, - InitEvent: fn.Some[protofsm.DaemonEvent](&spendEvent), + Daemon: daemonAdapters, + InitialState: &initialState, + Env: &env, + InitEvent: fn.Some[protofsm.DaemonEvent](&spendEvent), + ErrorReporter: newChanErrorReporter(chanID, p), MsgMapper: fn.Some[protofsm.MsgMapper[chancloser.ProtocolEvent]]( //nolint:ll msgMapper, ),