11///! Blockchain manager for block production and validation
2+ ///!
3+ ///! Provides functionality for:
4+ ///! - Block production with VRF-based proposer selection
5+ ///! - Block validation including signature, VRF, and transaction verification
6+ ///! - Transaction indexing for efficient lookups
7+ ///! - State management with Merkle tree root computation
28
39use crate :: { Result , MetricsRegistry } ;
410use bitcell_consensus:: { Block , BlockHeader , Transaction , BattleProof } ;
@@ -11,7 +17,17 @@ use std::collections::HashMap;
1117/// Genesis block height
1218pub const GENESIS_HEIGHT : u64 = 0 ;
1319
20+ /// Transaction location in blockchain (block height and index within block)
21+ #[ derive( Clone , Debug ) ]
22+ pub struct TxLocation {
23+ pub block_height : u64 ,
24+ pub tx_index : usize ,
25+ }
26+
1427/// Blockchain manager
28+ ///
29+ /// Maintains the blockchain state including blocks, transactions, and state root.
30+ /// Provides O(1) transaction lookup via hash index.
1531#[ derive( Clone ) ]
1632pub struct Blockchain {
1733 /// Current chain height
@@ -23,6 +39,9 @@ pub struct Blockchain {
2339 /// Block storage (height -> block)
2440 blocks : Arc < RwLock < HashMap < u64 , Block > > > ,
2541
42+ /// Transaction hash index for O(1) lookups (tx_hash -> location)
43+ tx_index : Arc < RwLock < HashMap < Hash256 , TxLocation > > > ,
44+
2645 /// State manager
2746 state : Arc < RwLock < StateManager > > ,
2847
@@ -46,6 +65,7 @@ impl Blockchain {
4665 height : Arc :: new ( RwLock :: new ( GENESIS_HEIGHT ) ) ,
4766 latest_hash : Arc :: new ( RwLock :: new ( genesis_hash) ) ,
4867 blocks : Arc :: new ( RwLock :: new ( blocks) ) ,
68+ tx_index : Arc :: new ( RwLock :: new ( HashMap :: new ( ) ) ) ,
4969 state : Arc :: new ( RwLock :: new ( StateManager :: new ( ) ) ) ,
5070 metrics,
5171 secret_key,
@@ -81,29 +101,64 @@ impl Blockchain {
81101 }
82102
83103 /// Get current chain height
104+ ///
105+ /// Returns the current blockchain height. If the lock is poisoned (indicating
106+ /// a prior panic while holding the lock), logs an error and recovers the guard.
84107 pub fn height ( & self ) -> u64 {
85108 * self . height . read ( ) . unwrap_or_else ( |e| {
86- eprintln ! ( "Lock poisoned in height(): {}" , e) ;
109+ tracing :: error !( "Lock poisoned in height() - prior panic detected : {}" , e) ;
87110 e. into_inner ( )
88111 } )
89112 }
90113
91114 /// Get latest block hash
115+ ///
116+ /// Returns the hash of the latest block. If the lock is poisoned (indicating
117+ /// a prior panic while holding the lock), logs an error and recovers the guard.
92118 pub fn latest_hash ( & self ) -> Hash256 {
93119 * self . latest_hash . read ( ) . unwrap_or_else ( |e| {
94- eprintln ! ( "Lock poisoned in latest_hash(): {}" , e) ;
120+ tracing :: error !( "Lock poisoned in latest_hash() - prior panic detected : {}" , e) ;
95121 e. into_inner ( )
96122 } )
97123 }
98124
99125 /// Get block by height
126+ ///
127+ /// Returns the block at the specified height, or None if not found.
128+ /// If the lock is poisoned, logs an error and recovers the guard.
100129 pub fn get_block ( & self , height : u64 ) -> Option < Block > {
101130 self . blocks . read ( ) . unwrap_or_else ( |e| {
102- eprintln ! ( "Lock poisoned in get_block(): {}" , e) ;
131+ tracing :: error !( "Lock poisoned in get_block() - prior panic detected : {}" , e) ;
103132 e. into_inner ( )
104133 } ) . get ( & height) . cloned ( )
105134 }
106-
135+
136+ /// Get transaction by hash using the O(1) hash index
137+ ///
138+ /// Returns the transaction and its location (block height, index) if found.
139+ /// This is significantly more efficient than linear scan for large blockchains.
140+ pub fn get_transaction_by_hash ( & self , tx_hash : & Hash256 ) -> Option < ( Transaction , TxLocation ) > {
141+ // First, look up the location in the index
142+ let location = {
143+ let index = self . tx_index . read ( ) . unwrap_or_else ( |e| {
144+ tracing:: error!( "Lock poisoned in get_transaction_by_hash() - prior panic detected: {}" , e) ;
145+ e. into_inner ( )
146+ } ) ;
147+ index. get ( tx_hash) . cloned ( )
148+ } ;
149+
150+ // Then retrieve the actual transaction from the block
151+ if let Some ( loc) = location {
152+ if let Some ( block) = self . get_block ( loc. block_height ) {
153+ if loc. tx_index < block. transactions . len ( ) {
154+ return Some ( ( block. transactions [ loc. tx_index ] . clone ( ) , loc) ) ;
155+ }
156+ }
157+ }
158+
159+ None
160+ }
161+
107162 /// Get state manager (read-only access)
108163 pub fn state ( & self ) -> Arc < RwLock < StateManager > > {
109164 Arc :: clone ( & self . state )
@@ -135,24 +190,45 @@ impl Blockchain {
135190
136191 // Get current state root
137192 let state_root = {
138- let state = self . state . read ( ) . unwrap ( ) ;
193+ let state = self . state . read ( ) . unwrap_or_else ( |e| {
194+ tracing:: error!( "Lock poisoned in produce_block() while reading state - prior panic detected: {}" , e) ;
195+ e. into_inner ( )
196+ } ) ;
139197 state. state_root
140198 } ;
141-
142- // Generate VRF output and proof
143- // Input is previous block's VRF output (or hash if genesis)
144- let vrf_input = if new_height == 1 {
145- prev_hash. as_bytes ( ) . to_vec ( )
199+
200+ // Generate VRF output and proof using proper VRF chaining
201+ // For genesis block (height 1), use previous hash as input
202+ // For all other blocks, use the previous block's VRF output for chaining
203+ //
204+ // NOTE: We generate VRF proof while holding the blocks lock to prevent race conditions
205+ // where the blockchain state could change between reading the VRF input and using it.
206+ let ( vrf_output, vrf_proof_bytes) = if new_height == 1 {
207+ // First block after genesis uses genesis hash as VRF input
208+ let vrf_input = prev_hash. as_bytes ( ) . to_vec ( ) ;
209+ let ( vrf_output, vrf_proof) = self . secret_key . vrf_prove ( & vrf_input) ;
210+ ( vrf_output, bincode:: serialize ( & vrf_proof) . unwrap_or_default ( ) )
146211 } else {
147- // In a real implementation, we'd get the previous block's VRF output
148- // For now, we mix the prev_hash with the height to ensure uniqueness
149- let mut input = prev_hash. as_bytes ( ) . to_vec ( ) ;
150- input. extend_from_slice ( & new_height. to_le_bytes ( ) ) ;
151- input
212+ // Use previous block's VRF output for proper VRF chaining
213+ // This ensures verifiable randomness chain where each output
214+ // deterministically derives from the previous output
215+ let blocks = self . blocks . read ( ) . unwrap_or_else ( |e| {
216+ tracing:: error!( "Lock poisoned in produce_block() - prior panic detected: {}" , e) ;
217+ e. into_inner ( )
218+ } ) ;
219+
220+ let vrf_input = if let Some ( prev_block) = blocks. get ( & current_height) {
221+ prev_block. header . vrf_output . to_vec ( )
222+ } else {
223+ // Fallback if previous block not found (shouldn't happen in normal operation)
224+ tracing:: warn!( "Previous block {} not found for VRF chaining, using hash fallback" , current_height) ;
225+ prev_hash. as_bytes ( ) . to_vec ( )
226+ } ;
227+
228+ // Generate VRF proof while still holding the read lock to prevent race conditions
229+ let ( vrf_output, vrf_proof) = self . secret_key . vrf_prove ( & vrf_input) ;
230+ ( vrf_output, bincode:: serialize ( & vrf_proof) . unwrap_or_default ( ) )
152231 } ;
153-
154- let ( vrf_output, vrf_proof) = self . secret_key . vrf_prove ( & vrf_input) ;
155- let vrf_proof_bytes = bincode:: serialize ( & vrf_proof) . unwrap_or_default ( ) ;
156232
157233 // Create block header
158234 let header = BlockHeader {
@@ -206,17 +282,28 @@ impl Blockchain {
206282 if block. signature . verify ( & block. header . proposer , header_hash. as_bytes ( ) ) . is_err ( ) {
207283 return Err ( crate :: Error :: Node ( "Invalid block signature" . to_string ( ) ) ) ;
208284 }
209-
210- // Verify VRF
285+
286+ // Verify VRF proof using proper VRF chaining
211287 let vrf_proof: bitcell_crypto:: VrfProof = bincode:: deserialize ( & block. header . vrf_proof )
212288 . map_err ( |_| crate :: Error :: Node ( "Invalid VRF proof format" . to_string ( ) ) ) ?;
213-
289+
290+ // Reconstruct VRF input using the same chaining logic as produce_block
214291 let vrf_input = if block. header . height == 1 {
292+ // First block after genesis uses genesis hash as VRF input
215293 block. header . prev_hash . as_bytes ( ) . to_vec ( )
216294 } else {
217- let mut input = block. header . prev_hash . as_bytes ( ) . to_vec ( ) ;
218- input. extend_from_slice ( & block. header . height . to_le_bytes ( ) ) ;
219- input
295+ // Use previous block's VRF output for proper VRF chaining
296+ let blocks = self . blocks . read ( ) . unwrap_or_else ( |e| {
297+ tracing:: error!( "Lock poisoned in validate_block() - prior panic detected: {}" , e) ;
298+ e. into_inner ( )
299+ } ) ;
300+ if let Some ( prev_block) = blocks. get ( & ( block. header . height - 1 ) ) {
301+ prev_block. header . vrf_output . to_vec ( )
302+ } else {
303+ return Err ( crate :: Error :: Node (
304+ format ! ( "Previous block {} not found for VRF verification" , block. header. height - 1 )
305+ ) ) ;
306+ }
220307 } ;
221308
222309 let vrf_output = vrf_proof. verify ( & block. header . proposer , & vrf_input)
@@ -250,7 +337,10 @@ impl Blockchain {
250337
251338 // Apply transactions to state
252339 {
253- let mut state = self . state . write ( ) . unwrap ( ) ;
340+ let mut state = self . state . write ( ) . unwrap_or_else ( |e| {
341+ tracing:: error!( "Lock poisoned in add_block() while writing state - prior panic detected: {}" , e) ;
342+ e. into_inner ( )
343+ } ) ;
254344
255345 // Apply block reward to proposer
256346 let reward = Self :: calculate_block_reward ( block_height) ;
@@ -287,19 +377,43 @@ impl Blockchain {
287377 }
288378 }
289379
380+ // Index transactions for O(1) lookup
381+ {
382+ let mut tx_index = self . tx_index . write ( ) . unwrap_or_else ( |e| {
383+ tracing:: error!( "Lock poisoned in add_block() while indexing transactions - prior panic detected: {}" , e) ;
384+ e. into_inner ( )
385+ } ) ;
386+ for ( idx, tx) in block. transactions . iter ( ) . enumerate ( ) {
387+ tx_index. insert ( tx. hash ( ) , TxLocation {
388+ block_height,
389+ tx_index : idx,
390+ } ) ;
391+ }
392+ tracing:: debug!( "Indexed {} transactions in block {}" , block. transactions. len( ) , block_height) ;
393+ }
394+
290395 // Store block
291396 {
292- let mut blocks = self . blocks . write ( ) . unwrap ( ) ;
397+ let mut blocks = self . blocks . write ( ) . unwrap_or_else ( |e| {
398+ tracing:: error!( "Lock poisoned in add_block() while storing block - prior panic detected: {}" , e) ;
399+ e. into_inner ( )
400+ } ) ;
293401 blocks. insert ( block_height, block) ;
294402 }
295403
296404 // Update chain tip
297405 {
298- let mut height = self . height . write ( ) . unwrap ( ) ;
406+ let mut height = self . height . write ( ) . unwrap_or_else ( |e| {
407+ tracing:: error!( "Lock poisoned in add_block() while updating height - prior panic detected: {}" , e) ;
408+ e. into_inner ( )
409+ } ) ;
299410 * height = block_height;
300411 }
301412 {
302- let mut latest_hash = self . latest_hash . write ( ) . unwrap ( ) ;
413+ let mut latest_hash = self . latest_hash . write ( ) . unwrap_or_else ( |e| {
414+ tracing:: error!( "Lock poisoned in add_block() while updating latest hash - prior panic detected: {}" , e) ;
415+ e. into_inner ( )
416+ } ) ;
303417 * latest_hash = block_hash;
304418 }
305419
@@ -332,7 +446,10 @@ impl Blockchain {
332446 }
333447
334448 // Check nonce and balance
335- let state = self . state . read ( ) . unwrap ( ) ;
449+ let state = self . state . read ( ) . unwrap_or_else ( |e| {
450+ tracing:: error!( "Lock poisoned in validate_transaction() - prior panic detected: {}" , e) ;
451+ e. into_inner ( )
452+ } ) ;
336453 if let Some ( account) = state. get_account ( tx. from . as_bytes ( ) ) {
337454 if tx. nonce != account. nonce {
338455 return Err ( crate :: Error :: Node ( format ! (
0 commit comments