lnwallet: ensure we re-sign retransmitted commits for taproot channels
In this commit, we fix an existing bug with the taproot channel type that can cause force closes if a peer disconnects while attempting to send the commitment signature. Before this commit, since the `PartialSig` we send is never committed to disk, the version read wouldn't contain the musig2 partial sig. We never write these signatures to disk, as each time we make a new session, we need to generate fresh nonces to avoid nonce-reuse. Due to the above interaction, if we went to re-send a signature after a disconnection, the `CommitSig` message we sent wouldn't actually contain a `PartialSigWithNonce`, causing a protocol error.
This commit is contained in:
1 file changed
+35
-3
+35
-3
@@ -4151,6 +4151,27 @@ func (lc *LightningChannel) SignNextCommitment() (*NewCommitState, error) {
|
|||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// resignMusigCommit is used to resign a commitment transaction for taproot
|
||||||
|
// channels when we need to retransmit a signature after a channel reestablish
|
||||||
|
// message. Taproot channels use musig2, which means we must use fresh nonces
|
||||||
|
// each time. After we receive the channel reestablish message, we learn the
|
||||||
|
// nonce we need to use for the remote party. As a result, we need to generate
|
||||||
|
// the partial signature again with the new nonce.
|
||||||
|
func (lc *LightningChannel) resignMusigCommit(commitTx *wire.MsgTx,
|
||||||
|
) (lnwire.OptPartialSigWithNonceTLV, error) {
|
||||||
|
|
||||||
|
remoteSession := lc.musigSessions.RemoteSession
|
||||||
|
musig, err := remoteSession.SignCommit(commitTx)
|
||||||
|
if err != nil {
|
||||||
|
var none lnwire.OptPartialSigWithNonceTLV
|
||||||
|
return none, err
|
||||||
|
}
|
||||||
|
|
||||||
|
partialSig := lnwire.MaybePartialSigWithNonce(musig.ToWireSig())
|
||||||
|
|
||||||
|
return partialSig, nil
|
||||||
|
}
|
||||||
|
|
||||||
// ProcessChanSyncMsg processes a ChannelReestablish message sent by the remote
|
// ProcessChanSyncMsg processes a ChannelReestablish message sent by the remote
|
||||||
// connection upon re establishment of our connection with them. This method
|
// connection upon re establishment of our connection with them. This method
|
||||||
// will return a single message if we are currently out of sync, otherwise a
|
// will return a single message if we are currently out of sync, otherwise a
|
||||||
@@ -4428,12 +4449,23 @@ func (lc *LightningChannel) ProcessChanSyncMsg(
|
|||||||
commitUpdates = append(commitUpdates, logUpdate.UpdateMsg)
|
commitUpdates = append(commitUpdates, logUpdate.UpdateMsg)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If this is a taproot channel, then we need to regenerate the
|
||||||
|
// musig2 signature for the remote party, using their fresh
|
||||||
|
// nonce.
|
||||||
|
if lc.channelState.ChanType.IsTaproot() {
|
||||||
|
partialSig, err := lc.resignMusigCommit(
|
||||||
|
commitDiff.Commitment.CommitTx,
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
commitDiff.CommitSig.PartialSig = partialSig
|
||||||
|
}
|
||||||
|
|
||||||
// With the batch of updates accumulated, we'll now re-send the
|
// With the batch of updates accumulated, we'll now re-send the
|
||||||
// original CommitSig message required to re-sync their remote
|
// original CommitSig message required to re-sync their remote
|
||||||
// commitment chain with our local version of their chain.
|
// 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)
|
commitUpdates = append(commitUpdates, commitDiff.CommitSig)
|
||||||
|
|
||||||
// NOTE: If a revocation is not owed, then updates is empty.
|
// NOTE: If a revocation is not owed, then updates is empty.
|
||||||
|
|||||||
Reference in new issue
Block a user