mirror of
https://github.com/sigp/lighthouse.git
synced 2026-03-03 00:31:50 +00:00
Add eip_3076 crate (#8206)
#7894 Moves the `Interchange` format from `slashing_protection` and thus removes the dependency on `slashing_protection` from `eth2` which can now just depend on the slimmer `eip_3076` crate. Co-Authored-By: Mac L <mjladson@pm.me>
This commit is contained in:
@@ -6,11 +6,12 @@ edition = { workspace = true }
|
||||
autotests = false
|
||||
|
||||
[features]
|
||||
arbitrary-fuzz = ["types/arbitrary-fuzz"]
|
||||
arbitrary-fuzz = ["types/arbitrary-fuzz", "eip_3076/arbitrary-fuzz"]
|
||||
portable = ["types/portable"]
|
||||
|
||||
[dependencies]
|
||||
arbitrary = { workspace = true, features = ["derive"] }
|
||||
eip_3076 = { workspace = true, features = ["json"] }
|
||||
ethereum_serde_utils = { workspace = true }
|
||||
filesystem = { workspace = true }
|
||||
r2d2 = { workspace = true }
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
use eip_3076::{Interchange, InterchangeData, InterchangeMetadata, SignedAttestation, SignedBlock};
|
||||
use slashing_protection::SUPPORTED_INTERCHANGE_FORMAT_VERSION;
|
||||
use slashing_protection::interchange::{
|
||||
Interchange, InterchangeData, InterchangeMetadata, SignedAttestation, SignedBlock,
|
||||
};
|
||||
use slashing_protection::interchange_test::{MultiTestCase, TestCase};
|
||||
use slashing_protection::test_utils::{DEFAULT_GENESIS_VALIDATORS_ROOT, pubkey};
|
||||
use std::fs::{self, File};
|
||||
|
||||
@@ -1,159 +0,0 @@
|
||||
use crate::InterchangeError;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::cmp::max;
|
||||
use std::collections::{HashMap, HashSet};
|
||||
use std::io;
|
||||
use types::{Epoch, Hash256, PublicKeyBytes, Slot};
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Deserialize, Serialize)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
#[cfg_attr(feature = "arbitrary-fuzz", derive(arbitrary::Arbitrary))]
|
||||
pub struct InterchangeMetadata {
|
||||
#[serde(with = "serde_utils::quoted_u64::require_quotes")]
|
||||
pub interchange_format_version: u64,
|
||||
pub genesis_validators_root: Hash256,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash, Deserialize, Serialize)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
#[cfg_attr(feature = "arbitrary-fuzz", derive(arbitrary::Arbitrary))]
|
||||
pub struct InterchangeData {
|
||||
pub pubkey: PublicKeyBytes,
|
||||
pub signed_blocks: Vec<SignedBlock>,
|
||||
pub signed_attestations: Vec<SignedAttestation>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash, Deserialize, Serialize)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
#[cfg_attr(feature = "arbitrary-fuzz", derive(arbitrary::Arbitrary))]
|
||||
pub struct SignedBlock {
|
||||
#[serde(with = "serde_utils::quoted_u64::require_quotes")]
|
||||
pub slot: Slot,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub signing_root: Option<Hash256>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash, Deserialize, Serialize)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
#[cfg_attr(feature = "arbitrary-fuzz", derive(arbitrary::Arbitrary))]
|
||||
pub struct SignedAttestation {
|
||||
#[serde(with = "serde_utils::quoted_u64::require_quotes")]
|
||||
pub source_epoch: Epoch,
|
||||
#[serde(with = "serde_utils::quoted_u64::require_quotes")]
|
||||
pub target_epoch: Epoch,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub signing_root: Option<Hash256>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Deserialize, Serialize)]
|
||||
#[cfg_attr(feature = "arbitrary-fuzz", derive(arbitrary::Arbitrary))]
|
||||
pub struct Interchange {
|
||||
pub metadata: InterchangeMetadata,
|
||||
pub data: Vec<InterchangeData>,
|
||||
}
|
||||
|
||||
impl Interchange {
|
||||
pub fn from_json_str(json: &str) -> Result<Self, serde_json::Error> {
|
||||
serde_json::from_str(json)
|
||||
}
|
||||
|
||||
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> {
|
||||
serde_json::to_writer(writer, self)
|
||||
}
|
||||
|
||||
/// Do these two `Interchange`s contain the same data (ignoring ordering)?
|
||||
pub fn equiv(&self, other: &Self) -> bool {
|
||||
let self_set = self.data.iter().collect::<HashSet<_>>();
|
||||
let other_set = other.data.iter().collect::<HashSet<_>>();
|
||||
self.metadata == other.metadata && self_set == other_set
|
||||
}
|
||||
|
||||
/// The number of entries in `data`.
|
||||
pub fn len(&self) -> usize {
|
||||
self.data.len()
|
||||
}
|
||||
|
||||
/// Is the `data` part of the interchange completely empty?
|
||||
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::MaxInconsistent),
|
||||
};
|
||||
|
||||
// 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,
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -1,8 +1,8 @@
|
||||
use crate::{
|
||||
SigningRoot, SlashingDatabase,
|
||||
interchange::{Interchange, SignedAttestation, SignedBlock},
|
||||
test_utils::{DEFAULT_GENESIS_VALIDATORS_ROOT, pubkey},
|
||||
};
|
||||
use eip_3076::{Interchange, SignedAttestation, SignedBlock};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::collections::HashSet;
|
||||
use tempfile::tempdir;
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
mod attestation_tests;
|
||||
mod block_tests;
|
||||
mod extra_interchange_tests;
|
||||
pub mod interchange;
|
||||
pub mod interchange_test;
|
||||
mod parallel_tests;
|
||||
mod registration_tests;
|
||||
@@ -10,6 +9,10 @@ mod signed_block;
|
||||
mod slashing_database;
|
||||
pub mod test_utils;
|
||||
|
||||
pub mod interchange {
|
||||
pub use eip_3076::{Interchange, InterchangeMetadata};
|
||||
}
|
||||
|
||||
pub use crate::signed_attestation::{InvalidAttestation, SignedAttestation};
|
||||
pub use crate::signed_block::{InvalidBlock, SignedBlock};
|
||||
pub use crate::slashing_database::{
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
use crate::interchange::{
|
||||
Interchange, InterchangeData, InterchangeMetadata, SignedAttestation as InterchangeAttestation,
|
||||
SignedBlock as InterchangeBlock,
|
||||
};
|
||||
use crate::signed_attestation::InvalidAttestation;
|
||||
use crate::signed_block::InvalidBlock;
|
||||
use crate::{NotSafe, Safe, SignedAttestation, SignedBlock, SigningRoot, signing_root_from_row};
|
||||
use eip_3076::{
|
||||
Interchange, InterchangeData, InterchangeMetadata, SignedAttestation as InterchangeAttestation,
|
||||
SignedBlock as InterchangeBlock,
|
||||
};
|
||||
use filesystem::restrict_file_permissions;
|
||||
use r2d2_sqlite::SqliteConnectionManager;
|
||||
use rusqlite::{OptionalExtension, Transaction, TransactionBehavior, params};
|
||||
@@ -1219,7 +1219,7 @@ pub enum InterchangeError {
|
||||
interchange_file: Hash256,
|
||||
client: Hash256,
|
||||
},
|
||||
MaxInconsistent,
|
||||
Eip3076(eip_3076::Error),
|
||||
SummaryInconsistent,
|
||||
SQLError(String),
|
||||
SQLPoolError(r2d2::Error),
|
||||
|
||||
Reference in New Issue
Block a user