|
|
|
@ -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",
|
|
|
|
|