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:
1 file changed
+104
-22
+104
-22
@@ -6433,29 +6433,84 @@ func (lc *LightningChannel) AbsoluteThawHeight() (uint32, error) {
|
|||||||
return lc.channelState.AbsoluteThawHeight()
|
return lc.channelState.AbsoluteThawHeight()
|
||||||
}
|
}
|
||||||
|
|
||||||
// getSignedCommitTx function take the latest commitment transaction and
|
// SignedCommitTxInputs contains data needed to create a signed commit
|
||||||
// populate it with witness data.
|
// transaction using a signer. See GetSignedCommitTx.
|
||||||
func (lc *LightningChannel) getSignedCommitTx() (*wire.MsgTx, error) {
|
type SignedCommitTxInputs struct {
|
||||||
// Fetch the current commitment transaction, along with their signature
|
// CommitTx is the latest version of the commitment state, broadcast
|
||||||
// for the transaction.
|
// able by us.
|
||||||
localCommit := lc.channelState.LocalCommitment
|
CommitTx *wire.MsgTx
|
||||||
commitTx := localCommit.CommitTx.Copy()
|
|
||||||
|
|
||||||
ourKey := lc.channelState.LocalChanCfg.MultiSigKey
|
// CommitSig is one half of the signature required to fully complete
|
||||||
theirKey := lc.channelState.RemoteChanCfg.MultiSigKey
|
// 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
|
var witness wire.TxWitness
|
||||||
switch {
|
switch {
|
||||||
// If this is a taproot channel, then we'll need to re-derive the nonce
|
// If this is a taproot channel, then we'll need to re-derive the nonce
|
||||||
// we need to generate a new signature
|
// 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
|
// 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 remote party to create this musig session. We pass in
|
||||||
// the same height here as we're generating the nonce needed
|
// the same height here as we're generating the nonce needed
|
||||||
// for the _current_ state.
|
// for the _current_ state.
|
||||||
localNonce, err := channeldb.NewMusigVerificationNonce(
|
localNonce, err := channeldb.NewMusigVerificationNonce(
|
||||||
ourKey.PubKey, lc.currentHeight,
|
inputs.OurKey.PubKey, taproot.CommitHeight,
|
||||||
lc.taprootNonceProducer,
|
taproot.TaprootNonceProducer,
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("unable to re-derive "+
|
return nil, fmt.Errorf("unable to re-derive "+
|
||||||
@@ -6463,19 +6518,20 @@ func (lc *LightningChannel) getSignedCommitTx() (*wire.MsgTx, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
tapscriptTweak := fn.MapOption(TapscriptRootToTweak)(
|
tapscriptTweak := fn.MapOption(TapscriptRootToTweak)(
|
||||||
lc.channelState.TapscriptRoot,
|
taproot.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, inputs.OurKey, inputs.TheirKey, signer,
|
||||||
&lc.fundingOutput, LocalMusigCommit, tapscriptTweak,
|
inputs.SignDesc.Output, LocalMusigCommit,
|
||||||
|
tapscriptTweak,
|
||||||
)
|
)
|
||||||
|
|
||||||
var remoteSig lnwire.PartialSigWithNonce
|
var remoteSig lnwire.PartialSigWithNonce
|
||||||
err = remoteSig.Decode(
|
err = remoteSig.Decode(
|
||||||
bytes.NewReader(localCommit.CommitSig),
|
bytes.NewReader(inputs.CommitSig),
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("unable to decode remote "+
|
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
|
// Otherwise, the final witness we generate will be a normal p2wsh
|
||||||
// multi-sig spend.
|
// multi-sig spend.
|
||||||
default:
|
default:
|
||||||
theirSig, err := ecdsa.ParseDERSignature(localCommit.CommitSig)
|
theirSig, err := ecdsa.ParseDERSignature(inputs.CommitSig)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// With this, we then generate the full witness so the caller
|
// With this, we then generate the full witness so the caller
|
||||||
// can broadcast a fully signed transaction.
|
// can broadcast a fully signed transaction.
|
||||||
lc.signDesc.SigHashes = input.NewTxSigHashesV0Only(commitTx)
|
inputs.SignDesc.SigHashes = input.NewTxSigHashesV0Only(commitTx)
|
||||||
ourSig, err := lc.Signer.SignOutputRaw(commitTx, lc.signDesc)
|
ourSig, err := signer.SignOutputRaw(commitTx, inputs.SignDesc)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -6537,9 +6593,9 @@ func (lc *LightningChannel) getSignedCommitTx() (*wire.MsgTx, error) {
|
|||||||
// With the final signature generated, create the witness stack
|
// With the final signature generated, create the witness stack
|
||||||
// required to spend from the multi-sig output.
|
// required to spend from the multi-sig output.
|
||||||
witness = input.SpendMultiSig(
|
witness = input.SpendMultiSig(
|
||||||
lc.signDesc.WitnessScript,
|
inputs.SignDesc.WitnessScript,
|
||||||
ourKey.PubKey.SerializeCompressed(), ourSig,
|
inputs.OurKey.PubKey.SerializeCompressed(), ourSig,
|
||||||
theirKey.PubKey.SerializeCompressed(), theirSig,
|
inputs.TheirKey.PubKey.SerializeCompressed(), theirSig,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -6548,6 +6604,32 @@ func (lc *LightningChannel) getSignedCommitTx() (*wire.MsgTx, error) {
|
|||||||
return commitTx, nil
|
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
|
// CommitOutputResolution carries the necessary information required to allow
|
||||||
// us to sweep our commitment output in the case that either party goes to
|
// us to sweep our commitment output in the case that either party goes to
|
||||||
// chain.
|
// chain.
|
||||||
|
|||||||
Reference in new issue
Block a user