@@ -9,8 +9,8 @@ use clap::Parser;
99use futures_util:: { SinkExt , StreamExt } ;
1010use parking_lot:: RwLock ;
1111use platform_bittensor:: {
12- signer_from_seed, sync_metagraph, BittensorClient , BittensorSigner , BlockSync , BlockSyncConfig ,
13- BlockSyncEvent , ExtrinsicWait , Subtensor ,
12+ signer_from_seed, sync_metagraph, BittensorClient , BittensorConfig , BittensorSigner , BlockSync ,
13+ BlockSyncConfig , BlockSyncEvent , ExtrinsicWait , Subtensor , SubtensorClient ,
1414} ;
1515use platform_core:: { production_sudo_key, ChainState , Keypair , NetworkConfig } ;
1616use platform_rpc:: { RpcConfig , RpcServer } ;
@@ -124,7 +124,11 @@ impl PlatformServerClient {
124124 }
125125
126126 /// Get weights with infinite retry loop (30s interval)
127- pub async fn get_weights ( & self , challenge_id : & str , epoch : u64 ) -> Result < Vec < ( u16 , u16 ) > > {
127+ /// Returns hotkey-based weights: Vec<(hotkey, weight_f64)>
128+ /// Supports both formats:
129+ /// - New format: { weights: [{ hotkey: "...", weight: 0.5 }] }
130+ /// - Legacy format: { weights: [{ uid: 1, weight: 65535 }] }
131+ pub async fn get_weights ( & self , challenge_id : & str , epoch : u64 ) -> Result < Vec < ( String , f64 ) > > {
128132 let url = format ! (
129133 "{}/api/v1/challenges/{}/get_weights?epoch={}" ,
130134 self . base_url, challenge_id, epoch
@@ -142,10 +146,18 @@ impl PlatformServerClient {
142146 . map ( |arr| {
143147 arr. iter ( )
144148 . filter_map ( |w| {
145- Some ( (
146- w. get ( "uid" ) ?. as_u64 ( ) ? as u16 ,
147- w. get ( "weight" ) ?. as_u64 ( ) ? as u16 ,
148- ) )
149+ // Try new format first: { hotkey, weight: f64 }
150+ if let Some ( hotkey) =
151+ w. get ( "hotkey" ) . and_then ( |h| h. as_str ( ) )
152+ {
153+ let weight = w
154+ . get ( "weight" )
155+ . and_then ( |v| v. as_f64 ( ) )
156+ . unwrap_or ( 0.0 ) ;
157+ return Some ( ( hotkey. to_string ( ) , weight) ) ;
158+ }
159+ // Legacy format: { uid, weight: u16 } - skip, not supported
160+ None
149161 } )
150162 . collect ( )
151163 } )
@@ -569,6 +581,7 @@ async fn main() -> Result<()> {
569581 // Bittensor setup
570582 let subtensor: Option < Arc < Subtensor > > ;
571583 let subtensor_signer: Option < Arc < BittensorSigner > > ;
584+ let subtensor_client: Option < Arc < RwLock < SubtensorClient > > > ;
572585 let mut block_rx: Option < tokio:: sync:: mpsc:: Receiver < BlockSyncEvent > > = None ;
573586
574587 if !args. no_bittensor {
@@ -601,22 +614,35 @@ async fn main() -> Result<()> {
601614
602615 subtensor = Some ( Arc :: new ( st) ) ;
603616
604- // Sync metagraph
605- let client = BittensorClient :: new ( & args. subtensor_endpoint ) . await ?;
606- match sync_metagraph ( & client, args. netuid ) . await {
607- Ok ( mg) => info ! ( "Metagraph: {} neurons" , mg. n) ,
617+ // Create SubtensorClient for metagraph lookups (hotkey -> UID conversion)
618+ let mut client = SubtensorClient :: new ( BittensorConfig {
619+ endpoint : args. subtensor_endpoint . clone ( ) ,
620+ netuid : args. netuid ,
621+ ..Default :: default ( )
622+ } ) ;
623+
624+ // Sync metagraph and store client for hotkey -> UID lookups
625+ let bittensor_client = BittensorClient :: new ( & args. subtensor_endpoint ) . await ?;
626+ match sync_metagraph ( & bittensor_client, args. netuid ) . await {
627+ Ok ( mg) => {
628+ info ! ( "Metagraph: {} neurons" , mg. n) ;
629+ // Store metagraph in our SubtensorClient
630+ client. set_metagraph ( mg) ;
631+ }
608632 Err ( e) => warn ! ( "Metagraph sync failed: {}" , e) ,
609633 }
610634
635+ subtensor_client = Some ( Arc :: new ( RwLock :: new ( client) ) ) ;
636+
611637 // Block sync
612638 let mut sync = BlockSync :: new ( BlockSyncConfig {
613639 netuid : args. netuid ,
614640 ..Default :: default ( )
615641 } ) ;
616642 let rx = sync. take_event_receiver ( ) ;
617643
618- let client = Arc :: new ( client ) ;
619- if let Err ( e) = sync. connect ( client ) . await {
644+ let bittensor_client = Arc :: new ( bittensor_client ) ;
645+ if let Err ( e) = sync. connect ( bittensor_client ) . await {
620646 warn ! ( "Block sync connect failed: {}" , e) ;
621647 } else {
622648 tokio:: spawn ( async move {
@@ -632,12 +658,14 @@ async fn main() -> Result<()> {
632658 error ! ( "Subtensor connection failed: {}" , e) ;
633659 subtensor = None ;
634660 subtensor_signer = None ;
661+ subtensor_client = None ;
635662 }
636663 }
637664 } else {
638665 info ! ( "Bittensor: disabled" ) ;
639666 subtensor = None ;
640667 subtensor_signer = None ;
668+ subtensor_client = None ;
641669 }
642670
643671 info ! ( "Validator running. Ctrl+C to stop." ) ;
@@ -668,6 +696,7 @@ async fn main() -> Result<()> {
668696 & platform_client,
669697 & subtensor,
670698 & subtensor_signer,
699+ & subtensor_client,
671700 netuid,
672701 version_key,
673702 ) . await ;
@@ -724,6 +753,7 @@ async fn handle_block_event(
724753 platform_client : & Arc < PlatformServerClient > ,
725754 subtensor : & Option < Arc < Subtensor > > ,
726755 signer : & Option < Arc < BittensorSigner > > ,
756+ subtensor_client : & Option < Arc < RwLock < SubtensorClient > > > ,
727757 netuid : u16 ,
728758 version_key : u64 ,
729759) {
@@ -744,7 +774,11 @@ async fn handle_block_event(
744774 info ! ( "=== COMMIT WINDOW: epoch {} block {} ===" , epoch, block) ;
745775
746776 // Submit weights via Subtensor (handles CRv4/commit-reveal automatically)
747- if let ( Some ( st) , Some ( sig) ) = ( subtensor. as_ref ( ) , signer. as_ref ( ) ) {
777+ if let ( Some ( st) , Some ( sig) , Some ( client) ) = (
778+ subtensor. as_ref ( ) ,
779+ signer. as_ref ( ) ,
780+ subtensor_client. as_ref ( ) ,
781+ ) {
748782 // Fetch weights from platform-server
749783 let mechanism_weights = match platform_client. list_challenges ( ) . await {
750784 Ok ( challenges) if !challenges. is_empty ( ) => {
@@ -753,17 +787,47 @@ async fn handle_block_event(
753787 for challenge in challenges. iter ( ) . filter ( |c| c. is_healthy ) {
754788 match platform_client. get_weights ( & challenge. id , epoch) . await {
755789 Ok ( w) if !w. is_empty ( ) => {
756- let uids: Vec < u16 > = w. iter ( ) . map ( |( u, _) | * u) . collect ( ) ;
757- let vals: Vec < u16 > = w. iter ( ) . map ( |( _, v) | * v) . collect ( ) ;
758-
759- info ! (
760- "Challenge {} (mech {}): {} weights" ,
761- challenge. id,
762- challenge. mechanism_id,
763- uids. len( )
764- ) ;
765-
766- weights. push ( ( challenge. mechanism_id as u8 , uids, vals) ) ;
790+ // Convert hotkeys to UIDs using metagraph
791+ let client_guard = client. read ( ) ;
792+ let mut uids = Vec :: new ( ) ;
793+ let mut vals = Vec :: new ( ) ;
794+
795+ for ( hotkey, weight_f64) in & w {
796+ if let Some ( uid) = client_guard. get_uid_for_hotkey ( hotkey) {
797+ // Convert f64 weight (0.0-1.0) to u16 (0-65535)
798+ let weight_u16 = ( weight_f64 * 65535.0 ) . round ( ) as u16 ;
799+ uids. push ( uid) ;
800+ vals. push ( weight_u16) ;
801+ info ! (
802+ " {} -> UID {} (weight: {:.4} = {})" ,
803+ & hotkey[ ..16 ] ,
804+ uid,
805+ weight_f64,
806+ weight_u16
807+ ) ;
808+ } else {
809+ warn ! (
810+ "Hotkey {} not found in metagraph, skipping" ,
811+ & hotkey[ ..16 ]
812+ ) ;
813+ }
814+ }
815+ drop ( client_guard) ;
816+
817+ if !uids. is_empty ( ) {
818+ info ! (
819+ "Challenge {} (mech {}): {} weights" ,
820+ challenge. id,
821+ challenge. mechanism_id,
822+ uids. len( )
823+ ) ;
824+ weights. push ( ( challenge. mechanism_id as u8 , uids, vals) ) ;
825+ } else {
826+ warn ! (
827+ "Challenge {} has weights but no UIDs resolved" ,
828+ challenge. id
829+ ) ;
830+ }
767831 }
768832 Ok ( _) => debug ! ( "Challenge {} has no weights" , challenge. id) ,
769833 Err ( e) => {
0 commit comments