Skip to content

Commit 475126d

Browse files
author
EchoBT
committed
feat(weights): Add CRv4 timelock encryption support
- Auto-detect commit-reveal version from chain - Use CRv4 (TLE) when version >= 4, fallback to hash-based CR - No persistence needed for CRv4 - chain auto-reveals via DRAND - Update bittensor-rs to 39e6ea3 with CRv4 support - Add w3f-bls patch for TLE compatibility
1 parent 4bd7164 commit 475126d

File tree

3 files changed

+213
-11
lines changed

3 files changed

+213
-11
lines changed

Cargo.lock

Lines changed: 75 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,8 +29,8 @@ authors = ["Platform Network"]
2929
license = "MIT"
3030

3131
[workspace.dependencies]
32-
# Bittensor
33-
bittensor-rs = { git = "https://github.com/CortexLM/bittensor-rs", rev = "52e9c1f" }
32+
# Bittensor (with CRv4 timelock encryption support)
33+
bittensor-rs = { git = "https://github.com/CortexLM/bittensor-rs", rev = "39e6ea3" }
3434

3535
# Async runtime
3636
tokio = { version = "1.40", features = ["full", "sync", "macros", "rt-multi-thread"] }
@@ -74,3 +74,7 @@ clap = { version = "4.5", features = ["derive"] }
7474

7575
# Testing
7676
tempfile = "3.12"
77+
78+
# Patch for TLE/CRv4 compatibility (w3f-bls version conflict)
79+
[patch.crates-io]
80+
w3f-bls = { git = "https://github.com/opentensor/bls", branch = "fix-no-std" }

crates/bittensor-integration/src/weights.rs

Lines changed: 132 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,10 @@
33
//! This module handles submitting weights to the Bittensor network using the
44
//! commit-reveal pattern that matches subtensor's exact format.
55
//!
6+
//! ## CRv4 Support
7+
//! When commit_reveal_version == 4, uses timelock encryption (TLE) for automatic
8+
//! on-chain reveal. The chain decrypts weights when DRAND pulse becomes available.
9+
//!
610
//! ## Persistence
711
//! Commits are persisted to disk to survive restarts. This ensures that if
812
//! the validator restarts between commit and reveal, it can still reveal
@@ -19,6 +23,12 @@ use bittensor_rs::{
1923
commit_mechanism_weights, get_next_epoch_start_block, reveal_mechanism_weights,
2024
set_mechanism_weights,
2125
};
26+
// CRv4 imports (no persistence needed - chain auto-reveals)
27+
use bittensor_rs::crv4::{
28+
calculate_reveal_round, commit_timelocked_mechanism_weights, get_commit_reveal_version,
29+
get_mechid_storage_index, get_reveal_period, get_tempo, prepare_crv4_commit,
30+
DEFAULT_COMMIT_REVEAL_VERSION,
31+
};
2232
use platform_challenge_sdk::WeightAssignment;
2333
use serde::{Deserialize, Serialize};
2434
use std::collections::HashMap;
@@ -104,12 +114,14 @@ impl PersistedCommitState {
104114
/// Weight submission manager with persistence
105115
pub struct WeightSubmitter {
106116
client: SubtensorClient,
107-
/// Persisted commit state
117+
/// Persisted commit state (for hash-based commit-reveal v2/v3)
108118
state: PersistedCommitState,
109119
/// Path to persistence file
110120
persist_path: PathBuf,
111121
/// Current epoch (updated externally)
112122
current_epoch: u64,
123+
/// Cached commit-reveal version from chain
124+
cached_crv_version: Option<u16>,
113125
}
114126

115127
/// Pending mechanism commit data
@@ -207,9 +219,30 @@ impl WeightSubmitter {
207219
state,
208220
persist_path,
209221
current_epoch: 0,
222+
cached_crv_version: None,
210223
}
211224
}
212225

