Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor: Do not use CScript for tapleaf scripts until the tapleaf version is known #25877

Merged
merged 2 commits into from
Jan 19, 2023

Conversation

roconnor-blockstream
Copy link
Contributor

While BIP-341 calls the contents of tapleaf a "script", only in the case that the tapleaf version is 0xc0 is this script known to be a tapscript. Otherwise the tapleaf "script" is simply an uninterpreted string of bytes.

This PR corrects the issue where the type CScript is used prior to the tapleaf version being known to be a tapscript. This prevents CScript methods from erroneously being called on non-tapscript data.

A second commit abstracts out the TapBranch hash computation in the same manner that the TapLeaf computation is already abstracted. These two abstractions ensure that the TapLeaf and TapBranch tagged hashes are always constructed properly.

@maflcko
Copy link
Member

maflcko commented Aug 19, 2022

@roconnor-blockstream
Copy link
Contributor Author

  1. I don't have any benchmarks, but I would expect the increased use of Spans ought to increase performance if anything.
  2. I'm happy to move to std::byte if the process is to use std::byte in PRs going forward. Just let me know.

@DrahtBot
Copy link
Contributor

DrahtBot commented Aug 20, 2022

The following sections might be updated with supplementary metadata relevant to reviewers and maintainers.

Reviews

See the guideline for information on the review process.

Type Reviewers
ACK sipa, aureleoules, ajtowns, instagibbs, achow101

If your review is incorrectly listed, please react with 👎 to this comment and the bot will ignore it on the next update.

Conflicts

No conflicts as of last run.

@maflcko maflcko changed the title Do not use CScript for tapleaf scripts until the tapleaf version is known refactor: Do not use CScript for tapleaf scripts until the tapleaf version is known Aug 20, 2022
@ajtowns
Copy link
Contributor

ajtowns commented Oct 3, 2022

CI is failing:

