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
59use std:: sync:: atomic:: { AtomicBool , Ordering } ;
610use std:: sync:: Arc ;
@@ -10,9 +14,12 @@ use anyhow::Result;
1014use chrono:: { Timelike , Utc } ;
1115use reqwest:: Client ;
1216use serde:: Deserialize ;
17+ use tokio:: sync:: Mutex ;
1318use 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
1825const 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
5159pub 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
6066impl 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