mirror of
https://github.com/sigp/lighthouse.git
synced 2026-03-15 02:42:38 +00:00
Minify slashing protection interchange data (#2380)
## Issue Addressed Closes #2354 ## Proposed Changes Add a `minify` method to `slashing_protection::Interchange` that keeps only the maximum-epoch attestation and maximum-slot block for each validator. Specifically, `minify` constructs "synthetic" attestations (with no `signing_root`) containing the maximum source epoch _and_ the maximum target epoch from the input. This is equivalent to the `minify_synth` algorithm that I've formally verified in this repository: https://github.com/michaelsproul/slashing-proofs ## Additional Info Includes the JSON loading optimisation from #2347
This commit is contained in:
@@ -1,5 +1,8 @@
|
||||
use crate::InterchangeError;
|
||||
use serde_derive::{Deserialize, Serialize};
|
||||
use std::collections::HashSet;
|
||||
use std::cmp::max;
|
||||
use std::collections::{HashMap, HashSet};
|
||||
use std::io;
|
||||
use types::{Epoch, Hash256, PublicKeyBytes, Slot};
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Deserialize, Serialize)]
|
||||
@@ -49,8 +52,12 @@ impl Interchange {
|
||||
serde_json::from_str(json)
|
||||
}
|
||||
|
||||
pub fn from_json_reader(reader: impl std::io::Read) -> Result<Self, serde_json::Error> {
|
||||
serde_json::from_reader(reader)
|
||||
pub fn from_json_reader(mut reader: impl std::io::Read) -> Result<Self, io::Error> {
|
||||
// We read the entire file into memory first, as this is *a lot* faster than using
|
||||
// `serde_json::from_reader`. See https://github.com/serde-rs/json/issues/160
|
||||
let mut json_str = String::new();
|
||||
reader.read_to_string(&mut json_str)?;
|
||||
Ok(Interchange::from_json_str(&json_str)?)
|
||||
}
|
||||
|
||||
pub fn write_to(&self, writer: impl std::io::Write) -> Result<(), serde_json::Error> {
|
||||
@@ -73,4 +80,75 @@ impl Interchange {
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.len() == 0
|
||||
}
|
||||
|
||||
/// Minify an interchange by constructing a synthetic block & attestation for each validator.
|
||||
pub fn minify(&self) -> Result<Self, InterchangeError> {
|
||||
// Map from pubkey to optional max block and max attestation.
|
||||
let mut validator_data =
|
||||
HashMap::<PublicKeyBytes, (Option<SignedBlock>, Option<SignedAttestation>)>::new();
|
||||
|
||||
for data in self.data.iter() {
|
||||
// Existing maximum attestation and maximum block.
|
||||
let (max_block, max_attestation) = validator_data
|
||||
.entry(data.pubkey)
|
||||
.or_insert_with(|| (None, None));
|
||||
|
||||
// Find maximum source and target epochs.
|
||||
let max_source_epoch = data
|
||||
.signed_attestations
|
||||
.iter()
|
||||
.map(|attestation| attestation.source_epoch)
|
||||
.max();
|
||||
let max_target_epoch = data
|
||||
.signed_attestations
|
||||
.iter()
|
||||
.map(|attestation| attestation.target_epoch)
|
||||
.max();
|
||||
|
||||
match (max_source_epoch, max_target_epoch) {
|
||||
(Some(source_epoch), Some(target_epoch)) => {
|
||||
if let Some(prev_max) = max_attestation {
|
||||
prev_max.source_epoch = max(prev_max.source_epoch, source_epoch);
|
||||
prev_max.target_epoch = max(prev_max.target_epoch, target_epoch);
|
||||
} else {
|
||||
*max_attestation = Some(SignedAttestation {
|
||||
source_epoch,
|
||||
target_epoch,
|
||||
signing_root: None,
|
||||
});
|
||||
}
|
||||
}
|
||||
(None, None) => {}
|
||||
_ => return Err(InterchangeError::MinAndMaxInconsistent),
|
||||
};
|
||||
|
||||
// Find maximum block slot.
|
||||
let max_block_slot = data.signed_blocks.iter().map(|block| block.slot).max();
|
||||
|
||||
if let Some(max_slot) = max_block_slot {
|
||||
if let Some(prev_max) = max_block {
|
||||
prev_max.slot = max(prev_max.slot, max_slot);
|
||||
} else {
|
||||
*max_block = Some(SignedBlock {
|
||||
slot: max_slot,
|
||||
signing_root: None,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let data = validator_data
|
||||
.into_iter()
|
||||
.map(|(pubkey, (maybe_block, maybe_att))| InterchangeData {
|
||||
pubkey,
|
||||
signed_blocks: maybe_block.into_iter().collect(),
|
||||
signed_attestations: maybe_att.into_iter().collect(),
|
||||
})
|
||||
.collect();
|
||||
|
||||
Ok(Self {
|
||||
metadata: self.metadata.clone(),
|
||||
data,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user