Skip to content

Commit 56ccd21

Browse files
committed
refactor: simplify WeightAssignment to hotkey + weight only
- WeightAssignment now only has hotkey (SS58) and weight fields - Removed agent_hash, confidence, reason fields - Removed weight aggregation (validators use shared chain DB) - Challenge calculates weights with stake-weighted scoring - Platform maps hotkeys to UIDs via metagraph - Unused emission goes to UID 0 (burn)
1 parent 169ac1b commit 56ccd21

File tree

14 files changed

+332
-580
lines changed

14 files changed

+332
-580
lines changed

bins/validator-node/src/main.rs

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -813,12 +813,20 @@ async fn main() -> Result<()> {
813813
let mut state = chain_state.write();
814814

815815
// Debug: collect top stakes for logging
816-
let mut top_stakes: Vec<(u64, u128, u128, u128)> = metagraph.neurons
816+
let mut top_stakes: Vec<(u64, u128, u128, u128)> = metagraph
817+
.neurons
817818
.iter()
818-
.map(|(uid, n)| (*uid, n.stake, n.root_stake, n.stake.saturating_add(n.root_stake)))
819+
.map(|(uid, n)| {
820+
(
821+
*uid,
822+
n.stake,
823+
n.root_stake,
824+
n.stake.saturating_add(n.root_stake),
825+
)
826+
})
819827
.collect();
820828
top_stakes.sort_by(|a, b| b.3.cmp(&a.3));
821-
829+
822830
info!("Top 10 neurons by effective stake:");
823831
for (uid, alpha, root, total) in top_stakes.iter().take(10) {
824832
info!(" UID {}: alpha={:.2} TAO, root={:.2} TAO, total={:.2} TAO",
@@ -838,8 +846,10 @@ async fn main() -> Result<()> {
838846
// This matches how Bittensor calculates validator weight
839847
let alpha_stake = neuron.stake;
840848
let root_stake = neuron.root_stake;
841-
let effective_stake = alpha_stake.saturating_add(root_stake);
842-
let stake_rao = effective_stake.min(u64::MAX as u128) as u64;
849+
let effective_stake =
850+
alpha_stake.saturating_add(root_stake);
851+
let stake_rao =
852+
effective_stake.min(u64::MAX as u128) as u64;
843853

844854
// Skip if below minimum stake
845855
if stake_rao < min_stake_rao {

crates/bittensor-integration/examples/check_metagraph.rs

Lines changed: 33 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -4,19 +4,20 @@ use bittensor_rs::BittensorClient;
44
#[tokio::main]
55
async fn main() -> Result<(), Box<dyn std::error::Error>> {
66
println!("Connecting to Bittensor finney...");
7-
7+
88
let client = BittensorClient::new("wss://entrypoint-finney.opentensor.ai:443").await?;
9-
9+
1010
println!("Querying subnet 100 metagraph...\n");
11-
11+
1212
let metagraph = sync_metagraph(&client, 100).await?;
13-
13+
1414
println!("Total neurons: {}", metagraph.n);
1515
println!("\nTop 30 by effective stake (alpha + root):");
1616
println!("{:-<90}", "");
17-
17+
1818
// Collect stakes
19-
let mut stakes: Vec<(u16, String, u128, u128, u128)> = metagraph.neurons
19+
let mut stakes: Vec<(u16, String, u128, u128, u128)> = metagraph
20+
.neurons
2021
.iter()
2122
.map(|(uid, neuron)| {
2223
let alpha = neuron.stake;
@@ -27,31 +28,45 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
2728
})
2829
.filter(|(_, _, _, _, total)| *total > 0)
2930
.collect();
30-
31+
3132
// Sort by total stake descending
3233
stakes.sort_by(|a, b| b.4.cmp(&a.4));
33-
34-
println!("{:<6} {:<50} {:>12} {:>12} {:>12}", "UID", "Hotkey", "Alpha", "Root", "Total TAO");
34+
35+
println!(
36+
"{:<6} {:<50} {:>12} {:>12} {:>12}",
37+
"UID", "Hotkey", "Alpha", "Root", "Total TAO"
38+
);
3539
println!("{:-<90}", "");
36-
40+
3741
for (uid, hotkey, alpha, root, total) in stakes.iter().take(30) {
3842
let alpha_tao = *alpha as f64 / 1_000_000_000.0;
3943
let root_tao = *root as f64 / 1_000_000_000.0;
4044
let total_tao = *total as f64 / 1_000_000_000.0;
41-
println!("{:<6} {:<50} {:>12.2} {:>12.2} {:>12.2}",
42-
uid, hotkey, alpha_tao, root_tao, total_tao);
45+
println!(
46+
"{:<6} {:<50} {:>12.2} {:>12.2} {:>12.2}",
47+
uid, hotkey, alpha_tao, root_tao, total_tao
48+
);
4349
}
44-
45-
let gte_1000 = stakes.iter().filter(|(_, _, _, _, t)| *t as f64 / 1e9 >= 1000.0).count();
46-
let gte_100 = stakes.iter().filter(|(_, _, _, _, t)| *t as f64 / 1e9 >= 100.0).count();
47-
let gte_10 = stakes.iter().filter(|(_, _, _, _, t)| *t as f64 / 1e9 >= 10.0).count();
50+
51+
let gte_1000 = stakes
52+
.iter()
53+
.filter(|(_, _, _, _, t)| *t as f64 / 1e9 >= 1000.0)
54+
.count();
55+
let gte_100 = stakes
56+
.iter()
57+
.filter(|(_, _, _, _, t)| *t as f64 / 1e9 >= 100.0)
58+
.count();
59+
let gte_10 = stakes
60+
.iter()
61+
.filter(|(_, _, _, _, t)| *t as f64 / 1e9 >= 10.0)
62+
.count();
4863
let gt_0 = stakes.len();
49-
64+
5065
println!("\n{:-<90}", "");
5166
println!("Validators with >= 1000 TAO: {}", gte_1000);
5267
println!("Validators with >= 100 TAO: {}", gte_100);
5368
println!("Validators with >= 10 TAO: {}", gte_10);
5469
println!("Validators with > 0 TAO: {}", gt_0);
55-
70+
5671
Ok(())
5772
}

crates/bittensor-integration/src/weights.rs

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -415,7 +415,7 @@ impl WeightSubmitter {
415415
/// Converts WeightAssignment to (UIDs, normalized u16 weights)
416416
fn prepare_weights(&self, weights: &[WeightAssignment]) -> Result<(Vec<u64>, Vec<u16>)> {
417417
// Get hotkeys from weights
418-
let hotkeys: Vec<String> = weights.iter().map(|w| w.agent_hash.clone()).collect();
418+
let hotkeys: Vec<String> = weights.iter().map(|w| w.hotkey.clone()).collect();
419419

420420
// Lookup UIDs for hotkeys from cached metagraph
421421
let uid_map = self.client.get_uids_for_hotkeys(&hotkeys);
@@ -424,13 +424,13 @@ impl WeightSubmitter {
424424
let mut weight_values = Vec::new();
425425

426426
for weight in weights {
427-
if let Some((_, uid)) = uid_map.iter().find(|(h, _)| h == &weight.agent_hash) {
427+
if let Some((_, uid)) = uid_map.iter().find(|(h, _)| h == &weight.hotkey) {
428428
uids.push(*uid as u64);
429429
// Convert 0-1 weight to u16 (0-65535)
430430
let w_u16 = (weight.weight.clamp(0.0, 1.0) * 65535.0) as u16;
431431
weight_values.push(w_u16);
432432
} else {
433-
warn!("No UID found for hotkey: {}", weight.agent_hash);
433+
warn!("No UID found for hotkey: {}", weight.hotkey);
434434
}
435435
}
436436

@@ -1043,20 +1043,20 @@ impl MechanismWeightManager {
10431043

10441044
/// Prepare weights for submission (convert hotkeys to UIDs)
10451045
fn prepare_weights(&self, weights: &[WeightAssignment]) -> Result<(Vec<u64>, Vec<u16>)> {
1046-
let hotkeys: Vec<String> = weights.iter().map(|w| w.agent_hash.clone()).collect();
1046+
let hotkeys: Vec<String> = weights.iter().map(|w| w.hotkey.clone()).collect();
10471047

10481048
let uid_map = self.client.get_uids_for_hotkeys(&hotkeys);
10491049

10501050
let mut uids = Vec::new();
10511051
let mut weight_values = Vec::new();
10521052

10531053
for weight in weights {
1054-
if let Some((_, uid)) = uid_map.iter().find(|(h, _)| h == &weight.agent_hash) {
1054+
if let Some((_, uid)) = uid_map.iter().find(|(h, _)| h == &weight.hotkey) {
10551055
uids.push(*uid as u64);
10561056
let w_u16 = (weight.weight.clamp(0.0, 1.0) * 65535.0) as u16;
10571057
weight_values.push(w_u16);
10581058
} else {
1059-
warn!("No UID found for hotkey: {}", weight.agent_hash);
1059+
warn!("No UID found for hotkey: {}", weight.hotkey);
10601060
}
10611061
}
10621062

crates/challenge-orchestrator/src/docker.rs

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -282,7 +282,10 @@ impl DockerClient {
282282
let container_name = format!(
283283
"challenge-{}-{}",
284284
config.name.to_lowercase().replace(' ', "-"),
285-
validator_suffix.to_lowercase().replace("-", "").replace(" ", "")
285+
validator_suffix
286+
.to_lowercase()
287+
.replace("-", "")
288+
.replace(" ", "")
286289
);
287290

288291
// Remove existing container if any
@@ -308,8 +311,8 @@ impl DockerClient {
308311
// Mount tasks directory both to internal path AND to host path for Docker-in-Docker
309312
binds: Some(vec![
310313
"/var/run/docker.sock:/var/run/docker.sock:rw".to_string(),
311-
"/tmp/platform-tasks:/app/data/tasks:rw".to_string(), // Override internal tasks
312-
"/tmp/platform-tasks:/tmp/platform-tasks:rw".to_string(), // For DinD path mapping
314+
"/tmp/platform-tasks:/app/data/tasks:rw".to_string(), // Override internal tasks
315+
"/tmp/platform-tasks:/tmp/platform-tasks:rw".to_string(), // For DinD path mapping
313316
]),
314317
..Default::default()
315318
};

crates/challenge-runtime/src/weight_calculator.rs

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ impl Default for WeightCalculatorConfig {
3434
Self {
3535
min_score_threshold: 0.0,
3636
temperature: 1.0,
37-
use_softmax: false, // Use simple linear normalization
37+
use_softmax: false, // Use simple linear normalization
3838
max_weight_fraction: 1.0, // No cap - pure weights
3939
mechanism_id: 0,
4040
}
@@ -191,7 +191,7 @@ impl WeightCalculator {
191191
}
192192

193193
/// Apply weight cap (DISABLED - pure pass-through)
194-
///
194+
///
195195
/// NOTE: Weight caps have been removed for simpler, more transparent weight distribution.
196196
/// Challenges receive weights purely based on their emission percentage.
197197
/// Unused weight is sent to UID 0 (burn address).
@@ -387,9 +387,17 @@ mod tests {
387387
// Score 0.9 / 1.0 = 90% weight, Score 0.1 / 1.0 = 10% weight
388388
let uid1_weight = result.weights.iter().find(|w| w.uid == 1).unwrap();
389389
let uid2_weight = result.weights.iter().find(|w| w.uid == 2).unwrap();
390-
391-
assert!(uid1_weight.normalized > 0.85, "UID 1 should get ~90% weight: {}", uid1_weight.normalized);
392-
assert!(uid2_weight.normalized < 0.15, "UID 2 should get ~10% weight: {}", uid2_weight.normalized);
390+
391+
assert!(
392+
uid1_weight.normalized > 0.85,
393+
"UID 1 should get ~90% weight: {}",
394+
uid1_weight.normalized
395+
);
396+
assert!(
397+
uid2_weight.normalized < 0.15,
398+
"UID 2 should get ~10% weight: {}",
399+
uid2_weight.normalized
400+
);
393401

394402
// Verify weights still sum to 1.0
395403
let sum: f64 = result.weights.iter().map(|w| w.normalized).sum();

crates/challenge-sdk/src/test_challenge.rs

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -95,15 +95,17 @@ impl Challenge for SimpleTestChallenge {
9595
return Ok(vec![]);
9696
}
9797

98-
// Convert scores to weights using softmax-like normalization
98+
// Convert scores to weights
99+
// Note: In real challenges, agent_hash would be mapped to miner hotkey
100+
// Here we use agent_hash as hotkey for testing
99101
let scores: Vec<(String, f64)> = results
100102
.iter()
101103
.map(|r| (r.agent_hash.clone(), r.score))
102104
.collect();
103105

104-
let weights = crate::weights::scores_to_weights(&scores, 1.0);
106+
let weights = crate::weights::scores_to_weights(&scores);
105107

106-
ctx.info(&format!("Calculated weights for {} agents", weights.len()));
108+
ctx.info(&format!("Calculated weights for {} miners", weights.len()));
107109

108110
Ok(weights)
109111
}

crates/challenge-sdk/src/types.rs

Lines changed: 20 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -222,34 +222,26 @@ impl EvaluationResult {
222222
}
223223
}
224224

225-
/// Weight assignment for an agent
225+
/// Weight assignment for a miner
226+
///
227+
/// The `hotkey` field is the SS58 address of the miner who should receive this weight.
228+
/// This is looked up in the metagraph to find the corresponding UID for Bittensor submission.
226229
#[derive(Clone, Debug, Serialize, Deserialize)]
227230
pub struct WeightAssignment {
228-
pub agent_hash: String,
231+
/// Miner hotkey (SS58 address) - used to look up UID in metagraph
232+
pub hotkey: String,
233+
/// Weight for this miner (0.0 - 1.0)
229234
pub weight: f64,
230-
pub confidence: f64,
231-
pub reason: Option<String>,
232235
}
233236

234237
impl WeightAssignment {
235-
pub fn new(agent_hash: String, weight: f64) -> Self {
238+
/// Create a weight assignment for a miner hotkey
239+
pub fn new(hotkey: String, weight: f64) -> Self {
236240
Self {
237-
agent_hash,
241+
hotkey,
238242
weight: weight.clamp(0.0, 1.0),
239-
confidence: 1.0,
240-
reason: None,
241243
}
242244
}
243-
244-
pub fn with_confidence(mut self, confidence: f64) -> Self {
245-
self.confidence = confidence.clamp(0.0, 1.0);
246-
self
247-
}
248-
249-
pub fn with_reason(mut self, reason: String) -> Self {
250-
self.reason = Some(reason);
251-
self
252-
}
253245
}
254246

255247
/// Weights submission from a validator for an epoch
@@ -363,28 +355,23 @@ mod tests {
363355

364356
#[test]
365357
fn test_weight_assignment() {
366-
let wa = WeightAssignment::new("agent".to_string(), 0.7);
358+
let wa = WeightAssignment::new(
359+
"5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY".to_string(),
360+
0.7,
361+
);
362+
assert_eq!(
363+
wa.hotkey,
364+
"5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY"
365+
);
367366
assert_eq!(wa.weight, 0.7);
368-
assert_eq!(wa.confidence, 1.0);
369-
assert!(wa.reason.is_none());
370-
}
371-
372-
#[test]
373-
fn test_weight_assignment_builders() {
374-
let wa = WeightAssignment::new("agent".to_string(), 0.8)
375-
.with_confidence(0.9)
376-
.with_reason("high performance".to_string());
377-
378-
assert_eq!(wa.confidence, 0.9);
379-
assert_eq!(wa.reason, Some("high performance".to_string()));
380367
}
381368

382369
#[test]
383370
fn test_weight_assignment_clamping() {
384-
let wa1 = WeightAssignment::new("a".to_string(), 2.0);
371+
let wa1 = WeightAssignment::new("hotkey1".to_string(), 2.0);
385372
assert_eq!(wa1.weight, 1.0);
386373

387-
let wa2 = WeightAssignment::new("a".to_string(), -1.0);
374+
let wa2 = WeightAssignment::new("hotkey2".to_string(), -1.0);
388375
assert_eq!(wa2.weight, 0.0);
389376
}
390377

0 commit comments

Comments
 (0)