Skip to content

Commit e455e29

Browse files
committed
feat: add tempo fetching from Bittensor chain
- Update bittensor-rs to 9cf5991 (exports get_tempo, get_reveal_period) - Add tempo and current_block fields to AppState - Fetch tempo from Bittensor on startup and cache it - Return tempo in /api/v1/network/state endpoint - NetworkStateEvent now includes tempo field
1 parent 372641c commit e455e29

File tree

7 files changed

+95
-25
lines changed

7 files changed

+95
-25
lines changed

Cargo.lock

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,8 +31,8 @@ license = "Apache-2.0"
3131

3232
[workspace.dependencies]
3333
# Bittensor (with CRv4 timelock encryption support and new Subtensor API)
34-
# Updated to 453ce56: proper SCALE decoding for stake (includes parent inheritance)
35-
bittensor-rs = { git = "https://github.com/CortexLM/bittensor-rs", rev = "453ce56" }
34+
# Updated to 9cf5991: export get_tempo and get_reveal_period functions
35+
bittensor-rs = { git = "https://github.com/CortexLM/bittensor-rs", rev = "9cf5991" }
3636

3737
# Async runtime
3838
tokio = { version = "1.40", features = ["full", "sync", "macros", "rt-multi-thread"] }

bins/platform/src/server.rs

