lnwallet: update genHtlcSigValidationJobs to be taproot aware

In this commit, we update the genHtlcSigValidationJobs function to be
taproot aware. As we actually need a schnorr signature for the taproot
validation, we need to coerce the entire wire type into a schnorr sig
with the ForceSchnorr() method.
This commit is contained in:
Olaoluwa Osuntokun 2023-01-19 19:24:48 -08:00
parent 5336f03ac6
commit 39d5dffd56
No known key found for this signature in database
GPG Key ID: 3BBD59E99B280306
3 changed files with 428 additions and 118 deletions

View File

@ -3863,8 +3863,6 @@ func putChanRevocationState(chanBucket kvdb.RwBucket, channel *OpenChannel) erro
return err
}
// TODO(roasbeef): don't keep producer on disk
// If the next revocation is present, which is only the case after the
// ChannelReady message has been sent, then we'll write it to disk.
if channel.RemoteNextRevocation != nil {

View File

@ -1317,12 +1317,16 @@ type LightningChannel struct {
// log is a channel-specific logging instance.
log btclog.Logger
// taprootNonceProducer is used to generate a shachain tree for the
// purpose of generating verification nonces for taproot channels.
taprootNonceProducer shachain.Producer
// musigSessions holds the current musig2 pair session for the channel.
musigSessions *MusigPairSession
// pendingVerificationNonce is the initial verification nonce generated
// for musig2 channels when the state machine is intiated. Once we know
// the verification nonce of the remote party, then we can star tto use
// the verification nonce of the remote party, then we can start to use
// the channel as normal.
pendingVerificationNonce *musig2.Nonces
@ -1391,24 +1395,40 @@ func NewLightningChannel(signer input.Signer,
logPrefix := fmt.Sprintf("ChannelPoint(%v):", state.FundingOutpoint)
lc := &LightningChannel{
Signer: signer,
sigPool: sigPool,
currentHeight: localCommit.CommitHeight,
remoteCommitChain: newCommitmentChain(),
localCommitChain: newCommitmentChain(),
channelState: state,
commitBuilder: NewCommitmentBuilder(state),
localUpdateLog: localUpdateLog,
remoteUpdateLog: remoteUpdateLog,
ChanPoint: &state.FundingOutpoint,
Capacity: state.Capacity,
LocalFundingKey: state.LocalChanCfg.MultiSigKey.PubKey,
RemoteFundingKey: state.RemoteChanCfg.MultiSigKey.PubKey,
log: build.NewPrefixLog(logPrefix, walletLog),
// In order to obtain the revocation root hash to create the taproot
// revocation, we'll encode the producer into a buffer, then use that
// to derive the shachain root needed.
var rootHashBuf bytes.Buffer
if err := state.RevocationProducer.Encode(&rootHashBuf); err != nil {
return nil, fmt.Errorf("unable to encode producer: %v", err)
}
// At this point, we mwy already have of nonces that were passed in, so
revRootHash := chainhash.HashH(rootHashBuf.Bytes())
taprootNonceProducer, err := deriveMusig2Shachain(revRootHash)
if err != nil {
return nil, fmt.Errorf("unable to derive shachain: %v", err)
}
lc := &LightningChannel{
Signer: signer,
sigPool: sigPool,
currentHeight: localCommit.CommitHeight,
remoteCommitChain: newCommitmentChain(),
localCommitChain: newCommitmentChain(),
channelState: state,
commitBuilder: NewCommitmentBuilder(state),
localUpdateLog: localUpdateLog,
remoteUpdateLog: remoteUpdateLog,
ChanPoint: &state.FundingOutpoint,
Capacity: state.Capacity,
LocalFundingKey: state.LocalChanCfg.MultiSigKey.PubKey,
RemoteFundingKey: state.RemoteChanCfg.MultiSigKey.PubKey,
taprootNonceProducer: taprootNonceProducer,
log: build.NewPrefixLog(logPrefix, walletLog),
}
// At this point, we may already have nonces that were passed in, so
// we'll check that now as this lets us skip some steps later.
if opts.localNonce != nil {
lc.pendingVerificationNonce = opts.localNonce
@ -1422,7 +1442,7 @@ func NewLightningChannel(signer input.Signer,
// With the main channel struct reconstructed, we'll now restore the
// commitment state in memory and also the update logs themselves.
err := lc.restoreCommitState(&localCommit, &remoteCommit)
err = lc.restoreCommitState(&localCommit, &remoteCommit)
if err != nil {
return nil, err
}
@ -3824,17 +3844,21 @@ func (lc *LightningChannel) validateCommitmentSanity(theirLogCounter,
return nil
}
// CommitSig holds the set of related signatures for a new commitment
// CommitSigs holds the set of related signatures for a new commitment
// transaction state.
type CommitSigs struct {
// CommitSig is the normal commitment signature. This will only be a
// non-zero commitment signature for taproot channels.
// non-zero commitment signature for non-taproot channels.
CommitSig lnwire.Sig
// HtlcSigs is the set of signatures for all HTLCs in the commitment
// transaction. Depending on the channel type, these will either be
// ECDSA or Schnorr signatures.
HtlcSigs []lnwire.Sig
// PartialSig is the musig2 partial signature for taproot commitment
// transactions.
PartialSig *lnwire.PartialSigWithNonce
}
// NewCommitState wraps the various signatures needed to properly
@ -3873,8 +3897,9 @@ func (lc *LightningChannel) SignNextCommitment() (*NewCommitState, error) {
}
var (
sig lnwire.Sig
htlcSigs []lnwire.Sig
sig lnwire.Sig
partialSig *lnwire.PartialSigWithNonce
htlcSigs []lnwire.Sig
)
// If we're awaiting for an ACK to a commitment signature, or if we
@ -3962,16 +3987,38 @@ func (lc *LightningChannel) SignNextCommitment() (*NewCommitState, error) {
// While the jobs are being carried out, we'll Sign their version of
// the new commitment transaction while we're waiting for the rest of
// the HTLC signatures to be processed.
lc.signDesc.SigHashes = input.NewTxSigHashesV0Only(newCommitView.txn)
rawSig, err := lc.Signer.SignOutputRaw(newCommitView.txn, lc.signDesc)
if err != nil {
close(cancelChan)
return nil, err
}
sig, err = lnwire.NewSigFromSignature(rawSig)
if err != nil {
close(cancelChan)
return nil, err
//
// TODO(roasbeef): abstract into CommitSigner interface?
if lc.channelState.ChanType.IsTaproot() {
// In this case, we'll send out a partial signature as this is
// a musig2 channel. The encoded normal ECDSA signature will be
// just blank.
remoteSession := lc.musigSessions.RemoteSession
musig, err := remoteSession.SignCommit(
newCommitView.txn,
)
if err != nil {
close(cancelChan)
return nil, err
}
partialSig = musig.ToWireSig()
} else {
lc.signDesc.SigHashes = input.NewTxSigHashesV0Only(
newCommitView.txn,
)
rawSig, err := lc.Signer.SignOutputRaw(
newCommitView.txn, lc.signDesc,
)
if err != nil {
close(cancelChan)
return nil, err
}
sig, err = lnwire.NewSigFromSignature(rawSig)
if err != nil {
close(cancelChan)
return nil, err
}
}
// We'll need to send over the signatures to the remote party in the
@ -4019,8 +4066,9 @@ func (lc *LightningChannel) SignNextCommitment() (*NewCommitState, error) {
return &NewCommitState{
CommitSigs: &CommitSigs{
CommitSig: sig,
HtlcSigs: htlcSigs,
CommitSig: sig,
HtlcSigs: htlcSigs,
PartialSig: partialSig,
},
PendingHTLCs: commitDiff.Commitment.Htlcs,
}, nil
@ -4051,6 +4099,8 @@ func (lc *LightningChannel) ProcessChanSyncMsg(
msg *lnwire.ChannelReestablish) ([]lnwire.Message, []models.CircuitKey,
[]models.CircuitKey, error) {
// TODO(roasbeef): need to replace w/ received nonces
// Now we'll examine the state we have, vs what was contained in the
// chain sync message. If we're de-synchronized, then we'll send a
// batch of messages which when applied will kick start the chain
@ -4191,8 +4241,9 @@ func (lc *LightningChannel) ProcessChanSyncMsg(
ChanID: lnwire.NewChanIDFromOutPoint(
&lc.channelState.FundingOutpoint,
),
CommitSig: newCommit.CommitSig,
HtlcSigs: newCommit.HtlcSigs,
CommitSig: newCommit.CommitSig,
HtlcSigs: newCommit.HtlcSigs,
PartialSig: newCommit.PartialSig,
}
updates = append(updates, commitSig)
@ -4275,6 +4326,9 @@ func (lc *LightningChannel) ProcessChanSyncMsg(
// With the batch of updates accumulated, we'll now re-send the
// original CommitSig message required to re-sync their remote
// commitment chain with our local version of their chain.
//
// TODO(roasbeef): need to re-sign commitment states w/
// fresh nonce
commitUpdates = append(commitUpdates, commitDiff.CommitSig)
// NOTE: If a revocation is not owed, then updates is empty.
@ -4503,11 +4557,30 @@ func genHtlcSigValidationJobs(localCommitmentView *commitment,
return nil, err
}
htlcAmt := int64(htlc.Amount.ToSatoshis())
if chanType.IsTaproot() {
// TODO(roasbeef): add abstraction in front
prevFetcher := txscript.NewCannedPrevOutputFetcher(
htlc.ourPkScript, htlcAmt,
)
hashCache := txscript.NewTxSigHashes(
successTx, prevFetcher,
)
tapLeaf := txscript.NewBaseTapLeaf(
htlc.ourWitnessScript,
)
return txscript.CalcTapscriptSignaturehash(
hashCache, sigHashType, successTx, 0,
prevFetcher, tapLeaf,
)
}
hashCache := input.NewTxSigHashesV0Only(successTx)
sigHash, err := txscript.CalcWitnessSigHash(
htlc.ourWitnessScript, hashCache,
sigHashType, successTx, 0,
int64(htlc.Amount.ToSatoshis()),
htlcAmt,
)
if err != nil {
return nil, err
@ -4522,6 +4595,13 @@ func genHtlcSigValidationJobs(localCommitmentView *commitment,
"signatures")
}
// If this is a taproot channel, then we'll convert it
// to a schnorr signature, so we can get correct type
// from ToSignature below.
if chanType.IsTaproot() {
htlcSigs[i].ForceSchnorr()
}
// With the sighash generated, we'll also store the
// signature so it can be written to disk if this state
// is valid.
@ -4558,11 +4638,30 @@ func genHtlcSigValidationJobs(localCommitmentView *commitment,
return nil, err
}
htlcAmt := int64(htlc.Amount.ToSatoshis())
if chanType.IsTaproot() {
// TODO(roasbeef): add abstraction in front
prevFetcher := txscript.NewCannedPrevOutputFetcher(
htlc.ourPkScript, htlcAmt,
)
hashCache := txscript.NewTxSigHashes(
timeoutTx, prevFetcher,
)
tapLeaf := txscript.NewBaseTapLeaf(
htlc.ourWitnessScript,
)
return txscript.CalcTapscriptSignaturehash(
hashCache, sigHashType, timeoutTx, 0,
prevFetcher, tapLeaf,
)
}
hashCache := input.NewTxSigHashesV0Only(timeoutTx)
sigHash, err := txscript.CalcWitnessSigHash(
htlc.ourWitnessScript, hashCache,
sigHashType, timeoutTx, 0,
int64(htlc.Amount.ToSatoshis()),
htlcAmt,
)
if err != nil {
return nil, err
@ -4577,6 +4676,13 @@ func genHtlcSigValidationJobs(localCommitmentView *commitment,
"signatures")
}
// If this is a taproot channel, then we'll convert it
// to a schnorr signature, so we can get correct type
// from ToSignature below.
if chanType.IsTaproot() {
htlcSigs[i].ForceSchnorr()
}
// With the sighash generated, we'll also store the
// signature so it can be written to disk if this state
// is valid.
@ -4584,6 +4690,7 @@ func genHtlcSigValidationJobs(localCommitmentView *commitment,
if err != nil {
return nil, err
}
htlc.sig = sig
default:
@ -4637,6 +4744,22 @@ func (i *InvalidCommitSigError) Error() string {
// error interface.
var _ error = (*InvalidCommitSigError)(nil)
// InvalidPartialCommitSigError is used when we encounter an invalid musig2
// partial signature.
type InvalidPartialCommitSigError struct {
InvalidCommitSigError
*invalidPartialSigError
}
// Error returns a detailed error string including the exact transaction that
// caused an invalid partial commit sig signature.
func (i *InvalidPartialCommitSigError) Error() string {
return fmt.Sprintf("rejected commitment: commit_height=%v, "+
"commit_tx=%x -- %v", i.commitHeight, i.commitTx,
i.invalidPartialSigError)
}
// InvalidHtlcSigError is a struct that implements the error interface to
// report a failure to validate an htlc signature from a remote peer. We'll use
// the items in this struct to generate a rich error message for the remote
@ -4746,23 +4869,8 @@ func (lc *LightningChannel) ReceiveNewCommitment(commitSigs *CommitSigs) error {
}),
)
// Construct the sighash of the commitment transaction corresponding to
// this newly proposed state update.
localCommitTx := localCommitmentView.txn
multiSigScript := lc.signDesc.WitnessScript
hashCache := input.NewTxSigHashesV0Only(localCommitTx)
sigHash, err := txscript.CalcWitnessSigHash(
multiSigScript, hashCache, txscript.SigHashAll,
localCommitTx, 0, int64(lc.channelState.Capacity),
)
if err != nil {
// TODO(roasbeef): fetchview has already mutated the HTLCs...
// * need to either roll-back, or make pure
return err
}
// As an optimization, we'll generate a series of jobs for the worker
// pool to verify each of the HTLc signatures presented. Once
// pool to verify each of the HTLC signatures presented. Once
// generated, we'll submit these jobs to the worker pool.
var leaseExpiry uint32
if lc.channelState.ChanType.HasLeaseExpiration() {
@ -4781,29 +4889,104 @@ func (lc *LightningChannel) ReceiveNewCommitment(commitSigs *CommitSigs) error {
cancelChan := make(chan struct{})
verifyResps := lc.sigPool.SubmitVerifyBatch(verifyJobs, cancelChan)
localCommitTx := localCommitmentView.txn
// While the HTLC verification jobs are proceeding asynchronously,
// we'll ensure that the newly constructed commitment state has a valid
// signature.
verifyKey := lc.channelState.RemoteChanCfg.MultiSigKey.PubKey
//
// To do that we'll, construct the sighash of the commitment
// transaction corresponding to this newly proposed state update. If
// this is a taproot channel, then in order to validate the sighash,
// we'll need to call into the relevant tapscript methods.
if lc.channelState.ChanType.IsTaproot() {
localSession := lc.musigSessions.LocalSession
cSig, err := commitSigs.CommitSig.ToSignature()
if err != nil {
return err
}
if !cSig.Verify(sigHash, verifyKey) {
close(cancelChan)
// As we want to ensure we never write nonces to disk, we'll
// use the shachain state to generate a nonce for our next
// local state. Similar to generateRevocation, we do height + 2
// (next height + 1) here, as this is for the _next_ local
// state, and we're about to accept height + 1.
localCtrNonce := WithLocalCounterNonce(
nextHeight+1, lc.taprootNonceProducer,
)
nextVerificationNonce, err := localSession.VerifyCommitSig(
localCommitTx, commitSigs.PartialSig, localCtrNonce,
)
if err != nil {
close(cancelChan)
// If we fail to validate their commitment signature, we'll
// generate a special error to send over the protocol. We'll
// include the exact signature and commitment we failed to
// verify against in order to aide debugging.
var txBytes bytes.Buffer
localCommitTx.Serialize(&txBytes)
return &InvalidCommitSigError{
commitHeight: nextHeight,
commitSig: commitSigs.CommitSig.ToSignatureBytes(),
sigHash: sigHash,
commitTx: txBytes.Bytes(),
var sigErr invalidPartialSigError
if errors.As(err, &sigErr) {
// If we fail to validate their commitment
// signature, we'll generate a special error to
// send over the protocol. We'll include the
// exact signature and commitment we failed to
// verify against in order to aide debugging.
var txBytes bytes.Buffer
localCommitTx.Serialize(&txBytes)
return &InvalidPartialCommitSigError{
invalidPartialSigError: &sigErr,
InvalidCommitSigError: InvalidCommitSigError{ //nolint:lll
commitHeight: nextHeight,
commitTx: txBytes.Bytes(),
},
}
}
return err
}
// Now that we have the next verification nonce for our local
// session, we'll refresh it to yield a new session we'll use
// for the next incoming signature.
newLocalSession, err := lc.musigSessions.LocalSession.Refresh(
nextVerificationNonce,
)
if err != nil {
return err
}
lc.musigSessions.LocalSession = newLocalSession
} else {
multiSigScript := lc.signDesc.WitnessScript
prevFetcher := txscript.NewCannedPrevOutputFetcher(
multiSigScript, int64(lc.channelState.Capacity),
)
hashCache := txscript.NewTxSigHashes(localCommitTx, prevFetcher)
sigHash, err := txscript.CalcWitnessSigHash(
multiSigScript, hashCache, txscript.SigHashAll,
localCommitTx, 0, int64(lc.channelState.Capacity),
)
if err != nil {
// TODO(roasbeef): fetchview has already mutated the HTLCs...
// * need to either roll-back, or make pure
return err
}
verifyKey := lc.channelState.RemoteChanCfg.MultiSigKey.PubKey
cSig, err := commitSigs.CommitSig.ToSignature()
if err != nil {
return err
}
if !cSig.Verify(sigHash, verifyKey) {
close(cancelChan)
// If we fail to validate their commitment signature,
// we'll generate a special error to send over the
// protocol. We'll include the exact signature and
// commitment we failed to verify against in order to
// aide debugging.
var txBytes bytes.Buffer
localCommitTx.Serialize(&txBytes)
return &InvalidCommitSigError{
commitHeight: nextHeight,
commitSig: commitSigs.CommitSig.ToSignatureBytes(),
sigHash: sigHash,
commitTx: txBytes.Bytes(),
}
}
}
@ -4840,8 +5023,21 @@ func (lc *LightningChannel) ReceiveNewCommitment(commitSigs *CommitSigs) error {
}
// The signature checks out, so we can now add the new commitment to
// our local commitment chain.
localCommitmentView.sig = commitSigs.CommitSig.ToSignatureBytes()
// our local commitment chain. For regular channels, we can just
// serialize the ECDSA sig. For taproot channels, we'll serialize the
// partial sig that includes the nonce that was used for signing.
if lc.channelState.ChanType.IsTaproot() {
var sigBytes [lnwire.PartialSigWithNonceLen]byte
b := bytes.NewBuffer(sigBytes[0:0])
if err := commitSigs.PartialSig.Encode(b); err != nil {
return err
}
localCommitmentView.sig = sigBytes[:]
} else {
localCommitmentView.sig = commitSigs.CommitSig.ToSignatureBytes()
}
lc.localCommitChain.addCommitment(localCommitmentView)
return nil
@ -5020,6 +5216,17 @@ func (lc *LightningChannel) RevokeCurrentCommitment() (*lnwire.RevokeAndAck,
&lc.channelState.FundingOutpoint,
)
// If this is a taproot channel, We've now accepted+revoked a new
// commitment, so we'll send the remote party another verification
// nonce they can use to generate new commitments.
if lc.channelState.ChanType.IsTaproot() {
localSession := lc.musigSessions.LocalSession
nextVerificationNonce := localSession.VerificationNonce()
revocationMsg.LocalNonce = (*lnwire.Musig2Nonce)(
&nextVerificationNonce.PubNonce,
)
}
return revocationMsg, newCommitment.Htlcs, finalHtlcs, nil
}
@ -5248,6 +5455,24 @@ func (lc *LightningChannel) ReceiveRevocation(revMsg *lnwire.RevokeAndAck) (
return nil, nil, nil, nil, err
}
// Now that we have a new verification nonce from them, we can refresh
// our remote musig2 session which allows us to create another state.
if lc.channelState.ChanType.IsTaproot() {
if revMsg.LocalNonce == nil {
return nil, nil, nil, nil, fmt.Errorf("next " +
"revocation nonce not set")
}
newRemoteSession, err := lc.musigSessions.RemoteSession.Refresh(
&musig2.Nonces{
PubNonce: *revMsg.LocalNonce,
},
)
if err != nil {
return nil, nil, nil, nil, err
}
lc.musigSessions.RemoteSession = newRemoteSession
}
// At this point, the revocation has been accepted, and we've rotated
// the current revocation key+hash for the remote party. Therefore we
// sync now to ensure the revocation producer state is consistent with
@ -5873,30 +6098,103 @@ func (lc *LightningChannel) getSignedCommitTx() (*wire.MsgTx, error) {
localCommit := lc.channelState.LocalCommitment
commitTx := localCommit.CommitTx.Copy()
theirSig, err := ecdsa.ParseDERSignature(localCommit.CommitSig)
if err != nil {
return nil, err
ourKey := lc.channelState.LocalChanCfg.MultiSigKey
theirKey := lc.channelState.RemoteChanCfg.MultiSigKey
var witness wire.TxWitness
switch {
// If this is a taproot channel, then we'll need to re-derive the nonce
// we need to generate a new signature
case lc.channelState.ChanType.IsTaproot():
// First, we'll need to re-derive the local nonce we sent to
// the remote party to create this musig session. We pass in
// the same height here as we're generating the nonce needed
// for the _current_ state.
localNonce, err := NewMusigVerificationNonce(
ourKey.PubKey, lc.currentHeight,
lc.taprootNonceProducer,
)
if err != nil {
return nil, err
}
// Now that we have the local nonce, we'll re-create the musig
// session we had for this height.
musigSession := NewPartialMusigSession(
*localNonce, ourKey, theirKey, lc.Signer,
&lc.fundingOutput, LocalMusigCommit,
)
var remoteSig lnwire.PartialSigWithNonce
err = remoteSig.Decode(
bytes.NewReader(localCommit.CommitSig),
)
if err != nil {
return nil, fmt.Errorf("unable to decode remote "+
"partial sig: %w", err)
}
// Next, we'll manually finalize the session with the signing
// nonce we got from the remote party which is embedded in the
// signature we have.
err = musigSession.FinalizeSession(musig2.Nonces{
PubNonce: remoteSig.Nonce,
})
if err != nil {
return nil, fmt.Errorf("unable to finalize musig "+
"session: %w", err)
}
// Now that the session has been finalized, we can generate our
// half of the signature for the state. We don't capture the
// sig as it's stored within the session.
if _, err := musigSession.SignCommit(commitTx); err != nil {
return nil, err
}
// The final step is now to combine this signature we generated
// above, with the remote party's signature. We only need to
// pass the remote sig, as the local sig was already cached in
// the session.
var partialSig MusigPartialSig
partialSig.FromWireSig(&remoteSig)
finalSig, err := musigSession.CombineSigs(partialSig.sig)
if err != nil {
return nil, fmt.Errorf("unable to combine musig "+
"partial sigs: %w", err)
}
// The witness is the single keyspend schnorr sig.
witness = wire.TxWitness{
finalSig.Serialize(),
}
// Otherwise, the final witness we generate will be a normal p2wsh
// multi-sig spend.
default:
theirSig, err := ecdsa.ParseDERSignature(localCommit.CommitSig)
if err != nil {
return nil, err
}
// With this, we then generate the full witness so the caller
// can broadcast a fully signed transaction.
lc.signDesc.SigHashes = input.NewTxSigHashesV0Only(commitTx)
ourSig, err := lc.Signer.SignOutputRaw(commitTx, lc.signDesc)
if err != nil {
return nil, err
}
// With the final signature generated, create the witness stack
// required to spend from the multi-sig output.
witness = input.SpendMultiSig(
lc.signDesc.WitnessScript,
ourKey.PubKey.SerializeCompressed(), ourSig,
theirKey.PubKey.SerializeCompressed(), theirSig,
)
}
// With this, we then generate the full witness so the caller can
// broadcast a fully signed transaction.
lc.signDesc.SigHashes = input.NewTxSigHashesV0Only(commitTx)
ourSig, err := lc.Signer.SignOutputRaw(commitTx, lc.signDesc)
if err != nil {
return nil, err
}
// With the final signature generated, create the witness stack
// required to spend from the multi-sig output.
ourKey := lc.channelState.LocalChanCfg.MultiSigKey.PubKey.
SerializeCompressed()
theirKey := lc.channelState.RemoteChanCfg.MultiSigKey.PubKey.
SerializeCompressed()
commitTx.TxIn[0].Witness = input.SpendMultiSig(
lc.signDesc.WitnessScript, ourKey,
ourSig, theirKey, theirSig,
)
commitTx.TxIn[0].Witness = witness
return commitTx, nil
}
@ -7775,14 +8073,17 @@ func (lc *LightningChannel) unsignedLocalUpdates(remoteMessageIndex,
// GenMusigNonces generates the verification nonce to start off a new musig2
// channel session.
func (lc *LightningChannel) GenMusigNonces() (*musig2.Nonces, error) {
lc.RLock()
defer lc.RUnlock()
lc.Lock()
defer lc.Unlock()
var err error
// We pass in the current height+1 as this'll be the set of
// verification nonces we'll send to the party to create our _next_
// state.
lc.pendingVerificationNonce, err = NewMusigVerificationNonce(
lc.channelState.LocalChanCfg.MultiSigKey.PubKey,
lc.currentHeight, lc.channelState.RevocationProducer,
false,
lc.currentHeight+1, lc.taprootNonceProducer,
)
if err != nil {
return nil, err
@ -7794,20 +8095,10 @@ func (lc *LightningChannel) GenMusigNonces() (*musig2.Nonces, error) {
// NewMusigVerificationNonce generates the local or verification nonce for
// another musig2 session. In order to permit our implementation to not have to
// write any secret nonce state to disk, we'll use the _next_ shachain
// pre-image as our primary randomness source.
func NewMusigVerificationNonce(pubKey *btcec.PublicKey, currentHeight uint64,
shaGen shachain.Producer, forBroadcast bool) (*musig2.Nonces, error) {
// If we're broadcasting this commitment, then we need to get the nonce
// for the current height. Otherwise, we'll add one, as we're
// generating a local nonce for the _next_ height.
targetHeight := func() uint64 {
if forBroadcast {
return currentHeight
}
return currentHeight + 1
}()
// pre-image as our primary randomness source. When used to generate the nonce
// again to broadcast our commitment hte current height will be used.
func NewMusigVerificationNonce(pubKey *btcec.PublicKey, targetHeight uint64,
shaGen shachain.Producer) (*musig2.Nonces, error) {
// Now that we know what height we need, we'll grab the shachain
// pre-image at the target destination.
@ -7834,8 +8125,12 @@ func (lc *LightningChannel) HasRemoteNonces() bool {
func (lc *LightningChannel) InitRemoteMusigNonces(remoteNonce *musig2.Nonces,
) error {
lc.RLock()
defer lc.RUnlock()
lc.Lock()
defer lc.Unlock()
if lc.pendingVerificationNonce == nil {
return fmt.Errorf("pending verification nonce is not set")
}
// Now that we have the set of local and remote nonces, we can generate
// a new pair of musig sessions for our local commitment and the

View File

@ -408,6 +408,23 @@ func WithLocalCounterNonce(targetHeight uint64,
}
}
// invalidPartialSigError is used to return additional debug information to a
// caller that encounters an invalid partial sig.
type invalidPartialSigError struct {
partialSig []byte
sigHash []byte
signingNonce [musig2.PubNonceSize]byte
verificationNonce [musig2.PubNonceSize]byte
}
// Error returns the error string for the partial sig error.
func (i invalidPartialSigError) Error() string {
return fmt.Sprintf("invalid partial sig: partial_sig=%x, "+
"sig_hash=%x, signing_nonce=%x, verification_nonce=%x",
i.partialSig, i.sigHash, i.signingNonce[:],
i.verificationNonce[:])
}
// VerifyCommitSig attempts to verify the passed partial signature against the
// passed commitment transaction. A keyspend sighash is assumed to generate the
// signed message. As we never re-use nonces, a new verification nonce (our