Skip to content

Commit f17e8ba

Browse files
author
MarcoFalke
committed
Merge #20207: Follow-up extra comments on taproot code and tests
2d8099c Mention units of MAX_STANDARD_ policy constants (Pieter Wuille) 84e29c7 Mention in validation that IsWitnessStandard tests for P2TR (Pieter Wuille) f867cbc Clean up assets test minimizer LDFLAGS (Pieter Wuille) ea0e786 Document additional IsWitnessStandard behavior (Pieter Wuille) 6040de9 Add comments on CPubKey::IsValid (Pieter Wuille) 8dbb7de Add comments to VerifyTaprootCommitment (Pieter Wuille) cdf900c Document need_vin_vout_mismatch argument to make_spender (Pieter Wuille) 18246ed Fix and improve taproot_construct comments (Pieter Wuille) Pull request description: Addressing some review comments raised here: bitcoin/bitcoin#19953 (review) and bitcoin/bitcoin#19953 (review) ACKs for top commit: jonatack: ACK 2d8099c per `git range-diff 5009159 4f10965 2d8099c` ariard: ACK 2d8099c, only changes are comment light improvements on IsValid/IsWitnessStandard. Tree-SHA512: c4881546c379ea8efc7ef99a43cbf3b9cd3f9dde5fd97a07ee66f2b593c78aef0bd8784853c5c9c737b66c269241a1048bbbdd6c964a3d872efd8ba0ec410b68
2 parents dfd0b70 + 2d8099c commit f17e8ba

File tree

7 files changed

+43
-12
lines changed

7 files changed

+43
-12
lines changed

src/Makefile.test.include

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1105,7 +1105,7 @@ test_fuzz_script_interpreter_SOURCES = test/fuzz/script_interpreter.cpp
11051105
test_fuzz_script_assets_test_minimizer_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES)
11061106
test_fuzz_script_assets_test_minimizer_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
11071107
test_fuzz_script_assets_test_minimizer_LDADD = $(FUZZ_SUITE_LD_COMMON)
1108-
test_fuzz_script_assets_test_minimizer_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
1108+
test_fuzz_script_assets_test_minimizer_LDFLAGS = $(FUZZ_SUITE_LDFLAGS_COMMON)
11091109
test_fuzz_script_assets_test_minimizer_SOURCES = test/fuzz/script_assets_test_minimizer.cpp
11101110

11111111
test_fuzz_script_ops_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES)

