@@ -219,9 +219,12 @@ impl Consensus {
219219 /// weaker amount check than that of traditional AssumeValid.
220220 pub fn verify_block_transactions_swiftsync (
221221 transactions : & [ Transaction ] ,
222+ txids : Vec < Txid > ,
222223 unspent_indexes : HashSet < u64 > ,
223224 salt : & SipHashKeys ,
224225 ) -> Result < ( SwiftSyncAgg , Amount ) , BlockchainError > {
226+ assert_eq ! ( transactions. len( ) , txids. len( ) ) ;
227+
225228 // Blocks must contain at least one transaction (i.e., the coinbase)
226229 if transactions. is_empty ( ) {
227230 return Err ( BlockValidationErrors :: EmptyBlock ) ?;
@@ -232,7 +235,7 @@ impl Consensus {
232235 let mut unspent_amount = Amount :: ZERO ;
233236 let mut agg = SwiftSyncAgg :: zero ( ) ;
234237
235- for ( n, transaction) in transactions. iter ( ) . enumerate ( ) {
238+ for ( n, ( transaction, txid ) ) in transactions. iter ( ) . zip ( txids ) . enumerate ( ) {
236239 if n == 0 {
237240 if !transaction. is_coinbase ( ) {
238241 return Err ( BlockValidationErrors :: FirstTxIsNotCoinbase ) ?;
@@ -267,7 +270,7 @@ impl Consensus {
267270 output_index += 1 ;
268271 }
269272 // Only add spent outputs to the aggregator
270- Self :: add_outputs_to_agg ( salt, & mut agg, transaction , spent_vouts) ;
273+ Self :: add_outputs_to_agg ( salt, & mut agg, txid , spent_vouts) ;
271274 }
272275
273276 Ok ( ( agg, unspent_amount) )
@@ -277,23 +280,21 @@ impl Consensus {
277280 fn add_outputs_to_agg (
278281 salt : & SipHashKeys ,
279282 agg : & mut SwiftSyncAgg ,
280- transaction : & Transaction ,
283+ txid : Txid ,
281284 spent_vouts : Vec < u32 > ,
282285 ) {
283- let txid = || transaction. compute_txid ( ) ;
284-
285286 match spent_vouts. len ( ) {
286287 // Nothing needs to be added to the aggregator
287288 0 => { }
288289
289290 // Only one `OutPoint` to add
290- 1 => agg. add ( salt, & OutPoint :: new ( txid ( ) , spent_vouts[ 0 ] ) ) ,
291+ 1 => agg. add ( salt, & OutPoint :: new ( txid, spent_vouts[ 0 ] ) ) ,
291292
292293 // All `OutPoints` to hash here will share the same txid, only differing in the vout.
293294 // Thus, we can compute the midstate once, amortizing this cost for all `OutPoints`,
294295 // which is around 57% less work given enough spent outputs.
295296 _ => {
296- let midstate = TxidHashMidstate :: new ( salt, txid ( ) . as_byte_array ( ) ) ;
297+ let midstate = TxidHashMidstate :: new ( salt, txid. as_byte_array ( ) ) ;
297298 for vout in spent_vouts {
298299 agg. add_with_vout ( midstate. clone ( ) , vout) ;
299300 }
@@ -453,17 +454,18 @@ impl Consensus {
453454 Ok ( out_value)
454455 }
455456
456- /// Runs inexpensive, consensus-critical block checks that don't require script execution.
457+ /// Runs inexpensive, consensus-critical block checks that don't require script execution. If
458+ /// successful, returns the list of [`Txid`]s computed for the merkle root check.
457459 ///
458460 /// This verifies:
459461 /// - the header merkle root matches the block's txids
460462 /// - BIP34 coinbase-encoded height once activated (at `bip34_height`)
461463 /// - if there are SegWit transactions, the witness commitment is present and correct
462464 /// - total block weight is within the 4,000,000 WU limit
463- pub fn check_block ( & self , block : & Block , height : u32 ) -> Result < ( ) , BlockchainError > {
464- if !block . check_merkle_root ( ) {
465+ pub fn check_block ( & self , block : & Block , height : u32 ) -> Result < Vec < Txid > , BlockchainError > {
466+ let Some ( txids ) = Self :: check_merkle_root ( block ) else {
465467 return Err ( BlockValidationErrors :: BadMerkleRoot ) ?;
466- }
468+ } ;
467469
468470 let bip34_height = self . parameters . params . bip34_height ;
469471 // If bip34 is active, check that the encoded block height is correct
@@ -479,7 +481,7 @@ impl Consensus {
479481 return Err ( BlockValidationErrors :: BlockTooBig ) ?;
480482 }
481483
482- Ok ( ( ) )
484+ Ok ( txids )
483485 }
484486
485487 /// Performs AssumeValid SwiftSync validation and returns the resulting [`SwiftSyncAgg`]
@@ -493,9 +495,28 @@ impl Consensus {
493495 unspent_indexes : HashSet < u64 > ,
494496 salt : & SipHashKeys ,
495497 ) -> Result < ( SwiftSyncAgg , Amount ) , BlockchainError > {
496- self . check_block ( block, height) ?;
498+ let txids = self . check_block ( block, height) ?;
497499
498- Consensus :: verify_block_transactions_swiftsync ( & block. txdata , unspent_indexes, salt)
500+ Consensus :: verify_block_transactions_swiftsync ( & block. txdata , txids, unspent_indexes, salt)
501+ }
502+
503+ /// Checks if the merkle root of the header matches the merkle root of the transaction list.
504+ ///
505+ /// Unlike [`Block::check_merkle_root`], this function returns the list of computed [`Txid`]s
506+ /// if the merkle roots matched, or `None` otherwise.
507+ ///
508+ /// The merkle root is computed in the same way as [`Block::compute_merkle_root`].
509+ pub fn check_merkle_root ( block : & Block ) -> Option < Vec < Txid > > {
510+ let txids: Vec < _ > = block. txdata . iter ( ) . map ( |obj| obj. compute_txid ( ) ) . collect ( ) ;
511+
512+ // Copy the hashes into an iterator, as `calculate_root` requires ownership
513+ let hashes_iter = txids. iter ( ) . copied ( ) . map ( |txid| txid. to_raw_hash ( ) ) ;
514+
515+ let calculated = bitcoin:: merkle_tree:: calculate_root ( hashes_iter) . map ( |h| h. into ( ) ) ;
516+ match calculated {
517+ Some ( merkle_root) if block. header . merkle_root == merkle_root => Some ( txids) ,
518+ _ => None ,
519+ }
499520 }
500521
501522 /// Returns the TxOut being spent by the given input.
0 commit comments