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
82
93use crate :: { Result , MetricsRegistry } ;
104use bitcell_consensus:: { Block , BlockHeader , Transaction , BattleProof } ;
115use bitcell_crypto:: { Hash256 , PublicKey , SecretKey } ;
12- use bitcell_economics:: { COIN , INITIAL_BLOCK_REWARD , HALVING_INTERVAL , MAX_HALVINGS } ;
6+ use bitcell_economics:: { COIN , INITIAL_BLOCK_REWARD , HALVING_INTERVAL } ;
137use bitcell_state:: StateManager ;
148use std:: sync:: { Arc , RwLock } ;
159use std:: collections:: HashMap ;
1610
1711/// Genesis block height
1812pub const GENESIS_HEIGHT : u64 = 0 ;
1913
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-
2714/// Blockchain manager
28- ///
29- /// Maintains the blockchain state including blocks, transactions, and state root.
30- /// Provides O(1) transaction lookup via hash index.
3115#[ derive( Clone ) ]
3216pub struct Blockchain {
3317 /// Current chain height
@@ -39,9 +23,6 @@ pub struct Blockchain {
3923 /// Block storage (height -> block)
4024 blocks : Arc < RwLock < HashMap < u64 , Block > > > ,
4125
42- /// Transaction hash index for O(1) lookups (tx_hash -> location)
43- tx_index : Arc < RwLock < HashMap < Hash256 , TxLocation > > > ,
44-
4526 /// State manager
4627 state : Arc < RwLock < StateManager > > ,
4728
@@ -65,7 +46,6 @@ impl Blockchain {
6546 height : Arc :: new ( RwLock :: new ( GENESIS_HEIGHT ) ) ,
6647 latest_hash : Arc :: new ( RwLock :: new ( genesis_hash) ) ,
6748 blocks : Arc :: new ( RwLock :: new ( blocks) ) ,
68- tx_index : Arc :: new ( RwLock :: new ( HashMap :: new ( ) ) ) ,
6949 state : Arc :: new ( RwLock :: new ( StateManager :: new ( ) ) ) ,
7050 metrics,
7151 secret_key,
@@ -101,64 +81,29 @@ impl Blockchain {
10181 }
10282
10383 /// 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.
10784 pub fn height ( & self ) -> u64 {
10885 * self . height . read ( ) . unwrap_or_else ( |e| {
109- tracing :: error !( "Lock poisoned in height() - prior panic detected : {}" , e) ;
86+ eprintln ! ( "Lock poisoned in height(): {}" , e) ;
11087 e. into_inner ( )
11188 } )
11289 }
11390
11491 /// 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.
11892 pub fn latest_hash ( & self ) -> Hash256 {
11993 * self . latest_hash . read ( ) . unwrap_or_else ( |e| {
120- tracing :: error !( "Lock poisoned in latest_hash() - prior panic detected : {}" , e) ;
94+ eprintln ! ( "Lock poisoned in latest_hash(): {}" , e) ;
12195 e. into_inner ( )
12296 } )
12397 }
12498
12599 /// 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.
129100 pub fn get_block ( & self , height : u64 ) -> Option < Block > {
130101 self . blocks . read ( ) . unwrap_or_else ( |e| {
131- tracing :: error !( "Lock poisoned in get_block() - prior panic detected : {}" , e) ;
102+ eprintln ! ( "Lock poisoned in get_block(): {}" , e) ;
132103 e. into_inner ( )
133104 } ) . get ( & height) . cloned ( )
134105 }
135106
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-
162107 /// Get state manager (read-only access)
163108 pub fn state ( & self ) -> Arc < RwLock < StateManager > > {
164109 Arc :: clone ( & self . state )
@@ -167,8 +112,8 @@ impl Blockchain {
167112 /// Calculate block reward based on height (halves every HALVING_INTERVAL blocks)
168113 pub fn calculate_block_reward ( height : u64 ) -> u64 {
169114 let halvings = height / HALVING_INTERVAL ;
170- if halvings >= MAX_HALVINGS {
171- // After MAX_HALVINGS halvings, reward is effectively 0
115+ if halvings >= 64 {
116+ // After 64 halvings, reward is effectively 0
172117 return 0 ;
173118 }
174119 INITIAL_BLOCK_REWARD >> halvings
@@ -190,45 +135,24 @@ impl Blockchain {
190135
191136 // Get current state root
192137 let state_root = {
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- } ) ;
138+ let state = self . state . read ( ) . unwrap ( ) ;
197139 state. state_root
198140 } ;
199141
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 ( ) )
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 ( )
211146 } else {
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 ( ) )
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
231152 } ;
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 ( ) ;
232156
233157 // Create block header
234158 let header = BlockHeader {
@@ -283,27 +207,16 @@ impl Blockchain {
283207 return Err ( crate :: Error :: Node ( "Invalid block signature" . to_string ( ) ) ) ;
284208 }
285209
286- // Verify VRF proof using proper VRF chaining
210+ // Verify VRF
287211 let vrf_proof: bitcell_crypto:: VrfProof = bincode:: deserialize ( & block. header . vrf_proof )
288212 . map_err ( |_| crate :: Error :: Node ( "Invalid VRF proof format" . to_string ( ) ) ) ?;
289213
290- // Reconstruct VRF input using the same chaining logic as produce_block
291214 let vrf_input = if block. header . height == 1 {
292- // First block after genesis uses genesis hash as VRF input
293215 block. header . prev_hash . as_bytes ( ) . to_vec ( )
294216 } else {
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- }
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
307220 } ;
308221
309222 let vrf_output = vrf_proof. verify ( & block. header . proposer , & vrf_input)
@@ -337,23 +250,13 @@ impl Blockchain {
337250
338251 // Apply transactions to state
339252 {
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- } ) ;
253+ let mut state = self . state . write ( ) . unwrap ( ) ;
344254
345255 // Apply block reward to proposer
346256 let reward = Self :: calculate_block_reward ( block_height) ;
347257 if reward > 0 {
348- match state. credit_account ( * block. header . proposer . as_bytes ( ) , reward) {
349- Ok ( _) => {
350- tracing:: info!( "Block reward credited: {} units to proposer" , reward) ;
351- }
352- Err ( e) => {
353- tracing:: error!( "Failed to credit block reward: {:?}" , e) ;
354- return Err ( crate :: Error :: Node ( "Failed to credit block reward" . to_string ( ) ) ) ;
355- }
356- }
258+ state. credit_account ( * block. header . proposer . as_bytes ( ) , reward) ;
259+ println ! ( "Block reward credited: {} units to proposer" , reward) ;
357260 }
358261
359262 for tx in & block. transactions {
@@ -366,54 +269,30 @@ impl Blockchain {
366269 ) {
367270 Ok ( new_state_root) => {
368271 // State updated successfully
369- tracing :: debug !( "Transaction applied, new state root: {:?}" , new_state_root) ;
272+ println ! ( "Transaction applied, new state root: {:?}" , new_state_root) ;
370273 }
371274 Err ( e) => {
372- tracing :: warn !( "Failed to apply transaction: {:?}" , e) ;
275+ println ! ( "Failed to apply transaction: {:?}" , e) ;
373276 // In production, this should rollback the entire block
374277 // For now, we just skip the transaction
375278 }
376279 }
377280 }
378281 }
379282
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-
395283 // Store block
396284 {
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- } ) ;
285+ let mut blocks = self . blocks . write ( ) . unwrap ( ) ;
401286 blocks. insert ( block_height, block) ;
402287 }
403288
404289 // Update chain tip
405290 {
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- } ) ;
291+ let mut height = self . height . write ( ) . unwrap ( ) ;
410292 * height = block_height;
411293 }
412294 {
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- } ) ;
295+ let mut latest_hash = self . latest_hash . write ( ) . unwrap ( ) ;
417296 * latest_hash = block_hash;
418297 }
419298
@@ -446,10 +325,7 @@ impl Blockchain {
446325 }
447326
448327 // Check nonce and balance
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- } ) ;
328+ let state = self . state . read ( ) . unwrap ( ) ;
453329 if let Some ( account) = state. get_account ( tx. from . as_bytes ( ) ) {
454330 if tx. nonce != account. nonce {
455331 return Err ( crate :: Error :: Node ( format ! (
0 commit comments