@@ -6,8 +6,8 @@ use crate::{FinalizedWeights, WeightCommitment, WeightReveal};
66use parking_lot:: RwLock ;
77use platform_challenge_sdk:: { weights, ChallengeId , WeightAssignment } ;
88use platform_core:: Hotkey ;
9- use std:: collections:: HashMap ;
10- use tracing:: { debug, info, warn} ;
9+ use std:: collections:: { HashMap , HashSet } ;
10+ use tracing:: { debug, error , info, warn} ;
1111
1212/// Commit-reveal state for a single epoch
1313pub struct CommitRevealState {
@@ -141,13 +141,20 @@ impl CommitRevealState {
141141 } ) ;
142142 }
143143
144+ // Validate that all submissions are consistent
144145 // All validators read from shared chain DB, so submissions should be identical
145- // Just take the first one and normalize
146- let aggregated = submissions
147- . into_iter ( )
148- . next ( )
149- . map ( |w| weights:: normalize_weights ( w) )
150- . unwrap_or_default ( ) ;
146+ let first = & submissions[ 0 ] ;
147+ let divergence_detected = self . check_submission_divergence ( & submissions) ;
148+
149+ if divergence_detected {
150+ error ! (
151+ "Epoch {}: Weight submissions diverged across {} validators! Using first submission." ,
152+ self . epoch,
153+ submissions. len( )
154+ ) ;
155+ }
156+
157+ let aggregated = weights:: normalize_weights ( first. clone ( ) ) ;
151158
152159 let participating: Vec < Hotkey > = self . reveals . keys ( ) . cloned ( ) . collect ( ) ;
153160 let mut excluded = self . missing_reveals . clone ( ) ;
@@ -191,6 +198,68 @@ impl CommitRevealState {
191198 pub fn has_revealed ( & self , validator : & Hotkey ) -> bool {
192199 self . reveals . contains_key ( validator)
193200 }
201+
202+ /// Check if submissions from different validators have diverged.
203+ /// Returns true if divergence is detected.
204+ fn check_submission_divergence ( & self , submissions : & [ Vec < WeightAssignment > ] ) -> bool {
205+ if submissions. len ( ) <= 1 {
206+ return false ;
207+ }
208+
209+ let first = & submissions[ 0 ] ;
210+
211+ // Build a map of hotkey -> weight for the first submission
212+ let first_weights: HashMap < & str , f64 > = first
213+ . iter ( )
214+ . map ( |w| ( w. hotkey . as_str ( ) , w. weight ) )
215+ . collect ( ) ;
216+
217+ // Tolerance for floating-point comparison (0.1% difference allowed)
218+ const WEIGHT_TOLERANCE : f64 = 0.001 ;
219+
220+ for ( idx, submission) in submissions. iter ( ) . enumerate ( ) . skip ( 1 ) {
221+ // Check if same number of weight assignments
222+ if submission. len ( ) != first. len ( ) {
223+ warn ! (
224+ "Epoch {}: Submission {} has {} weights, first has {}" ,
225+ self . epoch,
226+ idx,
227+ submission. len( ) ,
228+ first. len( )
229+ ) ;
230+ return true ;
231+ }
232+
233+ // Check if same hotkeys with similar weights
234+ for weight in submission {
235+ match first_weights. get ( weight. hotkey . as_str ( ) ) {
236+ None => {
237+ warn ! (
238+ "Epoch {}: Submission {} has hotkey {} not in first submission" ,
239+ self . epoch, idx, & weight. hotkey[ ..16 . min( weight. hotkey. len( ) ) ]
240+ ) ;
241+ return true ;
242+ }
243+ Some ( & first_weight) => {
244+ let diff = ( weight. weight - first_weight) . abs ( ) ;
245+ if diff > WEIGHT_TOLERANCE {
246+ warn ! (
247+ "Epoch {}: Weight divergence for hotkey {}: {} vs {} (diff: {:.4})" ,
248+ self . epoch,
249+ & weight. hotkey[ ..16 . min( weight. hotkey. len( ) ) ] ,
250+ first_weight,
251+ weight. weight,
252+ diff
253+ ) ;
254+ return true ;
255+ }
256+ }
257+ }
258+ }
259+ }
260+
261+ false
262+ }
194263}
195264
196265/// Errors for commit-reveal
0 commit comments