src/policy/policy.h

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -38,11 +38,11 @@ static const unsigned int DEFAULT_BYTES_PER_SIGOP = 20;
3838
static const bool DEFAULT_PERMIT_BAREMULTISIG = true;
3939
/** The maximum number of witness stack items in a standard P2WSH script */
4040
static const unsigned int MAX_STANDARD_P2WSH_STACK_ITEMS = 100;
41-
/** The maximum size of each witness stack item in a standard P2WSH script */
41+
/** The maximum size in bytes of each witness stack item in a standard P2WSH script */
4242
static const unsigned int MAX_STANDARD_P2WSH_STACK_ITEM_SIZE = 80;
43-
/** The maximum size of each witness stack item in a standard BIP 342 script (Taproot, leaf version 0xc0) */
43+
/** The maximum size in bytes of each witness stack item in a standard BIP 342 script (Taproot, leaf version 0xc0) */
4444
static const unsigned int MAX_STANDARD_TAPSCRIPT_STACK_ITEM_SIZE = 80;
45-
/** The maximum size of a standard witnessScript */
45+
/** The maximum size in bytes of a standard witnessScript */
4646
static const unsigned int MAX_STANDARD_P2WSH_SCRIPT_SIZE = 3600;
4747
/** The maximum size of a standard ScriptSig */
4848
static const unsigned int MAX_STANDARD_SCRIPTSIG_SIZE = 1650;
@@ -105,7 +105,9 @@ bool AreInputsStandard(const CTransaction& tx, const CCoinsViewCache& mapInputs,
105105
/**
106106
* Check if the transaction is over standard P2WSH resources limit:
107107
* 3600bytes witnessScript size, 80bytes per witness stack element, 100 witness stack elements
108-
* These limits are adequate for multi-signature up to n-of-100 using OP_CHECKSIG, OP_ADD, and OP_EQUAL,
108+
* These limits are adequate for multisignatures up to n-of-100 using OP_CHECKSIG, OP_ADD, and OP_EQUAL.
109+
*
110+
* Also enforce a maximum stack item size limit and no annexes for tapscript spends.
109111
*/
110112
bool IsWitnessStandard(const CTransaction& tx, const CCoinsViewCache& mapInputs);
111113

src/pubkey.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -170,6 +170,15 @@ class CPubKey
170170
/*
171171
* Check syntactic correctness.
172172
*
173+
* When setting a pubkey (Set()) or deserializing fails (its header bytes
174+
* don't match the length of the data), the size is set to 0. Thus,
175+
* by checking size, one can observe whether Set() or deserialization has
176+
* failed.
177+
*
178+
* This does not check for more than that. In particular, it does not verify
179+
* that the coordinates correspond to a point on the curve (see IsFullyValid()
180+
* for that instead).
181+
*
173182
* Note that this is consensus critical as CheckECDSASignature() calls it!
174183
*/
175184
bool IsValid() const

src/script/interpreter.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1834,9 +1834,13 @@ static bool ExecuteWitnessScript(const Span<const valtype>& stack_span, const CS
18341834
static bool VerifyTaprootCommitment(const std::vector<unsigned char>& control, const std::vector<unsigned char>& program, const CScript& script, uint256& tapleaf_hash)
18351835
{
18361836
const int path_len = (control.size() - TAPROOT_CONTROL_BASE_SIZE) / TAPROOT_CONTROL_NODE_SIZE;
1837+
//! The inner pubkey (x-only, so no Y coordinate parity).
18371838
const XOnlyPubKey p{uint256(std::vector<unsigned char>(control.begin() + 1, control.begin() + TAPROOT_CONTROL_BASE_SIZE))};
1839+
//! The output pubkey (taken from the scriptPubKey).
18381840
const XOnlyPubKey q{uint256(program)};
1841+
// Compute the tapleaf hash.
18391842
tapleaf_hash = (CHashWriter(HASHER_TAPLEAF) << uint8_t(control[0] & TAPROOT_LEAF_MASK) << script).GetSHA256();
1843+
// Compute the Merkle root from the leaf and the provided path.
18401844
uint256 k = tapleaf_hash;
18411845
for (int i = 0; i < path_len; ++i) {
18421846
CHashWriter ss_branch{HASHER_TAPBRANCH};
@@ -1848,7 +1852,9 @@ static bool VerifyTaprootCommitment(const std::vector<unsigned char>& control, c
18481852
}
18491853
k = ss_branch.GetSHA256();
18501854
}
1855+
// Compute the tweak from the Merkle root and the inner pubkey.
18511856
k = (CHashWriter(HASHER_TAPTWEAK) << MakeSpan(p) << k).GetSHA256();
1857+
// Verify that the output pubkey matches the tweaked inner pubkey, after correcting for parity.
18521858
return q.CheckPayToContract(p, k, control[0] & 1);
18531859
}
18541860

src/validation.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -696,7 +696,7 @@ bool MemPoolAccept::PreChecks(ATMPArgs& args, Workspace& ws)
696696
return state.Invalid(TxValidationResult::TX_INPUTS_NOT_STANDARD, "bad-txns-nonstandard-inputs");
697697
}
698698

699-
// Check for non-standard witness in P2WSH
699+
// Check for non-standard witnesses.
700700
if (tx.HasWitness() && fRequireStandard && !IsWitnessStandard(tx, m_view))
701701
return state.Invalid(TxValidationResult::TX_WITNESS_MUTATED, "bad-witness-nonstandard");
702702

test/functional/feature_taproot.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -444,6 +444,8 @@ def make_spender(comment, *, tap=None, witv0=False, script=None, pkh=None, p2sh=
444444
* standard: whether the (valid version of) spending is expected to be standard
445445
* err_msg: a string with an expected error message for failure (or None, if not cared about)
446446
* sigops_weight: the pre-taproot sigops weight consumed by a successful spend
447+
* need_vin_vout_mismatch: whether this test requires being tested in a transaction input that has no corresponding
448+
transaction output.
447449
"""
448450

449451
conf = dict()

test/functional/test_framework/script.py

Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -824,21 +824,33 @@ def taproot_tree_helper(scripts):
824824
h = TaggedHash("TapBranch", left_h + right_h)
825825
return (left + right, h)
826826

827+
# A TaprootInfo object has the following fields:
828+
# - scriptPubKey: the scriptPubKey (witness v1 CScript)
829+
# - inner_pubkey: the inner pubkey (32 bytes)
830+
# - negflag: whether the pubkey in the scriptPubKey was negated from inner_pubkey+tweak*G (bool).
831+
# - tweak: the tweak (32 bytes)
832+
# - leaves: a dict of name -> TaprootLeafInfo objects for all known leaves
827833
TaprootInfo = namedtuple("TaprootInfo", "scriptPubKey,inner_pubkey,negflag,tweak,leaves")
834+
835+
# A TaprootLeafInfo object has the following fields:
836+
# - script: the leaf script (CScript or bytes)
837+
# - version: the leaf version (0xc0 for BIP342 tapscript)
838+
# - merklebranch: the merkle branch to use for this leaf (32*N bytes)
828839
TaprootLeafInfo = namedtuple("TaprootLeafInfo", "script,version,merklebranch")
829840

830841
def taproot_construct(pubkey, scripts=None):
831842
"""Construct a tree of Taproot spending conditions
832843
833-
pubkey: an ECPubKey object for the internal pubkey
844+
pubkey: a 32-byte xonly pubkey for the internal pubkey (bytes)
834845
scripts: a list of items; each item is either:
835-
- a (name, CScript) tuple
836-
- a (name, CScript, leaf version) tuple
846+
- a (name, CScript or bytes, leaf version) tuple
847+
- a (name, CScript or bytes) tuple (defaulting to leaf version 0xc0)
837848
- another list of items (with the same structure)
838-
- a function, which specifies how to compute the hashing partner
839-
in function of the hash of whatever it is combined with
849+
- a list of two items; the first of which is an item itself, and the
850+
second is a function. The function takes as input the Merkle root of the
851+
first item, and produces a (fictitious) partner to hash with.
840852
841-
Returns: script (sPK or redeemScript), tweak, {name:(script, leaf version, negation flag, innerkey, merklepath), ...}
853+
Returns: a TaprootInfo object
842854
"""
843855
if scripts is None:
844856
scripts = []

0 commit comments

Comments
 (0)