Skip to content

Commit c9fe9df

Browse files
committed
fix: weight pipeline - accept WeightAssignment from WASM, convert hotkey->UID, apply emission_weight
- execute_get_weights() now returns Vec<WeightAssignment> (hotkey + f64) instead of Vec<(u16, u16)> matching what WASM challenges actually return - CommitWindowOpen handler converts hotkeys to UIDs via metagraph - Scales weights by challenge emission_weight, remainder to UID 0 (burn) - Fix auth: verify signature with original challenge_id before resolving to UUID
1 parent d37cc4a commit c9fe9df

File tree

3 files changed

+97
-29
lines changed

3 files changed

+97
-29
lines changed

bins/validator-node/src/main.rs

Lines changed: 83 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -855,6 +855,7 @@ async fn main() -> Result<()> {
855855
version_key,
856856
&wasm_executor,
857857
&keypair,
858+
&chain_state,
858859
).await;
859860
}
860861

@@ -2093,12 +2094,13 @@ async fn handle_block_event(
20932094
event: BlockSyncEvent,
20942095
subtensor: &Option<Arc<Subtensor>>,
20952096
signer: &Option<Arc<BittensorSigner>>,
2096-
_client: &Option<SubtensorClient>,
2097+
client: &Option<SubtensorClient>,
20972098
state_manager: &Arc<StateManager>,
20982099
netuid: u16,
20992100
version_key: u64,
21002101
wasm_executor: &Option<Arc<WasmChallengeExecutor>>,
21012102
keypair: &Keypair,
2103+
chain_state: &Arc<RwLock<platform_core::ChainState>>,
21022104
) {
21032105
match event {
21042106
BlockSyncEvent::NewBlock { block_number, .. } => {
@@ -2129,39 +2131,98 @@ async fn handle_block_event(
21292131
epoch, block
21302132
);
21312133

2132-
// Collect WASM-computed weights from challenges before finalizing
2134+
// Collect WASM-computed weights from challenges, convert hotkey->UID,
2135+
// apply emission_weight, and submit
21332136
if let Some(ref executor) = wasm_executor {
21342137
let challenges: Vec<String> = state_manager
21352138
.apply(|state| state.challenges.keys().map(|k| k.to_string()).collect());
21362139
let local_hotkey = keypair.hotkey();
21372140
for cid in &challenges {
21382141
match executor.execute_get_weights(cid) {
2139-
Ok(weights) if !weights.is_empty() => {
2140-
state_manager.apply(|state| {
2141-
if let Err(e) = state.submit_weight_vote(
2142-
local_hotkey.clone(),
2143-
netuid,
2144-
weights.clone(),
2145-
) {
2146-
warn!(
2147-
challenge_id = %cid,
2148-
error = %e,
2149-
"Failed to submit WASM-computed weights"
2150-
);
2142+
Ok(assignments) if !assignments.is_empty() => {
2143+
// Read emission_weight from chain state
2144+
let emission_weight = {
2145+
let cs = chain_state.read();
2146+
let challenge_uuid = uuid::Uuid::parse_str(cid)
2147+
.ok()
2148+
.map(platform_core::ChallengeId);
2149+
challenge_uuid
2150+
.and_then(|id| {
2151+
cs.wasm_challenge_configs
2152+
.get(&id)
2153+
.map(|c| c.config.emission_weight)
2154+
.or_else(|| {
2155+
cs.challenges
2156+
.get(&id)
2157+
.map(|c| c.config.emission_weight)
2158+
})
2159+
})
2160+
.unwrap_or(1.0)
2161+
};
2162+
2163+
// Convert hotkey -> UID via metagraph
2164+
let mut uid_weights: Vec<(u16, u16)> = Vec::new();
2165+
let total_weight: f64 = assignments.iter().map(|a| a.weight).sum();
2166+
2167+
if total_weight > 0.0 {
2168+
for assignment in &assignments {
2169+
let uid = client
2170+
.as_ref()
2171+
.and_then(|c| c.get_uid_for_hotkey(&assignment.hotkey));
2172+
2173+
if let Some(uid) = uid {
2174+
let normalized = assignment.weight / total_weight;
2175+
let scaled =
2176+
(normalized * emission_weight * 65535.0).round() as u16;
2177+
if scaled > 0 {
2178+
uid_weights.push((uid, scaled));
2179+
}
2180+
} else {
2181+
debug!(
2182+
hotkey = %assignment.hotkey,
2183+
challenge_id = %cid,
2184+
"Hotkey not found in metagraph, skipping"
2185+
);
2186+
}
21512187
}
2152-
});
2153-
info!(
2154-
challenge_id = %cid,
2155-
weight_count = weights.len(),
2156-
"Integrated WASM-computed weights"
2157-
);
2188+
2189+
// Remaining weight goes to UID 0 (burn)
2190+
let used: u64 = uid_weights.iter().map(|(_, w)| *w as u64).sum();
2191+
let burn = 65535u64.saturating_sub(used);
2192+
if burn > 0 {
2193+
uid_weights.insert(0, (0u16, burn as u16));
2194+
}
2195+
}
2196+
2197+
if !uid_weights.is_empty() {
2198+
state_manager.apply(|state| {
2199+
if let Err(e) = state.submit_weight_vote(
2200+
local_hotkey.clone(),
2201+
netuid,
2202+
uid_weights.clone(),
2203+
) {
2204+
warn!(
2205+
challenge_id = %cid,
2206+
error = %e,
2207+
"Failed to submit WASM-computed weights"
2208+
);
2209+
}
2210+
});
2211+
info!(
2212+
challenge_id = %cid,
2213+
emission_weight = emission_weight,
2214+
raw_assignments = assignments.len(),
2215+
uid_weights = uid_weights.len(),
2216+
"Integrated WASM weights (hotkey->UID converted, emission scaled)"
2217+
);
2218+
}
21582219
}
21592220
Ok(_) => {}
21602221
Err(e) => {
2161-
debug!(
2222+
warn!(
21622223
challenge_id = %cid,
21632224
error = %e,
2164-
"WASM get_weights not available for challenge"
2225+
"WASM get_weights failed for challenge"
21652226
);
21662227
}
21672228
}

bins/validator-node/src/wasm_executor.rs

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -949,7 +949,13 @@ impl WasmChallengeExecutor {
949949
})
950950
}
951951

952-
pub fn execute_get_weights(&self, module_path: &str) -> Result<Vec<(u16, u16)>> {
952+
/// Execute get_weights on a WASM challenge module.
953+
/// Returns Vec<WeightAssignment> with hotkey (SS58/hex) + f64 weight.
954+
/// The caller is responsible for converting hotkeys to UIDs via metagraph.
955+
pub fn execute_get_weights(
956+
&self,
957+
module_path: &str,
958+
) -> Result<Vec<platform_challenge_sdk::WeightAssignment>> {
953959
let start = Instant::now();
954960

955961
let module = self
@@ -991,12 +997,12 @@ impl WasmChallengeExecutor {
991997
return Ok(Vec::new());
992998
};
993999

994-
let weights: Vec<(u16, u16)> = bincode::DefaultOptions::new()
1000+
let weights: Vec<platform_challenge_sdk::WeightAssignment> = bincode::DefaultOptions::new()
9951001
.with_fixint_encoding()
9961002
.allow_trailing_bytes()
9971003
.with_limit(MAX_ROUTE_OUTPUT_SIZE)
9981004
.deserialize(&result_data)
999-
.context("Failed to deserialize get_weights output")?;
1005+
.context("Failed to deserialize get_weights output as Vec<WeightAssignment>")?;
10001006

10011007
info!(
10021008
module = module_path,

crates/rpc-server/src/jsonrpc.rs

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1217,21 +1217,22 @@ impl RpcHandler {
12171217
}
12181218
}
12191219

1220-
// Use resolved_id for the rest of the call
1221-
let challenge_id = resolved_id;
1222-
12231220
// Parse headers for authentication
12241221
let headers: std::collections::HashMap<String, String> = params
12251222
.get("headers")
12261223
.and_then(|v| serde_json::from_value(v.clone()).ok())
12271224
.unwrap_or_default();
12281225

1229-
// Verify authentication from headers if present
1226+
// Verify authentication using the ORIGINAL challenge_id (not resolved UUID)
1227+
// because the CLI signs with the name it knows (e.g. "bounty-challenge")
12301228
let body_bytes = serde_json::to_vec(&body).unwrap_or_default();
12311229
let auth_hotkey =
12321230
crate::auth::verify_route_auth(&headers, &challenge_id, &method, &path, &body_bytes)
12331231
.ok();
12341232

1233+
// Use resolved_id for routing
1234+
let challenge_id = resolved_id;
1235+
12351236
let request = RouteRequest {
12361237
method,
12371238
path,

0 commit comments

Comments
 (0)