From 15978a8691a77a5afb821cfc4843e04d616834c2 Mon Sep 17 00:00:00 2001 From: Olaoluwa Osuntokun Date: Thu, 19 Jan 2023 20:26:05 -0800 Subject: [PATCH] funding+peer: add support for new musig2 channel funding flow In this commit, we add support for the new musig2 channel funding flow. This flow is identical to the existing flow, but not both sides need to exchange local nonces up front, and then signatures sent are now partial signatures instead of regular signatures. The funding manager also gains some new state of the local nonces it needs to generate in order to send the funding locked message, and also process the funding locked message from the remote party. In order to allow the funding manger to generate the nonces that need to be applied to each channel, then AddNewChannel method has been modified to accept a set of options that the peer will then use to bind the nonces to a new channel. --- discovery/mock_test.go | 5 +- funding/manager.go | 361 +++++++++++++++++++++++++++++++++++----- funding/manager_test.go | 4 +- htlcswitch/link.go | 3 +- htlcswitch/link_test.go | 2 +- htlcswitch/mock.go | 2 +- lnpeer/mock_peer.go | 3 +- lnpeer/peer.go | 13 +- peer/brontide.go | 14 +- 9 files changed, 347 insertions(+), 60 deletions(-) diff --git a/discovery/mock_test.go b/discovery/mock_test.go index 446999233..4f5c5d4e4 100644 --- a/discovery/mock_test.go +++ b/discovery/mock_test.go @@ -7,7 +7,6 @@ import ( "github.com/btcsuite/btcd/btcec/v2" "github.com/btcsuite/btcd/wire" - "github.com/lightningnetwork/lnd/channeldb" "github.com/lightningnetwork/lnd/lnpeer" "github.com/lightningnetwork/lnd/lnwire" ) @@ -42,7 +41,9 @@ func (p *mockPeer) SendMessageLazy(sync bool, msgs ...lnwire.Message) error { return p.SendMessage(sync, msgs...) } -func (p *mockPeer) AddNewChannel(_ *channeldb.OpenChannel, _ <-chan struct{}) error { +func (p *mockPeer) AddNewChannel(_ *lnpeer.NewChannel, + _ <-chan struct{}) error { + return nil } func (p *mockPeer) WipeChannel(_ *wire.OutPoint) {} diff --git a/funding/manager.go b/funding/manager.go index 9fa860a58..36266cc5c 100644 --- a/funding/manager.go +++ b/funding/manager.go @@ -10,6 +10,7 @@ import ( "github.com/btcsuite/btcd/btcec/v2" "github.com/btcsuite/btcd/btcec/v2/ecdsa" + "github.com/btcsuite/btcd/btcec/v2/schnorr/musig2" "github.com/btcsuite/btcd/btcutil" "github.com/btcsuite/btcd/chaincfg/chainhash" "github.com/btcsuite/btcd/txscript" @@ -574,6 +575,17 @@ type Manager struct { nonceMtx sync.RWMutex chanIDNonce uint64 + // pendingMusigNonces is used to store the musig2 nonce we generate to + // send funding locked until we receive a funding locked message from + // the remote party. We'll use this to keep track of the nonce we + // generated, so we send the local+remote nonces to the peer state + // machine. + // + // NOTE: This map is protected by the nonceMtx above. + // + // TODO(roasbeef): replace w/ generic concurrent map + pendingMusigNonces map[lnwire.ChannelID]*musig2.Nonces + // activeReservations is a map which houses the state of all pending // funding workflows. activeReservations map[serializedPubKey]pendingChannels @@ -674,6 +686,9 @@ func NewFundingManager(cfg Config) (*Manager, error) { handleChannelReadyBarriers: &lnutils.SyncMap[ lnwire.ChannelID, struct{}, ]{}, + pendingMusigNonces: make( + map[lnwire.ChannelID]*musig2.Nonces, + ), quit: make(chan struct{}), }, nil } @@ -1504,15 +1519,25 @@ func (f *Manager) handleFundingOpen(peer lnpeer.Peer, } } + public := msg.ChannelFlags&lnwire.FFAnnounceChannel != 0 + switch { // Sending the option-scid-alias channel type for a public channel is // disallowed. - public := msg.ChannelFlags&lnwire.FFAnnounceChannel != 0 - if public && scid { + case public && scid: err = fmt.Errorf("option-scid-alias chantype for public " + "channel") log.Error(err) f.failFundingFlow(peer, cid, err) return + + // The current variant of taproot channels can only be used with + // unadvertised channels for now. + case commitType.IsTaproot() && public: + err = fmt.Errorf("taproot channel type for public channel") + log.Error(err) + f.failFundingFlow(peer, cid, err) + + return } req := &lnwallet.InitFundingReserveMsg{ @@ -1749,6 +1774,22 @@ func (f *Manager) handleFundingOpen(peer lnpeer.Peer, }, UpfrontShutdown: msg.UpfrontShutdownScript, } + + if resCtx.reservation.IsTaproot() { + if msg.LocalNonce == nil { + err := fmt.Errorf("local nonce not set for taproot " + + "chan") + log.Error(err) + f.failFundingFlow( + resCtx.peer, cid, err, + ) + } + + remoteContribution.LocalNonce = &musig2.Nonces{ + PubNonce: *msg.LocalNonce, + } + } + err = reservation.ProcessSingleContribution(remoteContribution) if err != nil { log.Errorf("unable to add contribution reservation: %v", err) @@ -1761,6 +1802,13 @@ func (f *Manager) handleFundingOpen(peer lnpeer.Peer, log.Debugf("Remote party accepted commitment constraints: %v", spew.Sdump(remoteContribution.ChannelConfig.ChannelConstraints)) + var localNonce *lnwire.Musig2Nonce + if commitType.IsTaproot() { + localNonce = (*lnwire.Musig2Nonce)( + &ourContribution.LocalNonce.PubNonce, + ) + } + // With the initiator's contribution recorded, respond with our // contribution in the next message of the workflow. fundingAccept := lnwire.AcceptChannel{ @@ -1781,6 +1829,7 @@ func (f *Manager) handleFundingOpen(peer lnpeer.Peer, UpfrontShutdownScript: ourContribution.UpfrontShutdown, ChannelType: chanType, LeaseExpiry: msg.LeaseExpiry, + LocalNonce: localNonce, } if err := peer.SendMessage(true, &fundingAccept); err != nil { @@ -1966,6 +2015,20 @@ func (f *Manager) handleFundingAccept(peer lnpeer.Peer, }, UpfrontShutdown: msg.UpfrontShutdownScript, } + + if resCtx.reservation.IsTaproot() { + if msg.LocalNonce == nil { + err := fmt.Errorf("local nonce not set for taproot " + + "chan") + log.Error(err) + f.failFundingFlow(resCtx.peer, cid, err) + } + + remoteContribution.LocalNonce = &musig2.Nonces{ + PubNonce: *msg.LocalNonce, + } + } + err = resCtx.reservation.ProcessContribution(remoteContribution) // The wallet has detected that a PSBT funding process was requested by @@ -2160,12 +2223,30 @@ func (f *Manager) continueFundingAccept(resCtx *reservationWithCtx, PendingChannelID: cid.tempChanID, FundingPoint: *outPoint, } - fundingCreated.CommitSig, err = lnwire.NewSigFromSignature(sig) - if err != nil { - log.Errorf("Unable to parse signature: %v", err) - f.failFundingFlow(resCtx.peer, cid, err) - return + + // If this is a taproot channel, then we'll need to populate the musig2 + // partial sig field instead of the regular commit sig field. + if resCtx.reservation.IsTaproot() { + partialSig, ok := sig.(*lnwallet.MusigPartialSig) + if !ok { + err := fmt.Errorf("expected musig partial sig, got %T", + sig) + log.Error(err) + f.failFundingFlow(resCtx.peer, cid, err) + + return + } + + fundingCreated.PartialSig = partialSig.ToWireSig() + } else { + fundingCreated.CommitSig, err = lnwire.NewSigFromSignature(sig) + if err != nil { + log.Errorf("Unable to parse signature: %v", err) + f.failFundingFlow(resCtx.peer, cid, err) + return + } } + if err := resCtx.peer.SendMessage(true, fundingCreated); err != nil { log.Errorf("Unable to send funding complete message: %v", err) f.failFundingFlow(resCtx.peer, cid, err) @@ -2202,11 +2283,27 @@ func (f *Manager) handleFundingCreated(peer lnpeer.Peer, // Create the channel identifier without setting the active channel ID. cid := newChanIdentifier(pendingChanID) - commitSig, err := msg.CommitSig.ToSignature() - if err != nil { - log.Errorf("unable to parse signature: %v", err) - f.failFundingFlow(peer, cid, err) - return + // For taproot channels, the commit signature is actually the partial + // signature. Otherwise, we can convert the ECDSA commit signature into + // our internal input.Signature type. + var commitSig input.Signature + if resCtx.reservation.IsTaproot() { + if msg.PartialSig == nil { + log.Errorf("partial sig not included: %v", err) + f.failFundingFlow(peer, cid, err) + return + } + + commitSig = new(lnwallet.MusigPartialSig).FromWireSig( + msg.PartialSig, + ) + } else { + commitSig, err = msg.CommitSig.ToSignature() + if err != nil { + log.Errorf("unable to parse signature: %v", err) + f.failFundingFlow(peer, cid, err) + return + } } // With all the necessary data available, attempt to advance the @@ -2266,18 +2363,33 @@ func (f *Manager) handleFundingCreated(peer lnpeer.Peer, log.Debugf("Creating chan barrier for ChanID(%v)", channelID) f.newChanBarriers.Store(channelID, make(chan struct{})) - log.Infof("sending FundingSigned for pending_id(%x) over "+ - "ChannelPoint(%v)", pendingChanID[:], fundingOut) + fundingSigned := &lnwire.FundingSigned{} - // With their signature for our version of the commitment transaction - // verified, we can now send over our signature to the remote peer. + // For taproot channels, we'll need to send over a partial signature + // that includes the nonce along side the signature. _, sig := resCtx.reservation.OurSignatures() - ourCommitSig, err := lnwire.NewSigFromSignature(sig) - if err != nil { - log.Errorf("unable to parse signature: %v", err) - f.failFundingFlow(peer, cid, err) - deleteFromDatabase() - return + if resCtx.reservation.IsTaproot() { + partialSig, ok := sig.(*lnwallet.MusigPartialSig) + if !ok { + err := fmt.Errorf("expected musig partial sig, got %T", + sig) + log.Error(err) + f.failFundingFlow(resCtx.peer, cid, err) + deleteFromDatabase() + + return + } + + fundingSigned.PartialSig = partialSig.ToWireSig() + } else { + fundingSigned.CommitSig, err = lnwire.NewSigFromSignature(sig) + if err != nil { + log.Errorf("unable to parse signature: %v", err) + f.failFundingFlow(peer, cid, err) + deleteFromDatabase() + + return + } } // Before sending FundingSigned, we notify Brontide first to keep track @@ -2293,10 +2405,13 @@ func (f *Manager) handleFundingCreated(peer lnpeer.Peer, // funding flow fails. cid.setChanID(channelID) - fundingSigned := &lnwire.FundingSigned{ - ChanID: cid.chanID, - CommitSig: ourCommitSig, - } + fundingSigned.ChanID = cid.chanID + + log.Infof("sending FundingSigned for pending_id(%x) over "+ + "ChannelPoint(%v)", pendingChanID[:], fundingOut) + + // With their signature for our version of the commitment transaction + // verified, we can now send over our signature to the remote peer. if err := peer.SendMessage(true, fundingSigned); err != nil { log.Errorf("unable to send FundingSigned message: %v", err) f.failFundingFlow(peer, cid, err) @@ -2417,14 +2532,27 @@ func (f *Manager) handleFundingSigned(peer lnpeer.Peer, log.Errorf("Unable to store the forwarding policy: %v", err) } - // The remote peer has responded with a signature for our commitment - // transaction. We'll verify the signature for validity, then commit - // the state to disk as we can now open the channel. - commitSig, err := msg.CommitSig.ToSignature() - if err != nil { - log.Errorf("Unable to parse signature: %v", err) - f.failFundingFlow(peer, cid, err) - return + // For taproot channels, the commit signature is actually the partial + // signature. Otherwise, we can convert the ECDSA commit signature into + // our internal input.Signature type. + var commitSig input.Signature + if resCtx.reservation.IsTaproot() { + if msg.PartialSig == nil { + log.Errorf("partial sig not included: %v", err) + f.failFundingFlow(peer, cid, err) + return + } + + commitSig = new(lnwallet.MusigPartialSig).FromWireSig( + msg.PartialSig, + ) + } else { + commitSig, err = msg.CommitSig.ToSignature() + if err != nil { + log.Errorf("unable to parse signature: %v", err) + f.failFundingFlow(peer, cid, err) + return + } } completeChan, err := resCtx.reservation.CompleteReservation( @@ -2657,10 +2785,24 @@ func (f *Manager) waitForFundingWithTimeout( // makeFundingScript re-creates the funding script for the funding transaction // of the target channel. func makeFundingScript(channel *channeldb.OpenChannel) ([]byte, error) { - localKey := channel.LocalChanCfg.MultiSigKey.PubKey.SerializeCompressed() - remoteKey := channel.RemoteChanCfg.MultiSigKey.PubKey.SerializeCompressed() + localKey := channel.LocalChanCfg.MultiSigKey.PubKey + remoteKey := channel.RemoteChanCfg.MultiSigKey.PubKey - multiSigScript, err := input.GenMultiSigScript(localKey, remoteKey) + if channel.ChanType.IsTaproot() { + pkScript, _, err := input.GenTaprootFundingScript( + localKey, remoteKey, int64(channel.Capacity), + ) + if err != nil { + return nil, err + } + + return pkScript, nil + } + + multiSigScript, err := input.GenMultiSigScript( + localKey.SerializeCompressed(), + remoteKey.SerializeCompressed(), + ) if err != nil { return nil, err } @@ -2956,6 +3098,38 @@ func (f *Manager) sendChannelReady(completeChan *channeldb.OpenChannel, } channelReadyMsg := lnwire.NewChannelReady(chanID, nextRevocation) + // If this is a taproot channel, then we also need to send along our + // set of musig2 nonces as well. + if completeChan.ChanType.IsTaproot() { + log.Infof("ChanID(%v): generating musig2 nonces...", + chanID) + + f.nonceMtx.Lock() + localNonce, ok := f.pendingMusigNonces[chanID] + if !ok { + // If we don't have any nonces generated yet for this + // first state, then we'll generate them now and stow + // them away. When we receive the funding locked + // message, we'll then pass along this same set of + // nonces. + newNonce, err := channel.GenMusigNonces() + if err != nil { + f.nonceMtx.Unlock() + return err + } + + // Now that we've generated the nonce for this channel, + // we'll store it in the set of pending nonces. + localNonce = newNonce + f.pendingMusigNonces[chanID] = localNonce + } + f.nonceMtx.Unlock() + + channelReadyMsg.NextLocalNonce = (*lnwire.Musig2Nonce)( + &localNonce.PubNonce, + ) + } + // If the channel negotiated the option-scid-alias feature bit, we'll // send a TLV segment that includes an alias the peer can use in their // invoice hop hints. We'll send the first alias we find for the @@ -3145,6 +3319,7 @@ func (f *Manager) addToRouterGraph(completeChan *channeldb.OpenChannel, &completeChan.LocalChanCfg.MultiSigKey, completeChan.RemoteChanCfg.MultiSigKey.PubKey, *shortChanID, chanID, fwdMinHTLC, fwdMaxHTLC, ourPolicy, + completeChan.ChanType, ) if err != nil { return fmt.Errorf("error generating channel "+ @@ -3336,7 +3511,7 @@ func (f *Manager) annAfterSixConfs(completeChan *channeldb.OpenChannel, f.cfg.IDKey, completeChan.IdentityPub, &completeChan.LocalChanCfg.MultiSigKey, completeChan.RemoteChanCfg.MultiSigKey.PubKey, - *shortChanID, chanID, + *shortChanID, chanID, completeChan.ChanType, ) if err != nil { return fmt.Errorf("channel announcement failed: %w", @@ -3440,6 +3615,35 @@ func (f *Manager) waitForZeroConfChannel(c *channeldb.OpenChannel, return nil } +// genFirstStateMusigNonce generates a nonces for the "first" local state. This +// is the verification nonce for the state created for us after the initial +// commitment transaction signed as part of the funding flow. +func genFirstStateMusigNonce(channel *channeldb.OpenChannel, +) (*musig2.Nonces, error) { + + musig2ShaChain, err := channeldb.DeriveMusig2Shachain( + channel.RevocationProducer, + ) + if err != nil { + return nil, fmt.Errorf("unable to generate musig channel "+ + "nonces: %v", err) + } + + // We use the _next_ commitment height here as we need to generate the + // nonce for the next state the remote party will sign for us. + verNonce, err := channeldb.NewMusigVerificationNonce( + channel.LocalChanCfg.MultiSigKey.PubKey, + channel.LocalCommitment.CommitHeight+1, + musig2ShaChain, + ) + if err != nil { + return nil, fmt.Errorf("unable to generate musig channel "+ + "nonces: %v", err) + } + + return verNonce, nil +} + // handleChannelReady finalizes the channel funding process and enables the // channel to enter normal operating mode. func (f *Manager) handleChannelReady(peer lnpeer.Peer, @@ -3517,6 +3721,17 @@ func (f *Manager) handleChannelReady(peer lnpeer.Peer, return } + // If this is a taproot channel, then we can generate the set of nonces + // the remote party needs to send the next remote commitment here. + var firstVerNonce *musig2.Nonces + if channel.ChanType.IsTaproot() { + firstVerNonce, err = genFirstStateMusigNonce(channel) + if err != nil { + log.Error(err) + return + } + } + // We'll need to store the received TLV alias if the option_scid_alias // feature was negotiated. This will be used to provide route hints // during invoice creation. In the zero-conf case, it is also used to @@ -3580,6 +3795,14 @@ func (f *Manager) handleChannelReady(peer lnpeer.Peer, ) channelReadyMsg.AliasScid = &alias + if firstVerNonce != nil { + wireNonce := (*lnwire.Musig2Nonce)( + &firstVerNonce.PubNonce, + ) + + channelReadyMsg.NextLocalNonce = wireNonce + } + err = peer.SendMessage(true, channelReadyMsg) if err != nil { log.Errorf("unable to send channel_ready: %v", @@ -3604,6 +3827,38 @@ func (f *Manager) handleChannelReady(peer lnpeer.Peer, return } + // If this is a taproot channel, then we'll need to map the received + // nonces to a nonce pair, and also fetch our pending nonces, which are + // required in order to make the channel whole. + var chanOpts []lnwallet.ChannelOpt + if channel.ChanType.IsTaproot() { + f.nonceMtx.Lock() + localNonce, ok := f.pendingMusigNonces[chanID] + if !ok { + // If there's no pending nonce for this channel ID, + // we'll use the one generatd above. + localNonce = firstVerNonce + f.pendingMusigNonces[chanID] = firstVerNonce + } + f.nonceMtx.Unlock() + + log.Infof("ChanID(%v): applying local+remote musig2 nonces", + chanID) + + if msg.NextLocalNonce == nil { + log.Errorf("remote nonces are nil") + return + } + + chanOpts = append( + chanOpts, + lnwallet.WithLocalMusigNonces(localNonce), + lnwallet.WithRemoteMusigNonces(&musig2.Nonces{ + PubNonce: *msg.NextLocalNonce, + }), + ) + } + // The channel_ready message contains the next commitment point we'll // need to create the next commitment state for the remote party. So // we'll insert that into the channel now before passing it along to @@ -3635,7 +3890,11 @@ func (f *Manager) handleChannelReady(peer lnpeer.Peer, err) } - if err := peer.AddNewChannel(channel, f.quit); err != nil { + err = peer.AddNewChannel(&lnpeer.NewChannel{ + OpenChannel: channel, + ChanOpts: chanOpts, + }, f.quit) + if err != nil { log.Errorf("Unable to add new channel %v with peer %x: %v", channel.FundingOutpoint, peer.IdentityKey().SerializeCompressed(), err, @@ -3654,6 +3913,12 @@ func (f *Manager) handleChannelReadyReceived(channel *channeldb.OpenChannel, chanID := lnwire.NewChanIDFromOutPoint(&channel.FundingOutpoint) + // Since we've sent+received funding locked at this point, we + // can clean up the pending musig2 nonce state. + f.nonceMtx.Lock() + delete(f.pendingMusigNonces, chanID) + f.nonceMtx.Unlock() + var peerAlias *lnwire.ShortChannelID if channel.IsZeroConf() { // We'll need to wait until channel_ready has been received and @@ -3788,9 +4053,9 @@ type chanAnnouncement struct { func (f *Manager) newChanAnnouncement(localPubKey, remotePubKey *btcec.PublicKey, localFundingKey *keychain.KeyDescriptor, remoteFundingKey *btcec.PublicKey, shortChanID lnwire.ShortChannelID, - chanID lnwire.ChannelID, fwdMinHTLC, - fwdMaxHTLC lnwire.MilliSatoshi, - ourPolicy *channeldb.ChannelEdgePolicy) (*chanAnnouncement, error) { + chanID lnwire.ChannelID, fwdMinHTLC, fwdMaxHTLC lnwire.MilliSatoshi, + ourPolicy *channeldb.ChannelEdgePolicy, + chanType channeldb.ChannelType) (*chanAnnouncement, error) { chainHash := *f.cfg.Wallet.Cfg.NetParams.GenesisHash @@ -3982,7 +4247,7 @@ func (f *Manager) newChanAnnouncement(localPubKey, func (f *Manager) announceChannel(localIDKey, remoteIDKey *btcec.PublicKey, localFundingKey *keychain.KeyDescriptor, remoteFundingKey *btcec.PublicKey, shortChanID lnwire.ShortChannelID, - chanID lnwire.ChannelID) error { + chanID lnwire.ChannelID, chanType channeldb.ChannelType) error { // First, we'll create the batch of announcements to be sent upon // initial channel creation. This includes the channel announcement @@ -3993,7 +4258,7 @@ func (f *Manager) announceChannel(localIDKey, remoteIDKey *btcec.PublicKey, // only use the channel announcement message from the returned struct. ann, err := f.newChanAnnouncement(localIDKey, remoteIDKey, localFundingKey, remoteFundingKey, shortChanID, chanID, - 0, 0, nil, + 0, 0, nil, chanType, ) if err != nil { log.Errorf("can't generate channel announcement: %v", err) @@ -4421,6 +4686,13 @@ func (f *Manager) handleInitFundingMsg(msg *InitFundingMsg) { log.Infof("Starting funding workflow with %v for pending_id(%x), "+ "committype=%v", msg.Peer.Address(), chanID, commitType) + var localNonce *lnwire.Musig2Nonce + if commitType.IsTaproot() { + localNonce = (*lnwire.Musig2Nonce)( + &ourContribution.LocalNonce.PubNonce, + ) + } + fundingOpen := lnwire.OpenChannel{ ChainHash: *f.cfg.Wallet.Cfg.NetParams.GenesisHash, PendingChannelID: chanID, @@ -4443,6 +4715,7 @@ func (f *Manager) handleInitFundingMsg(msg *InitFundingMsg) { UpfrontShutdownScript: shutdown, ChannelType: chanType, LeaseExpiry: leaseExpiry, + LocalNonce: localNonce, } if err := msg.Peer.SendMessage(true, &fundingOpen); err != nil { e := fmt.Errorf("unable to send funding request message: %v", diff --git a/funding/manager_test.go b/funding/manager_test.go index f9e3b2b18..9f922dafb 100644 --- a/funding/manager_test.go +++ b/funding/manager_test.go @@ -235,7 +235,7 @@ func (m *mockZeroConfAcceptor) Accept( } type newChannelMsg struct { - channel *channeldb.OpenChannel + channel *lnpeer.NewChannel err chan error } @@ -299,7 +299,7 @@ func (n *testNode) RemoteFeatures() *lnwire.FeatureVector { ) } -func (n *testNode) AddNewChannel(channel *channeldb.OpenChannel, +func (n *testNode) AddNewChannel(channel *lnpeer.NewChannel, quit <-chan struct{}) error { errChan := make(chan error) diff --git a/htlcswitch/link.go b/htlcswitch/link.go index baf8877ad..da7e048b2 100644 --- a/htlcswitch/link.go +++ b/htlcswitch/link.go @@ -706,7 +706,8 @@ func (l *channelLink) syncChanStates() error { // very same nonce that we sent above, as they should // take the latest verification nonce we send. if chanState.ChanType.IsTaproot() { - fundingLockedMsg.NextLocalNonce = localChanSyncMsg.LocalNonce //nolint:lll + //nolint:lll + channelReadyMsg.NextLocalNonce = localChanSyncMsg.LocalNonce } // For channels that negotiated the option-scid-alias diff --git a/htlcswitch/link_test.go b/htlcswitch/link_test.go index 4b792704b..37e306559 100644 --- a/htlcswitch/link_test.go +++ b/htlcswitch/link_test.go @@ -1868,7 +1868,7 @@ func (m *mockPeer) SendMessage(sync bool, msgs ...lnwire.Message) error { func (m *mockPeer) SendMessageLazy(sync bool, msgs ...lnwire.Message) error { return m.SendMessage(sync, msgs...) } -func (m *mockPeer) AddNewChannel(_ *channeldb.OpenChannel, +func (m *mockPeer) AddNewChannel(_ *lnpeer.NewChannel, _ <-chan struct{}) error { return nil } diff --git a/htlcswitch/mock.go b/htlcswitch/mock.go index ea76549b4..95e0f343b 100644 --- a/htlcswitch/mock.go +++ b/htlcswitch/mock.go @@ -666,7 +666,7 @@ func (s *mockServer) Address() net.Addr { return nil } -func (s *mockServer) AddNewChannel(channel *channeldb.OpenChannel, +func (s *mockServer) AddNewChannel(channel *lnpeer.NewChannel, cancel <-chan struct{}) error { return nil diff --git a/lnpeer/mock_peer.go b/lnpeer/mock_peer.go index a8953092c..908c9a0c9 100644 --- a/lnpeer/mock_peer.go +++ b/lnpeer/mock_peer.go @@ -5,7 +5,6 @@ import ( "github.com/btcsuite/btcd/btcec/v2" "github.com/btcsuite/btcd/wire" - "github.com/lightningnetwork/lnd/channeldb" "github.com/lightningnetwork/lnd/lnwire" "github.com/stretchr/testify/mock" ) @@ -28,7 +27,7 @@ func (m *MockPeer) SendMessageLazy(sync bool, msgs ...lnwire.Message) error { return args.Error(0) } -func (m *MockPeer) AddNewChannel(channel *channeldb.OpenChannel, +func (m *MockPeer) AddNewChannel(channel *NewChannel, cancel <-chan struct{}) error { args := m.Called(channel, cancel) diff --git a/lnpeer/peer.go b/lnpeer/peer.go index f66991e42..f7d2e971b 100644 --- a/lnpeer/peer.go +++ b/lnpeer/peer.go @@ -6,9 +6,20 @@ import ( "github.com/btcsuite/btcd/btcec/v2" "github.com/btcsuite/btcd/wire" "github.com/lightningnetwork/lnd/channeldb" + "github.com/lightningnetwork/lnd/lnwallet" "github.com/lightningnetwork/lnd/lnwire" ) +// NewChannel is a newly funded channel. This struct couples a channel along +// with the set of channel options that may change how the channel is created. +// This can be used to pass along the nonce state needed for taproot channels. +type NewChannel struct { + *channeldb.OpenChannel + + // ChanOpts can be used to change how the channel is created. + ChanOpts []lnwallet.ChannelOpt +} + // Peer is an interface which represents a remote lightning node. type Peer interface { // SendMessage sends a variadic number of high-priority message to @@ -25,7 +36,7 @@ type Peer interface { // AddNewChannel adds a new channel to the peer. The channel should fail // to be added if the cancel channel is closed. - AddNewChannel(channel *channeldb.OpenChannel, cancel <-chan struct{}) error + AddNewChannel(newChan *NewChannel, cancel <-chan struct{}) error // AddPendingChannel adds a pending open channel ID to the peer. The // channel should fail to be added if the cancel chan is closed. diff --git a/peer/brontide.go b/peer/brontide.go index c589afa2f..70dc477bd 100644 --- a/peer/brontide.go +++ b/peer/brontide.go @@ -90,7 +90,7 @@ type outgoingMsg struct { // completed. type newChannelMsg struct { // channel is used when the pending channel becomes active. - channel *channeldb.OpenChannel + channel *lnpeer.NewChannel // channelID is used when there's a new pending channel. channelID lnwire.ChannelID @@ -3506,12 +3506,12 @@ func (p *Brontide) Address() net.Addr { // added if the cancel channel is closed. // // NOTE: Part of the lnpeer.Peer interface. -func (p *Brontide) AddNewChannel(channel *channeldb.OpenChannel, +func (p *Brontide) AddNewChannel(newChan *lnpeer.NewChannel, cancel <-chan struct{}) error { errChan := make(chan error, 1) newChanMsg := &newChannelMsg{ - channel: channel, + channel: newChan, err: errChan, } @@ -3813,7 +3813,7 @@ func (p *Brontide) updateNextRevocation(c *channeldb.OpenChannel) error { // addActiveChannel adds a new active channel to the `activeChannels` map. It // takes a `channeldb.OpenChannel`, creates a `lnwallet.LightningChannel` from // it and assembles it with a channel link. -func (p *Brontide) addActiveChannel(c *channeldb.OpenChannel) error { +func (p *Brontide) addActiveChannel(c *lnpeer.NewChannel) error { chanPoint := &c.FundingOutpoint chanID := lnwire.NewChanIDFromOutPoint(chanPoint) @@ -3821,7 +3821,8 @@ func (p *Brontide) addActiveChannel(c *channeldb.OpenChannel) error { // channels, so we can look it up later easily according to its channel // ID. lnChan, err := lnwallet.NewLightningChannel( - p.cfg.Signer, c, p.cfg.SigPool, + p.cfg.Signer, c.OpenChannel, + p.cfg.SigPool, c.ChanOpts..., ) if err != nil { return fmt.Errorf("unable to create LightningChannel: %w", err) @@ -3887,7 +3888,8 @@ func (p *Brontide) handleNewActiveChannel(req *newChannelMsg) { close(req.err) // Update the next revocation point. - if err := p.updateNextRevocation(newChan); err != nil { + err := p.updateNextRevocation(newChan.OpenChannel) + if err != nil { p.log.Errorf(err.Error()) }