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