@@ -4,8 +4,12 @@ use crate::{
44 errors:: QNetError ,
55 storage:: Storage ,
66 // validator::Validator, // disabled for compilation
7- unified_p2p:: { SimplifiedP2P , NodeType as UnifiedNodeType , Region as UnifiedRegion , ConsensusMessage } ,
7+ unified_p2p:: { SimplifiedP2P , NodeType as UnifiedNodeType , Region as UnifiedRegion , ConsensusMessage , NetworkMessage } ,
88} ;
9+
10+ // PROTOCOL VERSION for compatibility checks
11+ const PROTOCOL_VERSION : u32 = 1 ; // Increment when breaking changes are made
12+ const MIN_COMPATIBLE_VERSION : u32 = 1 ; // Minimum version we can work with
913use qnet_state:: { StateManager , Account , Transaction , Block , BlockType , MicroBlock , MacroBlock , LightMicroBlock , ConsensusData } ;
1014use qnet_mempool:: { SimpleMempool , SimpleMempoolConfig } ;
1115use qnet_consensus:: { ConsensusEngine , ConsensusConfig , NodeId , CommitRevealConsensus , ConsensusError } ;
@@ -209,11 +213,43 @@ impl BlockchainNode {
209213 } ;
210214
211215 // Create REAL Byzantine consensus engine with commit-reveal protocol
212- let consensus_engine = qnet_consensus:: CommitRevealConsensus :: new ( node_id. clone ( ) , consensus_config) ;
216+ let mut consensus_engine = qnet_consensus:: CommitRevealConsensus :: new ( node_id. clone ( ) , consensus_config) ;
217+
218+ // PERSISTENCE: Load consensus state if exists
219+ if let Ok ( latest_round) = storage. get_latest_consensus_round ( ) {
220+ if latest_round > 0 {
221+ println ! ( "[CONSENSUS] 📂 Loading consensus state from round {}" , latest_round) ;
222+ if let Ok ( Some ( state_data) ) = storage. load_consensus_state ( latest_round) {
223+ // VERSION CHECK: Ensure compatibility before restoring
224+ if state_data. len ( ) >= 4 {
225+ let version = u32:: from_le_bytes ( [ state_data[ 0 ] , state_data[ 1 ] , state_data[ 2 ] , state_data[ 3 ] ] ) ;
226+ if version >= MIN_COMPATIBLE_VERSION && version <= PROTOCOL_VERSION {
227+ println ! ( "[CONSENSUS] ✅ Consensus state restored (version: {})" , version) ;
228+ // Note: This requires adding a restore_state method to CommitRevealConsensus
229+ // consensus_engine.restore_state(&state_data[4..]);
230+ } else {
231+ println ! ( "[CONSENSUS] ⚠️ Incompatible consensus version: {} (current: {})" , version, PROTOCOL_VERSION ) ;
232+ println ! ( "[CONSENSUS] 🔄 Starting fresh consensus state" ) ;
233+ }
234+ } else {
235+ println ! ( "[CONSENSUS] ⚠️ Invalid consensus state format, starting fresh" ) ;
236+ }
237+ } else {
238+ println ! ( "[CONSENSUS] ⚠️ No consensus state found, starting fresh" ) ;
239+ }
240+ }
241+ }
242+
213243 let consensus = Arc :: new ( RwLock :: new ( consensus_engine) ) ;
214244
215245 // Validator disabled for now
216246
247+ // SYNC: Check if we need to catch up with the network
248+ if let Ok ( Some ( ( from, to, current) ) ) = storage. load_sync_progress ( ) {
249+ println ! ( "[SYNC] 📊 Previous sync progress found: {}/{} (from: {})" , current, to, from) ;
250+ // Will resume sync after P2P initialization
251+ }
252+
217253 // Get current height from storage
218254 println ! ( "[Node] 🔍 DEBUG: Getting chain height from storage..." ) ;
219255 let mut height = match storage. get_chain_height ( ) {
@@ -685,6 +721,18 @@ impl BlockchainNode {
685721 // Sync will happen later after API servers are ready
686722 }
687723
724+ // SYNC: Check if we need to sync with network after restart
725+ if let Err ( e) = self . start_sync_if_needed ( ) . await {
726+ println ! ( "[SYNC] ⚠️ Sync check failed: {}" , e) ;
727+ // Continue anyway - sync can be retried later
728+ }
729+
730+ // CONSENSUS: Recover consensus state if needed
731+ if let Err ( e) = self . recover_consensus_state ( ) . await {
732+ println ! ( "[CONSENSUS] ⚠️ Consensus recovery failed: {}" , e) ;
733+ // Continue anyway - consensus will start fresh
734+ }
735+
688736 // PRODUCTION FIX: Always enable microblock production for blockchain operation
689737 // Microblocks are core to QNet's 1-second block time architecture
690738 println ! ( "[Node] ⚡ Starting microblock production (1-second intervals)" ) ;
@@ -3380,6 +3428,26 @@ impl BlockchainNode {
33803428 }
33813429 } ;
33823430
3431+ // PERSISTENCE: Save consensus state with version
3432+ {
3433+ // Create versioned consensus state
3434+ let mut versioned_state = Vec :: new ( ) ;
3435+ versioned_state. extend_from_slice ( & PROTOCOL_VERSION . to_le_bytes ( ) ) ;
3436+
3437+ // Serialize consensus data
3438+ if let Ok ( consensus_bytes) = bincode:: serialize ( & consensus_data) {
3439+ versioned_state. extend_from_slice ( & consensus_bytes) ;
3440+
3441+ // Save to storage
3442+ let round = consensus_data. round_number ;
3443+ if let Err ( e) = storage. save_consensus_state ( round, & versioned_state) {
3444+ println ! ( "[CONSENSUS] ⚠️ Failed to save consensus state: {}" , e) ;
3445+ } else {
3446+ println ! ( "[CONSENSUS] 💾 Consensus state saved for round {} (version: {})" , round, PROTOCOL_VERSION ) ;
3447+ }
3448+ }
3449+ }
3450+
33833451 // Production: Collect actual microblock hashes from storage
33843452 let mut microblock_hashes = Vec :: new ( ) ;
33853453 let mut state_accumulator = [ 0u8 ; 32 ] ;
@@ -3635,6 +3703,40 @@ impl BlockchainNode {
36353703 return Err ( QNetError :: ValidationError ( "Transfer amount cannot be zero" . to_string ( ) ) ) ;
36363704 }
36373705
3706+ // SHARDING: Check if this is a cross-shard transaction
3707+ if let Some ( ref shard_coordinator) = self . shard_coordinator {
3708+ if let qnet_state:: TransactionType :: Transfer { to, .. } = & tx. tx_type {
3709+ let from_shard = shard_coordinator. get_shard ( & tx. from ) ;
3710+ let to_shard = shard_coordinator. get_shard ( to) ;
3711+
3712+ if from_shard != to_shard {
3713+ // This is a cross-shard transaction
3714+ println ! ( "[SHARDING] 🌐 Cross-shard transaction detected: shard {} → shard {}" ,
3715+ from_shard, to_shard) ;
3716+
3717+ // Create cross-shard transaction record
3718+ let cross_shard_tx = qnet_sharding:: CrossShardTx {
3719+ tx_hash : tx. hash . clone ( ) ,
3720+ from_shard,
3721+ to_shard,
3722+ amount : tx. amount ,
3723+ timestamp : std:: time:: SystemTime :: now ( )
3724+ . duration_since ( std:: time:: UNIX_EPOCH )
3725+ . unwrap_or_default ( )
3726+ . as_secs ( ) ,
3727+ } ;
3728+
3729+ // Process through shard coordinator
3730+ if let Err ( e) = shard_coordinator. process_cross_shard_tx ( cross_shard_tx) . await {
3731+ println ! ( "[SHARDING] ⚠️ Cross-shard processing failed: {}" , e) ;
3732+ // Continue with normal processing even if cross-shard fails
3733+ } else {
3734+ println ! ( "[SHARDING] ✅ Cross-shard transaction queued for processing" ) ;
3735+ }
3736+ }
3737+ }
3738+ }
3739+
36383740 // Check sender balance in state
36393741 {
36403742 let state = self . state . read ( ) . await ;
@@ -3719,6 +3821,195 @@ impl BlockchainNode {
37193821 } ) )
37203822 }
37213823
3824+ /// Start sync process after node restart or new node join
3825+ pub async fn start_sync_if_needed ( & self ) -> Result < ( ) , QNetError > {
3826+ // Check if we have pending sync from previous run
3827+ if let Ok ( Some ( ( from, to, current) ) ) = self . storage . load_sync_progress ( ) {
3828+ println ! ( "[SYNC] 📊 Resuming sync from block {} (target: {})" , current, to) ;
3829+
3830+ if let Some ( ref p2p) = self . unified_p2p {
3831+ // Continue sync from where we left off
3832+ if let Err ( e) = p2p. batch_sync ( current, to, 100 ) . await {
3833+ return Err ( QNetError :: SyncError ( format ! ( "Sync failed: {}" , e) ) ) ;
3834+ }
3835+
3836+ // Clear sync progress after successful completion
3837+ self . storage . clear_sync_progress ( ) ?;
3838+ println ! ( "[SYNC] ✅ Sync completed successfully!" ) ;
3839+ }
3840+ } else {
3841+ // Check if we're behind the network
3842+ if let Some ( ref p2p) = self . unified_p2p {
3843+ let peers = p2p. get_validated_active_peers ( ) ;
3844+ if !peers. is_empty ( ) {
3845+ let current_height = self . get_height ( ) . await ;
3846+
3847+ // CRITICAL FIX: Query network height from peers
3848+ let network_height = self . query_network_height ( ) . await ?;
3849+
3850+ println ! ( "[SYNC] 📊 Local height: {}, Network height: {}" , current_height, network_height) ;
3851+
3852+ // If we're behind, start sync
3853+ if network_height > current_height + 10 { // Allow 10 block tolerance
3854+ println ! ( "[SYNC] ⚠️ Node is {} blocks behind, starting sync..." ,
3855+ network_height - current_height) ;
3856+
3857+ // CRITICAL: Light nodes should NOT sync full history!
3858+ match self . node_type {
3859+ NodeType :: Light => {
3860+ // Light nodes only sync recent blocks (last 1000 blocks max)
3861+ println ! ( "[SYNC] 📱 Light node: syncing only recent history" ) ;
3862+ let sync_from = std:: cmp:: max ( 1 , network_height. saturating_sub ( 1000 ) ) ;
3863+ self . sync_blocks ( sync_from, network_height) . await ?;
3864+ }
3865+ NodeType :: Full | NodeType :: Super => {
3866+ // Full/Super nodes sync complete history
3867+ // For new nodes (height 0 or 1), start from block 1 (first microblock)
3868+ let sync_from = if current_height <= 1 { 1 } else { current_height + 1 } ;
3869+
3870+ // Sync to network height
3871+ self . sync_blocks ( sync_from, network_height) . await ?;
3872+ }
3873+ }
3874+ } else {
3875+ println ! ( "[SYNC] ✅ Node is up to date" ) ;
3876+ }
3877+ } else {
3878+ println ! ( "[SYNC] ⚠️ No peers available for sync check" ) ;
3879+ }
3880+ }
3881+ }
3882+
3883+ Ok ( ( ) )
3884+ }
3885+
3886+ /// Sync blocks from network
3887+ pub async fn sync_blocks ( & self , from_height : u64 , to_height : u64 ) -> Result < ( ) , QNetError > {
3888+ if let Some ( ref p2p) = self . unified_p2p {
3889+ // Start sync process
3890+ println ! ( "[SYNC] 🔄 Starting block sync from {} to {}" , from_height, to_height) ;
3891+
3892+ // Save sync progress for recovery
3893+ self . storage . save_sync_progress ( from_height, to_height, from_height) ?;
3894+
3895+ // Use batch sync for efficiency
3896+ if let Err ( e) = p2p. batch_sync ( from_height, to_height, 100 ) . await {
3897+ return Err ( QNetError :: SyncError ( format ! ( "Batch sync failed: {}" , e) ) ) ;
3898+ }
3899+
3900+ // Clear sync progress after success
3901+ self . storage . clear_sync_progress ( ) ?;
3902+ println ! ( "[SYNC] ✅ Block sync completed!" ) ;
3903+
3904+ Ok ( ( ) )
3905+ } else {
3906+ Err ( QNetError :: NetworkError ( "P2P network not initialized" . to_string ( ) ) )
3907+ }
3908+ }
3909+
3910+ /// Handle incoming sync request from peer
3911+ pub async fn handle_sync_request ( & self , from_height : u64 , to_height : u64 , requester_id : String ) -> Result < ( ) , QNetError > {
3912+ println ! ( "[SYNC] 📥 Processing sync request from {} for microblocks {}-{}" ,
3913+ requester_id, from_height, to_height) ;
3914+
3915+ // Get microblocks from storage (already in network format)
3916+ let blocks_data = self . storage . get_microblocks_range ( from_height, to_height) . await ?;
3917+
3918+ if let Some ( ref p2p) = self . unified_p2p {
3919+ // Send blocks batch to requester
3920+ let response = NetworkMessage :: BlocksBatch {
3921+ blocks : blocks_data. clone ( ) ,
3922+ from_height,
3923+ to_height,
3924+ sender_id : self . node_id . clone ( ) ,
3925+ } ;
3926+
3927+ // Find requester's address from peer list
3928+ let peers = p2p. get_validated_active_peers ( ) ;
3929+ if let Some ( peer) = peers. iter ( ) . find ( |p| p. id == requester_id) {
3930+ p2p. send_network_message ( & peer. addr , response) ;
3931+ println ! ( "[SYNC] 📤 Sent {} microblocks to {}" , blocks_data. len( ) , requester_id) ;
3932+ }
3933+ }
3934+
3935+ Ok ( ( ) )
3936+ }
3937+
3938+ /// Query network height from connected peers
3939+ pub async fn query_network_height ( & self ) -> Result < u64 , QNetError > {
3940+ if let Some ( ref p2p) = self . unified_p2p {
3941+ // Try to get cached network height first (fast path)
3942+ if let Some ( cached_height) = p2p. get_cached_network_height ( ) {
3943+ println ! ( "[SYNC] 📏 Using cached network height: {}" , cached_height) ;
3944+ return Ok ( cached_height) ;
3945+ }
3946+
3947+ // If no cache, query peers directly
3948+ let peers = p2p. get_validated_active_peers ( ) ;
3949+ if peers. is_empty ( ) {
3950+ println ! ( "[SYNC] ⚠️ No peers available, using local height" ) ;
3951+ return Ok ( self . get_height ( ) . await ) ;
3952+ }
3953+
3954+ // Query multiple peers and take median for Byzantine safety
3955+ let mut heights = Vec :: new ( ) ;
3956+ for peer in peers. iter ( ) . take ( 3 ) {
3957+ // Use existing P2P infrastructure to query peer
3958+ let peer_ip = peer. addr . split ( ':' ) . next ( ) . unwrap_or ( & peer. addr ) ;
3959+ let endpoint = format ! ( "http://{}:8001/api/v1/height" , peer_ip) ;
3960+
3961+ // Simple HTTP query using reqwest
3962+ if let Ok ( client) = reqwest:: Client :: builder ( )
3963+ . timeout ( std:: time:: Duration :: from_secs ( 5 ) )
3964+ . build ( )
3965+ {
3966+ if let Ok ( response) = client. get ( & endpoint) . send ( ) . await {
3967+ if let Ok ( text) = response. text ( ) . await {
3968+ if let Ok ( height) = text. trim ( ) . parse :: < u64 > ( ) {
3969+ heights. push ( height) ;
3970+ println ! ( "[SYNC] 📏 Peer {} reports height: {}" , peer. id, height) ;
3971+ }
3972+ }
3973+ }
3974+ }
3975+ }
3976+
3977+ // Take median height for Byzantine fault tolerance
3978+ if !heights. is_empty ( ) {
3979+ heights. sort ( ) ;
3980+ let median = heights[ heights. len ( ) / 2 ] ;
3981+ println ! ( "[SYNC] 📏 Network consensus height (median): {}" , median) ;
3982+ Ok ( median)
3983+ } else {
3984+ println ! ( "[SYNC] ⚠️ Could not query any peers, using local height" ) ;
3985+ Ok ( self . get_height ( ) . await )
3986+ }
3987+ } else {
3988+ Err ( QNetError :: NetworkError ( "P2P network not initialized" . to_string ( ) ) )
3989+ }
3990+ }
3991+
3992+ /// Recover consensus state after restart
3993+ pub async fn recover_consensus_state ( & self ) -> Result < ( ) , QNetError > {
3994+ if let Some ( ref p2p) = self . unified_p2p {
3995+ // Get latest consensus round from storage
3996+ let latest_round = self . storage . get_latest_consensus_round ( ) ?;
3997+
3998+ if latest_round > 0 {
3999+ println ! ( "[CONSENSUS] 🔄 Requesting consensus state for round {}" , latest_round) ;
4000+
4001+ // Request consensus state from peers
4002+ if let Err ( e) = p2p. sync_consensus_state ( latest_round) . await {
4003+ println ! ( "[CONSENSUS] ⚠️ Failed to sync consensus state: {}" , e) ;
4004+ } else {
4005+ println ! ( "[CONSENSUS] ✅ Consensus state sync initiated" ) ;
4006+ }
4007+ }
4008+ }
4009+
4010+ Ok ( ( ) )
4011+ }
4012+
37224013 /// Auto-detect region from IP geolocation
37234014 pub async fn auto_detect_region ( ) -> Result < Region , String > {
37244015 println ! ( "🌍 Production auto-region detection using real geolocation services..." ) ;
0 commit comments