@@ -915,9 +915,9 @@ async fn handle_block_event(
915915 // We must merge weights by mechanism before submitting
916916 let challenges = cached_challenges. read ( ) . clone ( ) ;
917917 let mechanism_weights = if !challenges. is_empty ( ) {
918- // Collect weights per mechanism: HashMap<mechanism_id, HashMap<uid, weight_u32>>
919- // Using u32 for intermediate accumulation to avoid overflow
920- let mut mechanism_uid_weights: HashMap < u8 , HashMap < u16 , u32 > > = HashMap :: new ( ) ;
918+ // Collect weights per mechanism using f64 for accurate accumulation
919+ // HashMap<mechanism_id, HashMap<uid, weight_f64>>
920+ let mut mechanism_uid_weights: HashMap < u8 , HashMap < u16 , f64 > > = HashMap :: new ( ) ;
921921
922922 for challenge in challenges. iter ( ) {
923923 // Skip unhealthy challenges - don't send burn, just skip
@@ -930,118 +930,138 @@ async fn handle_block_event(
930930 continue ;
931931 }
932932
933+ let mech_id = challenge. mechanism_id as u8 ;
934+ let emission_weight = challenge. emission_weight . clamp ( 0.0 , 1.0 ) ;
935+
933936 match platform_client. get_weights ( & challenge. id , epoch) . await {
934937 Ok ( w) if !w. is_empty ( ) => {
935- // Get challenge emission weight (0.0-1.0)
936- let emission_weight = challenge. emission_weight . clamp ( 0.0 , 1.0 ) ;
937- let mech_id = challenge. mechanism_id as u8 ;
938-
939938 // Get or create the UID->weight map for this mechanism
940939 let uid_weights = mechanism_uid_weights. entry ( mech_id) . or_default ( ) ;
941940
942941 // Convert hotkeys to UIDs using metagraph
943942 let client_guard = client. read ( ) ;
944- let mut total_weight: f64 = 0.0 ;
943+ let mut resolved_count = 0usize ;
944+ let mut unresolved_count = 0usize ;
945945
946946 for ( hotkey, weight_f64) in & w {
947- // Apply emission_weight: scale the challenge weight
947+ // Scale by emission_weight: this challenge's share
948948 let scaled_weight = weight_f64 * emission_weight;
949949
950950 if let Some ( uid) = client_guard. get_uid_for_hotkey ( hotkey) {
951- // Convert f64 weight (0.0-1.0) to u16 (0-65535)
952- let weight_u16 = ( scaled_weight * 65535.0 ) . round ( ) as u16 ;
953- // Accumulate weight for this UID
954- * uid_weights. entry ( uid) . or_insert ( 0 ) += weight_u16 as u32 ;
955- total_weight += scaled_weight;
951+ // Accumulate f64 weight for this UID
952+ * uid_weights. entry ( uid) . or_insert ( 0.0 ) += scaled_weight;
953+ resolved_count += 1 ;
956954 info ! (
957- " [{}] {} -> UID {} (weight: {:.4} * {:.2} = {:.4} = {} )" ,
955+ " [{}] {} -> UID {} (weight: {:.4} * {:.2} = {:.4})" ,
958956 challenge. id,
959957 & hotkey[ ..16 ] ,
960958 uid,
961959 weight_f64,
962960 emission_weight,
963- scaled_weight,
964- weight_u16
961+ scaled_weight
965962 ) ;
966963 } else {
967- // Hotkey not in metagraph - this weight goes to burn
964+ // Hotkey not in metagraph - add to burn (UID 0)
965+ * uid_weights. entry ( 0 ) . or_insert ( 0.0 ) += scaled_weight;
966+ unresolved_count += 1 ;
968967 warn ! (
969- "Hotkey {} not found in metagraph, weight {:.4} goes to burn" ,
968+ " [{}] {} not in metagraph -> UID 0 (burn: {:.4})" ,
969+ challenge. id,
970970 & hotkey[ ..16 ] ,
971971 scaled_weight
972972 ) ;
973- // Don't add to total_weight - it will be added to burn below
974973 }
975974 }
976975 drop ( client_guard) ;
977976
978- // Add remaining weight to burn (UID 0)
979- // This includes: (1 - emission_weight) + any unresolved hotkey weights
980- let burn_weight = 1.0 - total_weight;
981- if burn_weight > 0.001 {
982- let burn_u16 = ( burn_weight * 65535.0 ) . round ( ) as u16 ;
983- * uid_weights. entry ( 0 ) . or_insert ( 0 ) += burn_u16 as u32 ;
977+ if unresolved_count > 0 {
978+ warn ! (
979+ "Challenge {}: {} hotkeys resolved, {} unresolved (sent to burn)" ,
980+ challenge. id, resolved_count, unresolved_count
981+ ) ;
982+ }
983+
984+ // Add (1 - sum_of_weights) * emission_weight to burn
985+ // This handles the case where challenge weights don't sum to 1.0
986+ let weights_sum: f64 = w. iter ( ) . map ( |( _, w) | w) . sum ( ) ;
987+ let unallocated = ( 1.0 - weights_sum. min ( 1.0 ) ) * emission_weight;
988+ if unallocated > 0.001 {
989+ * uid_weights. entry ( 0 ) . or_insert ( 0.0 ) += unallocated;
984990 info ! (
985- " [{}] Burn ( UID 0) : {:.4} = {} " ,
986- challenge. id, burn_weight , burn_u16
991+ " [{}] Unallocated -> UID 0 (burn : {:.4}) " ,
992+ challenge. id, unallocated
987993 ) ;
988994 }
989995
990996 info ! (
991- "Challenge {} (mech {}, emission_weight ={:.2}): collected weights" ,
992- challenge. id, challenge . mechanism_id , emission_weight
997+ "Challenge {} (mech {}, emission ={:.2}): collected weights" ,
998+ challenge. id, mech_id , emission_weight
993999 ) ;
9941000 }
9951001 Ok ( _) => {
996- // No weights returned - skip, chain keeps existing weights
1002+ // No weights returned - send this challenge's emission to burn
1003+ let uid_weights = mechanism_uid_weights. entry ( mech_id) . or_default ( ) ;
1004+ * uid_weights. entry ( 0 ) . or_insert ( 0.0 ) += emission_weight;
9971005 info ! (
998- "Challenge {} returned empty weights - skipping (chain keeps existing) " ,
999- challenge. id
1006+ "Challenge {} returned empty weights - {:.4} burn to UID 0 " ,
1007+ challenge. id, emission_weight
10001008 ) ;
10011009 }
10021010 Err ( e) => {
1003- // Error fetching weights - skip, chain keeps existing weights
1004- // Don't send burn - let chain maintain current state
1011+ // Error fetching weights - send this challenge's emission to burn
1012+ let uid_weights = mechanism_uid_weights. entry ( mech_id) . or_default ( ) ;
1013+ * uid_weights. entry ( 0 ) . or_insert ( 0.0 ) += emission_weight;
10051014 warn ! (
1006- "Failed to get weights for {} - skipping (chain keeps existing) : {}" ,
1007- challenge. id, e
1015+ "Failed to get weights for {} - {:.4} burn to UID 0 : {}" ,
1016+ challenge. id, emission_weight , e
10081017 ) ;
10091018 }
10101019 }
10111020 }
10121021
1013- // Convert HashMap<mechanism_id, HashMap<uid, weight>> to Vec<(mech, uids, weights)>
1014- // Apply max-upscaling per mechanism
1022+ // Convert HashMap<mechanism_id, HashMap<uid, weight_f64>> to Vec<(mech, uids, weights_u16)>
10151023 let mut weights: Vec < ( u8 , Vec < u16 > , Vec < u16 > ) > = Vec :: new ( ) ;
10161024
10171025 for ( mech_id, uid_weights) in mechanism_uid_weights {
10181026 if uid_weights. is_empty ( ) {
10191027 continue ;
10201028 }
10211029
1030+ // Normalize weights to sum to 1.0, then convert to u16
1031+ let total: f64 = uid_weights. values ( ) . sum ( ) ;
1032+ if total <= 0.0 {
1033+ // Should not happen, but fallback to 100% burn
1034+ warn ! (
1035+ "Mechanism {} has zero total weight - sending 100% burn" ,
1036+ mech_id
1037+ ) ;
1038+ weights. push ( ( mech_id, vec ! [ 0u16 ] , vec ! [ 65535u16 ] ) ) ;
1039+ continue ;
1040+ }
1041+
10221042 let uids: Vec < u16 > = uid_weights. keys ( ) . copied ( ) . collect ( ) ;
1023- let mut vals : Vec < u16 > = uids
1043+ let vals_f64 : Vec < f64 > = uids
10241044 . iter ( )
1025- . map ( |uid| {
1026- // Clamp accumulated value to u16 max
1027- uid_weights. get ( uid) . copied ( ) . unwrap_or ( 0 ) . min ( 65535 ) as u16
1028- } )
1045+ . map ( |uid| uid_weights. get ( uid) . copied ( ) . unwrap_or ( 0.0 ) / total)
10291046 . collect ( ) ;
10301047
1031- // Max-upscale weights so largest = 65535
1048+ // Max-upscale: largest weight becomes 65535
10321049 // This matches Python's convert_weights_and_uids_for_emit behavior
1033- let max_val = * vals . iter ( ) . max ( ) . unwrap_or ( & 0 ) as f64 ;
1034- if max_val > 0.0 && max_val < 65535 .0 {
1035- vals = vals
1050+ let max_val = vals_f64 . iter ( ) . cloned ( ) . fold ( 0.0_f64 , f64:: max ) ;
1051+ let vals : Vec < u16 > = if max_val > 0 .0 {
1052+ vals_f64
10361053 . iter ( )
1037- . map ( |v| ( ( * v as f64 / max_val) * 65535.0 ) . round ( ) as u16 )
1038- . collect ( ) ;
1039- }
1054+ . map ( |v| ( ( v / max_val) * 65535.0 ) . round ( ) as u16 )
1055+ . collect ( )
1056+ } else {
1057+ vec ! [ 0u16 ; uids. len( ) ]
1058+ } ;
10401059
10411060 info ! (
1042- "Mechanism {}: {} UIDs merged from all challenges ( max-upscaled)" ,
1061+ "Mechanism {}: {} UIDs, total_weight={:.4} (normalized & max-upscaled)" ,
10431062 mech_id,
1044- uids. len( )
1063+ uids. len( ) ,
1064+ total
10451065 ) ;
10461066 debug ! ( " UIDs: {:?}, Weights: {:?}" , uids, vals) ;
10471067 weights. push ( ( mech_id, uids, vals) ) ;
0 commit comments