input: use script path for revocation clause for to_local output

In this commit, we modify the to_local script to use a script path for
the revocation scenario. With this change, we ensure that the internal
key is always revealed which means the anchor outputs can still always
be swept.
This commit is contained in:
Olaoluwa Osuntokun 2023-05-23 17:19:06 -07:00
parent 7f05c765c3
commit 8fc8640432
No known key found for this signature in database
GPG Key ID: 3BBD59E99B280306
2 changed files with 74 additions and 27 deletions

View File

@ -1814,6 +1814,10 @@ type CommitScriptTree struct {
// SettleLeaf is the leaf used to settle the output after the delay. // SettleLeaf is the leaf used to settle the output after the delay.
SettleLeaf txscript.TapLeaf SettleLeaf txscript.TapLeaf
// RevocationLeaf is the leaf used to spend the output with the
// revocation key signature.
RevocationLeaf txscript.TapLeaf
// TapscriptTree is the full tapscript tree that also includes the // TapscriptTree is the full tapscript tree that also includes the
// control block needed to spend each of the leaves. // control block needed to spend each of the leaves.
TapscriptTree *txscript.IndexedTapScriptTree TapscriptTree *txscript.IndexedTapScriptTree
@ -1829,8 +1833,6 @@ func NewLocalCommitScriptTree(csvTimeout uint32,
// First, we'll need to construct the tapLeaf that'll be our delay CSV // First, we'll need to construct the tapLeaf that'll be our delay CSV
// clause. // clause.
//
// TODO(roasbeef): extract into diff func
builder := txscript.NewScriptBuilder() builder := txscript.NewScriptBuilder()
builder.AddData(schnorr.SerializePubKey(selfKey)) builder.AddData(schnorr.SerializePubKey(selfKey))
builder.AddOp(txscript.OP_CHECKSIG) builder.AddOp(txscript.OP_CHECKSIG)
@ -1843,10 +1845,26 @@ func NewLocalCommitScriptTree(csvTimeout uint32,
return nil, err return nil, err
} }
// With the delay script computed, we'll now create a tapscript tree // Next, we'll need to construct the revocation path, which is just a
// with a single leaf, and then obtain a root from that. // simple checksig script.
tapLeaf := txscript.NewBaseTapLeaf(delayScript) builder = txscript.NewScriptBuilder()
tapScriptTree := txscript.AssembleTaprootScriptTree(tapLeaf) builder.AddData(schnorr.SerializePubKey(selfKey))
builder.AddOp(txscript.OP_DROP)
builder.AddData(schnorr.SerializePubKey(revokeKey))
builder.AddOp(txscript.OP_CHECKSIG)
revokeScript, err := builder.Script()
if err != nil {
return nil, err
}
// With both scripts computed, we'll now create a tapscript tree with
// the two leaves, and then obtain a root from that.
delayTapLeaf := txscript.NewBaseTapLeaf(delayScript)
revokeTapLeaf := txscript.NewBaseTapLeaf(revokeScript)
tapScriptTree := txscript.AssembleTaprootScriptTree(
delayTapLeaf, revokeTapLeaf,
)
tapScriptRoot := tapScriptTree.RootNode.TapHash() tapScriptRoot := tapScriptTree.RootNode.TapHash()
// Now that we have our root, we can arrive at the final output script // Now that we have our root, we can arrive at the final output script
@ -1856,7 +1874,8 @@ func NewLocalCommitScriptTree(csvTimeout uint32,
) )
return &CommitScriptTree{ return &CommitScriptTree{
SettleLeaf: tapLeaf, SettleLeaf: delayTapLeaf,
RevocationLeaf: revokeTapLeaf,
TaprootKey: toLocalOutputKey, TaprootKey: toLocalOutputKey,
TapscriptTree: tapScriptTree, TapscriptTree: tapScriptTree,
TapscriptRoot: tapScriptRoot[:], TapscriptRoot: tapScriptRoot[:],
@ -1864,8 +1883,10 @@ func NewLocalCommitScriptTree(csvTimeout uint32,
} }
// TaprootCommitScriptToSelf creates the taproot witness program that commits // TaprootCommitScriptToSelf creates the taproot witness program that commits
// to the revocation (keyspend) and delay path (script path) in a single // to the revocation (script path) and delay path (script path) in a single
// taproot output key. // taproot output key. Both the delay script and the revocation script are part
// of the tapscript tree to ensure that the internal key is always revealed.
// This ensures that a 3rd party can always sweep the set of anchor outputs.
// //
// For the delay path we have the following tapscript leaf script: // For the delay path we have the following tapscript leaf script:
// //
@ -1879,12 +1900,20 @@ func NewLocalCommitScriptTree(csvTimeout uint32,
// Where the to_delay_script is listed above, and the delay_control_block // Where the to_delay_script is listed above, and the delay_control_block
// computed as: // computed as:
// //
// delay_control_block = (output_key_y_parity | 0xc0) || revocationpubkey // delay_control_block = (output_key_y_parity | 0xc0) || taproot_nums_key
// //
// The revocation key spend path will simply present a valid signature with the // The revocation path is simply:
// witness being just: //
// <local_delayedpubkey> OP_CHECKSIG
// <revocationkey> OP_CHECKSIG
//
// The revocation path can be spent with a control block similar to the above
// (but contains the hash of the other script), and with the following witness:
// //
// <revocation_sig> // <revocation_sig>
//
// We use a noop data push to ensure that the local public key is also revealed
// on chain, which enables the anchor output to be swept.
func TaprootCommitScriptToSelf(csvTimeout uint32, func TaprootCommitScriptToSelf(csvTimeout uint32,
selfKey, revokeKey *btcec.PublicKey) (*btcec.PublicKey, error) { selfKey, revokeKey *btcec.PublicKey) (*btcec.PublicKey, error) {
@ -1902,7 +1931,7 @@ func TaprootCommitScriptToSelf(csvTimeout uint32,
// sweep the settled taproot output after the delay has passed for a force // sweep the settled taproot output after the delay has passed for a force
// close. // close.
func TaprootCommitSpendSuccess(signer Signer, signDesc *SignDescriptor, func TaprootCommitSpendSuccess(signer Signer, signDesc *SignDescriptor,
sweepTx *wire.MsgTx, revokeKey *btcec.PublicKey, sweepTx *wire.MsgTx,
scriptTree *txscript.IndexedTapScriptTree) (wire.TxWitness, error) { scriptTree *txscript.IndexedTapScriptTree) (wire.TxWitness, error) {
// First, we'll need to construct a valid control block to execute the // First, we'll need to construct a valid control block to execute the
@ -1940,19 +1969,37 @@ func TaprootCommitSpendSuccess(signer Signer, signDesc *SignDescriptor,
// TaprootCommitSpendRevoke constructs a valid witness allowing a node to sweep // TaprootCommitSpendRevoke constructs a valid witness allowing a node to sweep
// the revoked taproot output of a malicious peer. // the revoked taproot output of a malicious peer.
func TaprootCommitSpendRevoke(signer Signer, signDesc *SignDescriptor, func TaprootCommitSpendRevoke(signer Signer, signDesc *SignDescriptor,
revokeTx *wire.MsgTx) (wire.TxWitness, error) { revokeTx *wire.MsgTx,
scriptTree *txscript.IndexedTapScriptTree) (wire.TxWitness, error) {
// For this spend type, we only need a single signature which'll be a // First, we'll need to construct a valid control block to execute the
// keyspend using the revoke private key. // leaf script for revocation path.
sweepSig, err := signer.SignOutputRaw(revokeTx, signDesc) revokeTapleafHash := txscript.NewBaseTapLeaf(
signDesc.WitnessScript,
).TapHash()
revokeIdx := scriptTree.LeafProofIndex[revokeTapleafHash]
revokeMerkleProof := scriptTree.LeafMerkleProofs[revokeIdx]
revokeControlBlock := revokeMerkleProof.ToControlBlock(
&TaprootNUMSKey,
)
// With the control block created, we'll now generate the signature we
// need to authorize the spend.
revokeSig, err := signer.SignOutputRaw(revokeTx, signDesc)
if err != nil { if err != nil {
return nil, err return nil, err
} }
// The witness stack in this case is pretty simple: we only need to // The final witness stack will be:
// specify the signature generated. //
witnessStack := make(wire.TxWitness, 1) // <revoke sig sig> <revoke script> <control block>
witnessStack[0] = maybeAppendSighash(sweepSig, signDesc.HashType) witnessStack := make(wire.TxWitness, 3)
witnessStack[0] = maybeAppendSighash(revokeSig, signDesc.HashType)
witnessStack[1] = signDesc.WitnessScript
witnessStack[2], err = revokeControlBlock.ToBytes()
if err != nil {
return nil, err
}
return witnessStack, nil return witnessStack, nil
} }

View File

@ -977,7 +977,6 @@ func localCommitSweepWitGen(sigHash txscript.SigHashType,
return TaprootCommitSpendSuccess( return TaprootCommitSpendSuccess(
signer, signDesc, spendTx, signer, signDesc, spendTx,
commitScriptTree.revokeKey.PubKey(),
commitScriptTree.TapscriptTree, commitScriptTree.TapscriptTree,
) )
} }
@ -1000,17 +999,18 @@ func localCommitRevokeWitGen(sigHash txscript.SigHashType,
KeyDesc: keychain.KeyDescriptor{ KeyDesc: keychain.KeyDescriptor{
PubKey: revokeKey.PubKey(), PubKey: revokeKey.PubKey(),
}, },
WitnessScript: commitScriptTree.RevocationLeaf.Script,
Output: commitScriptTree.txOut, Output: commitScriptTree.txOut,
HashType: sigHash, HashType: sigHash,
InputIndex: 0, InputIndex: 0,
SigHashes: hashCache, SigHashes: hashCache,
SignMethod: TaprootKeySpendSignMethod, SignMethod: TaprootScriptSpendSignMethod,
TapTweak: commitScriptTree.TapscriptRoot,
PrevOutputFetcher: prevOuts, PrevOutputFetcher: prevOuts,
} }
return TaprootCommitSpendRevoke( return TaprootCommitSpendRevoke(
signer, signDesc, spendTx, signer, signDesc, spendTx,
commitScriptTree.TapscriptTree,
) )
} }
} }