@@ -1376,6 +1376,9 @@ template PrecomputedTransactionData::PrecomputedTransactionData(const CTransacti
1376
1376
template PrecomputedTransactionData::PrecomputedTransactionData(const CMutableTransaction& txTo);
1377
1377
1378
1378
static const CHashWriter HASHER_TAPSIGHASH = TaggedHash(" TapSighash" );
1379
+ static const CHashWriter HASHER_TAPLEAF = TaggedHash(" TapLeaf" );
1380
+ static const CHashWriter HASHER_TAPBRANCH = TaggedHash(" TapBranch" );
1381
+ static const CHashWriter HASHER_TAPTWEAK = TaggedHash(" TapTweak" );
1379
1382
1380
1383
template <typename T>
1381
1384
bool SignatureHashSchnorr (uint256& hash_out, const T& tx_to, const uint32_t in_pos, const uint8_t hash_type, const SigVersion sigversion, const PrecomputedTransactionData* cache)
@@ -1671,14 +1674,34 @@ static bool ExecuteWitnessScript(const Span<const valtype>& stack_span, const CS
1671
1674
return true ;
1672
1675
}
1673
1676
1674
- static bool VerifyWitnessProgram (const CScriptWitness& witness, int witversion, const std::vector<unsigned char >& program, unsigned int flags, const BaseSignatureChecker& checker, ScriptError* serror)
1677
+ static bool VerifyTaprootCommitment (const std::vector<unsigned char >& control, const std::vector<unsigned char >& program, const CScript& script)
1678
+ {
1679
+ int path_len = (control.size () - TAPROOT_CONTROL_BASE_SIZE) / TAPROOT_CONTROL_NODE_SIZE;
1680
+ XOnlyPubKey p{uint256 (std::vector<unsigned char >(control.begin () + 1 , control.begin () + TAPROOT_CONTROL_BASE_SIZE))};
1681
+ XOnlyPubKey q{uint256 (program)};
1682
+ uint256 k = (CHashWriter (HASHER_TAPLEAF) << uint8_t (control[0 ] & TAPROOT_LEAF_MASK) << script).GetSHA256 ();
1683
+ for (int i = 0 ; i < path_len; ++i) {
1684
+ CHashWriter ss_branch = HASHER_TAPBRANCH;
1685
+ Span<const unsigned char > node (control.data () + TAPROOT_CONTROL_BASE_SIZE + TAPROOT_CONTROL_NODE_SIZE * i, TAPROOT_CONTROL_NODE_SIZE);
1686
+ if (std::lexicographical_compare (k.begin (), k.end (), node.begin (), node.end ())) {
1687
+ ss_branch << k << node;
1688
+ } else {
1689
+ ss_branch << node << k;
1690
+ }
1691
+ k = ss_branch.GetSHA256 ();
1692
+ }
1693
+ k = (CHashWriter (HASHER_TAPTWEAK) << MakeSpan (p) << k).GetSHA256 ();
1694
+ return q.CheckPayToContract (p, k, control[0 ] & 1 );
1695
+ }
1696
+
1697
+ static bool VerifyWitnessProgram (const CScriptWitness& witness, int witversion, const std::vector<unsigned char >& program, unsigned int flags, const BaseSignatureChecker& checker, ScriptError* serror, bool is_p2sh)
1675
1698
{
1676
1699
CScript scriptPubKey;
1677
1700
Span<const valtype> stack{witness.stack };
1678
1701
1679
1702
if (witversion == 0 ) {
1680
1703
if (program.size () == WITNESS_V0_SCRIPTHASH_SIZE) {
1681
- // Version 0 segregated witness program: SHA256(CScript) inside the program, CScript + inputs in witness
1704
+ // BIP141 P2WSH: 32-byte witness v0 program (which encodes SHA256(script))
1682
1705
if (stack.size () == 0 ) {
1683
1706
return set_error (serror, SCRIPT_ERR_WITNESS_PROGRAM_WITNESS_EMPTY);
1684
1707
}
@@ -1691,7 +1714,7 @@ static bool VerifyWitnessProgram(const CScriptWitness& witness, int witversion,
1691
1714
}
1692
1715
return ExecuteWitnessScript (stack, scriptPubKey, flags, SigVersion::WITNESS_V0, checker, serror);
1693
1716
} else if (program.size () == WITNESS_V0_KEYHASH_SIZE) {
1694
- // Special case for pay-to-pubkeyhash; signature + pubkey in witness
1717
+ // BIP141 P2WPKH: 20-byte witness v0 program (which encodes Hash160(pubkey))
1695
1718
if (stack.size () != 2 ) {
1696
1719
return set_error (serror, SCRIPT_ERR_WITNESS_PROGRAM_MISMATCH); // 2 items in witness
1697
1720
}
@@ -1700,11 +1723,41 @@ static bool VerifyWitnessProgram(const CScriptWitness& witness, int witversion,
1700
1723
} else {
1701
1724
return set_error (serror, SCRIPT_ERR_WITNESS_PROGRAM_WRONG_LENGTH);
1702
1725
}
1726
+ } else if (witversion == 1 && program.size () == WITNESS_V1_TAPROOT_SIZE && !is_p2sh) {
1727
+ // BIP341 Taproot: 32-byte non-P2SH witness v1 program (which encodes a P2C-tweaked pubkey)
1728
+ if (!(flags & SCRIPT_VERIFY_TAPROOT)) return set_success (serror);
1729
+ if (stack.size () == 0 ) return set_error (serror, SCRIPT_ERR_WITNESS_PROGRAM_WITNESS_EMPTY);
1730
+ if (stack.size () >= 2 && !stack.back ().empty () && stack.back ()[0 ] == ANNEX_TAG) {
1731
+ // Drop annex
1732
+ SpanPopBack (stack);
1733
+ }
1734
+ if (stack.size () == 1 ) {
1735
+ // Key path spending (stack size is 1 after removing optional annex)
1736
+ if (!checker.CheckSchnorrSignature (stack.front (), program, SigVersion::TAPROOT)) {
1737
+ return set_error (serror, SCRIPT_ERR_TAPROOT_INVALID_SIG);
1738
+ }
1739
+ return set_success (serror);
1740
+ } else {
1741
+ // Script path spending (stack size is >1 after removing optional annex)
1742
+ const valtype& control = SpanPopBack (stack);
1743
+ const valtype& script_bytes = SpanPopBack (stack);
1744
+ scriptPubKey = CScript (script_bytes.begin (), script_bytes.end ());
1745
+ if (control.size () < TAPROOT_CONTROL_BASE_SIZE || control.size () > TAPROOT_CONTROL_MAX_SIZE || ((control.size () - TAPROOT_CONTROL_BASE_SIZE) % TAPROOT_CONTROL_NODE_SIZE) != 0 ) {
1746
+ return set_error (serror, SCRIPT_ERR_TAPROOT_WRONG_CONTROL_SIZE);
1747
+ }
1748
+ if (!VerifyTaprootCommitment (control, program, scriptPubKey)) {
1749
+ return set_error (serror, SCRIPT_ERR_WITNESS_PROGRAM_MISMATCH);
1750
+ }
1751
+ if (flags & SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_TAPROOT_VERSION) {
1752
+ return set_error (serror, SCRIPT_ERR_DISCOURAGE_UPGRADABLE_TAPROOT_VERSION);
1753
+ }
1754
+ return set_success (serror);
1755
+ }
1703
1756
} else {
1704
1757
if (flags & SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_WITNESS_PROGRAM) {
1705
1758
return set_error (serror, SCRIPT_ERR_DISCOURAGE_UPGRADABLE_WITNESS_PROGRAM);
1706
1759
}
1707
- // Higher version witness scripts return true for future softfork compatibility
1760
+ // Other version/size/p2sh combinations return true for future softfork compatibility
1708
1761
return true ;
1709
1762
}
1710
1763
// There is intentionally no return statement here, to be able to use "control reaches end of non-void function" warnings to detect gaps in the logic above.
@@ -1750,7 +1803,7 @@ bool VerifyScript(const CScript& scriptSig, const CScript& scriptPubKey, const C
1750
1803
// The scriptSig must be _exactly_ CScript(), otherwise we reintroduce malleability.
1751
1804
return set_error (serror, SCRIPT_ERR_WITNESS_MALLEATED);
1752
1805
}
1753
- if (!VerifyWitnessProgram (*witness, witnessversion, witnessprogram, flags, checker, serror)) {
1806
+ if (!VerifyWitnessProgram (*witness, witnessversion, witnessprogram, flags, checker, serror, /* is_p2sh */ false )) {
1754
1807
return false ;
1755
1808
}
1756
1809
// Bypass the cleanstack check at the end. The actual stack is obviously not clean
@@ -1795,7 +1848,7 @@ bool VerifyScript(const CScript& scriptSig, const CScript& scriptPubKey, const C
1795
1848
// reintroduce malleability.
1796
1849
return set_error (serror, SCRIPT_ERR_WITNESS_MALLEATED_P2SH);
1797
1850
}
1798
- if (!VerifyWitnessProgram (*witness, witnessversion, witnessprogram, flags, checker, serror)) {
1851
+ if (!VerifyWitnessProgram (*witness, witnessversion, witnessprogram, flags, checker, serror, /* is_p2sh */ true )) {
1799
1852
return false ;
1800
1853
}
1801
1854
// Bypass the cleanstack check at the end. The actual stack is obviously not clean
0 commit comments