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