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.
This commit is contained in:
Olaoluwa Osuntokun 2023-01-19 20:26:05 -08:00
parent a8416300dd
commit 15978a8691
No known key found for this signature in database
GPG Key ID: 3BBD59E99B280306
9 changed files with 347 additions and 60 deletions

View File

@ -7,7 +7,6 @@ import (
"github.com/btcsuite/btcd/btcec/v2" "github.com/btcsuite/btcd/btcec/v2"
"github.com/btcsuite/btcd/wire" "github.com/btcsuite/btcd/wire"
"github.com/lightningnetwork/lnd/channeldb"
"github.com/lightningnetwork/lnd/lnpeer" "github.com/lightningnetwork/lnd/lnpeer"
"github.com/lightningnetwork/lnd/lnwire" "github.com/lightningnetwork/lnd/lnwire"
) )
@ -42,7 +41,9 @@ func (p *mockPeer) SendMessageLazy(sync bool, msgs ...lnwire.Message) error {
return p.SendMessage(sync, msgs...) 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 return nil
} }
func (p *mockPeer) WipeChannel(_ *wire.OutPoint) {} func (p *mockPeer) WipeChannel(_ *wire.OutPoint) {}

View File

@ -10,6 +10,7 @@ import (
"github.com/btcsuite/btcd/btcec/v2" "github.com/btcsuite/btcd/btcec/v2"
"github.com/btcsuite/btcd/btcec/v2/ecdsa" "github.com/btcsuite/btcd/btcec/v2/ecdsa"
"github.com/btcsuite/btcd/btcec/v2/schnorr/musig2"
"github.com/btcsuite/btcd/btcutil" "github.com/btcsuite/btcd/btcutil"
"github.com/btcsuite/btcd/chaincfg/chainhash" "github.com/btcsuite/btcd/chaincfg/chainhash"
"github.com/btcsuite/btcd/txscript" "github.com/btcsuite/btcd/txscript"
@ -574,6 +575,17 @@ type Manager struct {
nonceMtx sync.RWMutex nonceMtx sync.RWMutex
chanIDNonce uint64 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 // activeReservations is a map which houses the state of all pending
// funding workflows. // funding workflows.
activeReservations map[serializedPubKey]pendingChannels activeReservations map[serializedPubKey]pendingChannels
@ -674,6 +686,9 @@ func NewFundingManager(cfg Config) (*Manager, error) {
handleChannelReadyBarriers: &lnutils.SyncMap[ handleChannelReadyBarriers: &lnutils.SyncMap[
lnwire.ChannelID, struct{}, lnwire.ChannelID, struct{},
]{}, ]{},
pendingMusigNonces: make(
map[lnwire.ChannelID]*musig2.Nonces,
),
quit: make(chan struct{}), quit: make(chan struct{}),
}, nil }, 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 // Sending the option-scid-alias channel type for a public channel is
// disallowed. // disallowed.
public := msg.ChannelFlags&lnwire.FFAnnounceChannel != 0 case public && scid:
if public && scid {
err = fmt.Errorf("option-scid-alias chantype for public " + err = fmt.Errorf("option-scid-alias chantype for public " +
"channel") "channel")
log.Error(err) log.Error(err)
f.failFundingFlow(peer, cid, err) f.failFundingFlow(peer, cid, err)
return 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{ req := &lnwallet.InitFundingReserveMsg{
@ -1749,6 +1774,22 @@ func (f *Manager) handleFundingOpen(peer lnpeer.Peer,
}, },
UpfrontShutdown: msg.UpfrontShutdownScript, 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) err = reservation.ProcessSingleContribution(remoteContribution)
if err != nil { if err != nil {
log.Errorf("unable to add contribution reservation: %v", err) 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", log.Debugf("Remote party accepted commitment constraints: %v",
spew.Sdump(remoteContribution.ChannelConfig.ChannelConstraints)) 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 // With the initiator's contribution recorded, respond with our
// contribution in the next message of the workflow. // contribution in the next message of the workflow.
fundingAccept := lnwire.AcceptChannel{ fundingAccept := lnwire.AcceptChannel{
@ -1781,6 +1829,7 @@ func (f *Manager) handleFundingOpen(peer lnpeer.Peer,
UpfrontShutdownScript: ourContribution.UpfrontShutdown, UpfrontShutdownScript: ourContribution.UpfrontShutdown,
ChannelType: chanType, ChannelType: chanType,
LeaseExpiry: msg.LeaseExpiry, LeaseExpiry: msg.LeaseExpiry,
LocalNonce: localNonce,
} }
if err := peer.SendMessage(true, &fundingAccept); err != nil { if err := peer.SendMessage(true, &fundingAccept); err != nil {
@ -1966,6 +2015,20 @@ func (f *Manager) handleFundingAccept(peer lnpeer.Peer,
}, },
UpfrontShutdown: msg.UpfrontShutdownScript, 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) err = resCtx.reservation.ProcessContribution(remoteContribution)
// The wallet has detected that a PSBT funding process was requested by // 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, PendingChannelID: cid.tempChanID,
FundingPoint: *outPoint, FundingPoint: *outPoint,
} }
fundingCreated.CommitSig, err = lnwire.NewSigFromSignature(sig)
if err != nil { // If this is a taproot channel, then we'll need to populate the musig2
log.Errorf("Unable to parse signature: %v", err) // partial sig field instead of the regular commit sig field.
f.failFundingFlow(resCtx.peer, cid, err) if resCtx.reservation.IsTaproot() {
return 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 { if err := resCtx.peer.SendMessage(true, fundingCreated); err != nil {
log.Errorf("Unable to send funding complete message: %v", err) log.Errorf("Unable to send funding complete message: %v", err)
f.failFundingFlow(resCtx.peer, cid, 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. // Create the channel identifier without setting the active channel ID.
cid := newChanIdentifier(pendingChanID) cid := newChanIdentifier(pendingChanID)
commitSig, err := msg.CommitSig.ToSignature() // For taproot channels, the commit signature is actually the partial
if err != nil { // signature. Otherwise, we can convert the ECDSA commit signature into
log.Errorf("unable to parse signature: %v", err) // our internal input.Signature type.
f.failFundingFlow(peer, cid, err) var commitSig input.Signature
return 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 // 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) log.Debugf("Creating chan barrier for ChanID(%v)", channelID)
f.newChanBarriers.Store(channelID, make(chan struct{})) f.newChanBarriers.Store(channelID, make(chan struct{}))
log.Infof("sending FundingSigned for pending_id(%x) over "+ fundingSigned := &lnwire.FundingSigned{}
"ChannelPoint(%v)", pendingChanID[:], fundingOut)
// With their signature for our version of the commitment transaction // For taproot channels, we'll need to send over a partial signature
// verified, we can now send over our signature to the remote peer. // that includes the nonce along side the signature.
_, sig := resCtx.reservation.OurSignatures() _, sig := resCtx.reservation.OurSignatures()
ourCommitSig, err := lnwire.NewSigFromSignature(sig) if resCtx.reservation.IsTaproot() {
if err != nil { partialSig, ok := sig.(*lnwallet.MusigPartialSig)
log.Errorf("unable to parse signature: %v", err) if !ok {
f.failFundingFlow(peer, cid, err) err := fmt.Errorf("expected musig partial sig, got %T",
deleteFromDatabase() sig)
return 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 // Before sending FundingSigned, we notify Brontide first to keep track
@ -2293,10 +2405,13 @@ func (f *Manager) handleFundingCreated(peer lnpeer.Peer,
// funding flow fails. // funding flow fails.
cid.setChanID(channelID) cid.setChanID(channelID)
fundingSigned := &lnwire.FundingSigned{ fundingSigned.ChanID = cid.chanID
ChanID: cid.chanID,
CommitSig: ourCommitSig, 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 { if err := peer.SendMessage(true, fundingSigned); err != nil {
log.Errorf("unable to send FundingSigned message: %v", err) log.Errorf("unable to send FundingSigned message: %v", err)
f.failFundingFlow(peer, cid, 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) log.Errorf("Unable to store the forwarding policy: %v", err)
} }
// The remote peer has responded with a signature for our commitment // For taproot channels, the commit signature is actually the partial
// transaction. We'll verify the signature for validity, then commit // signature. Otherwise, we can convert the ECDSA commit signature into
// the state to disk as we can now open the channel. // our internal input.Signature type.
commitSig, err := msg.CommitSig.ToSignature() var commitSig input.Signature
if err != nil { if resCtx.reservation.IsTaproot() {
log.Errorf("Unable to parse signature: %v", err) if msg.PartialSig == nil {
f.failFundingFlow(peer, cid, err) log.Errorf("partial sig not included: %v", err)
return 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( completeChan, err := resCtx.reservation.CompleteReservation(
@ -2657,10 +2785,24 @@ func (f *Manager) waitForFundingWithTimeout(
// makeFundingScript re-creates the funding script for the funding transaction // makeFundingScript re-creates the funding script for the funding transaction
// of the target channel. // of the target channel.
func makeFundingScript(channel *channeldb.OpenChannel) ([]byte, error) { func makeFundingScript(channel *channeldb.OpenChannel) ([]byte, error) {
localKey := channel.LocalChanCfg.MultiSigKey.PubKey.SerializeCompressed() localKey := channel.LocalChanCfg.MultiSigKey.PubKey
remoteKey := channel.RemoteChanCfg.MultiSigKey.PubKey.SerializeCompressed() 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 { if err != nil {
return nil, err return nil, err
} }
@ -2956,6 +3098,38 @@ func (f *Manager) sendChannelReady(completeChan *channeldb.OpenChannel,
} }
channelReadyMsg := lnwire.NewChannelReady(chanID, nextRevocation) 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 // 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 // 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 // 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.LocalChanCfg.MultiSigKey,
completeChan.RemoteChanCfg.MultiSigKey.PubKey, *shortChanID, completeChan.RemoteChanCfg.MultiSigKey.PubKey, *shortChanID,
chanID, fwdMinHTLC, fwdMaxHTLC, ourPolicy, chanID, fwdMinHTLC, fwdMaxHTLC, ourPolicy,
completeChan.ChanType,
) )
if err != nil { if err != nil {
return fmt.Errorf("error generating channel "+ return fmt.Errorf("error generating channel "+
@ -3336,7 +3511,7 @@ func (f *Manager) annAfterSixConfs(completeChan *channeldb.OpenChannel,
f.cfg.IDKey, completeChan.IdentityPub, f.cfg.IDKey, completeChan.IdentityPub,
&completeChan.LocalChanCfg.MultiSigKey, &completeChan.LocalChanCfg.MultiSigKey,
completeChan.RemoteChanCfg.MultiSigKey.PubKey, completeChan.RemoteChanCfg.MultiSigKey.PubKey,
*shortChanID, chanID, *shortChanID, chanID, completeChan.ChanType,
) )
if err != nil { if err != nil {
return fmt.Errorf("channel announcement failed: %w", return fmt.Errorf("channel announcement failed: %w",
@ -3440,6 +3615,35 @@ func (f *Manager) waitForZeroConfChannel(c *channeldb.OpenChannel,
return nil 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 // handleChannelReady finalizes the channel funding process and enables the
// channel to enter normal operating mode. // channel to enter normal operating mode.
func (f *Manager) handleChannelReady(peer lnpeer.Peer, func (f *Manager) handleChannelReady(peer lnpeer.Peer,
@ -3517,6 +3721,17 @@ func (f *Manager) handleChannelReady(peer lnpeer.Peer,
return 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 // 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 // feature was negotiated. This will be used to provide route hints
// during invoice creation. In the zero-conf case, it is also used to // 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 channelReadyMsg.AliasScid = &alias
if firstVerNonce != nil {
wireNonce := (*lnwire.Musig2Nonce)(
&firstVerNonce.PubNonce,
)
channelReadyMsg.NextLocalNonce = wireNonce
}
err = peer.SendMessage(true, channelReadyMsg) err = peer.SendMessage(true, channelReadyMsg)
if err != nil { if err != nil {
log.Errorf("unable to send channel_ready: %v", log.Errorf("unable to send channel_ready: %v",
@ -3604,6 +3827,38 @@ func (f *Manager) handleChannelReady(peer lnpeer.Peer,
return 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 // The channel_ready message contains the next commitment point we'll
// need to create the next commitment state for the remote party. So // 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 // 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) 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", log.Errorf("Unable to add new channel %v with peer %x: %v",
channel.FundingOutpoint, channel.FundingOutpoint,
peer.IdentityKey().SerializeCompressed(), err, peer.IdentityKey().SerializeCompressed(), err,
@ -3654,6 +3913,12 @@ func (f *Manager) handleChannelReadyReceived(channel *channeldb.OpenChannel,
chanID := lnwire.NewChanIDFromOutPoint(&channel.FundingOutpoint) 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 var peerAlias *lnwire.ShortChannelID
if channel.IsZeroConf() { if channel.IsZeroConf() {
// We'll need to wait until channel_ready has been received and // We'll need to wait until channel_ready has been received and
@ -3788,9 +4053,9 @@ type chanAnnouncement struct {
func (f *Manager) newChanAnnouncement(localPubKey, func (f *Manager) newChanAnnouncement(localPubKey,
remotePubKey *btcec.PublicKey, localFundingKey *keychain.KeyDescriptor, remotePubKey *btcec.PublicKey, localFundingKey *keychain.KeyDescriptor,
remoteFundingKey *btcec.PublicKey, shortChanID lnwire.ShortChannelID, remoteFundingKey *btcec.PublicKey, shortChanID lnwire.ShortChannelID,
chanID lnwire.ChannelID, fwdMinHTLC, chanID lnwire.ChannelID, fwdMinHTLC, fwdMaxHTLC lnwire.MilliSatoshi,
fwdMaxHTLC lnwire.MilliSatoshi, ourPolicy *channeldb.ChannelEdgePolicy,
ourPolicy *channeldb.ChannelEdgePolicy) (*chanAnnouncement, error) { chanType channeldb.ChannelType) (*chanAnnouncement, error) {
chainHash := *f.cfg.Wallet.Cfg.NetParams.GenesisHash chainHash := *f.cfg.Wallet.Cfg.NetParams.GenesisHash
@ -3982,7 +4247,7 @@ func (f *Manager) newChanAnnouncement(localPubKey,
func (f *Manager) announceChannel(localIDKey, remoteIDKey *btcec.PublicKey, func (f *Manager) announceChannel(localIDKey, remoteIDKey *btcec.PublicKey,
localFundingKey *keychain.KeyDescriptor, localFundingKey *keychain.KeyDescriptor,
remoteFundingKey *btcec.PublicKey, shortChanID lnwire.ShortChannelID, 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 // First, we'll create the batch of announcements to be sent upon
// initial channel creation. This includes the channel announcement // 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. // only use the channel announcement message from the returned struct.
ann, err := f.newChanAnnouncement(localIDKey, remoteIDKey, ann, err := f.newChanAnnouncement(localIDKey, remoteIDKey,
localFundingKey, remoteFundingKey, shortChanID, chanID, localFundingKey, remoteFundingKey, shortChanID, chanID,
0, 0, nil, 0, 0, nil, chanType,
) )
if err != nil { if err != nil {
log.Errorf("can't generate channel announcement: %v", err) 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), "+ log.Infof("Starting funding workflow with %v for pending_id(%x), "+
"committype=%v", msg.Peer.Address(), chanID, commitType) "committype=%v", msg.Peer.Address(), chanID, commitType)
var localNonce *lnwire.Musig2Nonce
if commitType.IsTaproot() {
localNonce = (*lnwire.Musig2Nonce)(
&ourContribution.LocalNonce.PubNonce,
)
}
fundingOpen := lnwire.OpenChannel{ fundingOpen := lnwire.OpenChannel{
ChainHash: *f.cfg.Wallet.Cfg.NetParams.GenesisHash, ChainHash: *f.cfg.Wallet.Cfg.NetParams.GenesisHash,
PendingChannelID: chanID, PendingChannelID: chanID,
@ -4443,6 +4715,7 @@ func (f *Manager) handleInitFundingMsg(msg *InitFundingMsg) {
UpfrontShutdownScript: shutdown, UpfrontShutdownScript: shutdown,
ChannelType: chanType, ChannelType: chanType,
LeaseExpiry: leaseExpiry, LeaseExpiry: leaseExpiry,
LocalNonce: localNonce,
} }
if err := msg.Peer.SendMessage(true, &fundingOpen); err != nil { if err := msg.Peer.SendMessage(true, &fundingOpen); err != nil {
e := fmt.Errorf("unable to send funding request message: %v", e := fmt.Errorf("unable to send funding request message: %v",

View File

@ -235,7 +235,7 @@ func (m *mockZeroConfAcceptor) Accept(
} }
type newChannelMsg struct { type newChannelMsg struct {
channel *channeldb.OpenChannel channel *lnpeer.NewChannel
err chan error 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 { quit <-chan struct{}) error {
errChan := make(chan error) errChan := make(chan error)

View File

@ -706,7 +706,8 @@ func (l *channelLink) syncChanStates() error {
// very same nonce that we sent above, as they should // very same nonce that we sent above, as they should
// take the latest verification nonce we send. // take the latest verification nonce we send.
if chanState.ChanType.IsTaproot() { if chanState.ChanType.IsTaproot() {
fundingLockedMsg.NextLocalNonce = localChanSyncMsg.LocalNonce //nolint:lll //nolint:lll
channelReadyMsg.NextLocalNonce = localChanSyncMsg.LocalNonce
} }
// For channels that negotiated the option-scid-alias // For channels that negotiated the option-scid-alias

View File

@ -1868,7 +1868,7 @@ func (m *mockPeer) SendMessage(sync bool, msgs ...lnwire.Message) error {
func (m *mockPeer) SendMessageLazy(sync bool, msgs ...lnwire.Message) error { func (m *mockPeer) SendMessageLazy(sync bool, msgs ...lnwire.Message) error {
return m.SendMessage(sync, msgs...) return m.SendMessage(sync, msgs...)
} }
func (m *mockPeer) AddNewChannel(_ *channeldb.OpenChannel, func (m *mockPeer) AddNewChannel(_ *lnpeer.NewChannel,
_ <-chan struct{}) error { _ <-chan struct{}) error {
return nil return nil
} }

View File

@ -666,7 +666,7 @@ func (s *mockServer) Address() net.Addr {
return nil return nil
} }
func (s *mockServer) AddNewChannel(channel *channeldb.OpenChannel, func (s *mockServer) AddNewChannel(channel *lnpeer.NewChannel,
cancel <-chan struct{}) error { cancel <-chan struct{}) error {
return nil return nil

View File

@ -5,7 +5,6 @@ import (
"github.com/btcsuite/btcd/btcec/v2" "github.com/btcsuite/btcd/btcec/v2"
"github.com/btcsuite/btcd/wire" "github.com/btcsuite/btcd/wire"
"github.com/lightningnetwork/lnd/channeldb"
"github.com/lightningnetwork/lnd/lnwire" "github.com/lightningnetwork/lnd/lnwire"
"github.com/stretchr/testify/mock" "github.com/stretchr/testify/mock"
) )
@ -28,7 +27,7 @@ func (m *MockPeer) SendMessageLazy(sync bool, msgs ...lnwire.Message) error {
return args.Error(0) return args.Error(0)
} }
func (m *MockPeer) AddNewChannel(channel *channeldb.OpenChannel, func (m *MockPeer) AddNewChannel(channel *NewChannel,
cancel <-chan struct{}) error { cancel <-chan struct{}) error {
args := m.Called(channel, cancel) args := m.Called(channel, cancel)

View File

@ -6,9 +6,20 @@ import (
"github.com/btcsuite/btcd/btcec/v2" "github.com/btcsuite/btcd/btcec/v2"
"github.com/btcsuite/btcd/wire" "github.com/btcsuite/btcd/wire"
"github.com/lightningnetwork/lnd/channeldb" "github.com/lightningnetwork/lnd/channeldb"
"github.com/lightningnetwork/lnd/lnwallet"
"github.com/lightningnetwork/lnd/lnwire" "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. // Peer is an interface which represents a remote lightning node.
type Peer interface { type Peer interface {
// SendMessage sends a variadic number of high-priority message to // 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 // AddNewChannel adds a new channel to the peer. The channel should fail
// to be added if the cancel channel is closed. // 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 // AddPendingChannel adds a pending open channel ID to the peer. The
// channel should fail to be added if the cancel chan is closed. // channel should fail to be added if the cancel chan is closed.

View File

@ -90,7 +90,7 @@ type outgoingMsg struct {
// completed. // completed.
type newChannelMsg struct { type newChannelMsg struct {
// channel is used when the pending channel becomes active. // 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 is used when there's a new pending channel.
channelID lnwire.ChannelID channelID lnwire.ChannelID
@ -3506,12 +3506,12 @@ func (p *Brontide) Address() net.Addr {
// added if the cancel channel is closed. // added if the cancel channel is closed.
// //
// NOTE: Part of the lnpeer.Peer interface. // 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 { cancel <-chan struct{}) error {
errChan := make(chan error, 1) errChan := make(chan error, 1)
newChanMsg := &newChannelMsg{ newChanMsg := &newChannelMsg{
channel: channel, channel: newChan,
err: errChan, 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 // addActiveChannel adds a new active channel to the `activeChannels` map. It
// takes a `channeldb.OpenChannel`, creates a `lnwallet.LightningChannel` from // takes a `channeldb.OpenChannel`, creates a `lnwallet.LightningChannel` from
// it and assembles it with a channel link. // 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 chanPoint := &c.FundingOutpoint
chanID := lnwire.NewChanIDFromOutPoint(chanPoint) 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 // channels, so we can look it up later easily according to its channel
// ID. // ID.
lnChan, err := lnwallet.NewLightningChannel( lnChan, err := lnwallet.NewLightningChannel(
p.cfg.Signer, c, p.cfg.SigPool, p.cfg.Signer, c.OpenChannel,
p.cfg.SigPool, c.ChanOpts...,
) )
if err != nil { if err != nil {
return fmt.Errorf("unable to create LightningChannel: %w", err) return fmt.Errorf("unable to create LightningChannel: %w", err)
@ -3887,7 +3888,8 @@ func (p *Brontide) handleNewActiveChannel(req *newChannelMsg) {
close(req.err) close(req.err)
// Update the next revocation point. // 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()) p.log.Errorf(err.Error())
} }