Skip to content

Commit a01530f

Browse files
committed
Fix producer rotation issues at block boundaries
- Fixed emergency producer height check (was checking microblock_height instead of next_block_height) - Filter offline nodes from Genesis candidate list to prevent selecting unavailable producers - Reduced rotation timeout from 30s to 10s for faster failover - Maintain deterministic consensus while excluding inactive nodes - Ensure Byzantine fault tolerance with proper active node detection This fixes the network stall issue at block #30 where offline genesis_node_004 was being selected as producer
1 parent d24ad27 commit a01530f

1 file changed

Lines changed: 24 additions & 9 deletions

File tree

  • development/qnet-integration/src

development/qnet-integration/src/node.rs

Lines changed: 24 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -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

Comments
 (0)