Skip to content

Commit bb37f5c

Browse files
committed
fix: all validators set weights at same block interval, not at startup
- Add WEIGHT_SET_BLOCK_INTERVAL=100 constant: all validators submit weights when bittensor_block % 100 == 0, ensuring synchronized weight setting - Remove startup weight submission (90s delay) - weights only set at the defined block interval so all validators are in sync - Trigger CommitWindowOpen from NewBlock handler at the interval
1 parent 96a79e2 commit bb37f5c

2 files changed

Lines changed: 59 additions & 64 deletions

File tree

bins/validator-node/src/main.rs

Lines changed: 54 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -1045,29 +1045,45 @@ async fn main() -> Result<()> {
10451045
None => std::future::pending().await,
10461046
}
10471047
} => {
1048-
// Force metagraph refresh before weight submission to avoid stale hotkey->UID mappings
1049-
if matches!(event, BlockSyncEvent::CommitWindowOpen { .. }) {
1048+
// Check if this block triggers weight submission (every WEIGHT_SET_BLOCK_INTERVAL blocks)
1049+
let is_weight_block = if let BlockSyncEvent::NewBlock { block_number, .. } = &event {
1050+
*block_number > 0
1051+
&& *block_number % platform_core::constants::WEIGHT_SET_BLOCK_INTERVAL == 0
1052+
} else {
1053+
false
1054+
};
1055+
1056+
// Force metagraph refresh before weight submission
1057+
if is_weight_block || matches!(event, BlockSyncEvent::CommitWindowOpen { .. }) {
10501058
if let Some(bittensor_client) = bittensor_client_for_metagraph.as_ref() {
10511059
match tokio::time::timeout(
10521060
Duration::from_secs(15),
10531061
sync_metagraph(bittensor_client, netuid),
10541062
).await {
10551063
Ok(Ok(mg)) => {
1056-
info!("Pre-commit metagraph refresh: {} neurons", mg.n);
1064+
info!("Pre-weight metagraph refresh: {} neurons", mg.n);
10571065
update_validator_set_from_metagraph(&mg, &validator_set, &chain_state, &valid_voters, &state_root_consensus, &state_manager);
10581066
if let Some(sc) = subtensor_client.as_mut() {
10591067
sc.set_metagraph(mg);
10601068
}
10611069
}
10621070
Ok(Err(e)) => {
1063-
warn!("Pre-commit metagraph refresh failed: {}. Using cached.", e);
1071+
warn!("Pre-weight metagraph refresh failed: {}. Using cached.", e);
10641072
}
10651073
Err(_) => {
1066-
warn!("Pre-commit metagraph refresh timed out (15s). Using cached.");
1074+
warn!("Pre-weight metagraph refresh timed out (15s). Using cached.");
10671075
}
10681076
}
10691077
}
10701078
}
1079+
1080+
// Extract block number before moving event
1081+
let new_block_number = if let BlockSyncEvent::NewBlock { block_number, .. } = &event {
1082+
Some(*block_number)
1083+
} else {
1084+
None
1085+
};
1086+
10711087
handle_block_event(
10721088
event,
10731089
&subtensor,
@@ -1082,6 +1098,35 @@ async fn main() -> Result<()> {
10821098
&storage,
10831099
&mut last_weight_submission_epoch,
10841100
).await;
1101+
1102+
// After processing the block, trigger weight submission at the defined interval.
1103+
// All validators see the same block numbers so they all submit at the same time.
1104+
if is_weight_block {
1105+
let block_number = new_block_number.unwrap();
1106+
let tempo = 360u64;
1107+
let netuid_plus_one = (netuid as u64).saturating_add(1);
1108+
let epoch = block_number.saturating_add(netuid_plus_one) / (tempo + 1);
1109+
info!(
1110+
"=== WEIGHT SET BLOCK {} (every {} blocks) epoch {} ===",
1111+
block_number,
1112+
platform_core::constants::WEIGHT_SET_BLOCK_INTERVAL,
1113+
epoch,
1114+
);
1115+
handle_block_event(
1116+
BlockSyncEvent::CommitWindowOpen { epoch, block: block_number },
1117+
&subtensor,
1118+
&subtensor_signer,
1119+
&subtensor_client,
1120+
&state_manager,
1121+
netuid,
1122+
version_key,
1123+
&wasm_executor,
1124+
&keypair,
1125+
&chain_state,
1126+
&storage,
1127+
&mut last_weight_submission_epoch,
1128+
).await;
1129+
}
10851130
}
10861131

10871132
// RPC -> P2P commands (challenge updates from sudo)
@@ -1692,67 +1737,12 @@ async fn main() -> Result<()> {
16921737
}
16931738
}
16941739

1695-
// Submit weights on-chain 90s after boot (after RPC pre-compute at 70s).
1740+
// Startup weight submission disabled: all validators set weights at the
1741+
// same block via WEIGHT_SET_BLOCK_INTERVAL (every N Bittensor blocks).
16961742
_ = &mut startup_weight_delay, if !startup_weights_submitted => {
16971743
startup_weights_submitted = true;
1698-
let current_block = state_manager.apply(|state| state.bittensor_block);
1699-
if current_block == 0 {
1700-
warn!("Startup weight submission skipped: blockchain not yet synced");
1701-
} else if subtensor.is_none() || subtensor_signer.is_none() {
1702-
warn!("Startup weight submission skipped: subtensor not connected");
1703-
} else if wasm_executor.is_none() {
1704-
warn!("Startup weight submission skipped: WASM executor not ready");
1705-
} else {
1706-
let has_challenges = {
1707-
let cs = chain_state.read();
1708-
cs.wasm_challenge_configs.iter().any(|(_, cfg)| cfg.is_active)
1709-
};
1710-
if !has_challenges {
1711-
warn!("Startup weight submission skipped: no active challenges loaded");
1712-
} else {
1713-
// Refresh metagraph before startup weight submission
1714-
if let Some(bittensor_client) = bittensor_client_for_metagraph.as_ref() {
1715-
match tokio::time::timeout(
1716-
Duration::from_secs(15),
1717-
sync_metagraph(bittensor_client, netuid),
1718-
).await {
1719-
Ok(Ok(mg)) => {
1720-
info!("Startup metagraph refresh: {} neurons", mg.n);
1721-
update_validator_set_from_metagraph(&mg, &validator_set, &chain_state, &valid_voters, &state_root_consensus, &state_manager);
1722-
if let Some(sc) = subtensor_client.as_mut() {
1723-
sc.set_metagraph(mg);
1724-
}
1725-
}
1726-
Ok(Err(e)) => warn!("Startup metagraph refresh failed: {}", e),
1727-
Err(_) => warn!("Startup metagraph refresh timed out after 15s"),
1728-
}
1729-
}
1730-
let tempo = 360u64;
1731-
let netuid_plus_one = (netuid as u64).saturating_add(1);
1732-
let epoch = current_block.saturating_add(netuid_plus_one) / (tempo + 1);
1733-
// Only submit if we're actually in the commit window phase.
1734-
// The commit window is the first block of each epoch.
1735-
let epoch_start = epoch.saturating_mul(tempo + 1).saturating_sub(netuid_plus_one);
1736-
let blocks_into_epoch = current_block.saturating_sub(epoch_start);
1737-
// Commit window is typically ~1 block at the start of each epoch.
1738-
// If we're past it, wait for the next real CommitWindowOpen event.
1739-
if blocks_into_epoch > 5 {
1740-
info!(
1741-
"Startup weight submission skipped: block {} is {} blocks into epoch {} (past commit window), will wait for next epoch",
1742-
current_block, blocks_into_epoch, epoch
1743-
);
1744-
} else {
1745-
info!("Startup weight submission: epoch {} block {} (90s after boot)", epoch, current_block);
1746-
handle_block_event(
1747-
BlockSyncEvent::CommitWindowOpen { epoch, block: current_block },
1748-
&subtensor, &subtensor_signer, &subtensor_client,
1749-
&state_manager, netuid, version_key, &wasm_executor,
1750-
&keypair, &chain_state, &storage,
1751-
&mut last_weight_submission_epoch,
1752-
).await;
1753-
}
1754-
}
1755-
}
1744+
info!("Startup weight submission disabled: weights are set at block intervals (every {} blocks)",
1745+
platform_core::constants::WEIGHT_SET_BLOCK_INTERVAL);
17561746
}
17571747

17581748
// Periodic checkpoint

crates/core/src/constants.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,11 @@ pub const BOOTSTRAP_VALIDATOR_SS58: &str = "5GziQCcRpN8NCJktX343brnfuVe3w6gUYiee
9191
/// Weight assigned to UID 0 during the bootstrap period
9292
pub const BOOTSTRAP_UID0_WEIGHT: u16 = u16::MAX;
9393

94+
/// Block interval at which ALL validators must set weights simultaneously.
95+
/// Every N Bittensor blocks, when `bittensor_block % WEIGHT_SET_BLOCK_INTERVAL == 0`,
96+
/// all validators compute and submit weights at the same block.
97+
pub const WEIGHT_SET_BLOCK_INTERVAL: u64 = 100;
98+
9499
#[cfg(test)]
95100
mod tests {
96101
use super::*;

0 commit comments

Comments
 (0)