psbt_tests.cpp raw
1 // Copyright (c) 2022 The Bitcoin Core developers
2 // Distributed under the MIT software license, see the accompanying
3 // file COPYING or https://www.opensource.org/licenses/mit-license.php.
4
5 #include <psbt.h>
6
7 #include <boost/test/unit_test.hpp>
8 #include <test/util/setup_common.h>
9
10 BOOST_FIXTURE_TEST_SUITE(psbt_tests, BasicTestingSetup)
11
12 static PSBTProprietary MakeProprietary(uint64_t subtype, uint8_t key_data, uint8_t value)
13 {
14 return PSBTProprietary{
15 .subtype = subtype,
16 .identifier = {'p', 's', 'b', 't'},
17 .key = {key_data},
18 .value = {value},
19 };
20 }
21
22 void CheckTimeLock(const std::string& base64_psbt, std::optional<uint32_t> timelock)
23 {
24 util::Result<PartiallySignedTransaction> psbt = DecodeBase64PSBT(base64_psbt);
25 BOOST_CHECK(psbt);
26
27 std::optional<uint32_t> computed_timelock = psbt->ComputeTimeLock();
28 std::optional<CMutableTransaction> tx = psbt->GetUnsignedTx();
29 if (timelock) {
30 BOOST_CHECK(computed_timelock);
31 BOOST_CHECK_EQUAL(*computed_timelock, *timelock);
32 BOOST_CHECK(tx);
33 BOOST_CHECK_EQUAL(tx->nLockTime, *timelock);
34 } else {
35 BOOST_CHECK(!computed_timelock);
36 BOOST_CHECK(!tx);
37 }
38 }
39
40 BOOST_AUTO_TEST_CASE(psbt2_timelock_test)
41 {
42 CheckTimeLock("cHNidP8BAgQCAAAAAQQBAQEFAQIB+wQCAAAAAAEOIAsK2SFBnByHGXNdctxzn56p4GONH+TB7vD5lECEgV/IAQ8EAAAAAAABAwgACK8vAAAAAAEEFgAUxDD2TEdW2jENvRoIVXLvKZkmJywAAQMIi73rCwAAAAABBBYAFE3Rk6yWSlasG54cyoRU/i9HT4UTAA==", 0);
43 CheckTimeLock("cHNidP8BAgQCAAAAAQMEAAAAAAEEAQIBBQEBAfsEAgAAAAABDiAPdY2/vU2nwWyKMwnDyB4RAPVh6mRttbAXUsSF4b3enwEPBAEAAAAAAQ4gOhs7PIN9ZInqejHY5sfdUDwAG+8+BpWOdXSAjWjKeKUBDwQAAAAAAAEDCE+TNXcAAAAAAQQWABQLE1LKzQPPaqG388jWOIZxs0peEQA=", 0);
44 CheckTimeLock("cHNidP8BAgQCAAAAAQMEAAAAAAEEAQIBBQEBAfsEAgAAAAABDiAPdY2/vU2nwWyKMwnDyB4RAPVh6mRttbAXUsSF4b3enwEPBAEAAAABEgQQJwAAAAEOIDobOzyDfWSJ6nox2ObH3VA8ABvvPgaVjnV0gI1oynilAQ8EAAAAAAABAwhPkzV3AAAAAAEEFgAUCxNSys0Dz2qht/PI1jiGcbNKXhEA", 10000);
45 CheckTimeLock("cHNidP8BAgQCAAAAAQMEAAAAAAEEAQIBBQEBAfsEAgAAAAABDiAPdY2/vU2nwWyKMwnDyB4RAPVh6mRttbAXUsSF4b3enwEPBAEAAAABEgQQJwAAAAEOIDobOzyDfWSJ6nox2ObH3VA8ABvvPgaVjnV0gI1oynilAQ8EAAAAAAESBCgjAAAAAQMIT5M1dwAAAAABBBYAFAsTUsrNA89qobfzyNY4hnGzSl4RAA==", 10000);
46 CheckTimeLock("cHNidP8BAgQCAAAAAQMEAAAAAAEEAQIBBQEBAfsEAgAAAAABDiAPdY2/vU2nwWyKMwnDyB4RAPVh6mRttbAXUsSF4b3enwEPBAEAAAABEgQQJwAAAAEOIDobOzyDfWSJ6nox2ObH3VA8ABvvPgaVjnV0gI1oynilAQ8EAAAAAAERBIyNxGIBEgQoIwAAAAEDCE+TNXcAAAAAAQQWABQLE1LKzQPPaqG388jWOIZxs0peEQA=", 10000);
47 CheckTimeLock("cHNidP8BAgQCAAAAAQMEAAAAAAEEAQIBBQEBAfsEAgAAAAABDiAPdY2/vU2nwWyKMwnDyB4RAPVh6mRttbAXUsSF4b3enwEPBAEAAAABEQSLjcRiARIEECcAAAABDiA6Gzs8g31kiep6Mdjmx91QPAAb7z4GlY51dICNaMp4pQEPBAAAAAABEQSMjcRiARIEKCMAAAABAwhPkzV3AAAAAAEEFgAUCxNSys0Dz2qht/PI1jiGcbNKXhEA", 10000);
48 CheckTimeLock("cHNidP8BAgQCAAAAAQMEAAAAAAEEAQIBBQEBAfsEAgAAAAABDiAPdY2/vU2nwWyKMwnDyB4RAPVh6mRttbAXUsSF4b3enwEPBAEAAAABEQSLjcRiAAEOIDobOzyDfWSJ6nox2ObH3VA8ABvvPgaVjnV0gI1oynilAQ8EAAAAAAERBIyNxGIBEgQoIwAAAAEDCE+TNXcAAAAAAQQWABQLE1LKzQPPaqG388jWOIZxs0peEQA=", 1657048460);
49 CheckTimeLock("cHNidP8BAgQCAAAAAQMEAAAAAAEEAQIBBQEBAfsEAgAAAAABDiAPdY2/vU2nwWyKMwnDyB4RAPVh6mRttbAXUsSF4b3enwEPBAEAAAABEQSLjcRiARIEECcAAAABDiA6Gzs8g31kiep6Mdjmx91QPAAb7z4GlY51dICNaMp4pQEPBAAAAAABEQSMjcRiAAEDCE+TNXcAAAAAAQQWABQLE1LKzQPPaqG388jWOIZxs0peEQA=", 1657048460);
50 CheckTimeLock("cHNidP8BAgQCAAAAAQMEAAAAAAEEAQIBBQEBAfsEAgAAAAABDiAPdY2/vU2nwWyKMwnDyB4RAPVh6mRttbAXUsSF4b3enwEPBAEAAAAAAQ4gOhs7PIN9ZInqejHY5sfdUDwAG+8+BpWOdXSAjWjKeKUBDwQAAAAAAREEjI3EYgABAwhPkzV3AAAAAAEEFgAUCxNSys0Dz2qht/PI1jiGcbNKXhEA", 1657048460);
51 CheckTimeLock("cHNidP8BAgQCAAAAAQMEAAAAAAEEAQIBBQEBAfsEAgAAAAABDiAPdY2/vU2nwWyKMwnDyB4RAPVh6mRttbAXUsSF4b3enwEPBAEAAAABEgQQJwAAAAEOIDobOzyDfWSJ6nox2ObH3VA8ABvvPgaVjnV0gI1oynilAQ8EAAAAAAERBIyNxGIAAQMIT5M1dwAAAAABBBYAFAsTUsrNA89qobfzyNY4hnGzSl4RAA==", std::nullopt);
52 }
53
54 BOOST_AUTO_TEST_CASE(psbt2_addinput)
55 {
56 FastRandomContext rng(/*fDeterministic=*/true);
57
58 CMutableTransaction mtx;
59 PartiallySignedTransaction psbt(mtx, /*version=*/2);
60 psbt.m_tx_modifiable.emplace();
61 psbt.m_tx_modifiable->set(0, true);
62 BOOST_CHECK_EQUAL(psbt.inputs.size(), 0);
63
64 // Same PSBT version is required
65 uint256 txid;
66 rng.fillrand(MakeWritableByteSpan(txid));
67 PSBTInput psbtin_v0(/*psbt_version=*/0, Txid::FromUint256(txid), /*prev_out=*/0);
68 BOOST_CHECK(!psbt.AddInput(psbtin_v0));
69 BOOST_CHECK_EQUAL(psbt.inputs.size(), 0);
70 rng.fillrand(MakeWritableByteSpan(txid));
71 PSBTInput psbtin(/*psbt_version=*/2, Txid::FromUint256(txid), /*prev_out=*/0);
72 BOOST_CHECK(psbt.AddInput(psbtin));
73 BOOST_CHECK_EQUAL(psbt.inputs.size(), 1);
74
75 // Duplicates are not allowed
76 BOOST_CHECK(!psbt.AddInput(psbtin));
77 BOOST_CHECK_EQUAL(psbt.inputs.size(), 1);
78
79 // Input with a unique txid is allowed
80 rng.fillrand(MakeWritableByteSpan(txid));
81 PSBTInput psbtin2(/*psbt_version=*/2, Txid::FromUint256(txid), /*prev_out=*/0);
82 BOOST_CHECK(psbt.AddInput(psbtin2));
83 BOOST_CHECK_EQUAL(psbt.inputs.size(), 2);
84
85 // Disabling inputs modifiable flag prevents adding new inputs
86 psbt.m_tx_modifiable->set(0, false);
87 rng.fillrand(MakeWritableByteSpan(txid));
88 PSBTInput psbtin3(/*psbt_version=*/2, Txid::FromUint256(txid), /*prev_out=*/0);
89 BOOST_CHECK(!psbt.AddInput(psbtin3));
90 BOOST_CHECK_EQUAL(psbt.inputs.size(), 2);
91 psbt.m_tx_modifiable->set(0, true);
92
93 // Make sure that timelock compatibility checks are working
94 // No previous required timelocks, new input with both height and time timelocks is allowed
95 rng.fillrand(MakeWritableByteSpan(txid));
96 PSBTInput psbtin4(/*psbt_version=*/2, Txid::FromUint256(txid), /*prev_out=*/0);
97 psbtin4.time_locktime = LOCKTIME_THRESHOLD;
98 psbtin4.height_locktime = 100;
99 BOOST_CHECK(psbt.AddInput(psbtin4));
100 BOOST_CHECK_EQUAL(psbt.inputs.size(), 3);
101
102 // Input with only a time timelock is allowed
103 rng.fillrand(MakeWritableByteSpan(txid));
104 PSBTInput psbtin5(/*psbt_version=*/2, Txid::FromUint256(txid), /*prev_out=*/0);
105 psbtin5.time_locktime = LOCKTIME_THRESHOLD + 1;
106 BOOST_CHECK(psbt.AddInput(psbtin5));
107 BOOST_CHECK_EQUAL(psbt.inputs.size(), 4);
108
109 // Input with only a height timelock is not allowed because of previous
110 rng.fillrand(MakeWritableByteSpan(txid));
111 PSBTInput psbtin6(/*psbt_version=*/2, Txid::FromUint256(txid), /*prev_out=*/0);
112 psbtin6.height_locktime = 100;
113 BOOST_CHECK(!psbt.AddInput(psbtin6));
114 BOOST_CHECK_EQUAL(psbt.inputs.size(), 4);
115
116 // Adding an input that already has a signature is allowed
117 rng.fillrand(MakeWritableByteSpan(txid));
118 PSBTInput psbtin7(/*psbt_version=*/2, Txid::FromUint256(txid), /*prev_out=*/0);
119 psbtin7.final_script_sig << OP_1;
120 BOOST_CHECK(psbt.AddInput(psbtin7));
121 BOOST_CHECK_EQUAL(psbt.inputs.size(), 5);
122
123 // Same thing, but with other things that have signatures
124 psbtin7.final_script_sig.clear();
125 psbtin7.final_script_witness.stack.emplace_back();
126 BOOST_CHECK(!psbt.AddInput(psbtin7));
127 BOOST_CHECK_EQUAL(psbt.inputs.size(), 5);
128 psbtin7.final_script_witness.SetNull();
129 psbtin7.partial_sigs.emplace();
130 BOOST_CHECK(!psbt.AddInput(psbtin7));
131 BOOST_CHECK_EQUAL(psbt.inputs.size(), 5);
132 psbtin7.partial_sigs.clear();
133 psbtin7.m_tap_key_sig.push_back(0);
134 BOOST_CHECK(!psbt.AddInput(psbtin7));
135 BOOST_CHECK_EQUAL(psbt.inputs.size(), 5);
136 psbtin7.m_tap_key_sig.clear();
137 psbtin7.m_tap_script_sigs.emplace();
138 BOOST_CHECK(!psbt.AddInput(psbtin7));
139 BOOST_CHECK_EQUAL(psbt.inputs.size(), 5);
140 psbtin7.m_tap_script_sigs.clear();
141 psbtin7.m_musig2_partial_sigs.emplace();
142 BOOST_CHECK(!psbt.AddInput(psbtin7));
143 BOOST_CHECK_EQUAL(psbt.inputs.size(), 5);
144
145 // Adding an input that changes the timelock is no longer allowed
146 rng.fillrand(MakeWritableByteSpan(txid));
147 PSBTInput psbtin8(/*psbt_version=*/2, Txid::FromUint256(txid), /*prev_out=*/0);
148 psbtin8.time_locktime = LOCKTIME_THRESHOLD + 2;
149 BOOST_CHECK(!psbt.AddInput(psbtin8));
150 BOOST_CHECK_EQUAL(psbt.inputs.size(), 5);
151 }
152
153 BOOST_AUTO_TEST_CASE(psbt2_addoutput)
154 {
155 CMutableTransaction mtx;
156 PartiallySignedTransaction psbt(mtx, /*version=*/2);
157 psbt.m_tx_modifiable.emplace();
158 psbt.m_tx_modifiable->set(1, true);
159 BOOST_CHECK_EQUAL(psbt.outputs.size(), 0);
160
161 // Same PSBT version is required
162 PSBTOutput psbtout_v0(/*psbt_version=*/0, /*amount=*/1, CScript());
163 BOOST_CHECK(!psbt.AddOutput(psbtout_v0));
164 BOOST_CHECK_EQUAL(psbt.outputs.size(), 0);
165 PSBTOutput psbtout(/*psbt_version=*/2, /*amount=*/1, CScript());
166 BOOST_CHECK(psbt.AddOutput(psbtout));
167 BOOST_CHECK_EQUAL(psbt.outputs.size(), 1);
168
169 // Disabling outputs modifiable flag prevents adding new outputs
170 psbt.m_tx_modifiable->set(1, false);
171 PSBTOutput psbtout2(/*psbt_version=*/2, /*amount=*/1, CScript());
172 BOOST_CHECK(!psbt.AddOutput(psbtout2));
173 BOOST_CHECK_EQUAL(psbt.outputs.size(), 1);
174 psbt.m_tx_modifiable->set(1, true);
175 PSBTOutput psbtout3(/*psbt_version=*/2, /*amount=*/1, CScript());
176 BOOST_CHECK(psbt.AddOutput(psbtout3));
177 BOOST_CHECK_EQUAL(psbt.outputs.size(), 2);
178 }
179
180 BOOST_AUTO_TEST_CASE(merge_proprietary_fields)
181 {
182 CMutableTransaction tx;
183 tx.vin.emplace_back(COutPoint{});
184 tx.vout.emplace_back(0, CScript{});
185
186 PartiallySignedTransaction left(tx);
187 PartiallySignedTransaction right(tx);
188
189 const auto left_prop = MakeProprietary(/*subtype=*/1, /*key_data=*/0x01, /*value=*/0xaa);
190 const auto right_prop = MakeProprietary(/*subtype=*/2, /*key_data=*/0x02, /*value=*/0xbb);
191
192 left.m_proprietary.insert(left_prop);
193 left.inputs[0].m_proprietary.insert(left_prop);
194 left.outputs[0].m_proprietary.insert(left_prop);
195
196 right.m_proprietary.insert(right_prop);
197 right.inputs[0].m_proprietary.insert(right_prop);
198 right.outputs[0].m_proprietary.insert(right_prop);
199
200 BOOST_REQUIRE(left.Merge(right));
201
202 BOOST_REQUIRE_EQUAL(left.m_proprietary.size(), 2U);
203 BOOST_REQUIRE_EQUAL(left.inputs[0].m_proprietary.size(), 2U);
204 BOOST_REQUIRE_EQUAL(left.outputs[0].m_proprietary.size(), 2U);
205
206 const auto global_it = left.m_proprietary.find(right_prop);
207 BOOST_REQUIRE(global_it != left.m_proprietary.end());
208 BOOST_CHECK(global_it->value == right_prop.value);
209
210 const auto input_it = left.inputs[0].m_proprietary.find(right_prop);
211 BOOST_REQUIRE(input_it != left.inputs[0].m_proprietary.end());
212 BOOST_CHECK(input_it->value == right_prop.value);
213
214 const auto output_it = left.outputs[0].m_proprietary.find(right_prop);
215 BOOST_REQUIRE(output_it != left.outputs[0].m_proprietary.end());
216 BOOST_CHECK(output_it->value == right_prop.value);
217 }
218
219 BOOST_AUTO_TEST_SUITE_END()
220