Skip to content

Commit f4f9ad1

Browse files
authored
Merge pull request #16 from leonace924/fix/weight-validation
Add Weight Submission Divergence Validation
2 parents b05421b + d586a89 commit f4f9ad1

File tree

1 file changed

+77
-8
lines changed

1 file changed

+77
-8
lines changed

crates/epoch/src/commit_reveal.rs

Lines changed: 77 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,8 @@ use crate::{FinalizedWeights, WeightCommitment, WeightReveal};
66
use parking_lot::RwLock;
77
use platform_challenge_sdk::{weights, ChallengeId, WeightAssignment};
88
use 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
1313
pub 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

Comments
 (0)