p2spkh_tests.cpp raw
1 // Copyright (c) 2024-present The Limenka developers
2 // Distributed under the MIT software license, see the accompanying
3 // file COPYING or http://www.opensource.org/licenses/mit-license.php.
4
5 #include <addresstype.h>
6 #include <key.h>
7 #include <key_io.h>
8 #include <script/descriptor.h>
9 #include <script/interpreter.h>
10 #include <script/signingprovider.h>
11 #include <script/solver.h>
12 #include <policy/policy.h>
13 #include <test/util/setup_common.h>
14 #include <test/util/transaction_utils.h>
15 #include <util/strencodings.h>
16
17 #include <boost/test/unit_test.hpp>
18
19 BOOST_FIXTURE_TEST_SUITE(p2spkh_tests, BasicTestingSetup)
20
21 static XOnlyPubKey NewXOnlyPubKey()
22 {
23 CKey key = GenerateRandomKey();
24 return XOnlyPubKey(key.GetPubKey());
25 }
26
27 static std::vector<unsigned char> XOnlyPubKeyToBytes(const XOnlyPubKey& xpk)
28 {
29 return {xpk.data(), xpk.data() + XOnlyPubKey::size()};
30 }
31
32 BOOST_AUTO_TEST_CASE(solver_detects_p2spkh)
33 {
34 XOnlyPubKey xpk = NewXOnlyPubKey();
35 WitnessV3SpkHash hash(xpk);
36 CScript script = GetScriptForDestination(hash);
37 std::vector<std::vector<unsigned char>> solutions;
38 BOOST_CHECK_EQUAL(Solver(script, solutions), TxoutType::WITNESS_V3_SPKHASH);
39 BOOST_CHECK_EQUAL(solutions.size(), 1U);
40 BOOST_CHECK_EQUAL(solutions[0].size(), 32U);
41 }
42
43 BOOST_AUTO_TEST_CASE(solver_rejects_v3_not32)
44 {
45 std::vector<unsigned char> prog_31(31, 0x42);
46 CScript script = GetScriptForDestination(WitnessUnknown{3, prog_31});
47 std::vector<std::vector<unsigned char>> solutions;
48 BOOST_CHECK_EQUAL(Solver(script, solutions), TxoutType::WITNESS_UNKNOWN);
49 }
50
51 BOOST_AUTO_TEST_CASE(solver_rejects_v5_32)
52 {
53 // v3 is P2SPKH, v4 is P2BPCT; v5 + 32 bytes is still unknown.
54 std::vector<unsigned char> prog_32(32, 0x42);
55 CScript script = GetScriptForDestination(WitnessUnknown{5, prog_32});
56 std::vector<std::vector<unsigned char>> solutions;
57 BOOST_CHECK_EQUAL(Solver(script, solutions), TxoutType::WITNESS_UNKNOWN);
58 }
59
60 BOOST_AUTO_TEST_CASE(address_encode_decode_roundtrip)
61 {
62 XOnlyPubKey xpk = NewXOnlyPubKey();
63 WitnessV3SpkHash hash(xpk);
64 std::string addr = EncodeDestination(hash);
65 CTxDestination decoded = DecodeDestination(addr);
66 BOOST_CHECK(IsValidDestination(decoded));
67 BOOST_CHECK(std::holds_alternative<WitnessV3SpkHash>(decoded));
68 BOOST_CHECK_EQUAL(uint256(std::get<WitnessV3SpkHash>(decoded)), uint256(hash));
69 }
70
71 BOOST_AUTO_TEST_CASE(hash256_matches_manual)
72 {
73 XOnlyPubKey xpk = NewXOnlyPubKey();
74 WitnessV3SpkHash hash(xpk);
75 CSHA256 hasher;
76 hasher.Write(xpk.data(), 32);
77 unsigned char mid[CSHA256::OUTPUT_SIZE];
78 hasher.Finalize(mid);
79 hasher.Reset();
80 hasher.Write(mid, CSHA256::OUTPUT_SIZE);
81 unsigned char final[CSHA256::OUTPUT_SIZE];
82 hasher.Finalize(final);
83 BOOST_CHECK_EQUAL(uint256(hash), uint256(final));
84 }
85
86 BOOST_AUTO_TEST_CASE(hash256_different_keys_different_hash)
87 {
88 XOnlyPubKey xpk1 = NewXOnlyPubKey();
89 XOnlyPubKey xpk2 = NewXOnlyPubKey();
90 BOOST_CHECK_NE(uint256(WitnessV3SpkHash(xpk1)), uint256(WitnessV3SpkHash(xpk2)));
91 }
92
93 BOOST_AUTO_TEST_CASE(scriptpubkey_size)
94 {
95 CScript script = GetScriptForDestination(WitnessV3SpkHash(NewXOnlyPubKey()));
96 BOOST_CHECK_EQUAL(script.size(), 34U);
97 }
98
99 BOOST_AUTO_TEST_CASE(valid_destination_visitor)
100 {
101 WitnessV3SpkHash hash(NewXOnlyPubKey());
102 CTxDestination dest{hash};
103 BOOST_CHECK(IsValidDestination(dest));
104 }
105
106 BOOST_AUTO_TEST_CASE(output_type_from_destination)
107 {
108 WitnessV3SpkHash hash(NewXOnlyPubKey());
109 std::optional<OutputType> type = OutputTypeFromDestination(hash);
110 BOOST_CHECK(type.has_value());
111 BOOST_CHECK_EQUAL(*type, OutputType::P2SPKH);
112 }
113
114 BOOST_AUTO_TEST_CASE(descriptor_spk_parsing)
115 {
116 FlatSigningProvider provider;
117 std::string error;
118 auto descs = Parse("spk(" + HexStr(NewXOnlyPubKey()) + ")", provider, error, false);
119 BOOST_CHECK_MESSAGE(!descs.empty(), error);
120 BOOST_CHECK(descs[0]->GetOutputType() == OutputType::P2SPKH);
121 }
122
123 BOOST_AUTO_TEST_CASE(descriptor_spk_reject_not_top_level)
124 {
125 FlatSigningProvider provider;
126 std::string error;
127 auto descs = Parse("sh(spk(020202020202020202020202020202020202020202020202020202020202020202))", provider, error, false);
128 BOOST_CHECK(descs.empty());
129 }
130
131 BOOST_AUTO_TEST_CASE(p2spkh_valid_signature)
132 {
133 CKey key = GenerateRandomKey();
134 XOnlyPubKey xpk(key.GetPubKey());
135 WitnessV3SpkHash hash(xpk);
136 CScript scriptPubKey = GetScriptForDestination(hash);
137
138 const auto txCredit = MakeTransactionRef(BuildCreditingTransaction(scriptPubKey, 10000));
139 CMutableTransaction mtxSpend = BuildCreditingTransaction(CScript() << OP_RETURN, 5000);
140 mtxSpend.vin.resize(1);
141 mtxSpend.vin[0].prevout.hash = txCredit->GetHash();
142 mtxSpend.vin[0].prevout.n = 0;
143 const auto txSpend = MakeTransactionRef(mtxSpend);
144
145 PrecomputedTransactionData txdata;
146 txdata.Init(*txSpend, {txCredit->vout[0]}, true);
147 ScriptExecutionData execdata;
148 execdata.m_annex_init = true;
149 execdata.m_annex_present = false;
150 uint256 sighash;
151 BOOST_REQUIRE(SignatureHashSchnorr(sighash, execdata, *txSpend, 0, SIGHASH_DEFAULT,
152 SigVersion::TAPROOT, txdata, MissingDataBehavior::FAIL));
153
154 std::vector<unsigned char> sig(64);
155 BOOST_REQUIRE(key.SignSchnorr(sighash, sig, nullptr, uint256::ZERO));
156
157 CMutableTransaction mtxSpendSigned = mtxSpend;
158 mtxSpendSigned.vin[0].scriptSig.clear();
159 mtxSpendSigned.vin[0].scriptWitness.stack.push_back(sig);
160 mtxSpendSigned.vin[0].scriptWitness.stack.push_back(XOnlyPubKeyToBytes(xpk));
161 const auto txSpendSigned = MakeTransactionRef(mtxSpendSigned);
162
163 ScriptError serror;
164 bool result = VerifyScript(txSpendSigned->vin[0].scriptSig, scriptPubKey,
165 &txSpendSigned->vin[0].scriptWitness, STANDARD_SCRIPT_VERIFY_FLAGS,
166 TransactionSignatureChecker(txSpendSigned.get(), 0, txCredit->vout[0].nValue, txdata, MissingDataBehavior::FAIL),
167 &serror);
168 BOOST_CHECK_MESSAGE(result, ScriptErrorString(serror));
169 }
170
171 BOOST_AUTO_TEST_CASE(p2spkh_accept_no_flag)
172 {
173 // Without SCRIPT_VERIFY_P2SPKH, v3+32-byte programs are treated as
174 // unknown witness programs and always succeed (forward-compatibility).
175 CKey key = GenerateRandomKey();
176 XOnlyPubKey xpk(key.GetPubKey());
177 WitnessV3SpkHash hash(xpk);
178 CScript scriptPubKey = GetScriptForDestination(hash);
179
180 const auto txCredit = MakeTransactionRef(BuildCreditingTransaction(scriptPubKey, 10000));
181 CMutableTransaction mtxSpend = BuildCreditingTransaction(CScript() << OP_RETURN, 5000);
182 mtxSpend.vin.resize(1);
183 mtxSpend.vin[0].prevout.hash = txCredit->GetHash();
184 mtxSpend.vin[0].prevout.n = 0;
185
186 // Build a valid-looking witness with a wrong signature (should still succeed without flag)
187 mtxSpend.vin[0].scriptWitness.stack.emplace_back(72, 0x00); // nonsense sig
188 mtxSpend.vin[0].scriptWitness.stack.emplace_back(XOnlyPubKeyToBytes(xpk));
189
190 ScriptError serror;
191 bool result = VerifyScript(mtxSpend.vin[0].scriptSig, scriptPubKey,
192 &mtxSpend.vin[0].scriptWitness, 0,
193 MutableTransactionSignatureChecker(&mtxSpend, 0, txCredit->vout[0].nValue,
194 PrecomputedTransactionData{}, MissingDataBehavior::FAIL),
195 &serror);
196 BOOST_CHECK(result);
197 }
198
199 BOOST_AUTO_TEST_SUITE_END()
200