@@ -3553,15 +3553,9 @@ impl BlockchainNode {
35533553 false
35543554 };
35553555
3556- // Clear emergency flag if we were emergency producer
3557- if let Ok(mut emergency_flag) = EMERGENCY_PRODUCER_FLAG.lock() {
3558- if let Some((height, _)) = &*emergency_flag {
3559- if *height == next_block_height {
3560- println!("[EMERGENCY] ✅ Clearing emergency flag after entering production");
3561- *emergency_flag = None;
3562- }
3563- }
3564- }
3556+ // CRITICAL FIX: DO NOT clear emergency flag here - it causes deadlock!
3557+ // Flag will be cleared AFTER block is successfully created and saved
3558+ // This prevents the node from forgetting it's emergency producer in next iteration
35653559
35663560 // CRITICAL FIX: Emergency producer ALWAYS can produce
35673561 // Skip all checks if we're emergency producer to break deadlock
@@ -3869,8 +3863,30 @@ impl BlockchainNode {
38693863 // CRITICAL FIX: Get PoH state from PREVIOUS BLOCK for consistency
38703864 // This ensures all nodes use the same PoH baseline regardless of local state
38713865 let (poh_hash, poh_count) = if next_block_height > 1 {
3866+ // CRITICAL: At rotation boundaries, wait for previous block if needed
3867+ // This prevents PoH regression when producer changes
3868+ let is_rotation_start = next_block_height > 1 && ((next_block_height - 1) % 30) == 0;
3869+
3870+ // Try to load previous block with retry for rotation boundaries
3871+ let mut prev_block_result = storage.load_microblock(next_block_height - 1);
3872+
3873+ // Retry mechanism for rotation boundaries ONLY
3874+ if is_rotation_start && prev_block_result.as_ref().map(|r| r.is_none()).unwrap_or(false) {
3875+ println!("[PoH] 🔄 Rotation boundary: waiting for previous block #{}", next_block_height - 1);
3876+
3877+ // Try up to 3 times with 500ms delay
3878+ for retry in 1..=3 {
3879+ tokio::time::sleep(Duration::from_millis(500)).await;
3880+ prev_block_result = storage.load_microblock(next_block_height - 1);
3881+ if prev_block_result.as_ref().map(|r| r.is_some()).unwrap_or(false) {
3882+ println!("[PoH] ✅ Previous block received after {} retries", retry);
3883+ break;
3884+ }
3885+ }
3886+ }
3887+
38723888 // Load previous block to get its PoH state
3873- match storage.load_microblock(next_block_height - 1) {
3889+ match prev_block_result {
38743890 Ok(Some(prev_block_data)) => {
38753891 match bincode::deserialize::<qnet_state::MicroBlock>(&prev_block_data) {
38763892 Ok(prev_block) => {
@@ -3880,26 +3896,29 @@ impl BlockchainNode {
38803896 (prev_block.poh_hash.clone(), prev_block.poh_count)
38813897 },
38823898 Err(e) => {
3883- println!("[PoH] ⚠️ Cannot deserialize previous block: {}", e);
3884- // Fallback to local PoH if available
3885- if let Some(ref poh) = quantum_poh {
3886- let (hash, count, _slot) = poh.get_state().await;
3887- (hash, count)
3888- } else {
3889- (vec![0u8; 64], 0u64)
3890- }
3899+ println!("[PoH] ❌ Cannot deserialize previous block #{}: {}", next_block_height - 1, e);
3900+ println!("[PoH] 🔄 Corrupted block data - waiting for re-sync");
3901+
3902+ // CRITICAL FIX: DO NOT use local PoH as fallback - it causes regression!
3903+ // If block data is corrupted, we must wait for re-sync from network
3904+ // This prevents PoH regression attacks and maintains Byzantine safety
3905+
3906+ tokio::time::sleep(Duration::from_millis(500)).await;
3907+ continue; // Skip block creation - wait for valid previous block
38913908 }
38923909 }
38933910 },
38943911 _ => {
3895- println!("[PoH] ⚠️ Previous block #{} not found, using local PoH", next_block_height - 1);
3896- // Fallback to local PoH
3897- if let Some(ref poh) = quantum_poh {
3898- let (hash, count, _slot) = poh.get_state().await;
3899- (hash, count)
3900- } else {
3901- (vec![0u8; 64], 0u64)
3902- }
3912+ println!("[PoH] ❌ Previous block #{} not found - CANNOT CREATE BLOCK", next_block_height - 1);
3913+ println!("[PoH] 🔄 Waiting for previous block to maintain PoH continuity");
3914+
3915+ // CRITICAL FIX: DO NOT use local PoH as fallback - it causes regression!
3916+ // Producer MUST wait for previous block to maintain chain integrity
3917+ // This prevents PoH regression attacks and maintains Byzantine safety
3918+
3919+ // Skip this iteration and wait for sync
3920+ tokio::time::sleep(Duration::from_millis(500)).await;
3921+ continue; // Skip block creation - wait for previous block
39033922 }
39043923 }
39053924 } else {
@@ -4140,6 +4159,19 @@ impl BlockchainNode {
41404159 // This prevents phantom height where node claims height N without having block N
41414160 println!("[PRODUCER] ✅ Created and saved block #{}", microblock.height);
41424161
4162+ // CRITICAL FIX: Clear emergency flag AFTER successful block creation
4163+ // This prevents deadlock where node forgets it's emergency producer
4164+ if is_emergency_producer {
4165+ if let Ok(mut emergency_flag) = EMERGENCY_PRODUCER_FLAG.lock() {
4166+ if let Some((height, _)) = &*emergency_flag {
4167+ if *height == microblock.height {
4168+ println!("[EMERGENCY] ✅ Clearing emergency flag after successful block #{} creation", microblock.height);
4169+ *emergency_flag = None;
4170+ }
4171+ }
4172+ }
4173+ }
4174+
41434175 // CRITICAL FIX: Update global last block time for stall detection
41444176 LAST_BLOCK_PRODUCED_TIME.store(get_timestamp_safe(), Ordering::Relaxed);
41454177 LAST_BLOCK_PRODUCED_HEIGHT.store(microblock.height, Ordering::Relaxed);
@@ -6660,6 +6692,8 @@ impl BlockchainNode {
66606692 }
66616693
66626694 println!("[CONSENSUS] ✅ Executing commit phase for MACROBLOCK round {}", round_id);
6695+ println!("[CONSENSUS] 🔍 Round ID check: {} % 90 = {} (should be 0 for macroblock)",
6696+ round_id, round_id % 90);
66636697 use qnet_consensus::{commit_reveal::Commit, ConsensusError};
66646698 use sha3::{Sha3_256, Digest};
66656699
@@ -6720,14 +6754,22 @@ impl BlockchainNode {
67206754
67216755 // PRODUCTION: Broadcast OWN commit to P2P network for other nodes
67226756 if let Some(p2p) = unified_p2p {
6723- let _ = p2p.broadcast_consensus_commit(
6757+ match p2p.broadcast_consensus_commit(
67246758 round_id,
67256759 our_id.clone(),
67266760 commit.commit_hash.clone(),
67276761 commit.signature.clone(), // CONSENSUS FIX: Pass signature for Byzantine validation
67286762 commit.timestamp
6729- );
6730- println!("[CONSENSUS] 📤 Broadcasted OWN commit to {} peers", participants.len() - 1);
6763+ ) {
6764+ Ok(_) => {
6765+ println!("[CONSENSUS] 📤 Successfully broadcasted OWN commit to peers");
6766+ }
6767+ Err(e) => {
6768+ println!("[CONSENSUS] ⚠️ Failed to broadcast commit: {}", e);
6769+ println!("[CONSENSUS] 🔍 Round ID: {}, Expected macroblock: {}",
6770+ round_id, round_id % 90 == 0);
6771+ }
6772+ }
67316773 }
67326774 }
67336775 Err(ConsensusError::InvalidSignature(msg)) => {
@@ -6832,6 +6874,8 @@ impl BlockchainNode {
68326874 }
68336875
68346876 println!("[CONSENSUS] ✅ Executing reveal phase for MACROBLOCK round {}", round_id);
6877+ println!("[CONSENSUS] 🔍 Round ID check: {} % 90 = {} (should be 0 for macroblock)",
6878+ round_id, round_id % 90);
68356879 use qnet_consensus::commit_reveal::Reveal;
68366880 use sha3::{Sha3_256, Digest};
68376881
@@ -6876,14 +6920,22 @@ impl BlockchainNode {
68766920
68776921 // PRODUCTION: Broadcast OWN reveal to P2P network for other nodes
68786922 if let Some(p2p) = unified_p2p {
6879- let _ = p2p.broadcast_consensus_reveal(
6923+ match p2p.broadcast_consensus_reveal(
68806924 round_id,
68816925 our_id.clone(),
68826926 hex::encode(&reveal.reveal_data), // Convert Vec<u8> to String
68836927 hex::encode(&reveal.nonce), // CRITICAL: Include nonce for verification
68846928 reveal.timestamp
6885- );
6886- println!("[CONSENSUS] 📤 Broadcasted OWN reveal with nonce to {} peers", participants.len() - 1);
6929+ ) {
6930+ Ok(_) => {
6931+ println!("[CONSENSUS] 📤 Successfully broadcasted OWN reveal with nonce to peers");
6932+ }
6933+ Err(e) => {
6934+ println!("[CONSENSUS] ⚠️ Failed to broadcast reveal: {}", e);
6935+ println!("[CONSENSUS] 🔍 Round ID: {}, Expected macroblock: {}",
6936+ round_id, round_id % 90 == 0);
6937+ }
6938+ }
68876939 }
68886940 }
68896941 Err(e) => {
@@ -7598,10 +7650,15 @@ impl BlockchainNode {
75987650 let consensus_nonce_storage = std::sync::Arc::new(tokio::sync::RwLock::new(std::collections::HashMap::new()));
75997651 let unified_p2p_option = Some(p2p.clone()); // Pass REAL P2P system
76007652
7653+ // CRITICAL: Use macroblock height for P2P broadcast, not consensus round number!
7654+ // P2P expects height (90, 180, 270) to identify macroblock rounds
7655+ let macroblock_height = round_id; // round_id IS the end_height (90, 180, 270)
7656+ println!("[CONSENSUS] 🎯 Using macroblock height {} for P2P broadcast", macroblock_height);
7657+
76017658 Self::execute_real_commit_phase(
76027659 &mut consensus_engine,
76037660 &all_participants,
7604- round_id,
7661+ macroblock_height, // Pass height for P2P broadcast
76057662 &unified_p2p_option,
76067663 &consensus_nonce_storage,
76077664 node_id, // Pass the validated node_id
@@ -7613,7 +7670,7 @@ impl BlockchainNode {
76137670 Self::execute_real_reveal_phase(
76147671 &mut consensus_engine,
76157672 &all_participants,
7616- round_id,
7673+ macroblock_height, // Use same height as commit phase
76177674 &unified_p2p_option,
76187675 &consensus_nonce_storage,
76197676 node_id, // Pass the validated node_id
0 commit comments