test: add missing segwitv1 test cases to script_standard_tests #31340

pull theStack wants to merge 2 commits into bitcoin:master from theStack:202411-test-add_missing_segwitv1_test_cases changing 3 files +53 −9
  1. theStack commented at 3:20 pm on November 21, 2024: contributor

    Currently we have two segwitv1 output script types that are considered standard:

    • TxoutType::WITNESS_V1_TAPROOT (P2TR): witness program has size 32 (introduced with taproot soft-fork)
    • TxoutType::ANCHOR (P2A): witness program is {0x4e, 0x7e} (introduced with #30352)

    This PR adds them to the script standardness unit tests where missing, i.e. for using them with the ExtractDestination and GetScriptForDestination functions.

  2. DrahtBot commented at 3:20 pm on November 21, 2024: contributor

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

    Code Coverage & Benchmarks

    For details see: https://corecheck.dev/bitcoin/bitcoin/pulls/31340.

    Reviews

    See the guideline for information on the review process.

    Type Reviewers
    ACK rkrux, instagibbs, hodlinator
    Stale ACK tdb3

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

  3. DrahtBot added the label Tests on Nov 21, 2024
  4. in src/test/script_standard_tests.cpp:210 in 51dc01d288 outdated
    200@@ -201,6 +201,9 @@ BOOST_AUTO_TEST_CASE(script_standard_Solver_failure)
    201     s.clear();
    202     s << OP_0 << std::vector<unsigned char>(19, 0x01);
    203     BOOST_CHECK_EQUAL(Solver(s, solutions), TxoutType::NONSTANDARD);
    204+    s.clear();
    205+    s << OP_1 << std::vector<unsigned char>(33, 0x01);
    


    instagibbs commented at 3:59 pm on November 21, 2024:
    nit: this would be “undefined” rather than “incorrect” like the segwitv0 case

    theStack commented at 5:16 pm on November 21, 2024:

    Reworked the comments to match the pattern used in previous cases, which is // TxoutType::INTENDED_TYPE with <some minor change that leads to different solver outcome> (the comment “WITNESS_UNKNOWN with incorrect program size” didn’t make any sense imho)

    I kept the “incorrect” term, but wrote the outcome explanation in parantheses. Open for suggestions.

  5. instagibbs commented at 4:35 pm on November 21, 2024: member

    how about

     0diff --git a/src/addresstype.h b/src/addresstype.h
     1index 93cdf66c5b..09f3063c61 100644
     2--- a/src/addresstype.h
     3+++ b/src/addresstype.h
     4@@ -115,14 +115,17 @@ public:
     5         if (w1.GetWitnessVersion() > w2.GetWitnessVersion()) return false;
     6         return w1.GetWitnessProgram() < w2.GetWitnessProgram();
     7     }
     8 };
     9 
    10+/** Witness program for Pay-to-Anchor output script type */
    11+static const std::vector<unsigned char> anchor_bytes{0x4e, 0x73};
    12+
    13 struct PayToAnchor : public WitnessUnknown
    14 {
    15-    PayToAnchor() : WitnessUnknown(1, {0x4e, 0x73}) {
    16-        Assume(CScript::IsPayToAnchor(1, {0x4e, 0x73}));
    17+    PayToAnchor() : WitnessUnknown(1, anchor_bytes) {
    18+        Assume(CScript::IsPayToAnchor(1, anchor_bytes));
    19     };
    20 };
    21 
    22 /**
    23  * A txout script categorized into standard templates.
    24diff --git a/src/test/script_standard_tests.cpp b/src/test/script_standard_tests.cpp
    25index 74920b0fbd..67f91f43fd 100644
    26--- a/src/test/script_standard_tests.cpp
    27+++ b/src/test/script_standard_tests.cpp
    28@@ -2,16 +2,16 @@
    29 // Distributed under the MIT software license, see the accompanying
    30 // file COPYING or http://www.opensource.org/licenses/mit-license.php.
    31 
    32 #include <test/data/bip341_wallet_vectors.json.h>
    33 
    34+#include <addresstype.h>
    35 #include <key.h>
    36 #include <key_io.h>
    37 #include <script/script.h>
    38 #include <script/signingprovider.h>
    39 #include <script/solver.h>
    40-#include <test/util/script.h>
    41 #include <test/util/setup_common.h>
    42 #include <util/strencodings.h>
    43 
    44 #include <boost/test/unit_test.hpp>
    45 
    46diff --git a/src/test/util/script.h b/src/test/util/script.h
    47index c046bcab66..96c4d55e5a 100644
    48--- a/src/test/util/script.h
    49+++ b/src/test/util/script.h
    50@@ -28,12 +28,9 @@ static const CScript P2WSH_EMPTY{
    51            return hash;
    52        }())};
    53 static const std::vector<std::vector<uint8_t>> P2WSH_EMPTY_TRUE_STACK{{static_cast<uint8_t>(OP_TRUE)}, {}};
    54 static const std::vector<std::vector<uint8_t>> P2WSH_EMPTY_TWO_STACK{{static_cast<uint8_t>(OP_2)}, {}};
    55 
    56-/** Witness program for Pay-to-Anchor output script type */
    57-static const std::vector<unsigned char> anchor_bytes{0x4e, 0x73};
    58-
    59 /** Flags that are not forbidden by an assert in script validation */
    60 bool IsValidFlagCombination(unsigned flags);
    61 
    62 #endif // BITCOIN_TEST_UTIL_SCRIPT_H
    

    which should get rid of all instances of 0x4e, 0x73?

    Could also move it into script/script.h and make it a static member of CScript?

  6. theStack force-pushed on Nov 21, 2024
  7. theStack commented at 5:17 pm on November 21, 2024: contributor

    how about

    0...
    

    which should get rid of all instances of 0x4e, 0x73? @instagibbs: Thanks, taken!

    Could also move it into script/script.h and make it a static member of CScript?

    Kept as-is for minimal diff, but happy to also do that change if others feel strongly about it.

  8. in src/addresstype.h:121 in a3a4d199e2 outdated
    116@@ -117,10 +117,13 @@ struct WitnessUnknown
    117     }
    118 };
    119 
    120+/** Witness program for Pay-to-Anchor output script type */
    121+static const std::vector<unsigned char> anchor_bytes{0x4e, 0x73};
    


    tdb3 commented at 1:21 pm on November 25, 2024:

    hodlinator commented at 2:24 pm on November 26, 2024:

    Would prefer:

    • Capitalizing constant as suggested above.
    • Clearly associating the scope of the new constant with the witness type by putting it inside struct PayToAnchor.
    • Using constexpr & array to minimize dynamic memory use.
    • (Might as well send member data into Assume()).
     0struct PayToAnchor : public WitnessUnknown
     1{
     2    /** Witness program for output script */
     3    static constexpr std::array<unsigned char, 2> PROGRAM{0x4e, 0x73};
     4
     5    static constexpr unsigned int VERSION = 1;
     6
     7    PayToAnchor() : WitnessUnknown{VERSION, PROGRAM} {
     8        Assume(CScript::IsPayToAnchor(GetWitnessVersion(), GetWitnessProgram()));
     9    };
    10};
    
  9. in src/test/script_standard_tests.cpp:205 in a3a4d199e2 outdated
    196@@ -197,14 +197,18 @@ BOOST_AUTO_TEST_CASE(script_standard_Solver_failure)
    197     s << OP_RETURN << std::vector<unsigned char>({75}) << OP_ADD;
    198     BOOST_CHECK_EQUAL(Solver(s, solutions), TxoutType::NONSTANDARD);
    199 
    200-    // TxoutType::WITNESS_UNKNOWN with incorrect program size
    201+    // TxoutType::WITNESS_V0_{KEY,SCRIPT}HASH with incorrect program size (-> consensus-invalid, i.e. non-standard)
    202     s.clear();
    203     s << OP_0 << std::vector<unsigned char>(19, 0x01);
    204     BOOST_CHECK_EQUAL(Solver(s, solutions), TxoutType::NONSTANDARD);
    205+    // TxoutType::WITNESS_V1_TAPROOT with incorrect program size (-> undefined, but still policy-valid)
    


    tdb3 commented at 5:18 pm on November 25, 2024:

    Probably overkill, but could add a test for undersized v1 SPK s << OP_1 << std::vector<unsigned char>(31, 0x01); as well.

    pico nit: If updating the file again, could add a blank line before this comment to be consistent with other checks in the test.


    theStack commented at 9:11 pm on March 23, 2025:
    Done.
  10. tdb3 approved
  11. tdb3 commented at 5:27 pm on November 25, 2024: contributor

    ACK a3a4d199e298a76725d0c0424195ae54c7e95fe0

    Nice catch/updates. Left a couple of non-blocking minor comments.

    Induced failures locally by:

    • re-using the P2WSH spk of the previous check and seeing bad_variant_access occur with ExtractDestination()
      0- s << OP_1 << ToByteVector(xpk);
      1+ s << OP_0 << ToByteVector(scripthash);
      
    • purposefully using a different pubkey for the spk and seeing the comparison fail
      0+    CKey wrong_key = GenerateRandomKey();
      1+    CPubKey wrong_pubkey = wrong_key.GetPubKey();
      2+    auto wrong_xpk = XOnlyPubKey(wrong_pubkey);
      3     auto xpk = XOnlyPubKey(pubkey);
      4     s << OP_1 << ToByteVector(xpk);
      5     BOOST_CHECK(ExtractDestination(s, address));
      6-    BOOST_CHECK(std::get<WitnessV1Taproot>(address) == WitnessV1Taproot(xpk));
      7+    BOOST_CHECK(std::get<WitnessV1Taproot>(address) == WitnessV1Taproot(wrong_xpk));
      
  12. hodlinator commented at 2:51 pm on November 26, 2024: contributor

    Code review a3a4d199e298a76725d0c0424195ae54c7e95fe0

    Great to test more aspects of the protocol!

    Why is this being removed/changed? https://github.com/bitcoin/bitcoin/blob/22ef95dbe3e467039e6cd18988e66557d94041d1/src/test/script_standard_tests.cpp#L271-L276

    As I understand it we were creating a taproot output (v1) using a compressed pubkey with the type-prefix prepended, meaning it was 33 bytes instead of the standard 32. Maybe you could add it back with a clearer comment?


    Put some C++-style suggestions in https://github.com/bitcoin/bitcoin/compare/master...hodlinator:bitcoin:pr/31340_array_span

    • Changes witness program as suggested by inline comment.
    • Adds refactoring commits:
      • e43e1539a09b1bae4facc2fab56e8d6e750219d2 - prefer span input parameters over vector in the same vein as the change to CScript::operator<< in cac846c2fbf6fc69bfc288fd387aa3f68d84d584.
      • 78a0c8aa49dc3e82761796ff9e7258e3537fff93 - stop conflating PayToAnchor with WitnessUnknown (long-shot for inclusion in this PR)
  13. rkrux commented at 2:39 pm on March 13, 2025: contributor

    Concept ACK a3a4d199e298a76725d0c0424195ae54c7e95fe0

    I’m in favour of adding these tests. I’ve not reviewed the PR in detail but only glanced as of now. I’d be quick to complete the review once few of the outstanding feedback is addressed. Specially this one: #31340 (review), this #31340 (review) has a few interesting ideas as well.

    Re: #31340#pullrequestreview-2461686975

    Why is this being removed/changed? @hodlinator: IIUC, this change is done to conform with the “unknown version” portion of the corresponding comment here: https://github.com/bitcoin/bitcoin/blob/199d47d9629b603d5a23b96c5260c68add00ccb3/src/test/script_standard_tests.cpp#L271

    Only 0 and 1 are “known” witness versions as of now, hence, the usage of 2 in the test instead of 1. Though I’d prefer to add the new test instead of replacing the older one because the older test does cover the second branch of the condition on line 165 here: https://github.com/bitcoin/bitcoin/blob/199d47d9629b603d5a23b96c5260c68add00ccb3/src/script/solver.cpp#L156-L177

    This^ is non blocking because there might be some other test covering that branch as well that Idk about.

  14. ryanofsky commented at 4:00 pm on March 23, 2025: contributor
    @theStack this has two ACKs but also some unaddressed comments. It otherwise seems ok to merge since it is just a test change and small refactoring. Do you want to update the pr or respond to the comments?
  15. test: add missing segwitv1 test cases to `script_standard_tests` 41f2f058d0
  16. refactor: deduplicate anchor witness program bytes (`0x4e,0x73`)
    Co-authored-by: Gregory Sanders <gsanders87@gmail.com>
    8284229a28
  17. theStack force-pushed on Mar 23, 2025
  18. theStack commented at 9:10 pm on March 23, 2025: contributor

    @tdb3 @hodlinator @rkrux: Thanks for your detailed reviews, and sorry for the late reply.

    Why is this being removed/changed?

    https://github.com/bitcoin/bitcoin/blob/22ef95dbe3e467039e6cd18988e66557d94041d1/src/test/script_standard_tests.cpp#L271-L276

    As I understand it we were creating a taproot output (v1) using a compressed pubkey with the type-prefix prepended, meaning it was 33 bytes instead of the standard 32. Maybe you could add it back with a clearer comment?

    @hodlinator: IIUC, this change is done to conform with the “unknown version” portion of the corresponding comment here:

    https://github.com/bitcoin/bitcoin/blob/199d47d9629b603d5a23b96c5260c68add00ccb3/src/test/script_standard_tests.cpp#L271

    Only 0 and 1 are “known” witness versions as of now, hence, the usage of 2 in the test instead of 1. Though I’d prefer to add the new test instead of replacing the older one because the older test does cover the second branch of the condition on line 165 here:

    Good point, restored the segwitv1 test and added the segwitv2 one (in order to avoid reducing test coverage), and added corresponding comments. From the other suggestions, I only addressed #31340 (review) and capitalizing the constant. The scoping of the constant could be done in two places (CScript #31340#pullrequestreview-2451932627 vs. PayToAnchor #31340 (review)), and it’s not clear to me which one makes more sense. Maybe something to discuss in a follow-up, that could also include the other C++ style suggestions?

  19. in src/addresstype.h:126 in 8284229a28
    123 struct PayToAnchor : public WitnessUnknown
    124 {
    125-    PayToAnchor() : WitnessUnknown(1, {0x4e, 0x73}) {
    126-        Assume(CScript::IsPayToAnchor(1, {0x4e, 0x73}));
    127+    PayToAnchor() : WitnessUnknown(1, ANCHOR_BYTES) {
    128+        Assume(CScript::IsPayToAnchor(1, ANCHOR_BYTES));
    


    rkrux commented at 9:32 am on March 24, 2025:
    What’s exactly the point of having Assume here? We pass hardcoded values right in the call here and immediately test the correctness of those hardcoded values?

    instagibbs commented at 12:59 pm on March 24, 2025:
    exactly, in case somehow the IsPayToAnchor definition diverged. It’s a quick eye-ball to ensure correctness.

    rkrux commented at 1:31 pm on March 24, 2025:
    Hmm interesting, Assume is growing on me.

    hodlinator commented at 4:15 pm on March 24, 2025:
    Would have been nice if IsPayToAnchor was constexpr and provably side-effect-free so that compilers could optimize the call away (#32100).
  20. rkrux commented at 9:41 am on March 24, 2025: contributor

    Re: #31340 (comment)

    I’m ok with changing the scoping of ANCHOR_BYTES in a follow-up. IMHO it can be added in CScript that can be further be used by struct PayToAnchor as this file addresstype.h already uses CScript, otherwise in order to deduplicate 4e73, ANCHOR_BYTES from struct PayToAnchor would need to be used in CScript::IsPayToAnchor that would create a circular dependency.

    ACK 8284229a28c09c585356dcf7e4bddbc8f2a23755

  21. DrahtBot requested review from instagibbs on Mar 24, 2025
  22. DrahtBot requested review from tdb3 on Mar 24, 2025
  23. instagibbs commented at 1:04 pm on March 24, 2025: member
    reACK 8284229a28c09c585356dcf7e4bddbc8f2a23755
  24. in src/test/script_standard_tests.cpp:200 in 8284229a28
    196@@ -197,14 +197,22 @@ BOOST_AUTO_TEST_CASE(script_standard_Solver_failure)
    197     s << OP_RETURN << std::vector<unsigned char>({75}) << OP_ADD;
    198     BOOST_CHECK_EQUAL(Solver(s, solutions), TxoutType::NONSTANDARD);
    199 
    200-    // TxoutType::WITNESS_UNKNOWN with incorrect program size
    201+    // TxoutType::WITNESS_V0_{KEY,SCRIPT}HASH with incorrect program size (-> consensus-invalid, i.e. non-standard)
    


    hodlinator commented at 4:03 pm on March 24, 2025:

    Should this be:

    0    // TxoutType::WITNESS_V0_{KEY,SCRIPT}HASH with incorrect program size (-> policy-invalid, i.e. non-standard)
    

    ?

    Seems like the terminology could be out of sync with the comment below (mentioning “policy” instead of “consensus”). Or is it that segwit v0 program sizes are stricter and v1 program sizes are more flexible, consensus-wise?


    theStack commented at 9:07 pm on March 24, 2025:
    For segwitv0, program sizes other than 20 or 32 are indeed consensus-invalid (see function VerifyWitnessProgram), so the comment is correct. And yes, for segwitv1+, not-yet-defined programs are only non-standard, but consensus-valid: https://github.com/bitcoin/bitcoin/blob/a0d737cd7a7fafe2c743360c4ac7b2e72664e5c5/src/script/interpreter.cpp#L1952-L1953

    hodlinator commented at 9:29 pm on March 24, 2025:
    Thread #31340 (review): Thanks for the references! I’m still unfamiliar with that part. So for MANDATORY_SCRIPT_VERIFY_FLAGS we reach the return true there for Taproot. :+1:
  25. ryanofsky assigned ryanofsky on Mar 24, 2025
  26. in src/test/script_standard_tests.cpp:293 in 8284229a28
    288+    s << OP_1 << ANCHOR_BYTES;
    289+    BOOST_CHECK(ExtractDestination(s, address));
    290+    BOOST_CHECK(std::get<PayToAnchor>(address) == PayToAnchor());
    291+
    292     // TxoutType::WITNESS_UNKNOWN with unknown version
    293+    // -> segwit version 1 with an undefined program size (33 bytes in this test case)
    


    hodlinator commented at 4:22 pm on March 24, 2025:

    nit: This might be more precise & helpful? It took me a few minutes to re-grok the difference between the bytes derived from XOnlyPubKey vs compressed CPubKey.

    0    // -> segwit version 1 with an non-standard program size
    1    // (CPubKey::COMPRESSED_SIZE = 33 bytes in this test case)
    

    theStack commented at 9:08 pm on March 24, 2025:
    Will apply if I have to retouch.
  27. hodlinator commented at 4:27 pm on March 24, 2025: contributor

    Code review 8284229a28c09c585356dcf7e4bddbc8f2a23755

    Thanks for restoring the former test!

    Only semi-blocker for me is the consensus/policy question (see inline comment).

  28. ryanofsky unassigned ryanofsky on Mar 24, 2025
  29. hodlinator approved
  30. hodlinator commented at 9:40 pm on March 24, 2025: contributor

    Code Review ACK 8284229a28c09c585356dcf7e4bddbc8f2a23755

    Adds basic tests for WitnessV1Taproot & (WitnessV1)PayToAnchor outputs.

    Only non-test change is replacing array literal with std::vector (which sort of makes sense considering struct PayToAnchor (ab)uses WitnessUnknown as a base (predating this PR) which takes vector as a constructor input).

  31. ryanofsky assigned ryanofsky on Apr 1, 2025
  32. ryanofsky merged this on Apr 1, 2025
  33. ryanofsky closed this on Apr 1, 2025

  34. theStack deleted the branch on Apr 1, 2025

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: 2025-05-19 15:12 UTC

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