@@ -660,6 +660,13 @@ async fn main() -> Result<()> {
660660
661661 // Cast storage to trait object for WASM executor
662662 let storage_dyn: Arc < dyn DistributedStore > = Arc :: clone ( & storage) as Arc < dyn DistributedStore > ;
663+
664+ // Shared chain state data for WASM instances
665+ let shared_llm_validators_json: Arc < parking_lot:: RwLock < Vec < u8 > > > =
666+ Arc :: new ( parking_lot:: RwLock :: new ( Vec :: new ( ) ) ) ;
667+ let shared_registered_hotkeys_json: Arc < parking_lot:: RwLock < Vec < u8 > > > =
668+ Arc :: new ( parking_lot:: RwLock :: new ( Vec :: new ( ) ) ) ;
669+
663670 let wasm_executor = match WasmChallengeExecutor :: new ( WasmExecutorConfig {
664671 module_dir : wasm_module_dir. clone ( ) ,
665672 max_memory_bytes : args. wasm_max_memory ,
@@ -674,6 +681,8 @@ async fn main() -> Result<()> {
674681 ) ) ,
675682 chutes_api_key : args. chutes_api_key . clone ( ) ,
676683 distributed_storage : Some ( storage_dyn) ,
684+ llm_validators_json : Arc :: clone ( & shared_llm_validators_json) ,
685+ registered_hotkeys_json : Arc :: clone ( & shared_registered_hotkeys_json) ,
677686 } ) {
678687 Ok ( executor) => {
679688 info ! (
@@ -985,6 +994,8 @@ async fn main() -> Result<()> {
985994 & p2p_cmd_tx,
986995 & chain_state,
987996 & challenge_last_sync,
997+ & shared_llm_validators_json,
998+ & shared_registered_hotkeys_json,
988999 ) . await ;
9891000 }
9901001
@@ -1368,6 +1379,9 @@ async fn main() -> Result<()> {
13681379 stake: our_stake,
13691380 timestamp: chrono:: Utc :: now( ) . timestamp_millis( ) ,
13701381 signature: vec![ ] , // Will be signed by P2P layer
1382+ capabilities: platform_p2p_consensus:: ValidatorCapabilities {
1383+ has_llm: args. chutes_api_key. is_some( ) ,
1384+ } ,
13711385 } ) ;
13721386
13731387 if let Err ( e) = p2p_broadcast_tx. send( platform_p2p_consensus:: P2PCommand :: Broadcast ( heartbeat) ) . await {
@@ -1844,6 +1858,8 @@ async fn handle_network_event(
18441858 p2p_cmd_tx : & tokio:: sync:: mpsc:: Sender < platform_p2p_consensus:: P2PCommand > ,
18451859 chain_state : & Arc < RwLock < platform_core:: ChainState > > ,
18461860 challenge_last_sync : & Arc < RwLock < std:: collections:: HashMap < platform_core:: ChallengeId , u64 > > > ,
1861+ shared_llm_validators_json : & Arc < parking_lot:: RwLock < Vec < u8 > > > ,
1862+ shared_registered_hotkeys_json : & Arc < parking_lot:: RwLock < Vec < u8 > > > ,
18471863) {
18481864 match event {
18491865 NetworkEvent :: Message { source, message } => match message {
@@ -1932,6 +1948,33 @@ async fn handle_network_event(
19321948 . validators
19331949 . insert ( hb. validator . clone ( ) , validator_info) ;
19341950
1951+ // Track LLM-capable validators
1952+ if hb. capabilities . has_llm {
1953+ state. llm_capable_validators . insert ( hb. validator . clone ( ) ) ;
1954+ } else {
1955+ state. llm_capable_validators . remove ( & hb. validator ) ;
1956+ }
1957+
1958+ // Update shared WASM-accessible chain state
1959+ if let Ok ( json) = serde_json:: to_vec (
1960+ & state
1961+ . llm_capable_validators
1962+ . iter ( )
1963+ . map ( |h| h. to_ss58 ( ) )
1964+ . collect :: < Vec < _ > > ( ) ,
1965+ ) {
1966+ * shared_llm_validators_json. write ( ) = json;
1967+ }
1968+ if let Ok ( json) = serde_json:: to_vec (
1969+ & state
1970+ . registered_hotkeys
1971+ . iter ( )
1972+ . map ( |h| h. to_ss58 ( ) )
1973+ . collect :: < Vec < _ > > ( ) ,
1974+ ) {
1975+ * shared_registered_hotkeys_json. write ( ) = json;
1976+ }
1977+
19351978 // Check core state hash divergence
19361979 let our_core_hash = state. state_hash ;
19371980 drop ( state) ;
@@ -3626,16 +3669,54 @@ async fn handle_block_event(
36263669 }
36273670 }
36283671
3629- // Submit weights (or burn if none)
3630- let weights_to_submit = if mechanism_weights. is_empty ( ) {
3672+ // Merge weights by mechanism_id before submitting.
3673+ // Multiple challenges may produce weights for the same mechanism;
3674+ // we must merge them into a single submission to avoid nonce conflicts.
3675+ let weights_to_submit: Vec < ( u8 , Vec < u16 > , Vec < u16 > ) > = if mechanism_weights
3676+ . is_empty ( )
3677+ {
36313678 let mechanism_id = {
36323679 let cs = chain_state. read ( ) ;
36333680 cs. mechanism_configs . keys ( ) . next ( ) . copied ( ) . unwrap_or ( 0u8 )
36343681 } ;
36353682 info ! ( "No weights - submitting burn weights to UID 0" ) ;
36363683 vec ! [ ( mechanism_id, vec![ 0u16 ] , vec![ 65535u16 ] ) ]
36373684 } else {
3638- mechanism_weights
3685+ use std:: collections:: HashMap ;
3686+ let mut merged: HashMap < u8 , HashMap < u16 , u64 > > = HashMap :: new ( ) ;
3687+ for ( mid, uids, vals) in & mechanism_weights {
3688+ let entry = merged. entry ( * mid) . or_default ( ) ;
3689+ for ( uid, val) in uids. iter ( ) . zip ( vals. iter ( ) ) {
3690+ * entry. entry ( * uid) . or_default ( ) += * val as u64 ;
3691+ }
3692+ }
3693+ let mut result = Vec :: new ( ) ;
3694+ for ( mid, uid_map) in merged {
3695+ let max_val = uid_map. values ( ) . copied ( ) . max ( ) . unwrap_or ( 1 ) . max ( 1 ) ;
3696+ let mut uids = Vec :: new ( ) ;
3697+ let mut vals = Vec :: new ( ) ;
3698+ for ( uid, val) in & uid_map {
3699+ let scaled = ( ( * val as f64 / max_val as f64 ) * 65535.0 ) . round ( ) as u16 ;
3700+ if scaled > 0 {
3701+ uids. push ( * uid) ;
3702+ vals. push ( scaled) ;
3703+ }
3704+ }
3705+ if !uids. is_empty ( ) {
3706+ info ! ( "Merged weights for mechanism {}: {} UIDs" , mid, uids. len( ) ) ;
3707+ debug ! ( " Merged UIDs: {:?}, Weights: {:?}" , uids, vals) ;
3708+ result. push ( ( mid, uids, vals) ) ;
3709+ }
3710+ }
3711+ if result. is_empty ( ) {
3712+ let mechanism_id = {
3713+ let cs = chain_state. read ( ) ;
3714+ cs. mechanism_configs . keys ( ) . next ( ) . copied ( ) . unwrap_or ( 0u8 )
3715+ } ;
3716+ vec ! [ ( mechanism_id, vec![ 0u16 ] , vec![ 65535u16 ] ) ]
3717+ } else {
3718+ result
3719+ }
36393720 } ;
36403721
36413722 for ( mechanism_id, uids, weights) in weights_to_submit {
0 commit comments