Skip to content

Commit b6b7174

Browse files
committed
fix(validator): use CRv4 commit-reveal for standalone weight submitter
Change from direct set_weights to WeightSubmitter which handles: - CRv4 timelock encryption automatically - Chain auto-reveals when DRAND pulse arrives - No manual reveal needed The WeightSubmitter detects chain version and encrypts weights with DRAND-based timelock encryption.
1 parent 25c784f commit b6b7174

File tree

2 files changed

+74
-61
lines changed

2 files changed

+74
-61
lines changed

bins/validator-node/src/main.rs

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -739,12 +739,19 @@ async fn main() -> Result<()> {
739739
info!("Background hourly weight handler spawned");
740740

741741
// Spawn standalone hourly weight submitter (decoupled from P2P)
742-
standalone_weight_submitter = Some(spawn_standalone_weight_submitter(
743-
subtensor.as_ref().unwrap().clone(),
744-
(**subtensor_signer.as_ref().unwrap()).clone(),
742+
match spawn_standalone_weight_submitter(
743+
endpoint,
745744
args.netuid,
746-
));
747-
info!("Standalone hourly weight submitter spawned");
745+
secret,
746+
).await {
747+
Ok(handle) => {
748+
standalone_weight_submitter = Some(handle);
749+
info!("Standalone hourly weight submitter spawned");
750+
}
751+
Err(e) => {
752+
error!("Failed to spawn standalone weight submitter: {}", e);
753+
}
754+
}
748755

749756
if endpoint != &args.subtensor_endpoint {
750757
warn!(

bins/validator-node/src/standalone_weight_submitter.rs

Lines changed: 62 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
11
//! Standalone weight submission thread.
22
//! Completely decoupled from P2P rate limiting and block sync.
33
//! Fetches weights from chain.platform.zip and submits directly to Bittensor.
4+
//!
5+
//! Uses CRv4 commit-reveal for weight submission:
6+
//! - Commit: Encrypt weights with timelock (DRAND-based)
7+
//! - Reveal: Automatic when DRAND pulse becomes available
48
59
use std::sync::atomic::{AtomicBool, Ordering};
610
use std::sync::Arc;
@@ -10,9 +14,12 @@ use anyhow::Result;
1014
use chrono::{Timelike, Utc};
1115
use reqwest::Client;
1216
use serde::Deserialize;
17+
use tokio::sync::Mutex;
1318
use tracing::{debug, error, info, warn};
1419

15-
use platform_bittensor::{BittensorSigner, ExtrinsicWait, Subtensor};
20+
use platform_bittensor::{
21+
BittensorConfig, SubtensorClient, WeightSubmitter,
22+
};
1623

1724
/// URL for fetching pre-computed weights
1825
const WEIGHT_RPC_URL: &str = "https://chain.platform.zip/rpc";
@@ -48,35 +55,44 @@ struct WeightEntry {
4855
}
4956

5057
/// Standalone weight submitter - no P2P dependencies
58+
/// Uses WeightSubmitter internally for CRv4 commit-reveal support
5159
pub struct StandaloneWeightSubmitter {
52-
subtensor: Arc<Subtensor>,
53-
signer: BittensorSigner,
54-
netuid: u16,
60+
weight_submitter: Arc<Mutex<WeightSubmitter>>,
5561
http_client: Client,
56-
last_submission_hour: Arc<tokio::sync::Mutex<Option<i64>>>,
62+
last_submission_hour: Arc<Mutex<Option<i64>>>,
5763
running: Arc<AtomicBool>,
5864
}
5965

6066
impl StandaloneWeightSubmitter {
61-
/// Create a new standalone weight submitter
62-
pub fn new(
63-
subtensor: Arc<Subtensor>,
64-
signer: BittensorSigner,
67+
pub async fn new(
68+
endpoint: &str,
6569
netuid: u16,
66-
) -> Self {
70+
signer_seed: &str,
71+
) -> Result<Self> {
6772
let http_client = Client::builder()
6873
.timeout(Duration::from_secs(30))
6974
.build()
7075
.expect("Failed to create HTTP client");
7176

72-
Self {
73-
subtensor,
74-
signer,
77+
let config = BittensorConfig {
78+
endpoint: endpoint.to_string(),
7579
netuid,
80+
use_commit_reveal: true,
81+
version_key: 1,
82+
};
83+
84+
let mut client = SubtensorClient::new(config);
85+
client.connect().await?;
86+
client.set_signer(signer_seed)?;
87+
88+
let weight_submitter = WeightSubmitter::new(client, None);
89+
90+
Ok(Self {
91+
weight_submitter: Arc::new(Mutex::new(weight_submitter)),
7692
http_client,
77-
last_submission_hour: Arc::new(tokio::sync::Mutex::new(None)),
93+
last_submission_hour: Arc::new(Mutex::new(None)),
7894
running: Arc::new(AtomicBool::new(true)),
79-
}
95+
})
8096
}
8197

8298
/// Shutdown gracefully
@@ -85,26 +101,26 @@ impl StandaloneWeightSubmitter {
85101
info!("Standalone weight submitter shutdown requested");
86102
}
87103

88-
/// Run the submission loop (call in a spawned task)
89104
pub async fn run(&self) {
90105
let mut interval = tokio::time::interval(Duration::from_secs(CHECK_INTERVAL_SECS));
91106

92107
info!(
93108
url = WEIGHT_RPC_URL,
94-
netuid = self.netuid,
95109
"Standalone weight submitter started (hourly at :00)"
96110
);
97111

98-
while self.running.load(Ordering::SeqCst) {
112+
loop {
99113
interval.tick().await;
100114

115+
if !self.running.load(Ordering::SeqCst) {
116+
break;
117+
}
118+
101119
let now = Utc::now();
102120

103-
// Check if we're at the top of the hour (minute 0)
104121
if now.minute() == 0 {
105122
let hour_epoch = now.timestamp() / 3600;
106123

107-
// Dedup: only submit once per hour
108124
let mut last = self.last_submission_hour.lock().await;
109125
if let Some(last_hour) = *last {
110126
if last_hour == hour_epoch {
@@ -113,7 +129,6 @@ impl StandaloneWeightSubmitter {
113129
}
114130
}
115131

116-
// Try to submit with retries
117132
match self.submit_with_retry().await {
118133
Ok(()) => {
119134
*last = Some(hour_epoch);
@@ -155,40 +170,31 @@ impl StandaloneWeightSubmitter {
155170
Err(last_error.unwrap_or_else(|| anyhow::anyhow!("Unknown error")))
156171
}
157172

158-
/// Fetch weights from RPC and submit to chain
159173
async fn fetch_and_submit(&self) -> Result<()> {
160-
// 1. Fetch from chain.platform.zip
161174
let weights = self.fetch_weights().await?;
162175

163176
if weights.is_empty() {
164177
warn!("Empty weights from RPC");
165178
return Ok(());
166179
}
167180

168-
// 2. Submit each mechanism
169-
for mw in weights {
170-
let uids: Vec<u16> = mw.entries.iter().map(|e| e.uid).collect();
171-
let weights: Vec<u16> = mw.entries.iter().map(|e| e.weight).collect();
172-
173-
info!(
174-
mechanism_id = mw.mechanism_id,
175-
uid_count = uids.len(),
176-
total_weight = weights.iter().map(|w| *w as u64).sum::<u64>(),
177-
"Submitting weights to Bittensor"
178-
);
179-
180-
self.subtensor
181-
.set_weights(
182-
&self.signer,
183-
self.netuid,
184-
&uids,
185-
&weights,
186-
mw.mechanism_id as u64,
187-
ExtrinsicWait::Finalized,
188-
)
189-
.await
190-
.map_err(|e| anyhow::anyhow!("set_weights failed: {}", e))?;
191-
}
181+
let mechanism_weights: Vec<(u8, Vec<u16>, Vec<u16>)> = weights
182+
.into_iter()
183+
.map(|mw| {
184+
let uids: Vec<u16> = mw.entries.iter().map(|e| e.uid).collect();
185+
let weights: Vec<u16> = mw.entries.iter().map(|e| e.weight).collect();
186+
info!(
187+
mechanism_id = mw.mechanism_id,
188+
uid_count = uids.len(),
189+
total_weight = weights.iter().map(|w| *w as u64).sum::<u64>(),
190+
"Submitting weights to Bittensor via CRv4"
191+
);
192+
(mw.mechanism_id, uids, weights)
193+
})
194+
.collect();
195+
196+
let mut submitter = self.weight_submitter.lock().await;
197+
submitter.submit_mechanism_weights_batch(&mechanism_weights).await?;
192198

193199
Ok(())
194200
}
@@ -224,20 +230,20 @@ impl StandaloneWeightSubmitter {
224230
}
225231
}
226232

227-
/// Spawn the standalone weight submitter in a dedicated task.
228-
pub fn spawn_standalone_weight_submitter(
229-
subtensor: Arc<Subtensor>,
230-
signer: BittensorSigner,
233+
pub async fn spawn_standalone_weight_submitter(
234+
endpoint: &str,
231235
netuid: u16,
232-
) -> Arc<StandaloneWeightSubmitter> {
233-
let submitter = Arc::new(StandaloneWeightSubmitter::new(subtensor, signer, netuid));
236+
signer_seed: &str,
237+
) -> Result<Arc<StandaloneWeightSubmitter>> {
238+
let submitter = Arc::new(StandaloneWeightSubmitter::new(endpoint, netuid, signer_seed).await?);
234239
let submitter_clone = submitter.clone();
235240

236-
tokio::spawn(async move {
237-
submitter_clone.run().await;
241+
std::thread::spawn(move || {
242+
let rt = tokio::runtime::Runtime::new().expect("Failed to create runtime for weight submitter");
243+
rt.block_on(submitter_clone.run());
238244
});
239245

240-
submitter
246+
Ok(submitter)
241247
}
242248

243249
#[cfg(test)]

0 commit comments

Comments
 (0)