mirror of
https://github.com/sigp/lighthouse.git
synced 2026-03-06 10:11:44 +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:
15
Cargo.lock
generated
15
Cargo.lock
generated
@@ -2581,6 +2581,18 @@ dependencies = [
|
||||
"sha2 0.10.8",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "eip_3076"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"arbitrary",
|
||||
"ethereum_serde_utils",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"tempfile",
|
||||
"types",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "either"
|
||||
version = "1.15.0"
|
||||
@@ -2848,6 +2860,7 @@ name = "eth2"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"derivative",
|
||||
"eip_3076",
|
||||
"either",
|
||||
"enr",
|
||||
"eth2_keystore",
|
||||
@@ -2867,7 +2880,6 @@ dependencies = [
|
||||
"sensitive_url",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"slashing_protection",
|
||||
"ssz_types",
|
||||
"test_random_derive",
|
||||
"tokio",
|
||||
@@ -8832,6 +8844,7 @@ name = "slashing_protection"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"arbitrary",
|
||||
"eip_3076",
|
||||
"ethereum_serde_utils",
|
||||
"filesystem",
|
||||
"r2d2",
|
||||
|
||||
@@ -23,6 +23,7 @@ members = [
|
||||
"common/compare_fields_derive",
|
||||
"common/deposit_contract",
|
||||
"common/directory",
|
||||
"common/eip_3076",
|
||||
"common/eth2",
|
||||
"common/eth2_config",
|
||||
"common/eth2_interop_keypairs",
|
||||
@@ -135,6 +136,7 @@ directory = { path = "common/directory" }
|
||||
dirs = "3"
|
||||
discv5 = { version = "0.10", features = ["libp2p"] }
|
||||
doppelganger_service = { path = "validator_client/doppelganger_service" }
|
||||
eip_3076 = { path = "common/eip_3076" }
|
||||
either = "1.9"
|
||||
environment = { path = "lighthouse/environment" }
|
||||
eth2 = { path = "common/eth2" }
|
||||
|
||||
20
common/eip_3076/Cargo.toml
Normal file
20
common/eip_3076/Cargo.toml
Normal file
@@ -0,0 +1,20 @@
|
||||
[package]
|
||||
name = "eip_3076"
|
||||
version = "0.1.0"
|
||||
authors = ["Sigma Prime <contact@sigmaprime.io>"]
|
||||
edition = { workspace = true }
|
||||
|
||||
[features]
|
||||
default = []
|
||||
arbitrary-fuzz = ["dep:arbitrary", "types/arbitrary"]
|
||||
json = ["dep:serde_json"]
|
||||
|
||||
[dependencies]
|
||||
arbitrary = { workspace = true, features = ["derive"], optional = true }
|
||||
ethereum_serde_utils = { workspace = true }
|
||||
serde = { workspace = true }
|
||||
serde_json = { workspace = true, optional = true }
|
||||
types = { workspace = true }
|
||||
|
||||
[dev-dependencies]
|
||||
tempfile = { workspace = true }
|
||||
@@ -1,10 +1,15 @@
|
||||
use crate::InterchangeError;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::cmp::max;
|
||||
use std::collections::{HashMap, HashSet};
|
||||
#[cfg(feature = "json")]
|
||||
use std::io;
|
||||
use types::{Epoch, Hash256, PublicKeyBytes, Slot};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum Error {
|
||||
MaxInconsistent,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Deserialize, Serialize)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
#[cfg_attr(feature = "arbitrary-fuzz", derive(arbitrary::Arbitrary))]
|
||||
@@ -53,10 +58,12 @@ pub struct Interchange {
|
||||
}
|
||||
|
||||
impl Interchange {
|
||||
#[cfg(feature = "json")]
|
||||
pub fn from_json_str(json: &str) -> Result<Self, serde_json::Error> {
|
||||
serde_json::from_str(json)
|
||||
}
|
||||
|
||||
#[cfg(feature = "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
|
||||
@@ -65,6 +72,7 @@ impl Interchange {
|
||||
Ok(Interchange::from_json_str(&json_str)?)
|
||||
}
|
||||
|
||||
#[cfg(feature = "json")]
|
||||
pub fn write_to(&self, writer: impl std::io::Write) -> Result<(), serde_json::Error> {
|
||||
serde_json::to_writer(writer, self)
|
||||
}
|
||||
@@ -87,7 +95,7 @@ impl Interchange {
|
||||
}
|
||||
|
||||
/// Minify an interchange by constructing a synthetic block & attestation for each validator.
|
||||
pub fn minify(&self) -> Result<Self, InterchangeError> {
|
||||
pub fn minify(&self) -> Result<Self, Error> {
|
||||
// Map from pubkey to optional max block and max attestation.
|
||||
let mut validator_data =
|
||||
HashMap::<PublicKeyBytes, (Option<SignedBlock>, Option<SignedAttestation>)>::new();
|
||||
@@ -124,7 +132,7 @@ impl Interchange {
|
||||
}
|
||||
}
|
||||
(None, None) => {}
|
||||
_ => return Err(InterchangeError::MaxInconsistent),
|
||||
_ => return Err(Error::MaxInconsistent),
|
||||
};
|
||||
|
||||
// Find maximum block slot.
|
||||
@@ -157,3 +165,96 @@ impl Interchange {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "json")]
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use std::fs::File;
|
||||
use tempfile::tempdir;
|
||||
use types::FixedBytesExtended;
|
||||
|
||||
fn get_interchange() -> Interchange {
|
||||
Interchange {
|
||||
metadata: InterchangeMetadata {
|
||||
interchange_format_version: 5,
|
||||
genesis_validators_root: Hash256::from_low_u64_be(555),
|
||||
},
|
||||
data: vec![
|
||||
InterchangeData {
|
||||
pubkey: PublicKeyBytes::deserialize(&[1u8; 48]).unwrap(),
|
||||
signed_blocks: vec![SignedBlock {
|
||||
slot: Slot::new(100),
|
||||
signing_root: Some(Hash256::from_low_u64_be(1)),
|
||||
}],
|
||||
signed_attestations: vec![SignedAttestation {
|
||||
source_epoch: Epoch::new(0),
|
||||
target_epoch: Epoch::new(5),
|
||||
signing_root: Some(Hash256::from_low_u64_be(2)),
|
||||
}],
|
||||
},
|
||||
InterchangeData {
|
||||
pubkey: PublicKeyBytes::deserialize(&[2u8; 48]).unwrap(),
|
||||
signed_blocks: vec![],
|
||||
signed_attestations: vec![],
|
||||
},
|
||||
],
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_roundtrip() {
|
||||
let temp_dir = tempdir().unwrap();
|
||||
let file_path = temp_dir.path().join("interchange.json");
|
||||
|
||||
let interchange = get_interchange();
|
||||
|
||||
let mut file = File::create(&file_path).unwrap();
|
||||
interchange.write_to(&mut file).unwrap();
|
||||
|
||||
let file = File::open(&file_path).unwrap();
|
||||
let from_file = Interchange::from_json_reader(file).unwrap();
|
||||
|
||||
assert_eq!(interchange, from_file);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_empty_roundtrip() {
|
||||
let temp_dir = tempdir().unwrap();
|
||||
let file_path = temp_dir.path().join("empty.json");
|
||||
|
||||
let empty = Interchange {
|
||||
metadata: InterchangeMetadata {
|
||||
interchange_format_version: 5,
|
||||
genesis_validators_root: Hash256::zero(),
|
||||
},
|
||||
data: vec![],
|
||||
};
|
||||
|
||||
let mut file = File::create(&file_path).unwrap();
|
||||
empty.write_to(&mut file).unwrap();
|
||||
|
||||
let file = File::open(&file_path).unwrap();
|
||||
let from_file = Interchange::from_json_reader(file).unwrap();
|
||||
|
||||
assert_eq!(empty, from_file);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_minify_roundtrip() {
|
||||
let interchange = get_interchange();
|
||||
|
||||
let minified = interchange.minify().unwrap();
|
||||
|
||||
let temp_dir = tempdir().unwrap();
|
||||
let file_path = temp_dir.path().join("minified.json");
|
||||
|
||||
let mut file = File::create(&file_path).unwrap();
|
||||
minified.write_to(&mut file).unwrap();
|
||||
|
||||
let file = File::open(&file_path).unwrap();
|
||||
let from_file = Interchange::from_json_reader(file).unwrap();
|
||||
|
||||
assert_eq!(minified, from_file);
|
||||
}
|
||||
}
|
||||
@@ -10,6 +10,7 @@ lighthouse = []
|
||||
|
||||
[dependencies]
|
||||
derivative = { workspace = true }
|
||||
eip_3076 = { workspace = true }
|
||||
either = { workspace = true }
|
||||
enr = { version = "0.13.0", features = ["ed25519"] }
|
||||
eth2_keystore = { workspace = true }
|
||||
@@ -29,7 +30,6 @@ reqwest-eventsource = "0.5.0"
|
||||
sensitive_url = { workspace = true }
|
||||
serde = { workspace = true }
|
||||
serde_json = { workspace = true }
|
||||
slashing_protection = { workspace = true }
|
||||
ssz_types = { workspace = true }
|
||||
test_random_derive = { path = "../../common/test_random_derive" }
|
||||
types = { workspace = true }
|
||||
|
||||
@@ -3,7 +3,7 @@ use serde::{Deserialize, Serialize};
|
||||
use types::{Address, Graffiti, PublicKeyBytes};
|
||||
use zeroize::Zeroizing;
|
||||
|
||||
pub use slashing_protection::interchange::Interchange;
|
||||
pub use eip_3076::Interchange;
|
||||
|
||||
#[derive(Debug, Deserialize, Serialize, PartialEq)]
|
||||
pub struct GetFeeRecipientResponse {
|
||||
|
||||
@@ -10,7 +10,6 @@ use test_random_derive::TestRandom;
|
||||
use tree_hash_derive::TreeHash;
|
||||
|
||||
/// A Validators aggregate sync committee contribution and selection proof.
|
||||
|
||||
#[cfg_attr(
|
||||
feature = "arbitrary",
|
||||
derive(arbitrary::Arbitrary),
|
||||
|
||||
@@ -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,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