@@ -150,4 +150,215 @@ BOOST_AUTO_TEST_CASE(test_assumeutxo)
150
150
BOOST_CHECK_EQUAL (out110_2.nChainTx , 111U );
151
151
}
152
152
153
+ BOOST_AUTO_TEST_CASE (block_malleation)
154
+ {
155
+ // Test utilities that calls `IsBlockMutated` and then clears the validity
156
+ // cache flags on `CBlock`.
157
+ auto is_mutated = [](CBlock& block, bool check_witness_root) {
158
+ bool mutated{IsBlockMutated (block, check_witness_root)};
159
+ block.fChecked = false ;
160
+ block.m_checked_witness_commitment = false ;
161
+ block.m_checked_merkle_root = false ;
162
+ return mutated;
163
+ };
164
+ auto is_not_mutated = [&is_mutated](CBlock& block, bool check_witness_root) {
165
+ return !is_mutated (block, check_witness_root);
166
+ };
167
+
168
+ // Test utilities to create coinbase transactions and insert witness
169
+ // commitments.
170
+ //
171
+ // Note: this will not include the witness stack by default to avoid
172
+ // triggering the "no witnesses allowed for blocks that don't commit to
173
+ // witnesses" rule when testing other malleation vectors.
174
+ auto create_coinbase_tx = [](bool include_witness = false ) {
175
+ CMutableTransaction coinbase;
176
+ coinbase.vin .resize (1 );
177
+ if (include_witness) {
178
+ coinbase.vin [0 ].scriptWitness .stack .resize (1 );
179
+ coinbase.vin [0 ].scriptWitness .stack [0 ] = std::vector<unsigned char >(32 , 0x00 );
180
+ }
181
+
182
+ coinbase.vout .resize (1 );
183
+ coinbase.vout [0 ].scriptPubKey .resize (MINIMUM_WITNESS_COMMITMENT);
184
+ coinbase.vout [0 ].scriptPubKey [0 ] = OP_RETURN;
185
+ coinbase.vout [0 ].scriptPubKey [1 ] = 0x24 ;
186
+ coinbase.vout [0 ].scriptPubKey [2 ] = 0xaa ;
187
+ coinbase.vout [0 ].scriptPubKey [3 ] = 0x21 ;
188
+ coinbase.vout [0 ].scriptPubKey [4 ] = 0xa9 ;
189
+ coinbase.vout [0 ].scriptPubKey [5 ] = 0xed ;
190
+
191
+ auto tx = MakeTransactionRef (coinbase);
192
+ assert (tx->IsCoinBase ());
193
+ return tx;
194
+ };
195
+ auto insert_witness_commitment = [](CBlock& block, uint256 commitment) {
196
+ assert (!block.vtx .empty () && block.vtx [0 ]->IsCoinBase () && !block.vtx [0 ]->vout .empty ());
197
+
198
+ CMutableTransaction mtx{*block.vtx [0 ]};
199
+ CHash256 ().Write (commitment).Write (std::vector<unsigned char >(32 , 0x00 )).Finalize (commitment);
200
+ memcpy (&mtx.vout [0 ].scriptPubKey [6 ], commitment.begin (), 32 );
201
+ block.vtx [0 ] = MakeTransactionRef (mtx);
202
+ };
203
+
204
+ {
205
+ CBlock block;
206
+
207
+ // Empty block is expected to have merkle root of 0x0.
208
+ BOOST_CHECK (block.vtx .empty ());
209
+ block.hashMerkleRoot = uint256{1 };
210
+ BOOST_CHECK (is_mutated (block, /* check_witness_root=*/ false ));
211
+ block.hashMerkleRoot = uint256{};
212
+ BOOST_CHECK (is_not_mutated (block, /* check_witness_root=*/ false ));
213
+
214
+ // Block with a single coinbase tx is mutated if the merkle root is not
215
+ // equal to the coinbase tx's hash.
216
+ block.vtx .push_back (create_coinbase_tx ());
217
+ BOOST_CHECK (block.vtx [0 ]->GetHash () != block.hashMerkleRoot );
218
+ BOOST_CHECK (is_mutated (block, /* check_witness_root=*/ false ));
219
+ block.hashMerkleRoot = block.vtx [0 ]->GetHash ();
220
+ BOOST_CHECK (is_not_mutated (block, /* check_witness_root=*/ false ));
221
+
222
+ // Block with two transactions is mutated if the merkle root does not
223
+ // match the double sha256 of the concatenation of the two transaction
224
+ // hashes.
225
+ block.vtx .push_back (MakeTransactionRef (CMutableTransaction{}));
226
+ BOOST_CHECK (is_mutated (block, /* check_witness_root=*/ false ));
227
+ HashWriter hasher;
228
+ hasher.write (block.vtx [0 ]->GetHash ());
229
+ hasher.write (block.vtx [1 ]->GetHash ());
230
+ block.hashMerkleRoot = hasher.GetHash ();
231
+ BOOST_CHECK (is_not_mutated (block, /* check_witness_root=*/ false ));
232
+
233
+ // Block with two transactions is mutated if any node is duplicate.
234
+ {
235
+ block.vtx [1 ] = block.vtx [0 ];
236
+ BOOST_CHECK (is_mutated (block, /* check_witness_root=*/ false ));
237
+ HashWriter hasher;
238
+ hasher.write (block.vtx [0 ]->GetHash ());
239
+ hasher.write (block.vtx [1 ]->GetHash ());
240
+ block.hashMerkleRoot = hasher.GetHash ();
241
+ BOOST_CHECK (is_mutated (block, /* check_witness_root=*/ false ));
242
+ }
243
+
244
+ // Blocks with 64-byte coinbase transactions are not considered mutated
245
+ block.vtx .clear ();
246
+ {
247
+ CMutableTransaction mtx;
248
+ mtx.vin .resize (1 );
249
+ mtx.vout .resize (1 );
250
+ mtx.vout [0 ].scriptPubKey .resize (4 );
251
+ block.vtx .push_back (MakeTransactionRef (mtx));
252
+ block.hashMerkleRoot = block.vtx .back ()->GetHash ();
253
+ assert (block.vtx .back ()->IsCoinBase ());
254
+ assert (GetSerializeSize (TX_NO_WITNESS (block.vtx .back ())) == 64 );
255
+ }
256
+ BOOST_CHECK (is_not_mutated (block, /* check_witness_root=*/ false ));
257
+ }
258
+
259
+ {
260
+ // Test merkle root malleation
261
+
262
+ // Pseudo code to mine transactions tx{1,2,3}:
263
+ //
264
+ // ```
265
+ // loop {
266
+ // tx1 = random_tx()
267
+ // tx2 = random_tx()
268
+ // tx3 = deserialize_tx(txid(tx1) || txid(tx2));
269
+ // if serialized_size_without_witness(tx3) == 64 {
270
+ // print(hex(tx3))
271
+ // break
272
+ // }
273
+ // }
274
+ // ```
275
+ //
276
+ // The `random_tx` function used to mine the txs below simply created
277
+ // empty transactions with a random version field.
278
+ CMutableTransaction tx1;
279
+ BOOST_CHECK (DecodeHexTx (tx1, " ff204bd0000000000000" , /* try_no_witness=*/ true , /* try_witness=*/ false ));
280
+ CMutableTransaction tx2;
281
+ BOOST_CHECK (DecodeHexTx (tx2, " 8ae53c92000000000000" , /* try_no_witness=*/ true , /* try_witness=*/ false ));
282
+ CMutableTransaction tx3;
283
+ BOOST_CHECK (DecodeHexTx (tx3, " cdaf22d00002c6a7f848f8ae4d30054e61dcf3303d6fe01d282163341f06feecc10032b3160fcab87bdfe3ecfb769206ef2d991b92f8a268e423a6ef4d485f06" , /* try_no_witness=*/ true , /* try_witness=*/ false ));
284
+ {
285
+ // Verify that double_sha256(txid1||txid2) == txid3
286
+ HashWriter hasher;
287
+ hasher.write (tx1.GetHash ());
288
+ hasher.write (tx2.GetHash ());
289
+ assert (hasher.GetHash () == tx3.GetHash ());
290
+ // Verify that tx3 is 64 bytes in size (without witness).
291
+ assert (GetSerializeSize (TX_NO_WITNESS (tx3)) == 64 );
292
+ }
293
+
294
+ CBlock block;
295
+ block.vtx .push_back (MakeTransactionRef (tx1));
296
+ block.vtx .push_back (MakeTransactionRef (tx2));
297
+ uint256 merkle_root = block.hashMerkleRoot = BlockMerkleRoot (block);
298
+ BOOST_CHECK (is_not_mutated (block, /* check_witness_root=*/ false ));
299
+
300
+ // Mutate the block by replacing the two transactions with one 64-byte
301
+ // transaction that serializes into the concatenation of the txids of
302
+ // the transactions in the unmutated block.
303
+ block.vtx .clear ();
304
+ block.vtx .push_back (MakeTransactionRef (tx3));
305
+ BOOST_CHECK (!block.vtx .back ()->IsCoinBase ());
306
+ BOOST_CHECK (BlockMerkleRoot (block) == merkle_root);
307
+ BOOST_CHECK (is_mutated (block, /* check_witness_root=*/ false ));
308
+ }
309
+
310
+ {
311
+ CBlock block;
312
+ block.vtx .push_back (create_coinbase_tx (/* include_witness=*/ true ));
313
+ {
314
+ CMutableTransaction mtx;
315
+ mtx.vin .resize (1 );
316
+ mtx.vin [0 ].scriptWitness .stack .resize (1 );
317
+ mtx.vin [0 ].scriptWitness .stack [0 ] = {0 };
318
+ block.vtx .push_back (MakeTransactionRef (mtx));
319
+ }
320
+ block.hashMerkleRoot = BlockMerkleRoot (block);
321
+ // Block with witnesses is considered mutated if the witness commitment
322
+ // is not validated.
323
+ BOOST_CHECK (is_mutated (block, /* check_witness_root=*/ false ));
324
+ // Block with invalid witness commitment is considered mutated.
325
+ BOOST_CHECK (is_mutated (block, /* check_witness_root=*/ true ));
326
+
327
+ // Block with valid commitment is not mutated
328
+ {
329
+ auto commitment{BlockWitnessMerkleRoot (block)};
330
+ insert_witness_commitment (block, commitment);
331
+ block.hashMerkleRoot = BlockMerkleRoot (block);
332
+ }
333
+ BOOST_CHECK (is_not_mutated (block, /* check_witness_root=*/ true ));
334
+
335
+ // Malleating witnesses should be caught by `IsBlockMutated`.
336
+ {
337
+ CMutableTransaction mtx{*block.vtx [1 ]};
338
+ assert (!mtx.vin [0 ].scriptWitness .stack [0 ].empty ());
339
+ ++mtx.vin [0 ].scriptWitness .stack [0 ][0 ];
340
+ block.vtx [1 ] = MakeTransactionRef (mtx);
341
+ }
342
+ // Without also updating the witness commitment, the merkle root should
343
+ // not change when changing one of the witnesses.
344
+ BOOST_CHECK (block.hashMerkleRoot == BlockMerkleRoot (block));
345
+ BOOST_CHECK (is_mutated (block, /* check_witness_root=*/ true ));
346
+ {
347
+ auto commitment{BlockWitnessMerkleRoot (block)};
348
+ insert_witness_commitment (block, commitment);
349
+ block.hashMerkleRoot = BlockMerkleRoot (block);
350
+ }
351
+ BOOST_CHECK (is_not_mutated (block, /* check_witness_root=*/ true ));
352
+
353
+ // Test malleating the coinbase witness reserved value
354
+ {
355
+ CMutableTransaction mtx{*block.vtx [0 ]};
356
+ mtx.vin [0 ].scriptWitness .stack .resize (0 );
357
+ block.vtx [0 ] = MakeTransactionRef (mtx);
358
+ block.hashMerkleRoot = BlockMerkleRoot (block);
359
+ }
360
+ BOOST_CHECK (is_mutated (block, /* check_witness_root=*/ true ));
361
+ }
362
+ }
363
+
153
364
BOOST_AUTO_TEST_SUITE_END ()
0 commit comments