lnd/peer/musig_chan_closer.go

136 lines
3.8 KiB
Go
Raw Normal View History

package peer
import (
"fmt"
"github.com/btcsuite/btcd/btcec/v2/schnorr/musig2"
"github.com/lightningnetwork/lnd/fn"
"github.com/lightningnetwork/lnd/input"
"github.com/lightningnetwork/lnd/lnwallet"
"github.com/lightningnetwork/lnd/lnwallet/chancloser"
"github.com/lightningnetwork/lnd/lnwire"
)
// MusigChanCloser is an adapter over the normal channel state machine that
// allows the chan closer to handle the musig2 details of closing taproot
// channels.
type MusigChanCloser struct {
channel *lnwallet.LightningChannel
musigSession *lnwallet.MusigSession
localNonce *musig2.Nonces
remoteNonce *musig2.Nonces
}
// NewMusigChanCloser creates a new musig chan closer from a normal channel.
func NewMusigChanCloser(channel *lnwallet.LightningChannel) *MusigChanCloser {
return &MusigChanCloser{
channel: channel,
}
}
// ProposalClosingOpts returns the options that should be used when
// generating a new co-op close signature.
2023-07-23 17:35:20 +02:00
func (m *MusigChanCloser) ProposalClosingOpts() (
[]lnwallet.ChanCloseOpt, error) {
switch {
case m.localNonce == nil:
return nil, fmt.Errorf("local nonce not generated")
case m.remoteNonce == nil:
return nil, fmt.Errorf("remote nonce not generated")
}
localKey, remoteKey := m.channel.MultiSigKeys()
tapscriptTweak := fn.MapOption(lnwallet.TapscriptRootToTweak)(
m.channel.State().TapscriptRoot,
)
m.musigSession = lnwallet.NewPartialMusigSession(
*m.remoteNonce, localKey, remoteKey,
m.channel.Signer, m.channel.FundingTxOut(),
lnwallet.RemoteMusigCommit, tapscriptTweak,
)
err := m.musigSession.FinalizeSession(*m.localNonce)
if err != nil {
return nil, err
}
return []lnwallet.ChanCloseOpt{
lnwallet.WithCoopCloseMusigSession(m.musigSession),
}, nil
}
// CombineClosingOpts returns the options that should be used when combining
// the final musig partial signature. The method also maps the lnwire partial
// signatures into an input.Signature that can be used more generally.
func (m *MusigChanCloser) CombineClosingOpts(localSig,
remoteSig lnwire.PartialSig) (input.Signature, input.Signature,
[]lnwallet.ChanCloseOpt, error) {
if m.musigSession == nil {
return nil, nil, nil, fmt.Errorf("musig session not created")
}
// We'll convert the wire partial signatures into an input.Signature
// compliant struct so we can pass it into the final combination
// function.
localPartialSig := &lnwire.PartialSigWithNonce{
PartialSig: localSig,
Nonce: m.localNonce.PubNonce,
}
remotePartialSig := &lnwire.PartialSigWithNonce{
PartialSig: remoteSig,
Nonce: m.remoteNonce.PubNonce,
}
localMuSig := new(lnwallet.MusigPartialSig).FromWireSig(
localPartialSig,
)
remoteMuSig := new(lnwallet.MusigPartialSig).FromWireSig(
remotePartialSig,
)
opts := []lnwallet.ChanCloseOpt{
lnwallet.WithCoopCloseMusigSession(m.musigSession),
}
// For taproot channels, we'll need to pass along the session so the
// final combined signature can be created.
return localMuSig, remoteMuSig, opts, nil
}
// ClosingNonce returns the nonce that should be used when generating the our
// partial signature for the remote party.
func (m *MusigChanCloser) ClosingNonce() (*musig2.Nonces, error) {
if m.localNonce != nil {
return m.localNonce, nil
}
localKey, _ := m.channel.MultiSigKeys()
nonce, err := musig2.GenNonces(
musig2.WithPublicKey(localKey.PubKey),
)
if err != nil {
return nil, err
}
m.localNonce = nonce
return nonce, nil
}
// InitRemoteNonce saves the remote nonce the party sent during their shutdown
// message so it can be used later to generate and verify signatures.
func (m *MusigChanCloser) InitRemoteNonce(nonce *musig2.Nonces) {
m.remoteNonce = nonce
}
// A compile-time assertion to ensure MusigChanCloser implements the
// chancloser.MusigSession interface.
var _ chancloser.MusigSession = (*MusigChanCloser)(nil)