226+
/// Get commit-reveal version from chain (cached)
227+
pub async fn get_crv_version(&mut self) -> Result<u16> {
228+
if let Some(v) = self.cached_crv_version {
229+
return Ok(v);
230+
}
231+
232+
let client = self.client.client()?;
233+
let version = get_commit_reveal_version(client)
234+
.await
235+
.unwrap_or(DEFAULT_COMMIT_REVEAL_VERSION);
236+
self.cached_crv_version = Some(version);
237+
info!("Commit-reveal version from chain: {}", version);
238+
Ok(version)
239+
}
240+
241+
/// Check if CRv4 (timelock encryption) is enabled
242+
pub async fn is_crv4_enabled(&mut self) -> bool {
243+
self.get_crv_version().await.unwrap_or(0) >= 4
244+
}
245+
213246
/// Get mutable access to the client
214247
pub fn client_mut(&mut self) -> &mut SubtensorClient {
215248
&mut self.client
@@ -424,6 +457,11 @@ impl WeightSubmitter {
424457
/// This is used at epoch end to submit all mechanism weights at once.
425458
///
426459
/// mechanism_weights: Vec<(mechanism_id, uids, weights)>
460+
///
461+
/// Automatically selects the appropriate method:
462+
/// - CRv4: Uses timelock encryption (chain auto-reveals)
463+
/// - CRv2/v3: Uses hash-based commit-reveal (needs manual reveal)
464+
/// - Direct: No commit-reveal
427465
pub async fn submit_mechanism_weights_batch(
428466
&mut self,
429467
mechanism_weights: &[(u8, Vec<u16>, Vec<u16>)],
@@ -432,13 +470,100 @@ impl WeightSubmitter {
432470
return Err(anyhow::anyhow!("No mechanism weights to submit"));
433471
}
434472

435-
if self.client.use_commit_reveal() {
436-
self.submit_mechanism_weights_batch_commit_reveal(mechanism_weights)
437-
.await
438-
} else {
439-
self.submit_mechanism_weights_batch_direct(mechanism_weights)
440-
.await
473+
// Check commit-reveal mode
474+
if !self.client.use_commit_reveal() {
475+
return self
476+
.submit_mechanism_weights_batch_direct(mechanism_weights)
477+
.await;
478+
}
479+
480+
// Check CRv4 (timelock encryption)
481+
let crv_version = self.get_crv_version().await.unwrap_or(0);
482+
if crv_version >= 4 {
483+
info!("Using CRv4 (timelock encryption) for weight submission");
484+
return self
485+
.submit_mechanism_weights_batch_crv4(mechanism_weights)
486+
.await;
441487
}
488+
489+
// Fall back to hash-based commit-reveal
490+
self.submit_mechanism_weights_batch_commit_reveal(mechanism_weights)
491+
.await
492+
}
493+
494+
/// Submit mechanism weights using CRv4 (timelock encryption)
495+
/// No manual reveal needed - chain decrypts automatically when DRAND pulse arrives
496+
async fn submit_mechanism_weights_batch_crv4(
497+
&mut self,
498+
mechanism_weights: &[(u8, Vec<u16>, Vec<u16>)],
499+
) -> Result<String> {
500+
let client = self.client.client()?;
501+
let signer = self.client.signer()?;
502+
let netuid = self.client.netuid();
503+
let version_key = self.client.version_key();
504+
let hotkey_bytes = signer.account_id().0.to_vec();
505+
506+
// Get chain parameters for reveal round calculation
507+
let current_block = client.block_number().await?;
508+
let tempo = get_tempo(client, netuid).await.unwrap_or(360);
509+
let reveal_period = get_reveal_period(client, netuid).await.unwrap_or(1);
510+
let block_time = 12.0; // Standard Bittensor block time
511+
let crv_version = self
512+
.cached_crv_version
513+
.unwrap_or(DEFAULT_COMMIT_REVEAL_VERSION);
514+
515+
let mut last_tx = String::new();
516+
let mut committed_count = 0;
517+
518+
for (mechanism_id, uids, weights) in mechanism_weights {
519+
// Calculate reveal round for this mechanism
520+
let storage_index = get_mechid_storage_index(netuid, *mechanism_id);
521+
let reveal_round = calculate_reveal_round(
522+
tempo,
523+
current_block,
524+
storage_index,
525+
reveal_period,
526+
block_time,
527+
);
528+
529+
// Encrypt payload using TLE
530+
let encrypted =
531+
prepare_crv4_commit(&hotkey_bytes, uids, weights, version_key, reveal_round)?;
532+
533+
info!(
534+
"CRv4 committing mechanism {}: {} uids, reveal_round={}",
535+
mechanism_id,
536+
uids.len(),
537+
reveal_round
538+
);
539+
540+
// Submit timelocked commit - no persistence needed, chain auto-reveals
541+
let tx_hash = commit_timelocked_mechanism_weights(
542+
client,
543+
signer,
544+
netuid,
545+
*mechanism_id,
546+
&encrypted,
547+
reveal_round,
548+
crv_version,
549+
ExtrinsicWait::Finalized,
550+
)
551+
.await?;
552+
553+
info!(
554+
"CRv4 mechanism {} committed: {} (auto-reveal at round {})",
555+
mechanism_id, tx_hash, reveal_round
556+
);
557+
558+
last_tx = tx_hash;
559+
committed_count += 1;
560+
}
561+
562+
info!(
563+
"CRv4 batch complete: {} mechanisms committed (no reveal needed)",
564+
committed_count
565+
);
566+
Ok(last_tx)
442567
}
443568

444569
/// Submit mechanism weights directly (without commit-reveal)

0 commit comments

Comments
 (0)