From eaf5605d4d2d7d72bda7ef727d90f9ef0852c3c0 Mon Sep 17 00:00:00 2001 From: 0xModene <7647623+0xModene@users.noreply.github.com> Date: Mon, 14 Nov 2022 15:23:51 -0500 Subject: [PATCH] feat: verify checkpoint has valid age (#105) * check blockhash has valid timestamp * remove warn log * made checkpoint age req configurable * renamed method to make more sense * fixed broken tests * formatting * unit tests completed * removed needless imports * renaming vars --- client/src/client.rs | 1 + client/src/lib.rs | 2 -- config/src/lib.rs | 2 ++ config/src/networks.rs | 3 +++ consensus/src/consensus.rs | 44 ++++++++++++++++++++++++++++---------- consensus/src/errors.rs | 2 ++ consensus/tests/sync.rs | 1 + 7 files changed, 42 insertions(+), 13 deletions(-) diff --git a/client/src/client.rs b/client/src/client.rs index 45ef2c0..c735838 100644 --- a/client/src/client.rs +++ b/client/src/client.rs @@ -166,6 +166,7 @@ impl ClientBuilder { data_dir, chain: base_config.chain, forks: base_config.forks, + max_checkpoint_age: base_config.max_checkpoint_age, }; Client::new(config) diff --git a/client/src/lib.rs b/client/src/lib.rs index 177f7c1..99729fc 100644 --- a/client/src/lib.rs +++ b/client/src/lib.rs @@ -1,5 +1,3 @@ -#![feature(map_first_last)] - mod client; pub use crate::client::*; diff --git a/config/src/lib.rs b/config/src/lib.rs index b861562..0eed18b 100644 --- a/config/src/lib.rs +++ b/config/src/lib.rs @@ -26,6 +26,7 @@ pub struct Config { pub data_dir: Option, pub chain: ChainConfig, pub forks: Forks, + pub max_checkpoint_age: u64, } impl Config { @@ -93,6 +94,7 @@ impl Config { checkpoint: self.checkpoint.clone(), chain: self.chain.clone(), forks: self.forks.clone(), + max_checkpoint_age: self.max_checkpoint_age.clone(), } } } diff --git a/config/src/networks.rs b/config/src/networks.rs index 354a76b..436b1ba 100644 --- a/config/src/networks.rs +++ b/config/src/networks.rs @@ -28,6 +28,7 @@ pub struct BaseConfig { pub checkpoint: Vec, pub chain: ChainConfig, pub forks: Forks, + pub max_checkpoint_age: u64, } pub fn mainnet() -> BaseConfig { @@ -60,6 +61,7 @@ pub fn mainnet() -> BaseConfig { fork_version: hex_str_to_bytes("0x02000000").unwrap(), }, }, + max_checkpoint_age: 1_209_600, // 14 days } } @@ -93,5 +95,6 @@ pub fn goerli() -> BaseConfig { fork_version: hex_str_to_bytes("0x02001020").unwrap(), }, }, + max_checkpoint_age: 1_209_600, // 14 days } } diff --git a/consensus/src/consensus.rs b/consensus/src/consensus.rs index b54068b..2a77b9f 100644 --- a/consensus/src/consensus.rs +++ b/consensus/src/consensus.rs @@ -153,6 +153,11 @@ impl ConsensusClient { .await .map_err(|_| eyre!("could not fetch bootstrap"))?; + let is_valid = self.is_valid_checkpoint(bootstrap.header.slot); + if !is_valid { + return Err(ConsensusError::CheckpointTooOld.into()); + } + let committee_valid = is_current_committee_proof_valid( &bootstrap.header, &mut bootstrap.current_sync_committee, @@ -493,6 +498,17 @@ impl ConsensusClient { Duration::seconds(next_update as i64) } + + // Determines blockhash_slot age and returns true if it is less than 14 days old + fn is_valid_checkpoint(&self, blockhash_slot: u64) -> bool { + let current_slot = self.expected_current_slot(); + let current_slot_timestamp = self.slot_timestamp(current_slot); + let blockhash_slot_timestamp = self.slot_timestamp(blockhash_slot); + + let slot_age = current_slot_timestamp - blockhash_slot_timestamp; + + slot_age < self.config.max_checkpoint_age + } } fn get_participating_keys( @@ -574,13 +590,14 @@ mod tests { }; use config::{networks, Config}; - async fn get_client() -> ConsensusClient { + async fn get_client(large_checkpoint_age: bool) -> ConsensusClient { let base_config = networks::goerli(); let config = Config { consensus_rpc: String::new(), execution_rpc: String::new(), chain: base_config.chain, forks: base_config.forks, + max_checkpoint_age: if large_checkpoint_age { 123123123 } else { 123 }, ..Default::default() }; @@ -590,13 +607,12 @@ mod tests { let mut client = ConsensusClient::new("testdata/", &checkpoint, Arc::new(config)).unwrap(); client.bootstrap().await.unwrap(); - client } #[tokio::test] async fn test_verify_update() { - let client = get_client().await; + let client = get_client(true).await; let period = calc_sync_period(client.store.finalized_header.slot); let updates = client .rpc @@ -610,7 +626,7 @@ mod tests { #[tokio::test] async fn test_verify_update_invalid_committee() { - let client = get_client().await; + let client = get_client(true).await; let period = calc_sync_period(client.store.finalized_header.slot); let updates = client .rpc @@ -630,7 +646,7 @@ mod tests { #[tokio::test] async fn test_verify_update_invalid_finality() { - let client = get_client().await; + let client = get_client(true).await; let period = calc_sync_period(client.store.finalized_header.slot); let updates = client .rpc @@ -650,7 +666,7 @@ mod tests { #[tokio::test] async fn test_verify_update_invalid_sig() { - let client = get_client().await; + let client = get_client(true).await; let period = calc_sync_period(client.store.finalized_header.slot); let updates = client .rpc @@ -670,7 +686,7 @@ mod tests { #[tokio::test] async fn test_verify_finality() { - let mut client = get_client().await; + let mut client = get_client(true).await; client.sync().await.unwrap(); let update = client.rpc.get_finality_update().await.unwrap(); @@ -680,7 +696,7 @@ mod tests { #[tokio::test] async fn test_verify_finality_invalid_finality() { - let mut client = get_client().await; + let mut client = get_client(true).await; client.sync().await.unwrap(); let mut update = client.rpc.get_finality_update().await.unwrap(); @@ -695,7 +711,7 @@ mod tests { #[tokio::test] async fn test_verify_finality_invalid_sig() { - let mut client = get_client().await; + let mut client = get_client(true).await; client.sync().await.unwrap(); let mut update = client.rpc.get_finality_update().await.unwrap(); @@ -710,7 +726,7 @@ mod tests { #[tokio::test] async fn test_verify_optimistic() { - let mut client = get_client().await; + let mut client = get_client(true).await; client.sync().await.unwrap(); let update = client.rpc.get_optimistic_update().await.unwrap(); @@ -719,7 +735,7 @@ mod tests { #[tokio::test] async fn test_verify_optimistic_invalid_sig() { - let mut client = get_client().await; + let mut client = get_client(true).await; client.sync().await.unwrap(); let mut update = client.rpc.get_optimistic_update().await.unwrap(); @@ -731,4 +747,10 @@ mod tests { ConsensusError::InvalidSignature.to_string() ); } + + #[tokio::test] + #[should_panic] + async fn test_verify_checkpoint_age_invalid() { + get_client(false).await; + } } diff --git a/consensus/src/errors.rs b/consensus/src/errors.rs index 87bb923..41c5558 100644 --- a/consensus/src/errors.rs +++ b/consensus/src/errors.rs @@ -22,4 +22,6 @@ pub enum ConsensusError { InvalidHeaderHash(String, String), #[error("payload not found for slot: {0}")] PayloadNotFound(u64), + #[error("checkpoint is too old")] + CheckpointTooOld, } diff --git a/consensus/tests/sync.rs b/consensus/tests/sync.rs index 4ee142f..1b019c0 100644 --- a/consensus/tests/sync.rs +++ b/consensus/tests/sync.rs @@ -10,6 +10,7 @@ async fn setup() -> ConsensusClient { execution_rpc: String::new(), chain: base_config.chain, forks: base_config.forks, + max_checkpoint_age: 123123123, ..Default::default() };