Add basic block generator impl

This commit is contained in:
Paul Hauner
2021-09-28 10:18:39 +10:00
parent 92829c7b38
commit 5323681204
2 changed files with 181 additions and 0 deletions

View File

@@ -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<u64>,
}
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<ExecutionBlock> {
self.block_by_number(self.latest_block_number())
}
pub fn block_by_number(&self, number: u64) -> Option<ExecutionBlock> {
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<ExecutionBlock> {
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);
}
}
}

View File

@@ -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<T: EthSpec>() -> 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<Context<T>> = 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<T> {
pub config: Config,
pub log: Logger,
pub last_echo_request: Arc<RwLock<Option<Bytes>>>,
pub execution_block_generator: Arc<RwLock<ExecutionBlockGenerator>>,
pub _phantom: PhantomData<T>,
}