@@ -19,7 +19,8 @@ use std::sync::Arc;
1919
2020use anyhow:: { Context as _, Result , bail} ;
2121use bitcoin_rs_mempool:: { Mempool , MempoolLimits } ;
22- use bitcoin_rs_utxo:: UtxoSet ;
22+ use bitcoin_rs_primitives:: OutPoint ;
23+ use bitcoin_rs_utxo:: { BlockChanges , UtxoAdd , UtxoSet } ;
2324use parking_lot:: { Mutex , RwLock } ;
2425
2526use crate :: Config ;
@@ -38,6 +39,9 @@ pub enum ApplyError {
3839 /// Height arithmetic overflowed `u32::MAX`.
3940 #[ error( "height overflow at tip {0}" ) ]
4041 HeightOverflow ( u32 ) ,
42+ /// UTXO commit failed during block apply.
43+ #[ error( "utxo commit: {0}" ) ]
44+ UtxoCommit ( #[ from] bitcoin_rs_utxo:: UtxoError ) ,
4145}
4246
4347enum NodeStorage {
@@ -277,8 +281,8 @@ impl NodeState {
277281 /// tip is published yet), chainwork is approximated by accumulating the
278282 /// block header's own work onto the prior tip's chainwork, and the block
279283 /// is stored in `blocks` for RPC consumers. Real consensus validation,
280- /// UTXO commit, BIP30 / BIP34 / soft-fork checks, BIP9 deployment state,
281- /// and reorg planning land in follow-up turns.
284+ /// BIP30 / BIP34 / soft-fork checks, BIP9 deployment state, and reorg
285+ /// planning land in follow-up turns.
282286 ///
283287 /// Returns the new `TipSnapshot` so callers can publish it elsewhere.
284288 pub fn apply_block (
@@ -313,6 +317,38 @@ impl NodeState {
313317 None => ( 0_u32 , header_work) ,
314318 } ;
315319
320+ let mut changes = BlockChanges :: default ( ) ;
321+ for tx in & block. txdata {
322+ let txid = tx. compute_txid ( ) ;
323+ for ( vout_idx, txout) in tx. output . iter ( ) . enumerate ( ) {
324+ let outpoint = OutPoint :: new (
325+ bitcoin_rs_primitives:: Hash256 :: from_le_bytes ( txid. as_byte_array ( ) ) ,
326+ u32:: try_from ( vout_idx) . map_err ( |_| ApplyError :: HeightOverflow ( height) ) ?,
327+ ) ;
328+ changes. add ( UtxoAdd :: new (
329+ outpoint,
330+ txout. clone ( ) ,
331+ tx. is_coinbase ( ) ,
332+ height,
333+ ) ) ;
334+ }
335+
336+ if !tx. is_coinbase ( ) {
337+ for tx_input in & tx. input {
338+ let previous_output = tx_input. previous_output ;
339+ changes. remove ( OutPoint :: new (
340+ bitcoin_rs_primitives:: Hash256 :: from_le_bytes (
341+ previous_output. txid . as_byte_array ( ) ,
342+ ) ,
343+ previous_output. vout ,
344+ ) ) ;
345+ }
346+ }
347+ }
348+ self . utxo
349+ . commit_block ( & changes, & block_hash)
350+ . map_err ( ApplyError :: UtxoCommit ) ?;
351+
316352 let tip = bitcoin_rs_chain:: TipSnapshot {
317353 tip_id : bitcoin_rs_chain:: node:: NodeId :: new ( height) ,
318354 height,
@@ -323,6 +359,20 @@ impl NodeState {
323359 self . blocks
324360 . write ( )
325361 . push ( bitcoin_rs_rpc:: BlockRecord :: from_block ( height, block) ) ;
362+ for tx in & block. txdata {
363+ let txid = tx. compute_txid ( ) ;
364+ let evicted_count = self . mempool . write ( ) . remove_by_txid ( & txid) . len ( ) ;
365+ tracing:: debug!( %txid, evicted_count, "apply_block: evicted transaction from mempool" ) ;
366+ self . transactions . write ( ) . insert ( txid, tx. clone ( ) ) ;
367+ }
368+ tracing:: info!(
369+ height,
370+ %block_hash,
371+ tx_count = block. txdata. len( ) ,
372+ utxo_adds = changes. add_count( ) ,
373+ utxo_removes = changes. remove_count( ) ,
374+ "apply_block: synthetic chain advance committed"
375+ ) ;
326376 Ok ( tip)
327377 }
328378}
0 commit comments