Skip to content

Commit

Permalink
channeldb+input [temp]: add required Taproot variables
Browse files Browse the repository at this point in the history
  • Loading branch information
ellemouton committed May 31, 2023
1 parent 2997995 commit e83d218
Show file tree
Hide file tree
Showing 6 changed files with 186 additions and 41 deletions.
9 changes: 9 additions & 0 deletions channeldb/channel.go
Original file line number Diff line number Diff line change
Expand Up @@ -295,8 +295,17 @@ const (
// ScidAliasFeatureBit indicates that the scid-alias feature bit was
// negotiated during the lifetime of this channel.
ScidAliasFeatureBit ChannelType = 1 << 9

// SimpleTaprootFeatureBit indicates that the simple taproot channel
// feature bit was negotiated during the lifetime of this channel.
SimpleTaprootFeatureBit ChannelType = 1 << 10
)

// IsTaproot returns true if the channel is using taproot features.
func (c ChannelType) IsTaproot() bool {
return c&SimpleTaprootFeatureBit == SimpleTaprootFeatureBit
}

// IsSingleFunder returns true if the channel type if one of the known single
// funder variants.
func (c ChannelType) IsSingleFunder() bool {
Expand Down
108 changes: 69 additions & 39 deletions input/script_utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -1811,34 +1811,46 @@ type CommitScriptTree struct {
TapscriptRoot []byte
}

// NewLocalCommitScriptTree returns a new CommitScript tree that can be used to
// create and spend the commitment output for the local party.
func NewLocalCommitScriptTree(csvTimeout uint32,
selfKey, revokeKey *btcec.PublicKey) (*CommitScriptTree, error) {
func NewLocalCommitDelayScript(csvTimeout uint32,
selfKey *btcec.PublicKey) ([]byte, error) {

// First, we'll need to construct the tapLeaf that'll be our delay CSV
// clause.
builder := txscript.NewScriptBuilder()
builder.AddData(schnorr.SerializePubKey(selfKey))
builder.AddOp(txscript.OP_CHECKSIG)
builder.AddInt64(int64(csvTimeout))
builder.AddOp(txscript.OP_CHECKSEQUENCEVERIFY)
builder.AddOp(txscript.OP_DROP)

delayScript, err := builder.Script()
if err != nil {
return nil, err
}
return builder.Script()
}

// Next, we'll need to construct the revocation path, which is just a
// simple checksig script.
builder = txscript.NewScriptBuilder()
func NewLocalCommitRevokeScript(selfKey, revokeKey *btcec.PublicKey) ([]byte,
error) {

builder := txscript.NewScriptBuilder()
builder.AddData(schnorr.SerializePubKey(selfKey))
builder.AddOp(txscript.OP_DROP)
builder.AddData(schnorr.SerializePubKey(revokeKey))
builder.AddOp(txscript.OP_CHECKSIG)

revokeScript, err := builder.Script()
return builder.Script()
}

// NewLocalCommitScriptTree returns a new CommitScript tree that can be used to
// create and spend the commitment output for the local party.
func NewLocalCommitScriptTree(csvTimeout uint32,
selfKey, revokeKey *btcec.PublicKey) (*CommitScriptTree, error) {

// First, we'll need to construct the tapLeaf that'll be our delay CSV
// clause.
delayScript, err := NewLocalCommitDelayScript(csvTimeout, selfKey)
if err != nil {
return nil, err
}

// Next, we'll need to construct the revocation path, which is just a
// simple checksig script.
revokeScript, err := NewLocalCommitRevokeScript(selfKey, revokeKey)
if err != nil {
return nil, err
}
Expand Down Expand Up @@ -1955,19 +1967,30 @@ func TaprootCommitSpendSuccess(signer Signer, signDesc *SignDescriptor,
// TaprootCommitSpendRevoke constructs a valid witness allowing a node to sweep
// the revoked taproot output of a malicious peer.
func TaprootCommitSpendRevoke(signer Signer, signDesc *SignDescriptor,
revokeTx *wire.MsgTx,
scriptTree *txscript.IndexedTapScriptTree) (wire.TxWitness, error) {
revokeTx *wire.MsgTx, scriptTree *txscript.IndexedTapScriptTree,
ctrlBytes []byte) (wire.TxWitness, error) {

// First, we'll need to construct a valid control block to execute the
// leaf script for revocation path.
revokeTapleafHash := txscript.NewBaseTapLeaf(
signDesc.WitnessScript,
).TapHash()
revokeIdx := scriptTree.LeafProofIndex[revokeTapleafHash]
revokeMerkleProof := scriptTree.LeafMerkleProofs[revokeIdx]
revokeControlBlock := revokeMerkleProof.ToControlBlock(
&TaprootNUMSKey,
var (
ctrlB = ctrlBytes
err error
)
if len(ctrlBytes) == 0 {
revokeTapleafHash := txscript.NewBaseTapLeaf(
signDesc.WitnessScript,
).TapHash()
revokeIdx := scriptTree.LeafProofIndex[revokeTapleafHash]
revokeMerkleProof := scriptTree.LeafMerkleProofs[revokeIdx]
revokeControlBlock := revokeMerkleProof.ToControlBlock(
&TaprootNUMSKey,
)

ctrlB, err = revokeControlBlock.ToBytes()
if err != nil {
return nil, err
}
}

// With the control block created, we'll now generate the signature we
// need to authorize the spend.
Expand All @@ -1982,10 +2005,7 @@ func TaprootCommitSpendRevoke(signer Signer, signDesc *SignDescriptor,
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
}
witnessStack[2] = ctrlB

return witnessStack, nil
}
Expand Down Expand Up @@ -2263,17 +2283,30 @@ func TaprootCommitScriptToRemote(remoteKey *btcec.PublicKey,
// TaprootCommitRemoteSpend allows the remote party to sweep their output into
// their wallet after an enforced 1 block delay.
func TaprootCommitRemoteSpend(signer Signer, signDesc *SignDescriptor,
sweepTx *wire.MsgTx,
scriptTree *txscript.IndexedTapScriptTree) (wire.TxWitness, error) {
sweepTx *wire.MsgTx, scriptTree *txscript.IndexedTapScriptTree,
ctrl []byte) (wire.TxWitness, error) {

// First, we'll need to construct a valid control block to execute the
// leaf script for sweep settlement.
settleTapleafHash := txscript.NewBaseTapLeaf(
signDesc.WitnessScript,
).TapHash()
settleIdx := scriptTree.LeafProofIndex[settleTapleafHash]
settleMerkleProof := scriptTree.LeafMerkleProofs[settleIdx]
settleControlBlock := settleMerkleProof.ToControlBlock(&TaprootNUMSKey)
var (
ctrlBytes = ctrl
err error
)
if len(ctrlBytes) == 0 {
settleTapleafHash := txscript.NewBaseTapLeaf(
signDesc.WitnessScript,
).TapHash()
settleIdx := scriptTree.LeafProofIndex[settleTapleafHash]
settleMerkleProof := scriptTree.LeafMerkleProofs[settleIdx]
settleControlBlock := settleMerkleProof.ToControlBlock(
&TaprootNUMSKey,
)

ctrlBytes, err = settleControlBlock.ToBytes()
if err != nil {
return nil, err
}
}

// With the control block created, we'll now generate the signature we
// need to authorize the spend.
Expand All @@ -2288,10 +2321,7 @@ func TaprootCommitRemoteSpend(signer Signer, signDesc *SignDescriptor,
witnessStack := make(wire.TxWitness, 3)
witnessStack[0] = maybeAppendSighash(sweepSig, signDesc.HashType)
witnessStack[1] = signDesc.WitnessScript
witnessStack[2], err = settleControlBlock.ToBytes()
if err != nil {
return nil, err
}
witnessStack[2] = ctrlBytes

return witnessStack, nil
}
Expand Down
7 changes: 7 additions & 0 deletions input/signdescriptor.go
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,13 @@ type SignDescriptor struct {
// InputIndex is the target input within the transaction that should be
// signed.
InputIndex int

// ControlBlock is a fully serialized control block that contains the
// merkle proof necessary to spend a taproot output. This may
// optionally be set if the SignMethod is
// input.TaprootScriptSpendSignMethod. In which case, this should be an
// inclusion proof for the WitnessScript.
ControlBlock []byte
}

// SignMethod defines the different ways a signer can sign, given a specific
Expand Down
41 changes: 41 additions & 0 deletions input/size.go
Original file line number Diff line number Diff line change
Expand Up @@ -556,6 +556,47 @@ const (
// - leafVersionAndParity: 1 byte
// - schnorrPubKey: 32 byte
TaprootBaseControlBlockWitnessSize = 33

// TaprootToLocalRevokeScriptSize 68 bytes
// - OP_DATA: 1 byte
// - local key: 32 bytes
// - OP_DROP: 1 byte
// - OP_DATA: 1 byte
// - revocation key: 32 bytes
// - OP_CHECKSIG: 1 byte
TaprootToLocalRevokeScriptSize = 1 + 32 + 1 + 1 + 32 + 1

// TaprootToLocalRevokeWitnessSize
// - NumberOfWitnessElements: 1 byte
// - sigLength: 1 byte
// - sweep sig: 64 bytes
// - script len: 1 byte
// - revocation script size: 68 bytes
// - ctrl block size: 1 byte
// - base control block: 33 bytes
// - merkle proof: 32
TaprootToLocalRevokeWitnessSize = 1 + 1 + 64 + 1 +
TaprootToLocalRevokeScriptSize + 1 + 33 + 32

// TaprootToRemoteScriptSize
// - OP_DATA: 1 byte
// - remote key: 32 bytes
// - OP_CHECKSIG: 1 byte
// - OP_1: 1 byte
// - OP_CHECKSEQUENCEVERIFY: 1 byte
// - OP_DROP: 1 byte
TaprootToRemoteScriptSize = 1 + 32 + 1 + 1 + 1 + 1

// TaprootToRemoteWitnessSize
// - NumberOfWitnessElements: 1 byte
// - sigLength: 1 byte
// - sweep sig: 64 bytes
// - script len: 1 byte
// - remote script size: 37 bytes
// - ctrl block size: 1 byte
// - base control block: 33 bytes
TaprootToRemoteWitnessSize = 1 + 1 + 64 + 1 +
TaprootToRemoteScriptSize + 1 + 33
)

// EstimateCommitTxWeight estimate commitment transaction weight depending on
Expand Down
4 changes: 2 additions & 2 deletions input/taproot_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1011,7 +1011,7 @@ func localCommitRevokeWitGen(sigHash txscript.SigHashType,

return TaprootCommitSpendRevoke(
signer, signDesc, spendTx,
commitScriptTree.TapscriptTree,
commitScriptTree.TapscriptTree, nil,
)
}
}
Expand Down Expand Up @@ -1211,7 +1211,7 @@ func remoteCommitSweepWitGen(sigHash txscript.SigHashType,

return TaprootCommitRemoteSpend(
signer, signDesc, spendTx,
commitScriptTree.TapscriptTree,
commitScriptTree.TapscriptTree, nil,
)
}
}
Expand Down
58 changes: 58 additions & 0 deletions input/witnessgen.go
Original file line number Diff line number Diff line change
Expand Up @@ -180,6 +180,16 @@ const (
// regular p2tr output that's sent to an output which is under complete
// control of the backing wallet.
TaprootPubKeySpend StandardWitnessType = 21

// TaprootRemoteCommitSpend is a witness type that allows us to spend
// our settled local commitment after a CSV delay when the remote party
// has force closed the channel.
TaprootRemoteCommitSpend StandardWitnessType = 23

// TaprootCommitmentRevoke is a witness that allows us to sweep the
// settled output of a malicious counterparty's who broadcasts a
// revoked taproot commitment transaction.
TaprootCommitmentRevoke StandardWitnessType = 34
)

// String returns a human readable version of the target WitnessType.
Expand Down Expand Up @@ -253,6 +263,12 @@ func (wt StandardWitnessType) String() string {
case TaprootPubKeySpend:
return "TaprootPubKeySpend"

case TaprootCommitmentRevoke:
return "TaprootCommitmentRevoke"

case TaprootRemoteCommitSpend:
return "TaprootRemoteCommitSpend"

default:
return fmt.Sprintf("Unknown WitnessType: %v", uint32(wt))
}
Expand Down Expand Up @@ -400,7 +416,49 @@ func (wt StandardWitnessType) WitnessGenerator(signer Signer,
fallthrough
case NestedWitnessKeyHash:
return signer.ComputeInputScript(tx, desc)
case TaprootCommitmentRevoke:
// Ensure that the sign desc has the proper sign method
// set, and a valid prev output fetcher.
desc.SignMethod = TaprootScriptSpendSignMethod

// The control block bytes must be set at this point.
if desc.ControlBlock == nil {
return nil, fmt.Errorf("control block must be " +
"set for taproot script spend")
}

witness, err := TaprootCommitSpendRevoke(
signer, desc, tx, nil, desc.ControlBlock,
)
if err != nil {
return nil, err
}

return &Script{
Witness: witness,
}, nil

case TaprootRemoteCommitSpend:
// Ensure that the sign desc has the proper sign method
// set, and a valid prev output fetcher.
desc.SignMethod = TaprootScriptSpendSignMethod

// The control block bytes must be set at this point.
if desc.ControlBlock == nil {
return nil, fmt.Errorf("control block must be " +
"set for taproot spend")
}

witness, err := TaprootCommitRemoteSpend(
signer, desc, tx, nil, desc.ControlBlock,
)
if err != nil {
return nil, err
}

return &Script{
Witness: witness,
}, nil
default:
return nil, fmt.Errorf("unknown witness type: %v", wt)
}
Expand Down

0 comments on commit e83d218

Please sign in to comment.