mirror of
https://github.com/sigp/lighthouse.git
synced 2026-03-23 06:44:35 +00:00
## Issue Addressed Closes #1873 ## Proposed Changes Fixes the bug in slashing protection import (#1873) by pruning the database upon import. Also expands the test generator to cover this case and a few others which are under discussion here: https://ethereum-magicians.org/t/eip-3076-validator-client-interchange-format-slashing-protection/4883 ## Additional Info Depending on the outcome of the discussion on Eth Magicians, we can either wait for consensus before merging, or merge our preferred solution and patch things later.
147 lines
4.3 KiB
Rust
147 lines
4.3 KiB
Rust
use crate::*;
|
|
use tempfile::{tempdir, TempDir};
|
|
use types::{
|
|
test_utils::generate_deterministic_keypair, AttestationData, BeaconBlockHeader, Hash256,
|
|
};
|
|
|
|
pub const DEFAULT_VALIDATOR_INDEX: usize = 0;
|
|
pub const DEFAULT_DOMAIN: Hash256 = Hash256::zero();
|
|
pub const DEFAULT_GENESIS_VALIDATORS_ROOT: Hash256 = Hash256::zero();
|
|
|
|
pub fn pubkey(index: usize) -> PublicKey {
|
|
generate_deterministic_keypair(index).pk
|
|
}
|
|
|
|
pub struct Test<T> {
|
|
pubkey: PublicKey,
|
|
data: T,
|
|
domain: Hash256,
|
|
expected: Result<Safe, NotSafe>,
|
|
}
|
|
|
|
impl<T> Test<T> {
|
|
pub fn single(data: T) -> Self {
|
|
Self::with_pubkey(pubkey(DEFAULT_VALIDATOR_INDEX), data)
|
|
}
|
|
|
|
pub fn with_pubkey(pubkey: PublicKey, data: T) -> Self {
|
|
Self {
|
|
pubkey,
|
|
data,
|
|
domain: DEFAULT_DOMAIN,
|
|
expected: Ok(Safe::Valid),
|
|
}
|
|
}
|
|
|
|
pub fn with_domain(mut self, domain: Hash256) -> Self {
|
|
self.domain = domain;
|
|
self
|
|
}
|
|
|
|
pub fn expect_result(mut self, result: Result<Safe, NotSafe>) -> Self {
|
|
self.expected = result;
|
|
self
|
|
}
|
|
|
|
pub fn expect_invalid_att(self, error: InvalidAttestation) -> Self {
|
|
self.expect_result(Err(NotSafe::InvalidAttestation(error)))
|
|
}
|
|
|
|
pub fn expect_invalid_block(self, error: InvalidBlock) -> Self {
|
|
self.expect_result(Err(NotSafe::InvalidBlock(error)))
|
|
}
|
|
|
|
pub fn expect_same_data(self) -> Self {
|
|
self.expect_result(Ok(Safe::SameData))
|
|
}
|
|
}
|
|
|
|
pub struct StreamTest<T> {
|
|
/// Validators to register.
|
|
pub registered_validators: Vec<PublicKey>,
|
|
/// Vector of cases and the value expected when calling `check_and_insert_X`.
|
|
pub cases: Vec<Test<T>>,
|
|
}
|
|
|
|
impl<T> Default for StreamTest<T> {
|
|
fn default() -> Self {
|
|
Self {
|
|
registered_validators: vec![pubkey(DEFAULT_VALIDATOR_INDEX)],
|
|
cases: vec![],
|
|
}
|
|
}
|
|
}
|
|
|
|
impl<T> StreamTest<T> {
|
|
/// The number of test cases that are expected to pass processing successfully.
|
|
fn num_expected_successes(&self) -> usize {
|
|
self.cases
|
|
.iter()
|
|
.filter(|case| case.expected.is_ok())
|
|
.count()
|
|
}
|
|
}
|
|
|
|
impl StreamTest<AttestationData> {
|
|
pub fn run(&self) {
|
|
let dir = tempdir().unwrap();
|
|
let slashing_db_file = dir.path().join("slashing_protection.sqlite");
|
|
let slashing_db = SlashingDatabase::create(&slashing_db_file).unwrap();
|
|
|
|
for pubkey in &self.registered_validators {
|
|
slashing_db.register_validator(pubkey).unwrap();
|
|
}
|
|
|
|
for (i, test) in self.cases.iter().enumerate() {
|
|
assert_eq!(
|
|
slashing_db.check_and_insert_attestation(&test.pubkey, &test.data, test.domain),
|
|
test.expected,
|
|
"attestation {} not processed as expected",
|
|
i
|
|
);
|
|
}
|
|
|
|
roundtrip_database(&dir, &slashing_db, self.num_expected_successes() == 0);
|
|
}
|
|
}
|
|
|
|
impl StreamTest<BeaconBlockHeader> {
|
|
pub fn run(&self) {
|
|
let dir = tempdir().unwrap();
|
|
let slashing_db_file = dir.path().join("slashing_protection.sqlite");
|
|
let slashing_db = SlashingDatabase::create(&slashing_db_file).unwrap();
|
|
|
|
for pubkey in &self.registered_validators {
|
|
slashing_db.register_validator(pubkey).unwrap();
|
|
}
|
|
|
|
for (i, test) in self.cases.iter().enumerate() {
|
|
assert_eq!(
|
|
slashing_db.check_and_insert_block_proposal(&test.pubkey, &test.data, test.domain),
|
|
test.expected,
|
|
"attestation {} not processed as expected",
|
|
i
|
|
);
|
|
}
|
|
|
|
roundtrip_database(&dir, &slashing_db, self.num_expected_successes() == 0);
|
|
}
|
|
}
|
|
|
|
fn roundtrip_database(dir: &TempDir, db: &SlashingDatabase, is_empty: bool) {
|
|
let exported = db
|
|
.export_interchange_info(DEFAULT_GENESIS_VALIDATORS_ROOT)
|
|
.unwrap();
|
|
let new_db =
|
|
SlashingDatabase::create(&dir.path().join("roundtrip_slashing_protection.sqlite")).unwrap();
|
|
new_db
|
|
.import_interchange_info(exported.clone(), DEFAULT_GENESIS_VALIDATORS_ROOT)
|
|
.unwrap();
|
|
let reexported = new_db
|
|
.export_interchange_info(DEFAULT_GENESIS_VALIDATORS_ROOT)
|
|
.unwrap();
|
|
|
|
assert_eq!(exported, reexported);
|
|
assert_eq!(is_empty, exported.is_empty());
|
|
}
|