mirror of
https://github.com/lightningnetwork/lnd.git
synced 2024-11-19 01:43:16 +01:00
lnwallet+peer: add tapscript root awareness to musig2 sessions
With this commit, the channel is now aware of if it's a musig2 channel, that also has a tapscript root. We'll need to always pass in the tapscript root each time we: make the funding output, sign a new state, and also verify a new state.
This commit is contained in:
parent
c8b7987a39
commit
82ba5bf0bf
@ -14,6 +14,7 @@ import (
|
|||||||
"github.com/btcsuite/btcd/txscript"
|
"github.com/btcsuite/btcd/txscript"
|
||||||
"github.com/btcsuite/btcd/wire"
|
"github.com/btcsuite/btcd/wire"
|
||||||
"github.com/lightningnetwork/lnd/channeldb"
|
"github.com/lightningnetwork/lnd/channeldb"
|
||||||
|
"github.com/lightningnetwork/lnd/fn"
|
||||||
"github.com/lightningnetwork/lnd/input"
|
"github.com/lightningnetwork/lnd/input"
|
||||||
"github.com/lightningnetwork/lnd/keychain"
|
"github.com/lightningnetwork/lnd/keychain"
|
||||||
"github.com/lightningnetwork/lnd/lntypes"
|
"github.com/lightningnetwork/lnd/lntypes"
|
||||||
@ -178,8 +179,9 @@ func (m *mockChannel) RemoteUpfrontShutdownScript() lnwire.DeliveryAddress {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (m *mockChannel) CreateCloseProposal(fee btcutil.Amount,
|
func (m *mockChannel) CreateCloseProposal(fee btcutil.Amount,
|
||||||
localScript, remoteScript []byte, _ ...lnwallet.ChanCloseOpt,
|
localScript, remoteScript []byte,
|
||||||
) (input.Signature, *chainhash.Hash, btcutil.Amount, error) {
|
_ ...lnwallet.ChanCloseOpt) (input.Signature, *chainhash.Hash,
|
||||||
|
btcutil.Amount, error) {
|
||||||
|
|
||||||
if m.chanType.IsTaproot() {
|
if m.chanType.IsTaproot() {
|
||||||
return lnwallet.NewMusigPartialSig(
|
return lnwallet.NewMusigPartialSig(
|
||||||
@ -188,6 +190,7 @@ func (m *mockChannel) CreateCloseProposal(fee btcutil.Amount,
|
|||||||
R: new(btcec.PublicKey),
|
R: new(btcec.PublicKey),
|
||||||
},
|
},
|
||||||
lnwire.Musig2Nonce{}, lnwire.Musig2Nonce{}, nil,
|
lnwire.Musig2Nonce{}, lnwire.Musig2Nonce{}, nil,
|
||||||
|
fn.None[chainhash.Hash](),
|
||||||
), nil, 0, nil
|
), nil, 0, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1013,7 +1013,7 @@ func (lc *LightningChannel) createSignDesc() error {
|
|||||||
if chanState.ChanType.IsTaproot() {
|
if chanState.ChanType.IsTaproot() {
|
||||||
fundingPkScript, _, err = input.GenTaprootFundingScript(
|
fundingPkScript, _, err = input.GenTaprootFundingScript(
|
||||||
localKey, remoteKey, int64(lc.channelState.Capacity),
|
localKey, remoteKey, int64(lc.channelState.Capacity),
|
||||||
fn.None[chainhash.Hash](),
|
chanState.TapscriptRoot,
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@ -6057,11 +6057,15 @@ func (lc *LightningChannel) getSignedCommitTx() (*wire.MsgTx, error) {
|
|||||||
"verification nonce: %w", err)
|
"verification nonce: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
tapscriptTweak := fn.MapOption(TapscriptRootToTweak)(
|
||||||
|
lc.channelState.TapscriptRoot,
|
||||||
|
)
|
||||||
|
|
||||||
// Now that we have the local nonce, we'll re-create the musig
|
// Now that we have the local nonce, we'll re-create the musig
|
||||||
// session we had for this height.
|
// session we had for this height.
|
||||||
musigSession := NewPartialMusigSession(
|
musigSession := NewPartialMusigSession(
|
||||||
*localNonce, ourKey, theirKey, lc.Signer,
|
*localNonce, ourKey, theirKey, lc.Signer,
|
||||||
&lc.fundingOutput, LocalMusigCommit,
|
&lc.fundingOutput, LocalMusigCommit, tapscriptTweak,
|
||||||
)
|
)
|
||||||
|
|
||||||
var remoteSig lnwire.PartialSigWithNonce
|
var remoteSig lnwire.PartialSigWithNonce
|
||||||
@ -8672,12 +8676,13 @@ func (lc *LightningChannel) InitRemoteMusigNonces(remoteNonce *musig2.Nonces,
|
|||||||
// TODO(roasbeef): propagate rename of signing and verification nonces
|
// TODO(roasbeef): propagate rename of signing and verification nonces
|
||||||
|
|
||||||
sessionCfg := &MusigSessionCfg{
|
sessionCfg := &MusigSessionCfg{
|
||||||
LocalKey: localChanCfg.MultiSigKey,
|
LocalKey: localChanCfg.MultiSigKey,
|
||||||
RemoteKey: remoteChanCfg.MultiSigKey,
|
RemoteKey: remoteChanCfg.MultiSigKey,
|
||||||
LocalNonce: *localNonce,
|
LocalNonce: *localNonce,
|
||||||
RemoteNonce: *remoteNonce,
|
RemoteNonce: *remoteNonce,
|
||||||
Signer: lc.Signer,
|
Signer: lc.Signer,
|
||||||
InputTxOut: &lc.fundingOutput,
|
InputTxOut: &lc.fundingOutput,
|
||||||
|
TapscriptTweak: lc.channelState.TapscriptRoot,
|
||||||
}
|
}
|
||||||
lc.musigSessions = NewMusigPairSession(
|
lc.musigSessions = NewMusigPairSession(
|
||||||
sessionCfg,
|
sessionCfg,
|
||||||
|
@ -8,8 +8,10 @@ import (
|
|||||||
"github.com/btcsuite/btcd/btcec/v2"
|
"github.com/btcsuite/btcd/btcec/v2"
|
||||||
"github.com/btcsuite/btcd/btcec/v2/schnorr"
|
"github.com/btcsuite/btcd/btcec/v2/schnorr"
|
||||||
"github.com/btcsuite/btcd/btcec/v2/schnorr/musig2"
|
"github.com/btcsuite/btcd/btcec/v2/schnorr/musig2"
|
||||||
|
"github.com/btcsuite/btcd/chaincfg/chainhash"
|
||||||
"github.com/btcsuite/btcd/txscript"
|
"github.com/btcsuite/btcd/txscript"
|
||||||
"github.com/btcsuite/btcd/wire"
|
"github.com/btcsuite/btcd/wire"
|
||||||
|
"github.com/lightningnetwork/lnd/fn"
|
||||||
"github.com/lightningnetwork/lnd/input"
|
"github.com/lightningnetwork/lnd/input"
|
||||||
"github.com/lightningnetwork/lnd/keychain"
|
"github.com/lightningnetwork/lnd/keychain"
|
||||||
"github.com/lightningnetwork/lnd/lnwire"
|
"github.com/lightningnetwork/lnd/lnwire"
|
||||||
@ -37,6 +39,20 @@ var (
|
|||||||
ErrSessionNotFinalized = fmt.Errorf("musig2 session not finalized")
|
ErrSessionNotFinalized = fmt.Errorf("musig2 session not finalized")
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// tapscriptRootToSignOpt is a function that takes a tapscript root and returns
|
||||||
|
// a MuSig2 sign opt that'll apply the tweak when signing+verifying.
|
||||||
|
func tapscriptRootToSignOpt(root chainhash.Hash) musig2.SignOption {
|
||||||
|
return musig2.WithTaprootSignTweak(root[:])
|
||||||
|
}
|
||||||
|
|
||||||
|
// TapscriptRootToTweak is a helper function that converts a tapscript root
|
||||||
|
// into a tweak that can be used with the MuSig2 API.
|
||||||
|
func TapscriptRootToTweak(root chainhash.Hash) input.MuSig2Tweaks {
|
||||||
|
return input.MuSig2Tweaks{
|
||||||
|
TaprootTweak: root[:],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// MusigPartialSig is a wrapper around the base musig2.PartialSignature type
|
// MusigPartialSig is a wrapper around the base musig2.PartialSignature type
|
||||||
// that also includes information about the set of nonces used, and also the
|
// that also includes information about the set of nonces used, and also the
|
||||||
// signer. This allows us to implement the input.Signature interface, as that
|
// signer. This allows us to implement the input.Signature interface, as that
|
||||||
@ -54,25 +70,30 @@ type MusigPartialSig struct {
|
|||||||
|
|
||||||
// signerKeys is the set of public keys of all signers.
|
// signerKeys is the set of public keys of all signers.
|
||||||
signerKeys []*btcec.PublicKey
|
signerKeys []*btcec.PublicKey
|
||||||
|
|
||||||
|
// tapscriptTweak is an optional tweak, that if specified, will be used
|
||||||
|
// instead of the normal BIP 86 tweak when validating the signature.
|
||||||
|
tapscriptTweak fn.Option[chainhash.Hash]
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewMusigPartialSig creates a new musig partial signature.
|
// NewMusigPartialSig creates a new MuSig2 partial signature.
|
||||||
func NewMusigPartialSig(sig *musig2.PartialSignature,
|
func NewMusigPartialSig(sig *musig2.PartialSignature, signerNonce,
|
||||||
signerNonce, combinedNonce lnwire.Musig2Nonce,
|
combinedNonce lnwire.Musig2Nonce, signerKeys []*btcec.PublicKey,
|
||||||
signerKeys []*btcec.PublicKey) *MusigPartialSig {
|
tapscriptTweak fn.Option[chainhash.Hash]) *MusigPartialSig {
|
||||||
|
|
||||||
return &MusigPartialSig{
|
return &MusigPartialSig{
|
||||||
sig: sig,
|
sig: sig,
|
||||||
signerNonce: signerNonce,
|
signerNonce: signerNonce,
|
||||||
combinedNonce: combinedNonce,
|
combinedNonce: combinedNonce,
|
||||||
signerKeys: signerKeys,
|
signerKeys: signerKeys,
|
||||||
|
tapscriptTweak: tapscriptTweak,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// FromWireSig maps a wire partial sig to this internal type that we'll use to
|
// FromWireSig maps a wire partial sig to this internal type that we'll use to
|
||||||
// perform signature validation.
|
// perform signature validation.
|
||||||
func (p *MusigPartialSig) FromWireSig(sig *lnwire.PartialSigWithNonce,
|
func (p *MusigPartialSig) FromWireSig(
|
||||||
) *MusigPartialSig {
|
sig *lnwire.PartialSigWithNonce) *MusigPartialSig {
|
||||||
|
|
||||||
p.sig = &musig2.PartialSignature{
|
p.sig = &musig2.PartialSignature{
|
||||||
S: &sig.Sig,
|
S: &sig.Sig,
|
||||||
@ -135,9 +156,15 @@ func (p *MusigPartialSig) Verify(msg []byte, pub *btcec.PublicKey) bool {
|
|||||||
var m [32]byte
|
var m [32]byte
|
||||||
copy(m[:], msg)
|
copy(m[:], msg)
|
||||||
|
|
||||||
|
// If we have a tapscript tweak, then we'll use that as a tweak
|
||||||
|
// otherwise, we'll fall back to the normal BIP 86 sign tweak.
|
||||||
|
signOpts := fn.MapOption(tapscriptRootToSignOpt)(
|
||||||
|
p.tapscriptTweak,
|
||||||
|
).UnwrapOr(musig2.WithBip86SignTweak())
|
||||||
|
|
||||||
return p.sig.Verify(
|
return p.sig.Verify(
|
||||||
p.signerNonce, p.combinedNonce, p.signerKeys, pub, m,
|
p.signerNonce, p.combinedNonce, p.signerKeys, pub, m,
|
||||||
musig2.WithSortedKeys(), musig2.WithBip86SignTweak(),
|
musig2.WithSortedKeys(), signOpts,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -160,6 +187,14 @@ func (n *MusigNoncePair) String() string {
|
|||||||
n.SigningNonce.PubNonce[:])
|
n.SigningNonce.PubNonce[:])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TapscriptRootToTweak is a function that takes a MuSig2 taproot tweak and
|
||||||
|
// returns the root hash of the tapscript tree.
|
||||||
|
func muSig2TweakToRoot(tweak input.MuSig2Tweaks) chainhash.Hash {
|
||||||
|
var root chainhash.Hash
|
||||||
|
copy(root[:], tweak.TaprootTweak)
|
||||||
|
return root
|
||||||
|
}
|
||||||
|
|
||||||
// MusigSession abstracts over the details of a logical musig session. A single
|
// MusigSession abstracts over the details of a logical musig session. A single
|
||||||
// session is used for each commitment transactions. The sessions use a JIT
|
// session is used for each commitment transactions. The sessions use a JIT
|
||||||
// nonce style, wherein part of the session can be created using only the
|
// nonce style, wherein part of the session can be created using only the
|
||||||
@ -197,15 +232,20 @@ type MusigSession struct {
|
|||||||
// commitType tracks if this is the session for the local or remote
|
// commitType tracks if this is the session for the local or remote
|
||||||
// commitment.
|
// commitment.
|
||||||
commitType MusigCommitType
|
commitType MusigCommitType
|
||||||
|
|
||||||
|
// tapscriptTweak is an optional tweak, that if specified, will be used
|
||||||
|
// instead of the normal BIP 86 tweak when creating the MuSig2
|
||||||
|
// aggregate key and session.
|
||||||
|
tapscriptTweak fn.Option[input.MuSig2Tweaks]
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewPartialMusigSession creates a new musig2 session given only the
|
// NewPartialMusigSession creates a new musig2 session given only the
|
||||||
// verification nonce (local nonce), and the other information that has already
|
// verification nonce (local nonce), and the other information that has already
|
||||||
// been bound to the session.
|
// been bound to the session.
|
||||||
func NewPartialMusigSession(verificationNonce musig2.Nonces,
|
func NewPartialMusigSession(verificationNonce musig2.Nonces,
|
||||||
localKey, remoteKey keychain.KeyDescriptor,
|
localKey, remoteKey keychain.KeyDescriptor, signer input.MuSig2Signer,
|
||||||
signer input.MuSig2Signer, inputTxOut *wire.TxOut,
|
inputTxOut *wire.TxOut, commitType MusigCommitType,
|
||||||
commitType MusigCommitType) *MusigSession {
|
tapscriptTweak fn.Option[input.MuSig2Tweaks]) *MusigSession {
|
||||||
|
|
||||||
signerKeys := []*btcec.PublicKey{localKey.PubKey, remoteKey.PubKey}
|
signerKeys := []*btcec.PublicKey{localKey.PubKey, remoteKey.PubKey}
|
||||||
|
|
||||||
@ -214,13 +254,14 @@ func NewPartialMusigSession(verificationNonce musig2.Nonces,
|
|||||||
}
|
}
|
||||||
|
|
||||||
return &MusigSession{
|
return &MusigSession{
|
||||||
nonces: nonces,
|
nonces: nonces,
|
||||||
remoteKey: remoteKey,
|
remoteKey: remoteKey,
|
||||||
localKey: localKey,
|
localKey: localKey,
|
||||||
inputTxOut: inputTxOut,
|
inputTxOut: inputTxOut,
|
||||||
signerKeys: signerKeys,
|
signerKeys: signerKeys,
|
||||||
signer: signer,
|
signer: signer,
|
||||||
commitType: commitType,
|
commitType: commitType,
|
||||||
|
tapscriptTweak: tapscriptTweak,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -254,9 +295,9 @@ func (m *MusigSession) FinalizeSession(signingNonce musig2.Nonces) error {
|
|||||||
remoteNonce = m.nonces.SigningNonce
|
remoteNonce = m.nonces.SigningNonce
|
||||||
}
|
}
|
||||||
|
|
||||||
tweakDesc := input.MuSig2Tweaks{
|
tweakDesc := m.tapscriptTweak.UnwrapOr(input.MuSig2Tweaks{
|
||||||
TaprootBIP0086Tweak: true,
|
TaprootBIP0086Tweak: true,
|
||||||
}
|
})
|
||||||
m.session, err = m.signer.MuSig2CreateSession(
|
m.session, err = m.signer.MuSig2CreateSession(
|
||||||
input.MuSig2Version100RC2, m.localKey.KeyLocator, m.signerKeys,
|
input.MuSig2Version100RC2, m.localKey.KeyLocator, m.signerKeys,
|
||||||
&tweakDesc, [][musig2.PubNonceSize]byte{remoteNonce.PubNonce},
|
&tweakDesc, [][musig2.PubNonceSize]byte{remoteNonce.PubNonce},
|
||||||
@ -351,8 +392,11 @@ func (m *MusigSession) SignCommit(tx *wire.MsgTx) (*MusigPartialSig, error) {
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
tapscriptRoot := fn.MapOption(muSig2TweakToRoot)(m.tapscriptTweak)
|
||||||
|
|
||||||
return NewMusigPartialSig(
|
return NewMusigPartialSig(
|
||||||
sig, m.session.PublicNonce, m.combinedNonce, m.signerKeys,
|
sig, m.session.PublicNonce, m.combinedNonce, m.signerKeys,
|
||||||
|
tapscriptRoot,
|
||||||
), nil
|
), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -364,7 +408,7 @@ func (m *MusigSession) Refresh(verificationNonce *musig2.Nonces,
|
|||||||
|
|
||||||
return NewPartialMusigSession(
|
return NewPartialMusigSession(
|
||||||
*verificationNonce, m.localKey, m.remoteKey, m.signer,
|
*verificationNonce, m.localKey, m.remoteKey, m.signer,
|
||||||
m.inputTxOut, m.commitType,
|
m.inputTxOut, m.commitType, m.tapscriptTweak,
|
||||||
), nil
|
), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -451,9 +495,11 @@ func (m *MusigSession) VerifyCommitSig(commitTx *wire.MsgTx,
|
|||||||
// When we verify a commitment signature, we always assume that we're
|
// When we verify a commitment signature, we always assume that we're
|
||||||
// verifying a signature on our local commitment. Therefore, we'll use:
|
// verifying a signature on our local commitment. Therefore, we'll use:
|
||||||
// their remote nonce, and also public key.
|
// their remote nonce, and also public key.
|
||||||
|
tapscriptRoot := fn.MapOption(muSig2TweakToRoot)(m.tapscriptTweak)
|
||||||
partialSig := NewMusigPartialSig(
|
partialSig := NewMusigPartialSig(
|
||||||
&musig2.PartialSignature{S: &sig.Sig},
|
&musig2.PartialSignature{S: &sig.Sig},
|
||||||
m.nonces.SigningNonce.PubNonce, m.combinedNonce, m.signerKeys,
|
m.nonces.SigningNonce.PubNonce, m.combinedNonce, m.signerKeys,
|
||||||
|
tapscriptRoot,
|
||||||
)
|
)
|
||||||
|
|
||||||
// With the partial sig loaded with the proper context, we'll now
|
// With the partial sig loaded with the proper context, we'll now
|
||||||
@ -537,6 +583,10 @@ type MusigSessionCfg struct {
|
|||||||
// InputTxOut is the output that we're signing for. This will be the
|
// InputTxOut is the output that we're signing for. This will be the
|
||||||
// funding input.
|
// funding input.
|
||||||
InputTxOut *wire.TxOut
|
InputTxOut *wire.TxOut
|
||||||
|
|
||||||
|
// TapscriptRoot is an optional tweak that can be used to modify the
|
||||||
|
// MuSig2 public key used in the session.
|
||||||
|
TapscriptTweak fn.Option[chainhash.Hash]
|
||||||
}
|
}
|
||||||
|
|
||||||
// MusigPairSession houses the two musig2 sessions needed to do funding and
|
// MusigPairSession houses the two musig2 sessions needed to do funding and
|
||||||
@ -561,13 +611,14 @@ func NewMusigPairSession(cfg *MusigSessionCfg) *MusigPairSession {
|
|||||||
//
|
//
|
||||||
// Both sessions will be created using only the verification nonce for
|
// Both sessions will be created using only the verification nonce for
|
||||||
// the local+remote party.
|
// the local+remote party.
|
||||||
|
tapscriptTweak := fn.MapOption(TapscriptRootToTweak)(cfg.TapscriptTweak)
|
||||||
localSession := NewPartialMusigSession(
|
localSession := NewPartialMusigSession(
|
||||||
cfg.LocalNonce, cfg.LocalKey, cfg.RemoteKey,
|
cfg.LocalNonce, cfg.LocalKey, cfg.RemoteKey, cfg.Signer,
|
||||||
cfg.Signer, cfg.InputTxOut, LocalMusigCommit,
|
cfg.InputTxOut, LocalMusigCommit, tapscriptTweak,
|
||||||
)
|
)
|
||||||
remoteSession := NewPartialMusigSession(
|
remoteSession := NewPartialMusigSession(
|
||||||
cfg.RemoteNonce, cfg.LocalKey, cfg.RemoteKey,
|
cfg.RemoteNonce, cfg.LocalKey, cfg.RemoteKey, cfg.Signer,
|
||||||
cfg.Signer, cfg.InputTxOut, RemoteMusigCommit,
|
cfg.InputTxOut, RemoteMusigCommit, tapscriptTweak,
|
||||||
)
|
)
|
||||||
|
|
||||||
return &MusigPairSession{
|
return &MusigPairSession{
|
||||||
|
@ -4,6 +4,7 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"github.com/btcsuite/btcd/btcec/v2/schnorr/musig2"
|
"github.com/btcsuite/btcd/btcec/v2/schnorr/musig2"
|
||||||
|
"github.com/lightningnetwork/lnd/fn"
|
||||||
"github.com/lightningnetwork/lnd/input"
|
"github.com/lightningnetwork/lnd/input"
|
||||||
"github.com/lightningnetwork/lnd/lnwallet"
|
"github.com/lightningnetwork/lnd/lnwallet"
|
||||||
"github.com/lightningnetwork/lnd/lnwallet/chancloser"
|
"github.com/lightningnetwork/lnd/lnwallet/chancloser"
|
||||||
@ -43,10 +44,15 @@ func (m *MusigChanCloser) ProposalClosingOpts() (
|
|||||||
}
|
}
|
||||||
|
|
||||||
localKey, remoteKey := m.channel.MultiSigKeys()
|
localKey, remoteKey := m.channel.MultiSigKeys()
|
||||||
|
|
||||||
|
tapscriptTweak := fn.MapOption(lnwallet.TapscriptRootToTweak)(
|
||||||
|
m.channel.State().TapscriptRoot,
|
||||||
|
)
|
||||||
|
|
||||||
m.musigSession = lnwallet.NewPartialMusigSession(
|
m.musigSession = lnwallet.NewPartialMusigSession(
|
||||||
*m.remoteNonce, localKey, remoteKey,
|
*m.remoteNonce, localKey, remoteKey,
|
||||||
m.channel.Signer, m.channel.FundingTxOut(),
|
m.channel.Signer, m.channel.FundingTxOut(),
|
||||||
lnwallet.RemoteMusigCommit,
|
lnwallet.RemoteMusigCommit, tapscriptTweak,
|
||||||
)
|
)
|
||||||
|
|
||||||
err := m.musigSession.FinalizeSession(*m.localNonce)
|
err := m.musigSession.FinalizeSession(*m.localNonce)
|
||||||
|
Loading…
Reference in New Issue
Block a user