From 5323681204cbdae9de475c2d227933bed047630a Mon Sep 17 00:00:00 2001 From: Paul Hauner Date: Tue, 28 Sep 2021 10:18:39 +1000 Subject: [PATCH] Add basic block generator impl --- .../test_utils/execution_block_generator.rs | 171 ++++++++++++++++++ .../execution_layer/src/test_utils/mod.rs | 10 + 2 files changed, 181 insertions(+) create mode 100644 beacon_node/execution_layer/src/test_utils/execution_block_generator.rs diff --git a/beacon_node/execution_layer/src/test_utils/execution_block_generator.rs b/beacon_node/execution_layer/src/test_utils/execution_block_generator.rs new file mode 100644 index 0000000000..db352ac3a0 --- /dev/null +++ b/beacon_node/execution_layer/src/test_utils/execution_block_generator.rs @@ -0,0 +1,171 @@ +use crate::ExecutionBlock; +use types::{Hash256, Uint256}; + +pub struct ExecutionBlockGenerator { + pub seconds_since_genesis: u64, + pub block_interval_secs: u64, + pub terminal_total_difficulty: u64, + pub terminal_block_number: u64, + pub latest_merge_block: Option, +} + +impl ExecutionBlockGenerator { + pub fn new(terminal_total_difficulty: u64, terminal_block_number: u64) -> Self { + Self { + seconds_since_genesis: 0, + block_interval_secs: 1, + terminal_total_difficulty, + terminal_block_number, + latest_merge_block: None, + } + } + + pub fn increment_seconds_since_genesis(&mut self, inc: u64) { + self.seconds_since_genesis += inc; + } + + fn block_number_at(&self, unix_seconds: u64) -> u64 { + unix_seconds + .checked_div(self.block_interval_secs) + .expect("block interval cannot be zero") + } + + fn total_difficulty_for_block(&self, number: u64) -> u64 { + if number >= self.terminal_block_number { + self.terminal_total_difficulty + } else { + let increment = self + .terminal_total_difficulty + .checked_div(self.terminal_block_number) + .expect("terminal block number must be non-zero"); + increment + .checked_mul(number) + .expect("overflow computing total difficulty") + } + } + + fn latest_block_number(&self) -> u64 { + let time_based = self.block_number_at(self.seconds_since_genesis); + + if time_based < self.terminal_block_number { + time_based + } else { + self.latest_merge_block + .unwrap_or(self.terminal_block_number) + } + } + + pub fn latest_block(&self) -> Option { + self.block_by_number(self.latest_block_number()) + } + + pub fn block_by_number(&self, number: u64) -> Option { + let parent_hash = number + .checked_sub(1) + .map(block_number_to_block_hash) + .unwrap_or_else(Hash256::zero); + let block_hash = block_number_to_block_hash(number); + + if number <= self.terminal_block_number { + if number <= self.latest_block_number() { + Some(ExecutionBlock { + block_hash, + parent_hash, + total_difficulty: Uint256::from(self.total_difficulty_for_block(number)), + }) + } else { + None + } + } else { + let latest_block = self + .latest_merge_block + .unwrap_or(self.terminal_block_number); + + if number <= latest_block { + Some(ExecutionBlock { + block_hash, + parent_hash, + total_difficulty: Uint256::from(self.terminal_total_difficulty), + }) + } else { + None + } + } + } + + pub fn block_by_hash(&self, hash: Hash256) -> Option { + let block_number = block_hash_to_block_number(hash); + self.block_by_number(block_number) + } +} + +fn block_number_to_block_hash(n: u64) -> Hash256 { + Hash256::from_low_u64_be(n + 1) +} + +fn block_hash_to_block_number(hash: Hash256) -> u64 { + hash.to_low_u64_be() + .checked_sub(1) + .expect("do not query for zero hash") +} + +#[cfg(test)] +mod test { + use super::*; + + #[test] + fn simple() { + const TERMINAL_DIFFICULTY: u64 = 10; + const TERMINAL_BLOCK: u64 = 10; + const DIFFICULTY_INCREMENT: u64 = 1; + + let mut generator = ExecutionBlockGenerator::new(TERMINAL_DIFFICULTY, TERMINAL_BLOCK); + + for mut i in 0..(TERMINAL_BLOCK + 5) { + i = std::cmp::min(i, TERMINAL_BLOCK); + + /* + * Generate a block, inspect it. + */ + + let block = generator.latest_block().unwrap(); + assert_eq!(block.block_hash, block_number_to_block_hash(i)); + assert_eq!(block_hash_to_block_number(block.block_hash), i); + + let expected_parent = i + .checked_sub(1) + .map(block_number_to_block_hash) + .unwrap_or_else(Hash256::zero); + assert_eq!(block.parent_hash, expected_parent); + + assert_eq!(block.total_difficulty, (i * DIFFICULTY_INCREMENT).into()); + + assert_eq!(generator.block_by_hash(block.block_hash).unwrap(), block); + assert_eq!(generator.block_by_number(i).unwrap(), block); + + /* + * Check the parent is accessible. + */ + + if let Some(prev_i) = i.checked_sub(1) { + assert_eq!( + generator.block_by_number(prev_i).unwrap(), + generator.block_by_hash(block.parent_hash).unwrap() + ); + } + + /* + * Check the next block is inaccessible. + */ + + let next_i = i + 1; + dbg!(next_i); + assert!(generator.block_by_number(next_i).is_none()); + assert!(generator + .block_by_hash(block_number_to_block_hash(next_i)) + .is_none()); + + generator.increment_seconds_since_genesis(1); + } + } +} diff --git a/beacon_node/execution_layer/src/test_utils/mod.rs b/beacon_node/execution_layer/src/test_utils/mod.rs index 95dbb4cbb2..670eb54513 100644 --- a/beacon_node/execution_layer/src/test_utils/mod.rs +++ b/beacon_node/execution_layer/src/test_utils/mod.rs @@ -1,5 +1,6 @@ use bytes::Bytes; use environment::null_logger; +use execution_block_generator::ExecutionBlockGenerator; use serde::{Deserialize, Serialize}; use slog::{info, Logger}; use std::future::Future; @@ -10,6 +11,11 @@ use tokio::sync::{oneshot, RwLock}; use types::EthSpec; use warp::Filter; +const DEFAULT_TERMINAL_DIFFICULTY: u64 = 6400; +const DEFAULT_TERMINAL_BLOCK: u64 = 64; + +mod execution_block_generator; + pub struct MockServer { _shutdown_tx: oneshot::Sender<()>, listen_socket_addr: SocketAddr, @@ -19,11 +25,14 @@ pub struct MockServer { impl MockServer { pub fn unit_testing() -> Self { let last_echo_request = Arc::new(RwLock::new(None)); + let execution_block_generator = + ExecutionBlockGenerator::new(DEFAULT_TERMINAL_DIFFICULTY, DEFAULT_TERMINAL_BLOCK); let ctx: Arc> = Arc::new(Context { config: <_>::default(), log: null_logger().unwrap(), last_echo_request: last_echo_request.clone(), + execution_block_generator: Arc::new(RwLock::new(execution_block_generator)), _phantom: PhantomData, }); @@ -87,6 +96,7 @@ pub struct Context { pub config: Config, pub log: Logger, pub last_echo_request: Arc>>, + pub execution_block_generator: Arc>, pub _phantom: PhantomData, }