Skip to content

Commit 630990c

Browse files
committed
feat(validator): convert hotkey weights to UIDs using metagraph
- get_weights() now returns hotkey-based weights (hotkey, f64) - handle_block_event converts hotkeys to UIDs via SubtensorClient - Add set_metagraph() to SubtensorClient for external metagraph sync - Weights are converted from f64 (0.0-1.0) to u16 (0-65535)
1 parent b5102e5 commit 630990c

File tree

2 files changed

+94
-25
lines changed

2 files changed

+94
-25
lines changed

bins/validator-node/src/main.rs

Lines changed: 89 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,8 @@ use clap::Parser;
99
use futures_util::{SinkExt, StreamExt};
1010
use parking_lot::RwLock;
1111
use 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
};
1515
use platform_core::{production_sudo_key, ChainState, Keypair, NetworkConfig};
1616
use 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) => {

crates/bittensor-integration/src/client.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,11 @@ impl SubtensorClient {
9393
self.metagraph.as_ref()
9494
}
9595

96+
/// Set the metagraph directly (useful when synced externally)
97+
pub fn set_metagraph(&mut self, metagraph: Metagraph) {
98+
self.metagraph = Some(metagraph);
99+
}
100+
96101
/// Get UID for a hotkey from cached metagraph
97102
pub fn get_uid_for_hotkey(&self, hotkey: &str) -> Option<u16> {
98103
if let Some(uid) = self.uid_overrides.get(hotkey) {

0 commit comments

Comments
 (0)