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()) }