diff --git a/funding/aux_funding.go b/funding/aux_funding.go new file mode 100644 index 000000000..492612145 --- /dev/null +++ b/funding/aux_funding.go @@ -0,0 +1,51 @@ +package funding + +import ( + "github.com/btcsuite/btcd/chaincfg/chainhash" + "github.com/lightningnetwork/lnd/fn" + "github.com/lightningnetwork/lnd/lntypes" + "github.com/lightningnetwork/lnd/lnwallet" + "github.com/lightningnetwork/lnd/msgmux" +) + +// AuxFundingDescResult is a type alias for a function that returns an optional +// aux funding desc. +type AuxFundingDescResult = fn.Result[fn.Option[lnwallet.AuxFundingDesc]] + +// AuxTapscriptResult is a type alias for a function that returns an optional +// tapscript root. +type AuxTapscriptResult = fn.Result[fn.Option[chainhash.Hash]] + +// AuxFundingController permits the implementation of the funding of custom +// channels types. The controller serves as a MsgEndpoint which allows it to +// intercept custom messages, or even the regular funding messages. The +// controller might also pass along an aux funding desc based on an existing +// pending channel ID. +type AuxFundingController interface { + // Endpoint is the embedded interface that signals that the funding + // controller is also a message endpoint. This'll allow it to handle + // custom messages specific to the funding type. + msgmux.Endpoint + + // DescFromPendingChanID takes a pending channel ID, that may already be + // known due to prior custom channel messages, and maybe returns an aux + // funding desc which can be used to modify how a channel is funded. + DescFromPendingChanID(pid PendingChanID, openChan lnwallet.AuxChanState, + keyRing lntypes.Dual[lnwallet.CommitmentKeyRing], + initiator bool) AuxFundingDescResult + + // DeriveTapscriptRoot takes a pending channel ID and maybe returns a + // tapscript root that should be used when creating any MuSig2 sessions + // for a channel. + DeriveTapscriptRoot(PendingChanID) AuxTapscriptResult + + // ChannelReady is called when a channel has been fully opened (multiple + // confirmations) and is ready to be used. This can be used to perform + // any final setup or cleanup. + ChannelReady(openChan lnwallet.AuxChanState) error + + // ChannelFinalized is called when a channel has been fully finalized. + // In this state, we've received the commitment sig from the remote + // party, so we are safe to broadcast the funding transaction. + ChannelFinalized(PendingChanID) error +} diff --git a/funding/manager.go b/funding/manager.go index fb36bd990..3d3ed4c6f 100644 --- a/funding/manager.go +++ b/funding/manager.go @@ -549,6 +549,12 @@ type Config struct { // AuxLeafStore is an optional store that can be used to store auxiliary // leaves for certain custom channel types. AuxLeafStore fn.Option[lnwallet.AuxLeafStore] + + // AuxFundingController is an optional controller that can be used to + // modify the way we handle certain custom channel types. It's also + // able to automatically handle new custom protocol messages related to + // the funding process. + AuxFundingController fn.Option[AuxFundingController] } // Manager acts as an orchestrator/bridge between the wallet's @@ -1626,6 +1632,23 @@ func (f *Manager) fundeeProcessOpenChannel(peer lnpeer.Peer, return } + // At this point, if we have an AuxFundingController active, we'll + // check to see if we have a special tapscript root to use in our + // MuSig funding output. + tapscriptRoot, err := fn.MapOptionZ( + f.cfg.AuxFundingController, + func(c AuxFundingController) AuxTapscriptResult { + return c.DeriveTapscriptRoot(msg.PendingChannelID) + }, + ).Unpack() + if err != nil { + err = fmt.Errorf("error deriving tapscript root: %w", err) + log.Error(err) + f.failFundingFlow(peer, cid, err) + + return + } + req := &lnwallet.InitFundingReserveMsg{ ChainHash: &msg.ChainHash, PendingChanID: msg.PendingChannelID, @@ -1642,6 +1665,7 @@ func (f *Manager) fundeeProcessOpenChannel(peer lnpeer.Peer, ZeroConf: zeroConf, OptionScidAlias: scid, ScidAliasFeature: scidFeatureVal, + TapscriptRoot: tapscriptRoot, } reservation, err := f.cfg.Wallet.InitChannelReservation(req) @@ -4634,6 +4658,23 @@ func (f *Manager) handleInitFundingMsg(msg *InitFundingMsg) { scidFeatureVal = true } + // At this point, if we have an AuxFundingController active, we'll check + // to see if we have a special tapscript root to use in our MuSig2 + // funding output. + tapscriptRoot, err := fn.MapOptionZ( + f.cfg.AuxFundingController, + func(c AuxFundingController) AuxTapscriptResult { + return c.DeriveTapscriptRoot(chanID) + }, + ).Unpack() + if err != nil { + err = fmt.Errorf("error deriving tapscript root: %w", err) + log.Error(err) + msg.Err <- err + + return + } + req := &lnwallet.InitFundingReserveMsg{ ChainHash: &msg.ChainHash, PendingChanID: chanID, @@ -4673,6 +4714,7 @@ func (f *Manager) handleInitFundingMsg(msg *InitFundingMsg) { OptionScidAlias: scid, ScidAliasFeature: scidFeatureVal, Memo: msg.Memo, + TapscriptRoot: tapscriptRoot, } reservation, err := f.cfg.Wallet.InitChannelReservation(req)