@@ -189,11 +189,9 @@ impl BlockchainNode {
189189 reveal_phase_duration : Duration :: from_secs ( 15 ) , // Total consensus: 30s per round
190190 min_participants : 4 , // PRODUCTION: 4 nodes minimum for Byzantine safety (3f+1, f=1)
191191 max_participants : 1000 , // Maximum participants per round
192- max_validators_per_round : 100 , // PRODUCTION: 100 validators per round
192+ max_validators_per_round : 1000 , // PRODUCTION: 1000 validators per round (per NETWORK_LOAD_ANALYSIS.md)
193193 enable_validator_sampling : true , // Enable sampling for scalability
194194 reputation_threshold : 0.70 , // 70% minimum reputation for participation
195- super_node_guarantee : 30 , // 30% guaranteed super nodes
196- full_node_slots : 70 , // 70% slots for full nodes
197195 } ;
198196
199197 // Create REAL Byzantine consensus engine with commit-reveal protocol
@@ -590,6 +588,9 @@ impl BlockchainNode {
590588 // Only ONE node produces microblocks per round to prevent forks
591589 // Producer selection rotates based on reputation scoring (as per QNet specification)
592590
591+ // CRITICAL: Set current block height for deterministic validator sampling
592+ std:: env:: set_var ( "CURRENT_BLOCK_HEIGHT" , microblock_height. to_string ( ) ) ;
593+
593594 // Determine current microblock producer using reputation-based rotation (with REAL node type)
594595 let current_producer = Self :: select_microblock_producer ( microblock_height, & unified_p2p, & node_id, node_type) . await ;
595596 let is_my_turn_to_produce = current_producer == node_id;
@@ -973,11 +974,14 @@ impl BlockchainNode {
973974 }
974975 } ) ;
975976
976- // Show network state while waiting
977+ // Show network state while waiting - with validator sampling
977978 if let Some ( p2p) = & unified_p2p_clone {
978- let participants = p2p. get_validated_active_peers ( ) ;
979- println ! ( "[MACROBLOCK] 🔍 Network participants: {} nodes | Waiting for consensus leader" ,
980- participants. len( ) ) ;
979+ // CRITICAL: Apply same validator sampling for macroblock consensus
980+ let sampled_validators = Self :: calculate_qualified_candidates (
981+ p2p, & node_id_clone, node_type
982+ ) . await ;
983+ println ! ( "[MACROBLOCK] 🔍 Sampled validators: {} nodes (from all qualified) | Waiting for consensus leader" ,
984+ sampled_validators. len( ) ) ;
981985 }
982986 }
983987 } ) ;
@@ -1109,6 +1113,7 @@ impl BlockchainNode {
11091113 if let Some ( p2p) = unified_p2p {
11101114 // PRODUCTION: Direct calculation for consensus determinism (THREAD-SAFE)
11111115 // QNet requires consistent candidate lists across all nodes for Byzantine safety
1116+ // CRITICAL: Now includes validator sampling for millions of nodes
11121117 let candidates = Self :: calculate_qualified_candidates ( p2p, own_node_id, own_node_type) . await ;
11131118
11141119 // DEBUG: Show candidate info to understand producer selection
@@ -1251,14 +1256,36 @@ impl BlockchainNode {
12511256 // NOTE: get_validated_active_peers() ALREADY filters out Light nodes for consensus capability
12521257 let peers = p2p. get_validated_active_peers ( ) ;
12531258 for peer in peers {
1254- let peer_node_id = format ! ( "node_{}" , peer. addr. replace( ":" , "_" ) ) ;
1259+ // CRITICAL FIX: Use same Genesis peer matching logic as in calculate_qualified_candidates
1260+ let peer_ip = peer. addr . split ( ':' ) . next ( ) . unwrap_or ( & peer. addr ) ;
1261+
1262+ let peer_node_id = if let Some ( genesis_id) = crate :: genesis_constants:: get_genesis_id_by_ip ( peer_ip) {
1263+ // This is a Genesis node - use proper Genesis node_id format
1264+ format ! ( "genesis_node_{}" , genesis_id)
1265+ } else {
1266+ // Regular node - use IP-based format
1267+ format ! ( "node_{}" , peer. addr. replace( ":" , "_" ) )
1268+ } ;
12551269
12561270 // Exclude failed producer (Light nodes already filtered by P2P layer)
12571271 if peer_node_id == failed_producer {
12581272 println ! ( "[EMERGENCY_SELECTION] 💀 Excluding failed producer {} from emergency candidates" , peer_node_id) ;
12591273 continue ;
12601274 }
12611275
1276+ // Initialize Genesis peer reputation if needed
1277+ if peer_node_id. starts_with ( "genesis_node_" ) {
1278+ let current_rep = match p2p. get_reputation_system ( ) . lock ( ) {
1279+ Ok ( reputation) => reputation. get_reputation ( & peer_node_id) ,
1280+ Err ( _) => 0.0 ,
1281+ } ;
1282+
1283+ if current_rep == 0.0 {
1284+ p2p. update_node_reputation ( & peer_node_id, 90.0 ) ;
1285+ println ! ( "[EMERGENCY_SELECTION] 🔐 Genesis peer {} initialized with 90% reputation" , peer_node_id) ;
1286+ }
1287+ }
1288+
12621289 // All peers from get_validated_active_peers() are already Full/Super nodes
12631290 let reputation = Self :: get_node_reputation_score ( & peer_node_id, p2p) . await ;
12641291 if reputation >= 0.70 {
@@ -1369,15 +1396,16 @@ impl BlockchainNode {
13691396 }
13701397 }
13711398
1372- /// PERFORMANCE: Calculate qualified candidates for caching optimization
1399+ /// PRODUCTION: Calculate qualified candidates with validator sampling for scalability
1400+ /// Implements sampling to prevent millions of validators from participating in consensus
13731401 async fn calculate_qualified_candidates (
13741402 p2p : & Arc < SimplifiedP2P > ,
13751403 own_node_id : & str ,
13761404 own_node_type : NodeType ,
13771405 ) -> Vec < ( String , f64 ) > {
1378- let mut candidates = Vec :: new ( ) ;
1406+ let mut all_qualified = Vec :: new ( ) ;
13791407
1380- println ! ( "[DEBUG] 🔍 Calculating qualified candidates:" ) ;
1408+ println ! ( "[DEBUG] 🔍 Calculating qualified candidates with sampling :" ) ;
13811409 println ! ( " ├── Own node: {} (type: {:?})" , own_node_id, own_node_type) ;
13821410
13831411 // Check own node eligibility using SAME logic as original
@@ -1402,33 +1430,140 @@ impl BlockchainNode {
14021430
14031431 if can_participate_microblock {
14041432 let own_reputation = Self :: get_node_reputation_score ( own_node_id, p2p) . await ;
1405- candidates . push ( ( own_node_id. to_string ( ) , own_reputation) ) ;
1406- println ! ( " ├── ✅ Own node added as candidate " ) ;
1433+ all_qualified . push ( ( own_node_id. to_string ( ) , own_reputation) ) ;
1434+ println ! ( " ├── ✅ Own node added as qualified " ) ;
14071435 } else {
1408- println ! ( " ├── ❌ Own node excluded from candidates " ) ;
1436+ println ! ( " ├── ❌ Own node excluded from qualified nodes " ) ;
14091437 }
14101438
14111439 // Add peer candidates (already filtered by get_validated_active_peers)
14121440 let peers = p2p. get_validated_active_peers ( ) ;
14131441 println ! ( " ├── Checking {} active peers" , peers. len( ) ) ;
14141442
14151443 for peer in peers {
1416- let peer_node_id = format ! ( "node_{}" , peer. addr. replace( ":" , "_" ) ) ;
1444+ // CRITICAL FIX: Determine correct peer node_id for Genesis nodes
1445+ // Use IP-to-Genesis mapping to get correct node_id format
1446+ let peer_ip = peer. addr . split ( ':' ) . next ( ) . unwrap_or ( & peer. addr ) ;
1447+
1448+ let peer_node_id = if let Some ( genesis_id) = crate :: genesis_constants:: get_genesis_id_by_ip ( peer_ip) {
1449+ // This is a Genesis node - use proper Genesis node_id format
1450+ format ! ( "genesis_node_{}" , genesis_id)
1451+ } else {
1452+ // Regular node - use IP-based format
1453+ format ! ( "node_{}" , peer. addr. replace( ":" , "_" ) )
1454+ } ;
1455+
1456+ // CRITICAL FIX: Initialize Genesis peer reputation if not already set
1457+ if peer_node_id. starts_with ( "genesis_node_" ) {
1458+ // Check current reputation
1459+ let current_rep = match p2p. get_reputation_system ( ) . lock ( ) {
1460+ Ok ( reputation) => reputation. get_reputation ( & peer_node_id) ,
1461+ Err ( _) => 0.0 ,
1462+ } ;
1463+
1464+ // If Genesis peer has no reputation record, initialize with 90%
1465+ if current_rep == 0.0 {
1466+ p2p. update_node_reputation ( & peer_node_id, 90.0 ) ;
1467+ println ! ( " ├── 🔐 Genesis peer {} initialized with 90% reputation" , peer_node_id) ;
1468+ }
1469+ }
1470+
14171471 let reputation = Self :: get_node_reputation_score ( & peer_node_id, p2p) . await ;
14181472
1419- // Peer reputation is now handled by get_node_reputation_score with proper thresholds
1420- println ! ( " ├── Peer {} ({}): reputation {:.1}%" , peer_node_id, peer. addr, reputation * 100.0 ) ;
1473+ println ! ( " ├── Peer {} ({}): reputation {:.1}% [{}]" ,
1474+ peer_node_id, peer. addr, reputation * 100.0 ,
1475+ if peer_node_id. starts_with( "genesis_" ) { "GENESIS" } else { "REGULAR" } ) ;
14211476
14221477 if reputation >= 0.70 {
1423- candidates . push ( ( peer_node_id. clone ( ) , reputation) ) ;
1424- println ! ( " │ └── ✅ Added as candidate " ) ;
1478+ all_qualified . push ( ( peer_node_id. clone ( ) , reputation) ) ;
1479+ println ! ( " │ └── ✅ Added as qualified " ) ;
14251480 } else {
14261481 println ! ( " │ └── ❌ Excluded (low reputation)" ) ;
14271482 }
14281483 }
14291484
1430- println ! ( " └── Total qualified candidates: {}" , candidates. len( ) ) ;
1431- candidates
1485+ println ! ( " ├── Total qualified nodes: {}" , all_qualified. len( ) ) ;
1486+
1487+ // CRITICAL: Apply validator sampling for scalability (prevent millions of validators)
1488+ // QNet configuration: 1000 validators per round for optimal Byzantine safety + performance
1489+ const MAX_VALIDATORS_PER_ROUND : usize = 1000 ; // Per NETWORK_LOAD_ANALYSIS.md specification
1490+
1491+ let sampled_candidates = if all_qualified. len ( ) <= MAX_VALIDATORS_PER_ROUND {
1492+ // Small network: Use all qualified candidates
1493+ println ! ( " ├── Small network: using all {} qualified validators" , all_qualified. len( ) ) ;
1494+ all_qualified
1495+ } else {
1496+ // Large network: Apply deterministic sampling for Byzantine consensus
1497+ println ! ( " ├── Large network: sampling {} validators from {} qualified" ,
1498+ MAX_VALIDATORS_PER_ROUND , all_qualified. len( ) ) ;
1499+
1500+ Self :: deterministic_validator_sampling ( & all_qualified, MAX_VALIDATORS_PER_ROUND ) . await
1501+ } ;
1502+
1503+ println ! ( " └── Final sampled candidates: {}" , sampled_candidates. len( ) ) ;
1504+ sampled_candidates
1505+ }
1506+
1507+ /// PRODUCTION: Simple deterministic validator sampling per QNet specification
1508+ /// Implements "Simple reputation-based selection (NO WEIGHTS)" from NETWORK_LOAD_ANALYSIS.md
1509+ /// All qualified nodes (Full + Super, reputation ≥70%) have equal chance
1510+ async fn deterministic_validator_sampling (
1511+ all_qualified : & [ ( String , f64 ) ] ,
1512+ max_count : usize ,
1513+ ) -> Vec < ( String , f64 ) > {
1514+ use sha3:: { Sha3_256 , Digest } ;
1515+ let mut selected = Vec :: new ( ) ;
1516+
1517+ if all_qualified. is_empty ( ) || max_count == 0 {
1518+ return selected;
1519+ }
1520+
1521+ // Include current block height for rotation
1522+ let current_height = std:: env:: var ( "CURRENT_BLOCK_HEIGHT" )
1523+ . unwrap_or_default ( )
1524+ . parse :: < u64 > ( )
1525+ . unwrap_or ( 0 ) ;
1526+
1527+ // QNet specification: "Equal chance for all qualified nodes"
1528+ // No distinction between Full and Super nodes in consensus participation
1529+ for i in 0 ..max_count. min ( all_qualified. len ( ) ) {
1530+ let mut hasher = Sha3_256 :: new ( ) ;
1531+
1532+ // Deterministic seed for validator sampling with rotation
1533+ hasher. update ( format ! ( "validator_sampling_{}_{}" , current_height / 30 , i) . as_bytes ( ) ) ;
1534+
1535+ // Include all qualified validators for Byzantine consistency
1536+ for ( node_id, reputation) in all_qualified {
1537+ hasher. update ( node_id. as_bytes ( ) ) ;
1538+ hasher. update ( & reputation. to_le_bytes ( ) ) ;
1539+ }
1540+
1541+ let selection_hash = hasher. finalize ( ) ;
1542+ let selection_number = u64:: from_le_bytes ( [
1543+ selection_hash[ 0 ] , selection_hash[ 1 ] , selection_hash[ 2 ] , selection_hash[ 3 ] ,
1544+ selection_hash[ 4 ] , selection_hash[ 5 ] , selection_hash[ 6 ] , selection_hash[ 7 ] ,
1545+ ] ) ;
1546+
1547+ let selection_index = ( selection_number as usize ) % all_qualified. len ( ) ;
1548+ let selected_validator = all_qualified[ selection_index] . clone ( ) ;
1549+
1550+ // Avoid duplicates
1551+ if !selected. iter ( ) . any ( |( id, _) | id == & selected_validator. 0 ) {
1552+ selected. push ( selected_validator) ;
1553+
1554+ if i < 5 || i >= max_count - 5 {
1555+ // Log first 5 and last 5 selections for debugging
1556+ println ! ( " │ Validator {}: {} (reputation: {:.1}%)" ,
1557+ i + 1 , selected. last( ) . unwrap( ) . 0 , selected. last( ) . unwrap( ) . 1 * 100.0 ) ;
1558+ } else if i == 5 {
1559+ println ! ( " │ ... (sampling {} more validators) ..." , max_count - 10 ) ;
1560+ }
1561+ }
1562+ }
1563+
1564+ println ! ( " ├── Simple sampling complete: {} validators selected from {} qualified" ,
1565+ selected. len( ) , all_qualified. len( ) ) ;
1566+ selected
14321567 }
14331568
14341569 /// CRITICAL: Emergency macroblock consensus when leader fails
0 commit comments