Skip to content

Commit 8e3fc99

Browse files
Do not use CScript for tapleaf scripts until the tapleaf version is known
Prevents use of CScript methods until the tapleaf is known to be a tapscript.
1 parent 60a0088 commit 8e3fc99

File tree

8 files changed

+29
-29
lines changed

8 files changed

+29
-29
lines changed

src/psbt.h

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -206,7 +206,7 @@ struct PSBTInput
206206
// Taproot fields
207207
std::vector<unsigned char> m_tap_key_sig;
208208
std::map<std::pair<XOnlyPubKey, uint256>, std::vector<unsigned char>> m_tap_script_sigs;
209-
std::map<std::pair<CScript, int>, std::set<std::vector<unsigned char>, ShortestVectorFirstComparator>> m_tap_scripts;
209+
std::map<std::pair<std::vector<unsigned char>, int>, std::set<std::vector<unsigned char>, ShortestVectorFirstComparator>> m_tap_scripts;
210210
std::map<XOnlyPubKey, std::pair<std::set<uint256>, KeyOriginInfo>> m_tap_bip32_paths;
211211
XOnlyPubKey m_tap_internal_key;
212212
uint256 m_tap_merkle_root;
@@ -621,7 +621,7 @@ struct PSBTInput
621621
}
622622
uint8_t leaf_ver = script_v.back();
623623
script_v.pop_back();
624-
const auto leaf_script = std::make_pair(CScript(script_v.begin(), script_v.end()), (int)leaf_ver);
624+
const auto leaf_script = std::make_pair(script_v, (int)leaf_ver);
625625
m_tap_scripts[leaf_script].insert(std::vector<unsigned char>(key.begin() + 1, key.end()));
626626
break;
627627
}
@@ -713,7 +713,7 @@ struct PSBTOutput
713713
CScript witness_script;
714714
std::map<CPubKey, KeyOriginInfo> hd_keypaths;
715715
XOnlyPubKey m_tap_internal_key;
716-
std::vector<std::tuple<uint8_t, uint8_t, CScript>> m_tap_tree;
716+
std::vector<std::tuple<uint8_t, uint8_t, std::vector<unsigned char>>> m_tap_tree;
717717
std::map<XOnlyPubKey, std::pair<std::set<uint256>, KeyOriginInfo>> m_tap_bip32_paths;
718718
std::map<std::vector<unsigned char>, std::vector<unsigned char>> unknown;
719719
std::set<PSBTProprietary> m_proprietary;
@@ -864,7 +864,7 @@ struct PSBTOutput
864864
while (!s_tree.empty()) {
865865
uint8_t depth;
866866
uint8_t leaf_ver;
867-
CScript script;
867+
std::vector<unsigned char> script;
868868
s_tree >> depth;
869869
s_tree >> leaf_ver;
870870
s_tree >> script;

src/script/descriptor.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1643,7 +1643,7 @@ std::unique_ptr<DescriptorImpl> InferScript(const CScript& script, ParseScriptCo
16431643
for (const auto& [depth, script, leaf_ver] : *tree) {
16441644
std::unique_ptr<DescriptorImpl> subdesc;
16451645
if (leaf_ver == TAPROOT_LEAF_TAPSCRIPT) {
1646-
subdesc = InferScript(script, ParseScriptContext::P2TR, provider);
1646+
subdesc = InferScript(CScript(script.begin(), script.end()), ParseScriptContext::P2TR, provider);
16471647
}
16481648
if (!subdesc) {
16491649
ok = false;

src/script/interpreter.cpp

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1825,9 +1825,9 @@ static bool ExecuteWitnessScript(const Span<const valtype>& stack_span, const CS
18251825
return true;
18261826
}
18271827

1828-
uint256 ComputeTapleafHash(uint8_t leaf_version, const CScript& script)
1828+
uint256 ComputeTapleafHash(uint8_t leaf_version, Span<const unsigned char> script)
18291829
{
1830-
return (HashWriter{HASHER_TAPLEAF} << leaf_version << script).GetSHA256();
1830+
return (HashWriter{HASHER_TAPLEAF} << leaf_version << CompactSizeWriter(script.size()) << script).GetSHA256();
18311831
}
18321832

18331833
uint256 ComputeTaprootMerkleRoot(Span<const unsigned char> control, const uint256& tapleaf_hash)
@@ -1917,18 +1917,18 @@ static bool VerifyWitnessProgram(const CScriptWitness& witness, int witversion,
19171917
} else {
19181918
// Script path spending (stack size is >1 after removing optional annex)
19191919
const valtype& control = SpanPopBack(stack);
1920-
const valtype& script_bytes = SpanPopBack(stack);
1921-
exec_script = CScript(script_bytes.begin(), script_bytes.end());
1920+
const valtype& script = SpanPopBack(stack);
19221921
if (control.size() < TAPROOT_CONTROL_BASE_SIZE || control.size() > TAPROOT_CONTROL_MAX_SIZE || ((control.size() - TAPROOT_CONTROL_BASE_SIZE) % TAPROOT_CONTROL_NODE_SIZE) != 0) {
19231922
return set_error(serror, SCRIPT_ERR_TAPROOT_WRONG_CONTROL_SIZE);
19241923
}
1925-
execdata.m_tapleaf_hash = ComputeTapleafHash(control[0] & TAPROOT_LEAF_MASK, exec_script);
1924+
execdata.m_tapleaf_hash = ComputeTapleafHash(control[0] & TAPROOT_LEAF_MASK, script);
19261925
if (!VerifyTaprootCommitment(control, program, execdata.m_tapleaf_hash)) {
19271926
return set_error(serror, SCRIPT_ERR_WITNESS_PROGRAM_MISMATCH);
19281927
}
19291928
execdata.m_tapleaf_hash_init = true;
19301929
if ((control[0] & TAPROOT_LEAF_MASK) == TAPROOT_LEAF_TAPSCRIPT) {
19311930
// Tapscript (leaf version 0xc0)
1931+
exec_script = CScript(script.begin(), script.end());
19321932
execdata.m_validation_weight_left = ::GetSerializeSize(witness.stack, PROTOCOL_VERSION) + VALIDATION_WEIGHT_OFFSET;
19331933
execdata.m_validation_weight_left_init = true;
19341934
return ExecuteWitnessScript(stack, exec_script, flags, SigVersion::TAPSCRIPT, checker, execdata, serror);

src/script/interpreter.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -333,7 +333,7 @@ class DeferringSignatureChecker : public BaseSignatureChecker
333333
};
334334

335335
/** Compute the BIP341 tapleaf hash from leaf version & script. */
336-
uint256 ComputeTapleafHash(uint8_t leaf_version, const CScript& script);
336+
uint256 ComputeTapleafHash(uint8_t leaf_version, Span<const unsigned char> script);
337337
/** Compute the BIP341 taproot script tree Merkle root from control block and leaf hash.
338338
* Requires control block to have valid length (33 + k*32, with k in {0,1,..,128}). */
339339
uint256 ComputeTaprootMerkleRoot(Span<const unsigned char> control, const uint256& tapleaf_hash);

src/script/sign.cpp

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -169,13 +169,14 @@ static bool CreateTaprootScriptSig(const BaseSignatureCreator& creator, Signatur
169169
return false;
170170
}
171171

172-
static bool SignTaprootScript(const SigningProvider& provider, const BaseSignatureCreator& creator, SignatureData& sigdata, int leaf_version, const CScript& script, std::vector<valtype>& result)
172+
static bool SignTaprootScript(const SigningProvider& provider, const BaseSignatureCreator& creator, SignatureData& sigdata, int leaf_version, Span<const unsigned char> script_bytes, std::vector<valtype>& result)
173173
{
174174
// Only BIP342 tapscript signing is supported for now.
175175
if (leaf_version != TAPROOT_LEAF_TAPSCRIPT) return false;
176176
SigVersion sigversion = SigVersion::TAPSCRIPT;
177177

178-
uint256 leaf_hash = (HashWriter{HASHER_TAPLEAF} << uint8_t(leaf_version) << script).GetSHA256();
178+
uint256 leaf_hash = ComputeTapleafHash(leaf_version, script_bytes);
179+
CScript script = CScript(script_bytes.begin(), script_bytes.end());
179180

180181
// <xonly pubkey> OP_CHECKSIG
181182
if (script.size() == 34 && script[33] == OP_CHECKSIG && script[0] == 0x20) {

src/script/standard.cpp

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -443,14 +443,14 @@ void TaprootBuilder::Insert(TaprootBuilder::NodeInfo&& node, int depth)
443443
return branch.size() == 0 || (branch.size() == 1 && branch[0]);
444444
}
445445

446-
TaprootBuilder& TaprootBuilder::Add(int depth, const CScript& script, int leaf_version, bool track)
446+
TaprootBuilder& TaprootBuilder::Add(int depth, Span<const unsigned char> script, int leaf_version, bool track)
447447
{
448448
assert((leaf_version & ~TAPROOT_LEAF_MASK) == 0);
449449
if (!IsValid()) return *this;
450450
/* Construct NodeInfo object with leaf hash and (if track is true) also leaf information. */
451451
NodeInfo node;
452-
node.hash = (HashWriter{HASHER_TAPLEAF} << uint8_t(leaf_version) << script).GetSHA256();
453-
if (track) node.leaves.emplace_back(LeafInfo{script, leaf_version, {}});
452+
node.hash = ComputeTapleafHash(leaf_version, script);
453+
if (track) node.leaves.emplace_back(LeafInfo{std::vector<unsigned char>(script.begin(), script.end()), leaf_version, {}});
454454
/* Insert into the branch. */
455455
Insert(std::move(node), depth);
456456
return *this;
@@ -506,13 +506,13 @@ TaprootSpendData TaprootBuilder::GetSpendData() const
506506
return spd;
507507
}
508508

509-
std::optional<std::vector<std::tuple<int, CScript, int>>> InferTaprootTree(const TaprootSpendData& spenddata, const XOnlyPubKey& output)
509+
std::optional<std::vector<std::tuple<int, std::vector<unsigned char>, int>>> InferTaprootTree(const TaprootSpendData& spenddata, const XOnlyPubKey& output)
510510
{
511511
// Verify that the output matches the assumed Merkle root and internal key.
512512
auto tweak = spenddata.internal_key.CreateTapTweak(spenddata.merkle_root.IsNull() ? nullptr : &spenddata.merkle_root);
513513
if (!tweak || tweak->first != output) return std::nullopt;
514514
// If the Merkle root is 0, the tree is empty, and we're done.
515-
std::vector<std::tuple<int, CScript, int>> ret;
515+
std::vector<std::tuple<int, std::vector<unsigned char>, int>> ret;
516516
if (spenddata.merkle_root.IsNull()) return ret;
517517

518518
/** Data structure to represent the nodes of the tree we're going to build. */
@@ -523,7 +523,7 @@ std::optional<std::vector<std::tuple<int, CScript, int>>> InferTaprootTree(const
523523
std::unique_ptr<TreeNode> sub[2];
524524
/** If this is known to be a leaf node, a pointer to the (script, leaf_ver) pair.
525525
* nullptr otherwise. */
526-
const std::pair<CScript, int>* leaf = nullptr;
526+
const std::pair<std::vector<unsigned char>, int>* leaf = nullptr;
527527
/** Whether or not this node has been explored (is known to be a leaf, or known to have children). */
528528
bool explored = false;
529529
/** Whether or not this node is an inner node (unknown until explored = true). */
@@ -641,10 +641,10 @@ std::optional<std::vector<std::tuple<int, CScript, int>>> InferTaprootTree(const
641641
return ret;
642642
}
643643

644-
std::vector<std::tuple<uint8_t, uint8_t, CScript>> TaprootBuilder::GetTreeTuples() const
644+
std::vector<std::tuple<uint8_t, uint8_t, std::vector<unsigned char>>> TaprootBuilder::GetTreeTuples() const
645645
{
646646
assert(IsComplete());
647-
std::vector<std::tuple<uint8_t, uint8_t, CScript>> tuples;
647+
std::vector<std::tuple<uint8_t, uint8_t, std::vector<unsigned char>>> tuples;
648648
if (m_branch.size()) {
649649
const auto& leaves = m_branch[0]->leaves;
650650
for (const auto& leaf : leaves) {

src/script/standard.h

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -217,7 +217,7 @@ struct TaprootSpendData
217217
* inference can reconstruct the full tree. Within each set, the control
218218
* blocks are sorted by size, so that the signing logic can easily
219219
* prefer the cheapest one. */
220-
std::map<std::pair<CScript, int>, std::set<std::vector<unsigned char>, ShortestVectorFirstComparator>> scripts;
220+
std::map<std::pair<std::vector<unsigned char>, int>, std::set<std::vector<unsigned char>, ShortestVectorFirstComparator>> scripts;
221221
/** Merge other TaprootSpendData (for the same scriptPubKey) into this. */
222222
void Merge(TaprootSpendData other);
223223
};
@@ -229,7 +229,7 @@ class TaprootBuilder
229229
/** Information about a tracked leaf in the Merkle tree. */
230230
struct LeafInfo
231231
{
232-
CScript script; //!< The script.
232+
std::vector<unsigned char> script; //!< The script.
233233
int leaf_version; //!< The leaf version for that script.
234234
std::vector<uint256> merkle_branch; //!< The hashing partners above this leaf.
235235
};
@@ -296,7 +296,7 @@ class TaprootBuilder
296296
/** Add a new script at a certain depth in the tree. Add() operations must be called
297297
* in depth-first traversal order of binary tree. If track is true, it will be included in
298298
* the GetSpendData() output. */
299-
TaprootBuilder& Add(int depth, const CScript& script, int leaf_version, bool track = true);
299+
TaprootBuilder& Add(int depth, Span<const unsigned char> script, int leaf_version, bool track = true);
300300
/** Like Add(), but for a Merkle node with a given hash to the tree. */
301301
TaprootBuilder& AddOmitted(int depth, const uint256& hash);
302302
/** Finalize the construction. Can only be called when IsComplete() is true.
@@ -314,7 +314,7 @@ class TaprootBuilder
314314
/** Compute spending data (after Finalize()). */
315315
TaprootSpendData GetSpendData() const;
316316
/** Returns a vector of tuples representing the depth, leaf version, and script */
317-
std::vector<std::tuple<uint8_t, uint8_t, CScript>> GetTreeTuples() const;
317+
std::vector<std::tuple<uint8_t, uint8_t, std::vector<unsigned char>>> GetTreeTuples() const;
318318
/** Returns true if there are any tapscripts */
319319
bool HasScripts() const { return !m_branch.empty(); }
320320
};
@@ -325,6 +325,6 @@ class TaprootBuilder
325325
* std::nullopt is returned. Otherwise, a vector of (depth, script, leaf_ver) tuples is
326326
* returned, corresponding to a depth-first traversal of the script tree.
327327
*/
328-
std::optional<std::vector<std::tuple<int, CScript, int>>> InferTaprootTree(const TaprootSpendData& spenddata, const XOnlyPubKey& output);
328+
std::optional<std::vector<std::tuple<int, std::vector<unsigned char>, int>>> InferTaprootTree(const TaprootSpendData& spenddata, const XOnlyPubKey& output);
329329

330330
#endif // BITCOIN_SCRIPT_STANDARD_H

src/test/script_standard_tests.cpp

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -400,12 +400,11 @@ BOOST_AUTO_TEST_CASE(bip341_spk_test_vectors)
400400

401401
for (const auto& vec : vectors.getValues()) {
402402
TaprootBuilder spktest;
403-
std::map<std::pair<CScript, int>, int> scriptposes;
403+
std::map<std::pair<std::vector<unsigned char>, int>, int> scriptposes;
404404
std::function<void (const UniValue&, int)> parse_tree = [&](const UniValue& node, int depth) {
405405
if (node.isNull()) return;
406406
if (node.isObject()) {
407-
auto script_bytes = ParseHex(node["script"].get_str());
408-
CScript script(script_bytes.begin(), script_bytes.end());
407+
auto script = ParseHex(node["script"].get_str());
409408
int idx = node["id"].getInt<int>();
410409
int leaf_version = node["leafVersion"].getInt<int>();
411410
scriptposes[{script, leaf_version}] = idx;

0 commit comments

Comments
 (0)