Merge bitcoin/bitcoin#31340: test: add missing segwitv1 test cases to script_standard_tests

8284229a28 refactor: deduplicate anchor witness program bytes (`0x4e,0x73`) (Sebastian Falbesoner)
41f2f058d0 test: add missing segwitv1 test cases to `script_standard_tests` (Sebastian Falbesoner)

Pull request description:

  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.

ACKs for top commit:
  rkrux:
    ACK  8284229a28
  instagibbs:
    reACK 8284229a28
  hodlinator:
    Code Review ACK 8284229a28

Tree-SHA512: d4a3b47fd31ba33f62d4367811e72a7f442c01b046b0a7217a66be0b9dea5c9041eebfe812c31839ec0f0b14c56948c7c016d3d2de79283583ad8e32c192c6ff
This commit is contained in:
Ryan Ofsky 2025-04-01 12:01:20 -04:00
commit ea36d2720a
No known key found for this signature in database
GPG key ID: 46800E30FC748A66
3 changed files with 53 additions and 9 deletions

View file

@ -117,10 +117,13 @@ public:
}
};
/** Witness program for Pay-to-Anchor output script type */
static const std::vector<unsigned char> ANCHOR_BYTES{0x4e, 0x73};
struct PayToAnchor : public WitnessUnknown
{
PayToAnchor() : WitnessUnknown(1, {0x4e, 0x73}) {
Assume(CScript::IsPayToAnchor(1, {0x4e, 0x73}));
PayToAnchor() : WitnessUnknown(1, ANCHOR_BYTES) {
Assume(CScript::IsPayToAnchor(1, ANCHOR_BYTES));
};
};

View file

@ -4,6 +4,7 @@
#include <test/data/bip341_wallet_vectors.json.h>
#include <addresstype.h>
#include <key.h>
#include <key_io.h>
#include <script/script.h>
@ -130,9 +131,8 @@ BOOST_AUTO_TEST_CASE(script_standard_Solver_success)
BOOST_CHECK(solutions[1] == ToByteVector(uint256::ONE));
// TxoutType::ANCHOR
std::vector<unsigned char> anchor_bytes{0x4e, 0x73};
s.clear();
s << OP_1 << anchor_bytes;
s << OP_1 << ANCHOR_BYTES;
BOOST_CHECK_EQUAL(Solver(s, solutions), TxoutType::ANCHOR);
BOOST_CHECK(solutions.empty());
@ -197,14 +197,22 @@ BOOST_AUTO_TEST_CASE(script_standard_Solver_failure)
s << OP_RETURN << std::vector<unsigned char>({75}) << OP_ADD;
BOOST_CHECK_EQUAL(Solver(s, solutions), TxoutType::NONSTANDARD);
// TxoutType::WITNESS_UNKNOWN with incorrect program size
// TxoutType::WITNESS_V0_{KEY,SCRIPT}HASH with incorrect program size (-> consensus-invalid, i.e. non-standard)
s.clear();
s << OP_0 << std::vector<unsigned char>(19, 0x01);
BOOST_CHECK_EQUAL(Solver(s, solutions), TxoutType::NONSTANDARD);
// TxoutType::WITNESS_V1_TAPROOT with incorrect program size (-> undefined, but still policy-valid)
s.clear();
s << OP_1 << std::vector<unsigned char>(31, 0x01);
BOOST_CHECK_EQUAL(Solver(s, solutions), TxoutType::WITNESS_UNKNOWN);
s.clear();
s << OP_1 << std::vector<unsigned char>(33, 0x01);
BOOST_CHECK_EQUAL(Solver(s, solutions), TxoutType::WITNESS_UNKNOWN);
// TxoutType::ANCHOR but wrong witness version
s.clear();
s << OP_2 << std::vector<unsigned char>{0x4e, 0x73};
s << OP_2 << ANCHOR_BYTES;
BOOST_CHECK(!s.IsPayToAnchor());
BOOST_CHECK_EQUAL(Solver(s, solutions), TxoutType::WITNESS_UNKNOWN);
@ -268,12 +276,32 @@ BOOST_AUTO_TEST_CASE(script_standard_ExtractDestination)
BOOST_CHECK(ExtractDestination(s, address));
BOOST_CHECK(std::get<WitnessV0ScriptHash>(address) == scripthash);
// TxoutType::WITNESS_V1_TAPROOT
s.clear();
auto xpk = XOnlyPubKey(pubkey);
s << OP_1 << ToByteVector(xpk);
BOOST_CHECK(ExtractDestination(s, address));
BOOST_CHECK(std::get<WitnessV1Taproot>(address) == WitnessV1Taproot(xpk));
// TxoutType::ANCHOR
s.clear();
s << OP_1 << ANCHOR_BYTES;
BOOST_CHECK(ExtractDestination(s, address));
BOOST_CHECK(std::get<PayToAnchor>(address) == PayToAnchor());
// TxoutType::WITNESS_UNKNOWN with unknown version
// -> segwit version 1 with an undefined program size (33 bytes in this test case)
s.clear();
s << OP_1 << ToByteVector(pubkey);
BOOST_CHECK(ExtractDestination(s, address));
WitnessUnknown unk{1, ToByteVector(pubkey)};
BOOST_CHECK(std::get<WitnessUnknown>(address) == unk);
WitnessUnknown unk_v1{1, ToByteVector(pubkey)};
BOOST_CHECK(std::get<WitnessUnknown>(address) == unk_v1);
s.clear();
// -> segwit versions 2+ are not specified yet
s << OP_2 << ToByteVector(xpk);
BOOST_CHECK(ExtractDestination(s, address));
WitnessUnknown unk_v2{2, ToByteVector(xpk)};
BOOST_CHECK(std::get<WitnessUnknown>(address) == unk_v2);
}
BOOST_AUTO_TEST_CASE(script_standard_GetScriptFor_)
@ -341,6 +369,19 @@ BOOST_AUTO_TEST_CASE(script_standard_GetScriptFor_)
expected << OP_0 << ToByteVector(scriptHash);
result = GetScriptForDestination(WitnessV0ScriptHash(witnessScript));
BOOST_CHECK(result == expected);
// WitnessV1Taproot
auto xpk = XOnlyPubKey(pubkeys[0]);
expected.clear();
expected << OP_1 << ToByteVector(xpk);
result = GetScriptForDestination(WitnessV1Taproot(xpk));
BOOST_CHECK(result == expected);
// PayToAnchor
expected.clear();
expected << OP_1 << ANCHOR_BYTES;
result = GetScriptForDestination(PayToAnchor());
BOOST_CHECK(result == expected);
}
BOOST_AUTO_TEST_CASE(script_standard_taproot_builder)

View file

@ -1040,7 +1040,7 @@ BOOST_AUTO_TEST_CASE(test_IsStandard)
}
// Check anchor outputs
t.vout[0].scriptPubKey = CScript() << OP_1 << std::vector<unsigned char>{0x4e, 0x73};
t.vout[0].scriptPubKey = CScript() << OP_1 << ANCHOR_BYTES;
BOOST_CHECK(t.vout[0].scriptPubKey.IsPayToAnchor());
t.vout[0].nValue = 240;
CheckIsStandard(t);