diff --git a/funding/aux_funding.go b/funding/aux_funding.go index c13be8038..6c1c2b9b7 100644 --- a/funding/aux_funding.go +++ b/funding/aux_funding.go @@ -1,6 +1,7 @@ package funding import ( + "github.com/btcsuite/btcd/chaincfg/chainhash" "github.com/lightningnetwork/lnd/fn" "github.com/lightningnetwork/lnd/lnwallet" "github.com/lightningnetwork/lnd/protofsm" @@ -23,4 +24,9 @@ type AuxFundingController interface { // TODO(roasbeef): erorr on validation if fail due to invalid root // match? DescFromPendingChanID(PendingChanID) fn.Option[lnwallet.AuxFundingDesc] + + // 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) fn.Option[chainhash.Hash] } diff --git a/funding/manager.go b/funding/manager.go index 05f03deed..9859ef8d2 100644 --- a/funding/manager.go +++ b/funding/manager.go @@ -1620,11 +1620,11 @@ func (f *Manager) fundeeProcessOpenChannel(peer lnpeer.Peer, } // At this point, if we have an AuxFundingController active, we'll - // check to see if we have any aux info that we should carry along for - // this pid. - auxFundingDesc := fn.MapOption( - func(a AuxFundingController) fn.Option[lnwallet.AuxFundingDesc] { - return a.DescFromPendingChanID(msg.PendingChannelID) + // check to see if we have a special tapscript root to use in our + // musig2 funding output. + tapscriptRoot := fn.MapOption( + func(a AuxFundingController) fn.Option[chainhash.Hash] { + return a.DeriveTapscriptRoot(msg.PendingChannelID) }, )(f.cfg.AuxFundingController) @@ -1644,7 +1644,7 @@ func (f *Manager) fundeeProcessOpenChannel(peer lnpeer.Peer, ZeroConf: zeroConf, OptionScidAlias: scid, ScidAliasFeature: scidFeatureVal, - AuxFundingDesc: fn.FlattenOption(auxFundingDesc), + TapscriptRoot: fn.FlattenOption(tapscriptRoot), } reservation, err := f.cfg.Wallet.InitChannelReservation(req) @@ -4620,11 +4620,11 @@ func (f *Manager) handleInitFundingMsg(msg *InitFundingMsg) { } // At this point, if we have an AuxFundingController active, we'll - // check to see if we have any aux info that we should carry along for - // this pid. - auxFundingDesc := fn.MapOption( - func(a AuxFundingController) fn.Option[lnwallet.AuxFundingDesc] { - return a.DescFromPendingChanID(chanID) + // check to see if we have a special tapscript root to use in our + // musig2 funding output. + tapscriptRoot := fn.MapOption( + func(a AuxFundingController) fn.Option[chainhash.Hash] { + return a.DeriveTapscriptRoot(chanID) }, )(f.cfg.AuxFundingController) @@ -4651,7 +4651,7 @@ func (f *Manager) handleInitFundingMsg(msg *InitFundingMsg) { OptionScidAlias: scid, ScidAliasFeature: scidFeatureVal, Memo: msg.Memo, - AuxFundingDesc: fn.FlattenOption(auxFundingDesc), + TapscriptRoot: fn.FlattenOption(tapscriptRoot), } reservation, err := f.cfg.Wallet.InitChannelReservation(req) diff --git a/lnwallet/chanfunding/psbt_assembler.go b/lnwallet/chanfunding/psbt_assembler.go index b2686a225..8a9f41f3c 100644 --- a/lnwallet/chanfunding/psbt_assembler.go +++ b/lnwallet/chanfunding/psbt_assembler.go @@ -10,6 +10,7 @@ import ( "github.com/btcsuite/btcd/btcutil" "github.com/btcsuite/btcd/btcutil/psbt" "github.com/btcsuite/btcd/chaincfg" + "github.com/btcsuite/btcd/chaincfg/chainhash" "github.com/btcsuite/btcd/txscript" "github.com/btcsuite/btcd/wire" "github.com/lightningnetwork/lnd/fn" @@ -164,6 +165,13 @@ func (i *PsbtIntent) BindKeys(localKey *keychain.KeyDescriptor, i.State = PsbtOutputKnown } +// BindTapscriptRoot takes an optional tapscript root and binds it to the +// underlying funding intent. This only applies to musig2 channels, and will be +// used to make the musig2 funding output. +func (i *PsbtIntent) BindTapscriptRoot(root fn.Option[chainhash.Hash]) { + i.tapscriptRoot = root +} + // FundingParams returns the parameters that are necessary to start funding the // channel output this intent was created for. It returns the P2WSH funding // address, the exact funding amount and a PSBT packet that contains exactly one diff --git a/lnwallet/reservation.go b/lnwallet/reservation.go index f511ffb61..1e65f29b3 100644 --- a/lnwallet/reservation.go +++ b/lnwallet/reservation.go @@ -16,7 +16,6 @@ import ( "github.com/lightningnetwork/lnd/keychain" "github.com/lightningnetwork/lnd/lnwallet/chanfunding" "github.com/lightningnetwork/lnd/lnwire" - "github.com/lightningnetwork/lnd/tlv" ) // CommitmentType is an enum indicating the commitment type we should use for @@ -419,7 +418,7 @@ func NewChannelReservation(capacity, localFundingAmt btcutil.Amount, chanType |= channeldb.ScidAliasFeatureBit } - if req.AuxFundingDesc.IsSome() { + if req.TapscriptRoot.IsSome() { chanType |= channeldb.TapscriptRootBit } @@ -444,39 +443,25 @@ func NewChannelReservation(capacity, localFundingAmt btcutil.Amount, RemoteBalance: theirBalance, FeePerKw: btcutil.Amount(req.CommitFeePerKw), CommitFee: commitFee, - CustomBlob: fn.MapOption(func(desc AuxFundingDesc) tlv.Blob { - return desc.CustomLocalCommitBlob - })(req.AuxFundingDesc), }, RemoteCommitment: channeldb.ChannelCommitment{ LocalBalance: ourBalance, RemoteBalance: theirBalance, FeePerKw: btcutil.Amount(req.CommitFeePerKw), CommitFee: commitFee, - CustomBlob: fn.MapOption(func(desc AuxFundingDesc) tlv.Blob { - return desc.CustomRemoteCommitBlob - })(req.AuxFundingDesc), }, ThawHeight: thawHeight, Db: wallet.Cfg.Database, InitialLocalBalance: ourBalance, InitialRemoteBalance: theirBalance, Memo: req.Memo, - CustomBlob: fn.MapOption(func(desc AuxFundingDesc) tlv.Blob { - return desc.CustomFundingBlob - })(req.AuxFundingDesc), - TapscriptRoot: fn.MapOption(func(desc AuxFundingDesc) chainhash.Hash { - return desc.TapscriptRoot - })(req.AuxFundingDesc), + TapscriptRoot: req.TapscriptRoot, }, pushMSat: req.PushMSat, pendingChanID: req.PendingChanID, reservationID: id, wallet: wallet, chanFunder: req.ChanFunder, - initAuxLeaves: fn.MapOption(func(desc AuxFundingDesc) CommitAuxLeaves { - return desc.InitAuxLeaves - })(req.AuxFundingDesc), }, nil } diff --git a/lnwallet/wallet.go b/lnwallet/wallet.go index 0e9521188..08abcb423 100644 --- a/lnwallet/wallet.go +++ b/lnwallet/wallet.go @@ -91,25 +91,21 @@ func (p *PsbtFundingRequired) Error() string { } // AuxFundingDesc stores a series of attributes that may be used to modify the -// way the channel funding occurs +// way the channel funding occurs. This struct contains information that can +// only be derived once both sides have received and sent their contributions +// to the channel (keys, etc). type AuxFundingDesc struct { // CustomFundingBlob is a custom blob that'll be stored in the database // within the OpenChannel struct. This should represent information // static to the channel lifetime. CustomFundingBlob tlv.Blob - // TapscriptRoot is the root of the tapscript tree that will be used to - // create the funding output. - TapscriptRoot chainhash.Hash - // CustomLocalCommitBlob is a custom blob that'll be stored in the // first commitment entry for the local party. CustomLocalCommitBlob tlv.Blob // CustomRemoteCommitBlob is a custom blob that'll be stored in the // first commitment entry for the remote party. - // - // TODO(roasbeef): have this just use the leaf fetcher? CustomRemoteCommitBlob tlv.Blob // InitAuxLeaves is the set of aux leaves that'll be used for the very @@ -229,9 +225,9 @@ type InitFundingReserveMsg struct { // channel that will be useful to our future selves. Memo []byte - // AuxFundingDesc is an optional descriptor that can be used to modify - // the way channel funding occurs. - AuxFundingDesc fn.Option[AuxFundingDesc] + // TapscriptRoot is an optional tapscript root that if provided, will + // be used to create the combined key for musig2 based channels. + TapscriptRoot fn.Option[chainhash.Hash] // err is a channel in which all errors will be sent across. Will be // nil if this initial set is successful. @@ -268,7 +264,6 @@ type fundingReserveCancelMsg struct { type addContributionMsg struct { pendingFundingID uint64 - // TODO(roasbeef): Should also carry SPV proofs in we're in SPV mode contribution *ChannelContribution // NOTE: In order to avoid deadlocks, this channel MUST be buffered. @@ -439,8 +434,6 @@ type LightningWallet struct { quit chan struct{} wg sync.WaitGroup - - // TODO(roasbeef): handle wallet lock/unlock } // NewLightningWallet creates/opens and initializes a LightningWallet instance. @@ -485,7 +478,6 @@ func (l *LightningWallet) Startup() error { } l.wg.Add(1) - // TODO(roasbeef): multiple request handlers? go l.requestHandler() return nil @@ -1439,7 +1431,6 @@ func (l *LightningWallet) initOurContribution(reservation *ChannelReservation, // transaction via coin selection are freed allowing future reservations to // include them. func (l *LightningWallet) handleFundingCancelRequest(req *fundingReserveCancelMsg) { - // TODO(roasbeef): holding lock too long l.limboMtx.Lock() defer l.limboMtx.Unlock() @@ -1464,11 +1455,6 @@ func (l *LightningWallet) handleFundingCancelRequest(req *fundingReserveCancelMs ) } - // TODO(roasbeef): is it even worth it to keep track of unused keys? - - // TODO(roasbeef): Is it possible to mark the unused change also as - // available? - delete(l.fundingLimbo, req.pendingFundingID) pid := pendingReservation.pendingChanID @@ -1642,16 +1628,24 @@ func (l *LightningWallet) handleContributionMsg(req *addContributionMsg) { // and remote key which will be needed to calculate the multisig // funding output in a next step. pendingChanID := pendingReservation.pendingChanID + walletLog.Debugf("Advancing PSBT funding flow for "+ "pending_id(%x), binding keys local_key=%v, "+ "remote_key=%x", pendingChanID, &ourContribution.MultiSigKey, theirContribution.MultiSigKey.PubKey.SerializeCompressed()) + fundingIntent.BindKeys( &ourContribution.MultiSigKey, theirContribution.MultiSigKey.PubKey, ) + // We might have a tapscript root, so we'll bind that now to + // ensure we make the proper funding output. + fundingIntent.BindTapscriptRoot( + pendingReservation.partialState.TapscriptRoot, + ) + // Exit early because we can't continue the funding flow yet. req.err <- &PsbtFundingRequired{ Intent: fundingIntent, @@ -1724,16 +1718,17 @@ func (l *LightningWallet) handleContributionMsg(req *addContributionMsg) { // the commitment transaction for the remote party, and verify their incoming // partial signature. func genMusigSession(ourContribution, theirContribution *ChannelContribution, - signer input.MuSig2Signer, - fundingOutput *wire.TxOut) *MusigPairSession { + signer input.MuSig2Signer, fundingOutput *wire.TxOut, + tapscriptRoot fn.Option[chainhash.Hash]) *MusigPairSession { return NewMusigPairSession(&MusigSessionCfg{ - LocalKey: ourContribution.MultiSigKey, - RemoteKey: theirContribution.MultiSigKey, - LocalNonce: *ourContribution.LocalNonce, - RemoteNonce: *theirContribution.LocalNonce, - Signer: signer, - InputTxOut: fundingOutput, + LocalKey: ourContribution.MultiSigKey, + RemoteKey: theirContribution.MultiSigKey, + LocalNonce: *ourContribution.LocalNonce, + RemoteNonce: *theirContribution.LocalNonce, + Signer: signer, + InputTxOut: fundingOutput, + TapscriptTweak: tapscriptRoot, }) } @@ -1783,6 +1778,7 @@ func (l *LightningWallet) signCommitTx(pendingReservation *ChannelReservation, musigSessions := genMusigSession( ourContribution, theirContribution, l.Cfg.Signer, fundingOutput, + pendingReservation.partialState.TapscriptRoot, ) pendingReservation.musigSessions = musigSessions } @@ -2166,6 +2162,7 @@ func (l *LightningWallet) verifyCommitSig(res *ChannelReservation, res.musigSessions = genMusigSession( res.ourContribution, res.theirContribution, l.Cfg.Signer, fundingOutput, + res.partialState.TapscriptRoot, ) } @@ -2256,9 +2253,6 @@ func (l *LightningWallet) handleFundingCounterPartySigs(msg *addCounterPartySigs // As we're about to broadcast the funding transaction, we'll take note // of the current height for record keeping purposes. - // - // TODO(roasbeef): this info can also be piped into light client's - // basic fee estimation? _, bestHeight, err := l.Cfg.ChainIO.GetBestBlock() if err != nil { msg.err <- err