lnwallet: factor out func GetSignedCommitTx

This pure function creates signed commit transaction, using various
inputs passed as struct TaprootSignedCommitTxInputs and a signer.

This is needed to be able to store the inputs without a signature
in SCB and sign the transaction in chantools scbforceclose.

See https://github.com/lightningnetwork/lnd/pull/8183/files#r1423959791
This commit is contained in:
Boris Nagaev 2023-12-31 16:00:01 +01:00
parent e7776a4c1e
commit 90c45ddd8f
No known key found for this signature in database

View File

@ -6433,29 +6433,84 @@ func (lc *LightningChannel) AbsoluteThawHeight() (uint32, error) {
return lc.channelState.AbsoluteThawHeight()
}
// getSignedCommitTx function take the latest commitment transaction and
// populate it with witness data.
func (lc *LightningChannel) getSignedCommitTx() (*wire.MsgTx, error) {
// Fetch the current commitment transaction, along with their signature
// for the transaction.
localCommit := lc.channelState.LocalCommitment
commitTx := localCommit.CommitTx.Copy()
// SignedCommitTxInputs contains data needed to create a signed commit
// transaction using a signer. See GetSignedCommitTx.
type SignedCommitTxInputs struct {
// CommitTx is the latest version of the commitment state, broadcast
// able by us.
CommitTx *wire.MsgTx
ourKey := lc.channelState.LocalChanCfg.MultiSigKey
theirKey := lc.channelState.RemoteChanCfg.MultiSigKey
// CommitSig is one half of the signature required to fully complete
// the script for the commitment transaction above. This is the
// signature signed by the remote party for our version of the
// commitment transactions.
CommitSig []byte
// OurKey is our key to be used within the 2-of-2 output script
// for the owner of this channel.
OurKey keychain.KeyDescriptor
// TheirKey is their key to be used within the 2-of-2 output script
// for the owner of this channel.
TheirKey keychain.KeyDescriptor
// SignDesc is the primary sign descriptor that is capable of signing
// the commitment transaction that spends the multi-sig output.
SignDesc *input.SignDescriptor
// Taproot holds fields needed in case of a taproot channel.
// Iff the channel is of taproot type, this field is filled.
Taproot fn.Option[TaprootSignedCommitTxInputs]
}
// TaprootSignedCommitTxInputs contains additional data needed to create a
// signed commit transaction using a signer, used in case of a taproot channel.
// See GetSignedCommitTx.
type TaprootSignedCommitTxInputs struct {
// CommitHeight is the update number that this channel state represents.
// It is the total number of commitment updates up to this point. This
// can be viewed as sort of a "commitment height" as this number is
// monotonically increasing. This number is used to make a signature
// for a taproot channel, since it is used by shachain nonce producer
// (TaprootNonceProducer).
CommitHeight uint64
// TaprootNonceProducer is used to generate a shachain tree for the
// purpose of generating verification nonces for taproot channels.
TaprootNonceProducer shachain.Producer
// TapscriptRoot is the root of the tapscript tree that will be used to
// create the funding output. This is an optional field that should
// only be set for taproot channels.
TapscriptRoot fn.Option[chainhash.Hash]
}
// GetSignedCommitTx creates the witness stack of a channel commitment
// transaction. It can handle all commitment types (taproot, legacy). It is
// exported to give outside tooling the possibility to recreate the witness.
// A key use case is generating the witness data for a commitment transaction
// from a Static Channel Backup (SCB).
func GetSignedCommitTx(inputs SignedCommitTxInputs,
signer input.Signer) (*wire.MsgTx, error) {
commitTx := inputs.CommitTx.Copy()
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():
case inputs.Taproot.IsSome():
// Extract Taproot from fn.Option. It is safe to call
// UnsafeFromSome because we just checked that it is some.
taproot := inputs.Taproot.UnsafeFromSome()
// 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 := channeldb.NewMusigVerificationNonce(
ourKey.PubKey, lc.currentHeight,
lc.taprootNonceProducer,
inputs.OurKey.PubKey, taproot.CommitHeight,
taproot.TaprootNonceProducer,
)
if err != nil {
return nil, fmt.Errorf("unable to re-derive "+
@ -6463,19 +6518,20 @@ func (lc *LightningChannel) getSignedCommitTx() (*wire.MsgTx, error) {
}
tapscriptTweak := fn.MapOption(TapscriptRootToTweak)(
lc.channelState.TapscriptRoot,
taproot.TapscriptRoot,
)
// 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, tapscriptTweak,
*localNonce, inputs.OurKey, inputs.TheirKey, signer,
inputs.SignDesc.Output, LocalMusigCommit,
tapscriptTweak,
)
var remoteSig lnwire.PartialSigWithNonce
err = remoteSig.Decode(
bytes.NewReader(localCommit.CommitSig),
bytes.NewReader(inputs.CommitSig),
)
if err != nil {
return nil, fmt.Errorf("unable to decode remote "+
@ -6521,15 +6577,15 @@ func (lc *LightningChannel) getSignedCommitTx() (*wire.MsgTx, error) {
// Otherwise, the final witness we generate will be a normal p2wsh
// multi-sig spend.
default:
theirSig, err := ecdsa.ParseDERSignature(localCommit.CommitSig)
theirSig, err := ecdsa.ParseDERSignature(inputs.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)
inputs.SignDesc.SigHashes = input.NewTxSigHashesV0Only(commitTx)
ourSig, err := signer.SignOutputRaw(commitTx, inputs.SignDesc)
if err != nil {
return nil, err
}
@ -6537,9 +6593,9 @@ func (lc *LightningChannel) getSignedCommitTx() (*wire.MsgTx, error) {
// 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,
inputs.SignDesc.WitnessScript,
inputs.OurKey.PubKey.SerializeCompressed(), ourSig,
inputs.TheirKey.PubKey.SerializeCompressed(), theirSig,
)
}
@ -6548,6 +6604,32 @@ func (lc *LightningChannel) getSignedCommitTx() (*wire.MsgTx, error) {
return commitTx, nil
}
// getSignedCommitTx method takes the latest commitment transaction and
// populates it with witness data.
func (lc *LightningChannel) getSignedCommitTx() (*wire.MsgTx, error) {
// Fetch the current commitment transaction, along with their signature
// for the transaction.
localCommit := lc.channelState.LocalCommitment
inputs := SignedCommitTxInputs{
CommitTx: localCommit.CommitTx,
CommitSig: localCommit.CommitSig,
OurKey: lc.channelState.LocalChanCfg.MultiSigKey,
TheirKey: lc.channelState.RemoteChanCfg.MultiSigKey,
SignDesc: lc.signDesc,
}
if lc.channelState.ChanType.IsTaproot() {
inputs.Taproot = fn.Some(TaprootSignedCommitTxInputs{
CommitHeight: lc.currentHeight,
TaprootNonceProducer: lc.taprootNonceProducer,
TapscriptRoot: lc.channelState.TapscriptRoot,
})
}
return GetSignedCommitTx(inputs, lc.Signer)
}
// CommitOutputResolution carries the necessary information required to allow
// us to sweep our commitment output in the case that either party goes to
// chain.