@@ -3526,8 +3526,8 @@ impl BlockchainNode {
35263526 // CRITICAL: Check if we became emergency producer
35273527 let should_produce_emergency = if let Ok ( emergency_flag) = EMERGENCY_PRODUCER_FLAG . lock ( ) {
35283528 if let Some ( ( height, producer) ) = & * emergency_flag {
3529- // FIX: Emergency producer should produce CURRENT height, not +1
3530- if * height == microblock_height && * producer == node_id {
3529+ // FIX: Check against next_block_height since emergency is set for the block we're waiting for
3530+ if * height == next_block_height && * producer == node_id {
35313531 println ! ( "[EMERGENCY] 🚨 WE ARE EMERGENCY PRODUCER FOR BLOCK #{}" , height) ;
35323532 true
35333533 } else {
@@ -3693,7 +3693,7 @@ impl BlockchainNode {
36933693 let is_rotation_boundary = expected_height_timeout > 0 && ( expected_height_timeout % 30 ) == 0 ;
36943694 let rotation_timeout = if is_rotation_boundary {
36953695 // Double timeout at rotation boundaries to account for producer switch
3696- Duration :: from_secs ( 30 )
3696+ Duration :: from_secs ( 10 )
36973697 } else {
36983698 microblock_timeout
36993699 } ;
@@ -4880,19 +4880,34 @@ impl BlockchainNode {
48804880 // Do NOT filter by connectivity - this causes different candidate lists on different nodes
48814881 // Instead, all 5 Genesis nodes are ALWAYS candidates (deterministic consensus)
48824882
4883- // CONSENSUS FIX: Use DETERMINISTIC list of ALL Genesis nodes (not just connected)
4884- // This ensures all nodes have IDENTICAL candidate lists for consistent producer selection
4883+ // CONSENSUS FIX: Use DETERMINISTIC list but ONLY include ACTIVE nodes
4884+ // This ensures all nodes have IDENTICAL candidate lists while excluding offline nodes
48854885
4886- // Add Genesis nodes that meet reputation threshold (deterministic order maintained)
4886+ // Get list of actually connected peers for activity check
4887+ let validated_peers = p2p. get_validated_active_peers ( ) ;
4888+ let active_peer_ids: std:: collections:: HashSet < String > = validated_peers
4889+ . iter ( )
4890+ . map ( |p| p. id . clone ( ) )
4891+ . collect ( ) ;
4892+
4893+ // Add Genesis nodes that meet reputation threshold AND are active (deterministic order maintained)
48874894 for ( node_id, _ip) in & static_genesis_nodes {
48884895 // CRITICAL FIX: Check REAL reputation from P2P system, not static value
48894896 // This ensures failed/inactive nodes are excluded from candidates
48904897 let real_reputation = Self :: get_node_reputation_score ( node_id, p2p) . await ;
48914898
4892- if real_reputation >= 0.70 {
4893- // Node meets consensus threshold - add as candidate
4899+ // Check if node is active (connected to network OR is self)
4900+ let is_active = active_peer_ids. contains ( node_id) ||
4901+ ( node_id == own_node_id && is_own_genesis) ;
4902+
4903+ if real_reputation >= 0.70 && is_active {
4904+ // Node meets consensus threshold AND is active - add as candidate
48944905 all_qualified. push ( ( node_id. clone ( ) , real_reputation) ) ;
4895- println ! ( "[GENESIS] ✅ {} qualified with reputation {:.1}%" , node_id, real_reputation * 100.0 ) ;
4906+ println ! ( "[GENESIS] ✅ {} qualified with reputation {:.1}% (ACTIVE)" , node_id, real_reputation * 100.0 ) ;
4907+ } else if real_reputation >= 0.70 && !is_active {
4908+ // Node has reputation but is OFFLINE - exclude
4909+ println ! ( "[GENESIS] ❌ {} excluded - OFFLINE (reputation {:.1}%)" ,
4910+ node_id, real_reputation * 100.0 ) ;
48964911 } else {
48974912 // Node below threshold - exclude from candidates
48984913 println ! ( "[GENESIS] ⚠️ {} excluded - reputation {:.1}% < 70% threshold" ,
0 commit comments