From 6dff1bd5ded44537dbc07a899ceb8c821c9977d9 Mon Sep 17 00:00:00 2001 From: George Tsagkarelis Date: Thu, 28 Aug 2025 13:45:29 +0200 Subject: [PATCH] htlcswitch+peer: set and read aux custom records This is the final step, we actually call the interface and either provide or retrieve the custom features over the message. We also notify the aux components when channel reestablish is received. --- htlcswitch/link.go | 21 +++++++++++++++++++++ peer/brontide.go | 47 ++++++++++++++++++++++++++++++++++++++++++++-- server.go | 1 + 3 files changed, 67 insertions(+), 2 deletions(-) diff --git a/htlcswitch/link.go b/htlcswitch/link.go index 2d1dd7de0..4c81964cc 100644 --- a/htlcswitch/link.go +++ b/htlcswitch/link.go @@ -298,6 +298,11 @@ type ChannelLinkConfig struct { // used to manage the bandwidth of the link. AuxTrafficShaper fn.Option[AuxTrafficShaper] + // AuxChannelNegotiator is an optional interface that allows aux channel + // implementations to inject and process custom records over channel + // related wire messages. + AuxChannelNegotiator fn.Option[lnwallet.AuxChannelNegotiator] + // QuiescenceTimeout is the max duration that the channel can be // quiesced. Any dependent protocols (dynamic commitments, splicing, // etc.) must finish their operations under this timeout value, @@ -987,6 +992,22 @@ func (l *channelLink) syncChanStates(ctx context.Context) error { // In any case, we'll then process their ChanSync message. l.log.Info("received re-establishment message from remote side") + // If we have an AuxChannelNegotiator we notify any external + // component for this message. This serves as a notification + // that the reestablish message was received. + l.cfg.AuxChannelNegotiator.WhenSome( + func(acn lnwallet.AuxChannelNegotiator) { + fundingPoint := l.channel.ChannelPoint() + cid := lnwire.NewChanIDFromOutPoint( + fundingPoint, + ) + + acn.ProcessReestablish( + cid, l.cfg.Peer.PubKey(), + ) + }, + ) + var ( openedCircuits []CircuitKey closedCircuits []CircuitKey diff --git a/peer/brontide.go b/peer/brontide.go index 57e340fb0..1ea16d1e8 100644 --- a/peer/brontide.go +++ b/peer/brontide.go @@ -456,6 +456,11 @@ type Config struct { // used to modify the way the co-op close transaction is constructed. AuxChanCloser fn.Option[chancloser.AuxChanCloser] + // AuxChannelNegotiator is an optional interface that allows aux channel + // implementations to inject and process custom records over channel + // related wire messages. + AuxChannelNegotiator fn.Option[lnwallet.AuxChannelNegotiator] + // ShouldFwdExpEndorsement is a closure that indicates whether // experimental endorsement signals should be set. ShouldFwdExpEndorsement func() bool @@ -1454,8 +1459,9 @@ func (p *Brontide) addLink(chanPoint *wire.OutPoint, ShouldFwdExpEndorsement: p.cfg.ShouldFwdExpEndorsement, DisallowQuiescence: p.cfg.DisallowQuiescence || !p.remoteFeatures.HasFeature(lnwire.QuiescenceOptional), - AuxTrafficShaper: p.cfg.AuxTrafficShaper, - QuiescenceTimeout: p.cfg.QuiescenceTimeout, + AuxTrafficShaper: p.cfg.AuxTrafficShaper, + AuxChannelNegotiator: p.cfg.AuxChannelNegotiator, + QuiescenceTimeout: p.cfg.QuiescenceTimeout, } // Before adding our new link, purge the switch of any pending or live @@ -4537,6 +4543,19 @@ func (p *Brontide) handleInitMsg(msg *lnwire.Init) error { return fmt.Errorf("data loss protection required") } + // If we have an AuxChannelNegotiator and the peer sent aux features, + // process them. + p.cfg.AuxChannelNegotiator.WhenSome( + func(acn lnwallet.AuxChannelNegotiator) { + err = acn.ProcessInitRecords( + p.cfg.PubKeyBytes, msg.CustomRecords.Copy(), + ) + }, + ) + if err != nil { + return fmt.Errorf("could not process init records: %w", err) + } + return nil } @@ -4597,6 +4616,30 @@ func (p *Brontide) sendInitMsg(legacyChan bool) error { features.RawFeatureVector, ) + var err error + + // If we have an AuxChannelNegotiator, get custom feature bits to + // include in the init message. + p.cfg.AuxChannelNegotiator.WhenSome( + func(negotiator lnwallet.AuxChannelNegotiator) { + var auxRecords lnwire.CustomRecords + auxRecords, err = negotiator.GetInitRecords( + p.cfg.PubKeyBytes, + ) + if err != nil { + p.log.Warnf("Failed to get aux init features: "+ + "%v", err) + return + } + + mergedRecs := msg.CustomRecords.MergedCopy(auxRecords) + msg.CustomRecords = mergedRecs + }, + ) + if err != nil { + return err + } + return p.writeMessage(msg) } diff --git a/server.go b/server.go index 44be180ea..33b8fd787 100644 --- a/server.go +++ b/server.go @@ -4431,6 +4431,7 @@ func (s *server) peerConnected(conn net.Conn, connReq *connmgr.ConnReq, AuxChanCloser: s.implCfg.AuxChanCloser, AuxResolver: s.implCfg.AuxContractResolver, AuxTrafficShaper: s.implCfg.TrafficShaper, + AuxChannelNegotiator: s.implCfg.AuxChannelNegotiator, ShouldFwdExpEndorsement: func() bool { if s.cfg.ProtocolOptions.NoExperimentalEndorsement() { return false