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

pull roconnor-blockstream wants to merge 2 commits into bitcoin:master from roconnor-blockstream:202208_tapleaf_script changing 9 files +53 −43
  1. roconnor-blockstream commented at 4:42 PM on August 19, 2022: 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.

  2. maflcko commented at 4:56 PM on August 19, 2022: member
  3. in src/script/standard.cpp:606 in 293132ac96 outdated
     601 | @@ -607,7 +602,8 @@ std::optional<std::vector<std::tuple<int, CScript, int>>> InferTaprootTree(const
     602 |              node.done = true;
     603 |              stack.pop_back();
     604 |          } else if (node.sub[0]->done && !node.sub[1]->done && !node.sub[1]->explored && !node.sub[1]->hash.IsNull() &&
     605 | -                   (HashWriter{HASHER_TAPBRANCH} << node.sub[1]->hash << node.sub[1]->hash).GetSHA256() == node.hash) {
     606 | +                   ComputeTapbranchHash(node.sub[1]->hash, node.sub[1]->hash) == node.hash) {
     607 | +
    


    roconnor-blockstream commented at 5:02 PM on August 19, 2022:

    Nit: Extra empty line here.


    roconnor-blockstream commented at 3:59 PM on October 4, 2022:

    Fixed.

  4. roconnor-blockstream commented at 5:16 PM on August 19, 2022: contributor
    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.
  5. DrahtBot commented at 6:30 AM on August 20, 2022: contributor

    <!--e57a25ab6845829454e8d69fc972939a-->

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

    <!--021abf342d371248e50ceaed478a90ca-->

    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.

    <!--174a7506f384e20aa4161008e828411d-->

    Conflicts

    No conflicts as of last run.

  6. maflcko renamed this:
    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
    on Aug 20, 2022
  7. DrahtBot added the label Refactoring on Aug 20, 2022
  8. ajtowns commented at 7:21 PM on October 3, 2022: contributor

    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()))) {
    
  9. roconnor-blockstream force-pushed on Oct 4, 2022
  10. roconnor-blockstream force-pushed on Oct 4, 2022
  11. roconnor-blockstream commented at 4:46 PM on October 4, 2022: contributor

    @ajtowns I've now addressed the issue.

  12. in src/psbt.h:209 in a147a04c1d outdated
     205 | @@ -206,7 +206,7 @@ struct PSBTInput
     206 |      // Taproot fields
     207 |      std::vector<unsigned char> m_tap_key_sig;
     208 |      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;
    


    ajtowns commented at 1:02 AM on October 5, 2022:

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


    roconnor-blockstream commented at 6:11 PM on October 11, 2022:

    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.

  13. in src/script/interpreter.cpp:1836 in 729aa3ce08 outdated
    1829 | @@ -1830,6 +1830,19 @@ uint256 ComputeTapleafHash(uint8_t leaf_version, Span<const unsigned char> scrip
    1830 |      return (HashWriter{HASHER_TAPLEAF} << leaf_version << CompactSizeWriter(script.size()) << script).GetSHA256();
    1831 |  }
    1832 |  
    1833 | +uint256 ComputeTapbranchHash(Span<const unsigned char> a, Span<const unsigned char> b)
    1834 | +{
    1835 | +    assert(a.size() == 32);
    1836 | +    assert(b.size() == 32);
    


    ajtowns commented at 1:07 AM on October 5, 2022:

    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.


    roconnor-blockstream commented at 6:01 PM on October 11, 2022:

    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.

  14. ajtowns commented at 2:39 AM on October 5, 2022: contributor

    Concept ACK.

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

  15. in src/rpc/rawtransaction.cpp:1250 in a147a04c1d outdated
    1246 | @@ -1247,7 +1247,7 @@ static RPCHelpMan decodepsbt()
    1247 |              for (const auto& tuple : tuples) {
    1248 |                  uint8_t depth = std::get<0>(tuple);
    1249 |                  uint8_t leaf_ver = std::get<1>(tuple);
    1250 | -                CScript script = std::get<2>(tuple);
    1251 | +                std::vector<unsigned char> script = std::get<2>(tuple);
    


    aureleoules commented at 9:27 AM on October 5, 2022:

    if you retouch: this seems simplier, same above

                for (const auto& [depth, leaf_ver, script] : tuples) {
    

    roconnor-blockstream commented at 5:54 PM on October 11, 2022:

    Done.

  16. aureleoules commented at 9:29 AM on October 5, 2022: member

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

  17. roconnor-blockstream force-pushed on Oct 11, 2022
  18. roconnor-blockstream force-pushed on Oct 12, 2022
  19. roconnor-blockstream commented at 8:47 PM on October 12, 2022: contributor

    Added @aureleoules's compute_tapbranch unit test.

  20. DrahtBot added the label Needs rebase on Oct 13, 2022
  21. roconnor-blockstream force-pushed on Oct 13, 2022
  22. roconnor-blockstream commented at 4:03 PM on October 13, 2022: contributor

    Rebased.

  23. DrahtBot removed the label Needs rebase on Oct 13, 2022
  24. roconnor-blockstream force-pushed on Oct 17, 2022
  25. roconnor-blockstream commented at 4:25 PM on October 17, 2022: contributor

    Rebased above the fix for CI.

  26. aureleoules approved
  27. aureleoules commented at 8:37 AM on October 18, 2022: member

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

  28. 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.
    8e3fc99427
  29. Abstract out ComputeTapbranchHash dee89438b8
  30. roconnor-blockstream force-pushed on Nov 21, 2022
  31. roconnor-blockstream commented at 7:00 PM on November 21, 2022: contributor

    rebased

  32. fanquake requested review from ajtowns on Nov 22, 2022
  33. fanquake requested review from instagibbs on Nov 22, 2022
  34. fanquake requested review from sipa on Nov 22, 2022
  35. sipa approved
  36. sipa commented at 9:30 PM on November 22, 2022: member

    ACK dee89438b82e94474ebaa31367035f98b4636dac

  37. sipa commented at 9:31 PM on November 22, 2022: member

    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;
    
  38. aureleoules approved
  39. aureleoules commented at 3:04 PM on January 18, 2023: member

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

  40. fanquake commented at 3:50 PM on January 18, 2023: member
  41. in src/test/script_tests.cpp:1827 in dee89438b8
    1822 | +BOOST_AUTO_TEST_CASE(compute_tapbranch)
    1823 | +{
    1824 | +    uint256 hash1 = uint256S("8ad69ec7cf41c2a4001fd1f738bf1e505ce2277acdcaa63fe4765192497f47a7");
    1825 | +    uint256 hash2 = uint256S("f224a923cd0021ab202ab139cc56802ddb92dcfc172b9212261a539df79a112a");
    1826 | +    uint256 result = uint256S("a64c5b7b943315f9b805d7a7296bedfcfd08919270a1f7a1466e98f8693d8cd9");
    1827 | +    BOOST_CHECK_EQUAL(ComputeTapbranchHash(hash1, hash2), result);
    


    instagibbs commented at 4:03 PM on January 18, 2023:

    I'd also appreciate a test case for ComputeTapleafHash since this PR touches the function.


    ajtowns commented at 7:45 PM on January 19, 2023:

    Add:

        BOOST_CHECK_EQUAL(ComputeTapbranchHash(hash2, hash1), result);
    
        const uint8_t script[6] = {'f','o','o','b','a','r'};
        uint256 tlc0 = uint256S("edbc10c272a1215dcdcc11d605b9027b5ad6ed97cd45521203f136767b5b9c06");
        uint256 tlc2 = uint256S("8b5c4f90ae6bf76e259dbef5d8a59df06359c391b59263741b25eca76451b27a");
    
        BOOST_CHECK_EQUAL(ComputeTapleafHash(0xc0, Span(script)), tlc0);
        BOOST_CHECK_EQUAL(ComputeTapleafHash(0xc2, Span(script)), tlc2);
    

    ?

    Could be done in a later PR though.


    instagibbs commented at 8:05 PM on January 19, 2023:

    was a bit more interested that it didn't somehow change the definition(would be caught in other tests surely but still!)

    BOOST_AUTO_TEST_CASE(compute_tapleaf)
    {
        const std::vector<uint8_t> script = {'f','o','o','b','a','r'};
        uint256 tlc0 = uint256S("edbc10c272a1215dcdcc11d605b9027b5ad6ed97cd45521203f136767b5b9c06");
        uint256 tlc2 = uint256S("8b5c4f90ae6bf76e259dbef5d8a59df06359c391b59263741b25eca76451b27a");
    
        BOOST_CHECK_EQUAL(ComputeTapleafHash(0xc0, CScript(script.begin(), script.end())), tlc0);
        BOOST_CHECK_EQUAL(ComputeTapleafHash(0xc2, CScript(script.begin(), script.end())), tlc2);
    }
    

    passes on master. Your suggestion can be PR'd later.


    fanquake commented at 4:04 PM on January 22, 2023:

    Done in #26934.

  42. instagibbs commented at 4:04 PM on January 18, 2023: member

    looking good

  43. ajtowns commented at 7:47 PM on January 19, 2023: contributor

    ACK dee89438b82e94474ebaa31367035f98b4636dac

  44. ajtowns approved
  45. instagibbs commented at 8:05 PM on January 19, 2023: member

    ACK dee89438b82e94474ebaa31367035f98b4636dac

  46. achow101 commented at 10:42 PM on January 19, 2023: member

    ACK dee89438b82e94474ebaa31367035f98b4636dac

  47. achow101 merged this on Jan 19, 2023
  48. achow101 closed this on Jan 19, 2023

  49. maflcko referenced this in commit c0b6c40bb0 on Jan 20, 2023
  50. sidhujag referenced this in commit 8ac9c19bc2 on Jan 20, 2023
  51. sidhujag referenced this in commit 1ef6a9b02d on Jan 20, 2023
  52. bitcoin locked this on Jan 22, 2024

github-metadata-mirror

This is a metadata mirror of the GitHub repository bitcoin/bitcoin. This site is not affiliated with GitHub. Content is generated from a GitHub metadata backup.
generated: 2026-04-19 15:13 UTC

This site is hosted by @0xB10C
More mirrored repositories can be found on mirror.b10c.me