@@ -912,6 +912,9 @@ async fn main() -> Result<()> {
912912 let mut stale_job_interval = tokio:: time:: interval ( Duration :: from_secs ( 120 ) ) ;
913913 let mut weight_check_interval = tokio:: time:: interval ( Duration :: from_secs ( 30 ) ) ;
914914 let mut last_weight_submission_epoch: u64 = 0 ; // Local tracking of weight submissions
915+ let mut challenge_sync_interval = tokio:: time:: interval ( Duration :: from_secs ( 60 ) ) ; // Check every minute
916+ let mut last_sync_block: u64 = 0 ; // Last block where sync was triggered
917+ let sync_block_interval: u64 = 60 ; // Sync every 60 blocks
915918
916919 // Clone p2p_cmd_tx for use in the loop
917920 let p2p_broadcast_tx = p2p_cmd_tx. clone ( ) ;
@@ -1497,6 +1500,78 @@ async fn main() -> Result<()> {
14971500 }
14981501 }
14991502
1503+ // Challenge sync ticker - every 60 blocks, sync all challenges
1504+ _ = challenge_sync_interval. tick( ) => {
1505+ if !is_bootnode {
1506+ let current_block = state_manager. apply( |state| state. bittensor_block) ;
1507+
1508+ // Check if we should sync (every 60 blocks)
1509+ if current_block / sync_block_interval > last_sync_block / sync_block_interval {
1510+ last_sync_block = current_block;
1511+
1512+ // Get all active challenges
1513+ let challenges: Vec <_> = {
1514+ let cs = chain_state. read( ) ;
1515+ cs. wasm_challenge_configs
1516+ . iter( )
1517+ . filter( |( _, cfg) | cfg. is_active)
1518+ . map( |( id, _) | * id)
1519+ . collect( )
1520+ } ;
1521+
1522+ for challenge_id in challenges {
1523+ let challenge_id_str = challenge_id. to_string( ) ;
1524+ let module_path = format!( "{}.wasm" , challenge_id_str) ;
1525+
1526+ if let Some ( ref executor) = wasm_executor {
1527+ match executor. execute_sync( & module_path) {
1528+ Ok ( sync_result) => {
1529+ info!(
1530+ challenge_id = %challenge_id,
1531+ block = current_block,
1532+ total_users = sync_result. total_users,
1533+ "Challenge sync completed, broadcasting proposal"
1534+ ) ;
1535+
1536+ // Broadcast sync proposal
1537+ let timestamp = chrono:: Utc :: now( ) . timestamp_millis( ) ;
1538+ let proposal_data = bincode:: serialize( & (
1539+ & challenge_id,
1540+ & sync_result. leaderboard_hash,
1541+ current_block,
1542+ timestamp
1543+ ) ) . unwrap_or_default( ) ;
1544+ let signature = keypair. sign_bytes( & proposal_data) . unwrap_or_default( ) ;
1545+
1546+ let proposal_msg = P2PMessage :: ChallengeSyncProposal (
1547+ platform_p2p_consensus:: ChallengeSyncProposalMessage {
1548+ challenge_id,
1549+ sync_result_hash: sync_result. leaderboard_hash,
1550+ proposer: keypair. hotkey( ) ,
1551+ block_number: current_block,
1552+ timestamp,
1553+ signature,
1554+ }
1555+ ) ;
1556+
1557+ if let Err ( e) = p2p_broadcast_tx. send( platform_p2p_consensus:: P2PCommand :: Broadcast ( proposal_msg) ) . await {
1558+ warn!( error = %e, "Failed to broadcast sync proposal" ) ;
1559+ }
1560+ }
1561+ Err ( e) => {
1562+ debug!(
1563+ challenge_id = %challenge_id,
1564+ error = %e,
1565+ "Failed to execute sync (WASM may not support sync)"
1566+ ) ;
1567+ }
1568+ }
1569+ }
1570+ }
1571+ }
1572+ }
1573+ }
1574+
15001575 // Ctrl+C
15011576 _ = tokio:: signal:: ctrl_c( ) => {
15021577 info!( "Received shutdown signal, persisting state..." ) ;
@@ -3002,6 +3077,21 @@ async fn handle_network_event(
30023077 }
30033078 }
30043079 }
3080+ P2PMessage :: ChallengeSyncProposal ( proposal) => {
3081+ info ! (
3082+ challenge_id = %proposal. challenge_id,
3083+ block = proposal. block_number,
3084+ proposer = %proposal. proposer. to_ss58( ) ,
3085+ "Received challenge sync proposal (consensus voting not yet implemented)"
3086+ ) ;
3087+ }
3088+ P2PMessage :: ChallengeSyncVote ( vote) => {
3089+ debug ! (
3090+ challenge_id = %vote. challenge_id,
3091+ voter = %vote. voter. to_ss58( ) ,
3092+ "Received challenge sync vote (consensus voting not yet implemented)"
3093+ ) ;
3094+ }
30053095 } ,
30063096 NetworkEvent :: PeerConnected ( peer_id) => {
30073097 info ! ( "Peer connected: {}" , peer_id) ;
0 commit comments