script/sign.cpp:193:34: error: temporary whose address is used as value of local variable 'match' will be destroyed at the end of the full-expression [-Werror,-Wdangling]
    if (auto match = MatchMultiA(CScript(script.begin(), script.end()))) {

@roconnor-blockstream roconnor-blockstream force-pushed the 202208_tapleaf_script branch 2 times, most recently from f138c3b to 729aa3c Compare October 4, 2022 15:27
@roconnor-blockstream
Copy link
Contributor Author

@ajtowns I've now addressed the issue.

Copy link
Contributor

@ajtowns ajtowns left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Concept ACK.

Don't see much need to switch to std::byte here. Would be nice to do this together with #13062 though.

@@ -206,7 +206,7 @@ struct PSBTInput
// Taproot fields
std::vector<unsigned char> m_tap_key_sig;
std::map<std::pair<XOnlyPubKey, uint256>, std::vector<unsigned char>> m_tap_script_sigs;
std::map<std::pair<CScript, int>, std::set<std::vector<unsigned char>, ShortestVectorFirstComparator>> m_tap_scripts;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Perhaps promote valtype into a header file and use that instead of std::vector<unsigned char> everywhere?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My understanding is that valtype is the type of values that occur on the stacks within Bitcoin Script's execution environment, that its programs manipulate. However, the use of std::vector<unsigned char> in this PR is the type of unparsed binary blob that is either a CScript or some yet unknown other language in some non-tapscript tagged tapleaf. (I admit the line between these two data types can get a bit blurry especially with P2SH).

I've left this as is for now, however, a couple of type annotations have been removed after following @aureleoules's suggestion, which is nice. If we do want to give it a typedef, we will need a new name.

uint256 ComputeTapbranchHash(Span<const unsigned char> a, Span<const unsigned char> b)
{
assert(a.size() == 32);
assert(b.size() == 32);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These assertions don't seem necessary? Wrong sizes won't cause a crash, they just mean you're probably misusing the api, but there's a million ways to misuse the api.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Presumably we want to stop if we are misusing the API somewhere, rather than proceeding with erroneous computations? In particular these assertions were a cheap way of helping ensure that the invariant that ComputeTapbranchHash only ever uses its tagged hash on tapbranch data. Normally I'd use uint256 arguments here (and perhaps I should), but that would involve extra copying in some cases.

Anyhow, I don't feel strongly, so I've removed the assertions.

Copy link
Member

@aureleoules aureleoules left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code review ACK 729aa3c
Verified in BIP-341 that a tapleaf is a script if its version is 0xc0.

src/rpc/rawtransaction.cpp Outdated Show resolved Hide resolved
@roconnor-blockstream roconnor-blockstream force-pushed the 202208_tapleaf_script branch 2 times, most recently from 88d888b to ce3c85c Compare October 12, 2022 20:47
@roconnor-blockstream
Copy link
Contributor Author

Added @aureleoules's compute_tapbranch unit test.

@roconnor-blockstream
Copy link
Contributor Author

Rebased.

@roconnor-blockstream
Copy link
Contributor Author

Rebased above the fix for CI.

Copy link
Member

@aureleoules aureleoules left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

re-crACK 95b0442 - rebased, minor changes and added a unit test
LGTM
CI failure is unrelated (#26330)

@roconnor-blockstream
Copy link
Contributor Author

rebased

@fanquake fanquake requested a review from sipa November 22, 2022 17:07
Copy link
Member

@sipa sipa left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ACK dee8943

@sipa
Copy link
Member

sipa commented Nov 22, 2022

As I've noticed the newly introduced code contains some redundant constructors, this may be useful to add (not a blocker):

diff --git a/src/script/descriptor.cpp b/src/script/descriptor.cpp
index 5815a059ae..6bbe0967d2 100644
--- a/src/script/descriptor.cpp
+++ b/src/script/descriptor.cpp
@@ -1643,7 +1643,7 @@ std::unique_ptr<DescriptorImpl> InferScript(const CScript& script, ParseScriptCo
                 for (const auto& [depth, script, leaf_ver] : *tree) {
                     std::unique_ptr<DescriptorImpl> subdesc;
                     if (leaf_ver == TAPROOT_LEAF_TAPSCRIPT) {
-                        subdesc = InferScript(CScript(script.begin(), script.end()), ParseScriptContext::P2TR, provider);
+                        subdesc = InferScript({script.begin(), script.end()}, ParseScriptContext::P2TR, provider);
                     }
                     if (!subdesc) {
                         ok = false;
diff --git a/src/script/interpreter.cpp b/src/script/interpreter.cpp
index c06d0370c9..d63c3cde88 100644
--- a/src/script/interpreter.cpp
+++ b/src/script/interpreter.cpp
@@ -1933,7 +1933,7 @@ static bool VerifyWitnessProgram(const CScriptWitness& witness, int witversion,
             execdata.m_tapleaf_hash_init = true;
             if ((control[0] & TAPROOT_LEAF_MASK) == TAPROOT_LEAF_TAPSCRIPT) {
                 // Tapscript (leaf version 0xc0)
-                exec_script = CScript(script.begin(), script.end());
+                exec_script = {script.begin(), script.end()};
                 execdata.m_validation_weight_left = ::GetSerializeSize(witness.stack, PROTOCOL_VERSION) + VALIDATION_WEIGHT_OFFSET;
                 execdata.m_validation_weight_left_init = true;
                 return ExecuteWitnessScript(stack, exec_script, flags, SigVersion::TAPSCRIPT, checker, execdata, serror);
diff --git a/src/script/sign.cpp b/src/script/sign.cpp
index 53377560e8..18b6c09236 100644
--- a/src/script/sign.cpp
+++ b/src/script/sign.cpp
@@ -176,7 +176,7 @@ static bool SignTaprootScript(const SigningProvider& provider, const BaseSignatu
     SigVersion sigversion = SigVersion::TAPSCRIPT;
 
     uint256 leaf_hash = ComputeTapleafHash(leaf_version, script_bytes);
-    CScript script = CScript(script_bytes.begin(), script_bytes.end());
+    CScript script = {script_bytes.begin(), script_bytes.end()};
 
     // <xonly pubkey> OP_CHECKSIG
     if (script.size() == 34 && script[33] == OP_CHECKSIG && script[0] == 0x20) {
diff --git a/src/script/standard.cpp b/src/script/standard.cpp
index 2b7c120eaf..ad7ccca7df 100644
--- a/src/script/standard.cpp
+++ b/src/script/standard.cpp
@@ -445,7 +445,7 @@ TaprootBuilder& TaprootBuilder::Add(int depth, Span<const unsigned char> script,
     /* Construct NodeInfo object with leaf hash and (if track is true) also leaf information. */
     NodeInfo node;
     node.hash = ComputeTapleafHash(leaf_version, script);
-    if (track) node.leaves.emplace_back(LeafInfo{std::vector<unsigned char>(script.begin(), script.end()), leaf_version, {}});
+    if (track) node.leaves.emplace_back(LeafInfo{{script.begin(), script.end()}, leaf_version, {}});
     /* Insert into the branch. */
     Insert(std::move(node), depth);
     return *this;

Copy link
Member

@aureleoules aureleoules left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

reACK dee8943 - I verified that there is no behavior change.

@fanquake
Copy link
Member

cc @ajtowns @instagibbs

Copy link
Member

@instagibbs instagibbs left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

looking good

src/test/script_tests.cpp Show resolved Hide resolved
@ajtowns
Copy link
Contributor

ajtowns commented Jan 19, 2023

ACK dee8943

@instagibbs
Copy link
Member

ACK dee8943

1 similar comment
@achow101
Copy link
Member

ACK dee8943

@achow101 achow101 merged commit 58da161 into bitcoin:master Jan 19, 2023
@fanquake fanquake mentioned this pull request Jan 20, 2023
maflcko pushed a commit that referenced this pull request Jan 20, 2023
f34ada8 Add unit test for ComputeTapleafHash (Greg Sanders)

Pull request description:

  Quick follow-up to #25877

ACKs for top commit:
  sipa:
    ACK f34ada8

Tree-SHA512: ebec658c9b33859874a3e5d13ca0a00a2484233f00f2da09c7d3fb47ed7f56fc6d476ddd0473fe1396a514dffd6ea6a200f26c6dbca45bac2473e729ffef04c2
sidhujag pushed a commit to syscoin/syscoin that referenced this pull request Jan 20, 2023
… until the tapleaf version is known

dee8943 Abstract out ComputeTapbranchHash (Russell O'Connor)
8e3fc99 Do not use CScript for tapleaf scripts until the tapleaf version is known (Russell O'Connor)

Pull request description:

  While BIP-341 calls the contents of tapleaf a "script", only in the case that the tapleaf version is `0xc0` is this script known to be a tapscript.  Otherwise the tapleaf "script" is simply an uninterpreted string of bytes.

  This PR corrects the issue where the type `CScript` is used prior to the tapleaf version being known to be a tapscript.  This prevents `CScript` methods from erroneously being called on non-tapscript data.

  A second commit abstracts out the TapBranch hash computation in the same manner that the TapLeaf computation is already abstracted.  These two abstractions ensure that the TapLeaf and TapBranch tagged hashes are always constructed properly.

ACKs for top commit:
  ajtowns:
    ACK dee8943
  instagibbs:
    ACK dee8943
  achow101:
    ACK dee8943
  sipa:
    ACK dee8943
  aureleoules:
    reACK dee8943 - I verified that there is no behavior change.

Tree-SHA512: 4a1d37f3e9a1890e7f5eadcf65562688cc451389581fe6e2da0feb2368708edacdd95392578d8afff05270d88fc61dce732d83d1063d84d12cf47b5f4633ec7e
sidhujag pushed a commit to syscoin/syscoin that referenced this pull request Jan 20, 2023
f34ada8 Add unit test for ComputeTapleafHash (Greg Sanders)

Pull request description:

  Quick follow-up to bitcoin#25877

ACKs for top commit:
  sipa:
    ACK f34ada8

Tree-SHA512: ebec658c9b33859874a3e5d13ca0a00a2484233f00f2da09c7d3fb47ed7f56fc6d476ddd0473fe1396a514dffd6ea6a200f26c6dbca45bac2473e729ffef04c2
@bitcoin bitcoin locked and limited conversation to collaborators Jan 22, 2024
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
Status: Done
Development

Successfully merging this pull request may close these issues.

None yet

9 participants