@@ -508,6 +508,11 @@ async fn main() -> Result<()> {
508508 state_manager. clone ( ) ,
509509 ) ) ) ;
510510
511+ // Shared UID map (hotkey_ss58 -> uid) updated on every metagraph sync.
512+ // Used by the subnet_getWeights RPC handler to resolve hotkeys in real-time.
513+ let shared_uid_map: Arc < RwLock < std:: collections:: HashMap < String , u16 > > > =
514+ Arc :: new ( RwLock :: new ( std:: collections:: HashMap :: new ( ) ) ) ;
515+
511516 // Connect to Bittensor
512517 let subtensor: Option < Arc < Subtensor > > ;
513518 let subtensor_signer: Option < Arc < BittensorSigner > > ;
@@ -551,6 +556,14 @@ async fn main() -> Result<()> {
551556 "Validator set: {} active validators" ,
552557 validator_set. active_count( )
553558 ) ;
559+ // Update shared UID map for real-time weight RPC
560+ {
561+ let mut uid_map = shared_uid_map. write ( ) ;
562+ uid_map. clear ( ) ;
563+ for ( uid, neuron) in & mg. neurons {
564+ uid_map. insert ( neuron. hotkey . to_string ( ) , * uid as u16 ) ;
565+ }
566+ }
554567 client. set_metagraph ( mg) ;
555568 }
556569 Err ( e) => warn ! ( "Metagraph sync failed: {}" , e) ,
@@ -616,6 +629,14 @@ async fn main() -> Result<()> {
616629 validator_set. active_count( )
617630 ) ;
618631
632+ // Update shared UID map for real-time weight RPC
633+ {
634+ let mut uid_map = shared_uid_map. write ( ) ;
635+ uid_map. clear ( ) ;
636+ for ( uid, neuron) in & mg. neurons {
637+ uid_map. insert ( neuron. hotkey . to_string ( ) , * uid as u16 ) ;
638+ }
639+ }
619640 client. set_metagraph ( mg) ;
620641 }
621642 Err ( e) => warn ! ( "Metagraph sync failed: {}" , e) ,
@@ -841,6 +862,19 @@ async fn main() -> Result<()> {
841862 rpc_server. rpc_handler ( ) . set_route_handler ( handler) ;
842863 }
843864
865+ // Wire real-time weight computation for subnet_getWeights RPC
866+ {
867+ let uid_map = Arc :: clone ( & shared_uid_map) ;
868+ let cs = Arc :: clone ( & chain_state) ;
869+ let executor = wasm_executor. clone ( ) ;
870+ let sm = Arc :: clone ( & state_manager) ;
871+
872+ let handler: platform_rpc:: GetWeightsHandler = Arc :: new ( move || {
873+ compute_weights_for_rpc_with_uid_map ( & executor, & uid_map, & sm, & cs)
874+ } ) ;
875+ rpc_server. rpc_handler ( ) . set_get_weights_handler ( handler) ;
876+ }
877+
844878 rpc_handler_opt = Some ( rpc_server. rpc_handler ( ) ) ;
845879
846880 tokio:: spawn ( async move {
@@ -1079,6 +1113,14 @@ async fn main() -> Result<()> {
10791113 info!( "Pre-weight metagraph refresh: {} neurons" , mg. n) ;
10801114 let our_hk = keypair. hotkey( ) ;
10811115 update_validator_set_from_metagraph( & mg, & validator_set, & chain_state, & valid_voters, & state_root_consensus, & state_manager, Some ( & our_hk) ) ;
1116+ // Update shared UID map for real-time weight RPC
1117+ {
1118+ let mut uid_map = shared_uid_map. write( ) ;
1119+ uid_map. clear( ) ;
1120+ for ( uid, neuron) in & mg. neurons {
1121+ uid_map. insert( neuron. hotkey. to_string( ) , * uid as u16 ) ;
1122+ }
1123+ }
10821124 if let Some ( sc) = subtensor_client. as_mut( ) {
10831125 sc. set_metagraph( mg) ;
10841126 }
@@ -4169,8 +4211,145 @@ async fn handle_network_event(
41694211 }
41704212}
41714213
4172- /// Compute mechanism weights from WASM challenges and cache them in chain_state
4173- /// so they are available via the `subnet_getWeights` RPC immediately.
4214+ /// Compute mechanism weights in real-time using a shared UID map.
4215+ /// Called by the subnet_getWeights RPC handler on every request.
4216+ fn compute_weights_for_rpc_with_uid_map (
4217+ wasm_executor : & Option < Arc < WasmChallengeExecutor > > ,
4218+ uid_map : & Arc < RwLock < std:: collections:: HashMap < String , u16 > > > ,
4219+ state_manager : & Arc < StateManager > ,
4220+ chain_state : & Arc < RwLock < platform_core:: ChainState > > ,
4221+ ) -> Vec < ( u8 , Vec < u16 > , Vec < u16 > ) > {
4222+ let executor = match wasm_executor. as_ref ( ) {
4223+ Some ( e) => e,
4224+ None => return Vec :: new ( ) ,
4225+ } ;
4226+
4227+ let challenges: Vec < ( String , u8 , f64 ) > = {
4228+ let cs = chain_state. read ( ) ;
4229+ cs. wasm_challenge_configs
4230+ . iter ( )
4231+ . filter ( |( _, cfg) | cfg. is_active )
4232+ . map ( |( id, cfg) | {
4233+ (
4234+ id. to_string ( ) ,
4235+ cfg. config . mechanism_id ,
4236+ cfg. config . emission_weight ,
4237+ )
4238+ } )
4239+ . collect ( )
4240+ } ;
4241+
4242+ let block_height = state_manager. apply ( |state| state. bittensor_block ) ;
4243+ let epoch = {
4244+ let cs = chain_state. read ( ) ;
4245+ cs. epoch
4246+ } ;
4247+
4248+ let map = uid_map. read ( ) ;
4249+ let mut mechanism_weights: Vec < ( u8 , Vec < u16 > , Vec < u16 > ) > = Vec :: new ( ) ;
4250+
4251+ for ( cid, mechanism_id, emission_weight) in & challenges {
4252+ let emission_weight = emission_weight. clamp ( 0.0 , 1.0 ) ;
4253+ if emission_weight < 0.001 {
4254+ continue ;
4255+ }
4256+ match executor. execute_get_weights_with_block ( cid, block_height, epoch) {
4257+ Ok ( assignments) if !assignments. is_empty ( ) => {
4258+ let total_weight: f64 = assignments. iter ( ) . map ( |a| a. weight ) . sum ( ) ;
4259+ let mut uid_weight_map: std:: collections:: BTreeMap < u16 , f64 > =
4260+ std:: collections:: BTreeMap :: new ( ) ;
4261+ let mut assigned_weight: f64 = 0.0 ;
4262+
4263+ if total_weight > 0.0 {
4264+ for assignment in & assignments {
4265+ if let Some ( uid) = map. get ( & assignment. hotkey ) {
4266+ let normalized = assignment. weight / total_weight;
4267+ let scaled = normalized * emission_weight;
4268+ * uid_weight_map. entry ( * uid) . or_insert ( 0.0 ) += scaled;
4269+ assigned_weight += scaled;
4270+ }
4271+ }
4272+ }
4273+
4274+ let mut uids: Vec < u16 > = Vec :: new ( ) ;
4275+ let mut vals: Vec < u16 > = Vec :: new ( ) ;
4276+ for ( uid, w) in & uid_weight_map {
4277+ let weight_u16 = ( w * 65535.0 ) . round ( ) as u16 ;
4278+ if weight_u16 > 0 {
4279+ uids. push ( * uid) ;
4280+ vals. push ( weight_u16) ;
4281+ }
4282+ }
4283+
4284+ let burn_weight = 1.0 - assigned_weight;
4285+ if burn_weight > 0.001 {
4286+ let burn_u16 = ( burn_weight * 65535.0 ) . round ( ) as u16 ;
4287+ if let Some ( pos) = uids. iter ( ) . position ( |& u| u == 0 ) {
4288+ vals[ pos] = vals[ pos] . saturating_add ( burn_u16) ;
4289+ } else {
4290+ uids. push ( 0 ) ;
4291+ vals. push ( burn_u16) ;
4292+ }
4293+ }
4294+
4295+ if !uids. is_empty ( ) {
4296+ let max_val = * vals. iter ( ) . max ( ) . unwrap ( ) as f64 ;
4297+ if max_val > 0.0 && max_val < 65535.0 {
4298+ vals = vals
4299+ . iter ( )
4300+ . map ( |v| ( ( * v as f64 / max_val) * 65535.0 ) . round ( ) as u16 )
4301+ . collect ( ) ;
4302+ }
4303+ mechanism_weights. push ( ( * mechanism_id, uids, vals) ) ;
4304+ } else {
4305+ mechanism_weights. push ( ( * mechanism_id, vec ! [ 0u16 ] , vec ! [ 65535u16 ] ) ) ;
4306+ }
4307+ }
4308+ Ok ( _) => {
4309+ mechanism_weights. push ( ( * mechanism_id, vec ! [ 0u16 ] , vec ! [ 65535u16 ] ) ) ;
4310+ }
4311+ Err ( _) => {
4312+ mechanism_weights. push ( ( * mechanism_id, vec ! [ 0u16 ] , vec ! [ 65535u16 ] ) ) ;
4313+ }
4314+ }
4315+ }
4316+
4317+ // Dedup by mechanism_id (prefer most UIDs)
4318+ if mechanism_weights. is_empty ( ) {
4319+ let mechanism_id = {
4320+ let cs = chain_state. read ( ) ;
4321+ cs. mechanism_configs . keys ( ) . next ( ) . copied ( ) . unwrap_or ( 0u8 )
4322+ } ;
4323+ return vec ! [ ( mechanism_id, vec![ 0u16 ] , vec![ 65535u16 ] ) ] ;
4324+ }
4325+
4326+ let mut best: std:: collections:: BTreeMap < u8 , ( Vec < u16 > , Vec < u16 > ) > =
4327+ std:: collections:: BTreeMap :: new ( ) ;
4328+ for ( mid, uids, vals) in & mechanism_weights {
4329+ let is_burn = uids. len ( ) == 1 && uids[ 0 ] == 0 ;
4330+ let replace = match best. get ( mid) {
4331+ None => true ,
4332+ Some ( ( ex, _) ) => {
4333+ let ex_burn = ex. len ( ) == 1 && ex[ 0 ] == 0 ;
4334+ if is_burn && !ex_burn {
4335+ false
4336+ } else if !is_burn && ex_burn {
4337+ true
4338+ } else {
4339+ uids. len ( ) > ex. len ( )
4340+ }
4341+ }
4342+ } ;
4343+ if replace {
4344+ best. insert ( * mid, ( uids. clone ( ) , vals. clone ( ) ) ) ;
4345+ }
4346+ }
4347+ best. into_iter ( )
4348+ . map ( |( mid, ( uids, vals) ) | ( mid, uids, vals) )
4349+ . collect ( )
4350+ }
4351+
4352+ /// Compute mechanism weights from WASM challenges and cache them in chain_state.
41744353/// This is called at startup (first block) and at each epoch transition so that
41754354/// non-bootstrap validators can always fetch the bootstrap validator's weights.
41764355fn compute_weights_for_rpc (
0 commit comments