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+ } ;
2232use platform_challenge_sdk:: WeightAssignment ;
2333use serde:: { Deserialize , Serialize } ;
2434use std:: collections:: HashMap ;
@@ -104,12 +114,14 @@ impl PersistedCommitState {
104114/// Weight submission manager with persistence
105115pub 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