Lines changed: 40 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -108,30 +108,49 @@ pub async fn run(args: ServerArgs) -> Result<()> {
108108
let db = db::init_db(&args.database_url).await?;
109109
info!("Database: platform_server");
110110

111-
// Sync metagraph BEFORE accepting connections (blocking)
111+
// Sync metagraph and fetch tempo BEFORE accepting connections (blocking)
112112
info!("Syncing metagraph (netuid={})...", args.netuid);
113-
let metagraph = match platform_bittensor::BittensorClient::new(&args.subtensor_endpoint).await {
114-
Ok(client) => match platform_bittensor::sync_metagraph(&client, args.netuid).await {
115-
Ok(mg) => {
116-
info!("Metagraph synced: {} neurons", mg.n);
117-
Some(mg)
113+
let (metagraph, tempo) =
114+
match platform_bittensor::BittensorClient::new(&args.subtensor_endpoint).await {
115+
Ok(client) => {
116+
// Fetch tempo from chain
117+
let tempo = match platform_bittensor::get_tempo(&client, args.netuid).await {
118+
Ok(t) => {
119+
info!("Tempo fetched from chain: {}", t);
120+
t as u64
121+
}
122+
Err(e) => {
123+
warn!("Failed to fetch tempo: {} (using default 360)", e);
124+
360u64
125+
}
126+
};
127+
128+
// Sync metagraph
129+
let metagraph = match platform_bittensor::sync_metagraph(&client, args.netuid).await
130+
{
131+
Ok(mg) => {
132+
info!("Metagraph synced: {} neurons", mg.n);
133+
Some(mg)
134+
}
135+
Err(e) => {
136+
warn!(
137+
"Metagraph sync failed: {} (validators will have stake=0)",
138+
e
139+
);
140+
None
141+
}
142+
};
143+
144+
(metagraph, tempo)
118145
}
119146
Err(e) => {
120147
warn!(
121-
"Metagraph sync failed: {} (validators will have stake=0)",
148+
"Could not connect to subtensor: {} (validators will have stake=0, tempo=360)",
122149
e
123150
);
124-
None
151+
(None, 360u64)
125152
}
126-
},
127-
Err(e) => {
128-
warn!(
129-
"Could not connect to subtensor: {} (validators will have stake=0)",
130-
e
131-
);
132-
None
133-
}
134-
};
153+
};
135154

136155
// Initialize challenge orchestrator
137156
info!("Initializing Challenge Orchestrator...");
@@ -157,6 +176,10 @@ pub async fn run(args: ServerArgs) -> Result<()> {
157176
metagraph,
158177
));
159178

179+
// Set cached tempo from Bittensor
180+
state.set_tempo(tempo);
181+
info!("Tempo cached: {} blocks per epoch", tempo);
182+
160183
// Build router
161184
let app = Router::new()
162185
.route("/health", get(health_check))

crates/bittensor-integration/src/lib.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,5 +39,8 @@ pub use bittensor_rs::{
3939
WeightResponseData,
4040
};
4141

42+
// Re-export tempo/epoch functions
43+
pub use bittensor_rs::{get_reveal_period, get_tempo};
44+
4245
// Re-export chain types needed for weight submission
4346
pub use bittensor_rs::chain::{signer_from_seed, BittensorSigner, ExtrinsicWait};

crates/platform-server/src/api/challenges.rs

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,22 @@ pub async fn get_network_state(
1010
State(state): State<Arc<AppState>>,
1111
) -> Result<Json<NetworkStateEvent>, StatusCode> {
1212
let current_epoch = queries::get_current_epoch(&state.db).await.unwrap_or(0);
13-
let current_block = queries::get_network_state(&state.db, "current_block")
14-
.await
15-
.unwrap_or(None)
16-
.and_then(|s| s.parse().ok())
17-
.unwrap_or(0u64);
13+
// Use cached current_block from state (updated by block sync)
14+
// Fall back to database if not set
15+
let current_block = {
16+
let cached = state.get_current_block();
17+
if cached > 0 {
18+
cached
19+
} else {
20+
queries::get_network_state(&state.db, "current_block")
21+
.await
22+
.unwrap_or(None)
23+
.and_then(|s| s.parse().ok())
24+
.unwrap_or(0u64)
25+
}
26+
};
27+
// Use cached tempo from state (fetched from Bittensor)
28+
let tempo = state.get_tempo();
1829
let total_stake = queries::get_total_stake(&state.db).await.unwrap_or(0);
1930
let validators = queries::get_validators(&state.db).await.unwrap_or_default();
2031
let pending = queries::get_pending_submissions(&state.db)
@@ -24,6 +35,7 @@ pub async fn get_network_state(
2435
Ok(Json(NetworkStateEvent {
2536
current_epoch,
2637
current_block,
38+
tempo,
2739
total_stake,
2840
active_validators: validators.len() as u32,
2941
pending_submissions: pending.len() as u32,

crates/platform-server/src/models/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -498,6 +498,7 @@ pub struct ValidatorEvent {
498498
pub struct NetworkStateEvent {
499499
pub current_epoch: u64,
500500
pub current_block: u64,
501+
pub tempo: u64,
501502
pub total_stake: u64,
502503
pub active_validators: u32,
503504
pub pending_submissions: u32,

crates/platform-server/src/state.rs

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,9 @@ impl Default for MetricsCache {
5959
}
6060
}
6161

62+
/// Default tempo (blocks per epoch) - Bittensor default
63+
pub const DEFAULT_TEMPO: u64 = 360;
64+
6265
pub struct AppState {
6366
pub db: DbPool,
6467
pub sessions: DashMap<String, crate::models::AuthSession>,
@@ -72,6 +75,10 @@ pub struct AppState {
7275
pub validator_whitelist: RwLock<HashSet<String>>,
7376
/// In-memory cache for validator metrics
7477
pub metrics_cache: MetricsCache,
78+
/// Cached tempo (blocks per epoch) from Bittensor
79+
pub tempo: RwLock<u64>,
80+
/// Current block number from Bittensor
81+
pub current_block: RwLock<u64>,
7582
}
7683

7784
impl AppState {
@@ -92,6 +99,8 @@ impl AppState {
9299
metagraph: RwLock::new(metagraph),
93100
validator_whitelist: RwLock::new(HashSet::new()),
94101
metrics_cache: MetricsCache::new(),
102+
tempo: RwLock::new(DEFAULT_TEMPO),
103+
current_block: RwLock::new(0),
95104
}
96105
}
97106

@@ -112,9 +121,31 @@ impl AppState {
112121
metagraph: RwLock::new(metagraph),
113122
validator_whitelist: RwLock::new(validator_whitelist.into_iter().collect()),
114123
metrics_cache: MetricsCache::new(),
124+
tempo: RwLock::new(DEFAULT_TEMPO),
125+
current_block: RwLock::new(0),
115126
}
116127
}
117128

129+
/// Set tempo (called when syncing with Bittensor)
130+
pub fn set_tempo(&self, tempo: u64) {
131+
*self.tempo.write() = tempo;
132+
}
133+
134+
/// Get cached tempo
135+
pub fn get_tempo(&self) -> u64 {
136+
*self.tempo.read()
137+
}
138+
139+
/// Set current block
140+
pub fn set_current_block(&self, block: u64) {
141+
*self.current_block.write() = block;
142+
}
143+
144+
/// Get current block
145+
pub fn get_current_block(&self) -> u64 {
146+
*self.current_block.read()
147+
}
148+
118149
/// Get validator stake from metagraph (returns 0 if not found)
119150
/// If validator is in whitelist, returns a high stake value (for testing)
120151
pub fn get_validator_stake(&self, hotkey: &str) -> u64 {

0 commit comments

Comments
 (0)