test: cover invalid codesep positions for signature in taproot
What changed, and why it matters
This commit only changes Bitcoin Core's own test code and test framework. It fixes how the test suite encodes the 'code separator position' used in Taproot signature hashing, switching from a signed 32-bit integer to an unsigned 32-bit integer. It also adds two new test cases that check what happens when an invalid code-separator position is used. There is no change to the actual consensus or networking code that runs on the Bitcoin network, so this does not create or fix a live security vulnerability in Bitcoin Core itself. It is a test-coverage improvement.
No action required for production deployments. This is a test-only change. Reviewers may verify that the new test cases correctly exercise the invalid-codesep-position failure paths in the Taproot implementation.
Security signals we found
Serialization of codeseparator_pos corrected from signed to unsigned 4-byte little-endian in test framework
New negative test cases added for invalid Taproot code-separator positions
Default sentinel value changed from -1 to 0xffffffff to match unsigned encoding
No changes to consensus, mempool, P2P, wallet, or RPC code
Evidence from the diff
The diff modifies test/functional/feature_taproot.py and test/functional/test_framework/script.py. In script.py, codeseparator_pos.to_bytes(4, ‘little’, signed=True) is changed to signed=False, matching BIP-341’s specification that the position is serialized as a 4-byte little-endian unsigned integer. In feature_taproot.py, the default ‘codeseppos’ sentinel is changed from -1 to 0xffffffff, and two new negative test spenders are added (‘codesep_pk_wrongpos1’ with codeseppos=1 and ‘codesep_pk_wrongpos2’ with codeseppos=0xfffffffe) to verify that signatures with incorrect code-separator positions fail validation. The change is purely in test infrastructure.
Changed components
test/functional/feature_taproot.pytest/functional/test_framework/script.pyInspect captured patch +5 / −3
diff --git a/test/functional/feature_taproot.py b/test/functional/feature_taproot.py
index b22d5731..2ebec7a3 100755
--- a/test/functional/feature_taproot.py
+++ b/test/functional/feature_taproot.py
@@ -431,7 +431,7 @@ DEFAULT_CONTEXT = {
# The annex (only when mode=="taproot").
"annex": None,
# The codeseparator position (only when mode=="taproot").
- "codeseppos": -1,
+ "codeseppos": 0xffffffff,
# Which OP_CODESEPARATOR is the last executed one in the script (in legacy/P2SH/P2WSH).
"codesepnum": -1,
# The redeemscript to add to the scriptSig (if P2SH; None implies not P2SH).
@@ -779,6 +779,8 @@ def spenders_taproot_active():
add_spender(spenders, "sighash/codesep_pk", tap=tap, leaf="codesep_pk", key=secs[1], codeseppos=0, **common, **SINGLE_SIG, **SIGHASH_BITFLIP, **ERR_SCHNORR_SIG)
add_spender(spenders, "sighash/branched_codesep/left", tap=tap, leaf="branched_codesep", key=secs[0], codeseppos=3, **common, inputs=[getter("sign"), b'\x01'], **SIGHASH_BITFLIP, **ERR_SCHNORR_SIG)
add_spender(spenders, "sighash/branched_codesep/right", tap=tap, leaf="branched_codesep", key=secs[1], codeseppos=6, **common, inputs=[getter("sign"), b''], **SIGHASH_BITFLIP, **ERR_SCHNORR_SIG)
+ add_spender(spenders, "sighash/codesep_pk_wrongpos1", tap=tap, leaf="codesep_pk", key=secs[1], codeseppos=0, **common, **SINGLE_SIG, failure={"codeseppos": 1}, **ERR_SCHNORR_SIG)
+ add_spender(spenders, "sighash/codesep_pk_wrongpos2", tap=tap, leaf="codesep_pk", key=secs[1], codeseppos=0, **common, **SINGLE_SIG, failure={"codeseppos": 0xfffffffe}, **ERR_SCHNORR_SIG)
# Reusing the scripts above, test that various features affect the sighash.
add_spender(spenders, "sighash/annex", tap=tap, leaf="pk_codesep", key=secs[1], hashtype=hashtype, standard=False, **SINGLE_SIG, annex=bytes([ANNEX_TAG]), failure={"sighash": override(default_sighash, annex=None)}, **ERR_SCHNORR_SIG)
@@ -1280,7 +1282,7 @@ def spenders_taproot_active():
script = [pubs[1]]
inputs = []
opcount = 1
- codeseppos = -1
+ codeseppos = 0xffffffff
for pos, op in enumerate(ops):
if op == -1:
codeseppos = opcount
diff --git a/test/functional/test_framework/script.py b/test/functional/test_framework/script.py
index f3cee7c6..369bd2d7 100644
--- a/test/functional/test_framework/script.py
+++ b/test/functional/test_framework/script.py
@@ -847,7 +847,7 @@ def TaprootSignatureMsg(txTo, spent_utxos, hash_type, input_index=0, *, scriptpa
if scriptpath:
ss += TaggedHash("TapLeaf", bytes([leaf_ver]) + ser_string(leaf_script))
ss += bytes([0])
- ss += codeseparator_pos.to_bytes(4, "little", signed=True)
+ ss += codeseparator_pos.to_bytes(4, "little", signed=False)
assert len(ss) == 175 - (in_type == SIGHASH_ANYONECANPAY) * 49 - (out_type != SIGHASH_ALL and out_type != SIGHASH_SINGLE) * 32 + (annex is not None) * 32 + scriptpath * 37
return ss
Why this scored 17/100
Community notes
Notes can correct, qualify, or add evidence to the AI analysis. Every note shown here has been validated by a human moderator.
The AI analysis stands alone for now. Submit a note if you can add evidence or important context.