Refactor block gen

This commit is contained in:
Paul Hauner
2021-09-29 12:17:38 +10:00
parent 837acc11e6
commit 57f6a9b1f1
7 changed files with 230 additions and 201 deletions

1
Cargo.lock generated
View File

@@ -2085,6 +2085,7 @@ dependencies = [
"task_executor", "task_executor",
"tokio", "tokio",
"tree_hash", "tree_hash",
"tree_hash_derive",
"types", "types",
"warp", "warp",
] ]

View File

@@ -26,3 +26,4 @@ eth2_ssz_types = { path = "../../consensus/ssz_types"}
lru = "0.6.0" lru = "0.6.0"
exit-future = "0.2.0" exit-future = "0.2.0"
tree_hash = { path = "../../consensus/tree_hash"} tree_hash = { path = "../../consensus/tree_hash"}
tree_hash_derive = { path = "../../consensus/tree_hash_derive"}

View File

@@ -107,6 +107,7 @@ pub enum BlockByNumberQuery<'a> {
#[serde(rename_all = "camelCase")] #[serde(rename_all = "camelCase")]
pub struct ExecutionBlock { pub struct ExecutionBlock {
pub block_hash: Hash256, pub block_hash: Hash256,
pub block_number: u64,
pub parent_hash: Hash256, pub parent_hash: Hash256,
pub total_difficulty: Uint256, pub total_difficulty: Uint256,
} }

View File

@@ -410,7 +410,7 @@ impl ExecutionLayer {
#[cfg(test)] #[cfg(test)]
mod test { mod test {
use super::*; use super::*;
use crate::test_utils::{block_number_to_hash, MockServer, DEFAULT_TERMINAL_DIFFICULTY}; use crate::test_utils::{MockServer, DEFAULT_TERMINAL_DIFFICULTY};
use environment::null_logger; use environment::null_logger;
use types::MainnetEthSpec; use types::MainnetEthSpec;
@@ -424,6 +424,7 @@ mod test {
impl SingleEngineTester { impl SingleEngineTester {
pub fn new() -> Self { pub fn new() -> Self {
let server = MockServer::unit_testing(); let server = MockServer::unit_testing();
let url = SensitiveUrl::parse(&server.url()).unwrap(); let url = SensitiveUrl::parse(&server.url()).unwrap();
let log = null_logger().unwrap(); let log = null_logger().unwrap();
@@ -457,26 +458,37 @@ mod test {
} }
pub async fn move_to_block_prior_to_terminal_block(self) -> Self { pub async fn move_to_block_prior_to_terminal_block(self) -> Self {
{ let target_block = {
let mut block_gen = self.server.execution_block_generator().await; let block_gen = self.server.execution_block_generator().await;
let target_block = block_gen.terminal_block_number.checked_sub(1).unwrap(); block_gen.terminal_block_number.checked_sub(1).unwrap()
block_gen.set_clock_for_block_number(target_block) };
} self.move_to_pow_block(target_block).await
self
} }
pub async fn move_to_terminal_block(self) -> Self { pub async fn move_to_terminal_block(self) -> Self {
let target_block = {
let block_gen = self.server.execution_block_generator().await;
block_gen.terminal_block_number
};
self.move_to_pow_block(target_block).await
}
pub async fn move_to_pow_block(self, target_block: u64) -> Self {
{ {
let mut block_gen = self.server.execution_block_generator().await; let mut block_gen = self.server.execution_block_generator().await;
let target_block = block_gen.terminal_block_number; let next_block = block_gen.latest_block().unwrap().block_number() + 1;
block_gen.set_clock_for_block_number(target_block) assert!(target_block >= next_block);
block_gen
.insert_pow_blocks(next_block..=target_block)
.unwrap();
} }
self self
} }
pub async fn with_terminal_block_number<'a, T, U>(self, func: T) -> Self pub async fn with_terminal_block<'a, T, U>(self, func: T) -> Self
where where
T: Fn(ExecutionLayer, u64) -> U, T: Fn(ExecutionLayer, Option<ExecutionBlock>) -> U,
U: Future<Output = ()>, U: Future<Output = ()>,
{ {
let terminal_block_number = self let terminal_block_number = self
@@ -484,7 +496,13 @@ mod test {
.execution_block_generator() .execution_block_generator()
.await .await
.terminal_block_number; .terminal_block_number;
func(self.el.clone(), terminal_block_number).await; let terminal_block = self
.server
.execution_block_generator()
.await
.execution_block_by_number(terminal_block_number);
func(self.el.clone(), terminal_block).await;
self self
} }
@@ -506,7 +524,7 @@ mod test {
SingleEngineTester::new() SingleEngineTester::new()
.move_to_block_prior_to_terminal_block() .move_to_block_prior_to_terminal_block()
.await .await
.with_terminal_block_number(|el, _| async move { .with_terminal_block(|el, _| async move {
assert_eq!( assert_eq!(
el.get_pow_block_hash_at_total_difficulty().await.unwrap(), el.get_pow_block_hash_at_total_difficulty().await.unwrap(),
None None
@@ -515,10 +533,10 @@ mod test {
.await .await
.move_to_terminal_block() .move_to_terminal_block()
.await .await
.with_terminal_block_number(|el, terminal_block_number| async move { .with_terminal_block(|el, terminal_block| async move {
assert_eq!( assert_eq!(
el.get_pow_block_hash_at_total_difficulty().await.unwrap(), el.get_pow_block_hash_at_total_difficulty().await.unwrap(),
Some(block_number_to_hash(terminal_block_number)) Some(terminal_block.unwrap().block_hash)
) )
}) })
.await; .await;
@@ -529,13 +547,11 @@ mod test {
SingleEngineTester::new() SingleEngineTester::new()
.move_to_terminal_block() .move_to_terminal_block()
.await .await
.with_terminal_block_number(|el, terminal_block_number| async move { .with_terminal_block(|el, terminal_block| async move {
assert_eq!( assert_eq!(
el.is_valid_terminal_pow_block_hash(block_number_to_hash( el.is_valid_terminal_pow_block_hash(terminal_block.unwrap().block_hash)
terminal_block_number .await
)) .unwrap(),
.await
.unwrap(),
Some(true) Some(true)
) )
}) })
@@ -547,15 +563,13 @@ mod test {
SingleEngineTester::new() SingleEngineTester::new()
.move_to_terminal_block() .move_to_terminal_block()
.await .await
.with_terminal_block_number(|el, terminal_block_number| async move { .with_terminal_block(|el, terminal_block| async move {
let invalid_terminal_block = terminal_block_number.checked_sub(1).unwrap(); let invalid_terminal_block = terminal_block.unwrap().parent_hash;
assert_eq!( assert_eq!(
el.is_valid_terminal_pow_block_hash(block_number_to_hash( el.is_valid_terminal_pow_block_hash(invalid_terminal_block)
invalid_terminal_block .await
)) .unwrap(),
.await
.unwrap(),
Some(false) Some(false)
) )
}) })
@@ -567,15 +581,13 @@ mod test {
SingleEngineTester::new() SingleEngineTester::new()
.move_to_terminal_block() .move_to_terminal_block()
.await .await
.with_terminal_block_number(|el, terminal_block_number| async move { .with_terminal_block(|el, _| async move {
let missing_terminal_block = terminal_block_number.checked_add(1).unwrap(); let missing_terminal_block = Hash256::repeat_byte(42);
assert_eq!( assert_eq!(
el.is_valid_terminal_pow_block_hash(block_number_to_hash( el.is_valid_terminal_pow_block_hash(missing_terminal_block)
missing_terminal_block .await
)) .unwrap(),
.await
.unwrap(),
None None
) )
}) })

View File

@@ -1,13 +1,67 @@
use crate::engine_api::{http::JsonPreparePayloadRequest, ExecutePayloadResponse}; use crate::engine_api::{http::JsonPreparePayloadRequest, ExecutePayloadResponse, ExecutionBlock};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use std::cmp::Ordering;
use std::collections::HashMap; use std::collections::HashMap;
use tree_hash::TreeHash; use tree_hash::TreeHash;
use tree_hash_derive::TreeHash;
use types::{EthSpec, ExecutionPayload, Hash256, Uint256}; use types::{EthSpec, ExecutionPayload, Hash256, Uint256};
#[derive(Clone, Copy, Debug, PartialEq, Serialize, Deserialize)] #[derive(Clone, Debug, PartialEq)]
#[allow(clippy::large_enum_variant)] // This struct is only for testing.
pub enum Block<T: EthSpec> {
PoW(PoWBlock),
PoS(ExecutionPayload<T>),
}
impl<T: EthSpec> Block<T> {
pub fn block_number(&self) -> u64 {
match self {
Block::PoW(block) => block.block_number,
Block::PoS(payload) => payload.block_number,
}
}
pub fn parent_hash(&self) -> Hash256 {
match self {
Block::PoW(block) => block.parent_hash,
Block::PoS(payload) => payload.parent_hash,
}
}
pub fn block_hash(&self) -> Hash256 {
match self {
Block::PoW(block) => block.block_hash,
Block::PoS(payload) => payload.block_hash,
}
}
pub fn total_difficulty(&self) -> Option<Uint256> {
match self {
Block::PoW(block) => Some(block.total_difficulty),
Block::PoS(_) => None,
}
}
pub fn as_execution_block(&self, total_difficulty: u64) -> ExecutionBlock {
match self {
Block::PoW(block) => ExecutionBlock {
block_hash: block.block_hash,
block_number: block.block_number,
parent_hash: block.parent_hash,
total_difficulty: block.total_difficulty,
},
Block::PoS(payload) => ExecutionBlock {
block_hash: payload.block_hash,
block_number: payload.block_number,
parent_hash: payload.parent_hash,
total_difficulty: total_difficulty.into(),
},
}
}
}
#[derive(Clone, Copy, Debug, PartialEq, Serialize, Deserialize, TreeHash)]
#[serde(rename_all = "camelCase")] #[serde(rename_all = "camelCase")]
pub struct Block { pub struct PoWBlock {
pub block_number: u64, pub block_number: u64,
pub block_hash: Hash256, pub block_hash: Hash256,
pub parent_hash: Hash256, pub parent_hash: Hash256,
@@ -15,113 +69,145 @@ pub struct Block {
} }
pub struct ExecutionBlockGenerator<T: EthSpec> { pub struct ExecutionBlockGenerator<T: EthSpec> {
/*
* Common database
*/
blocks: HashMap<Hash256, Block<T>>,
block_hashes: HashMap<u64, Hash256>,
/* /*
* PoW block parameters * PoW block parameters
*/ */
pub seconds_since_genesis: u64,
pub block_interval_secs: u64,
pub terminal_total_difficulty: u64, pub terminal_total_difficulty: u64,
pub terminal_block_number: u64, pub terminal_block_number: u64,
/* /*
* PoS block parameters * PoS block parameters
*/ */
pub pending_payloads: HashMap<Hash256, ExecutionPayload<T>>, pub pending_payloads: HashMap<Hash256, ExecutionPayload<T>>,
pub merge_blocks: HashMap<Hash256, ExecutionPayload<T>>,
pub merge_block_numbers: HashMap<u64, Hash256>,
pub latest_merge_block: Option<u64>,
pub next_payload_id: u64, pub next_payload_id: u64,
pub payload_ids: HashMap<u64, ExecutionPayload<T>>, pub payload_ids: HashMap<u64, ExecutionPayload<T>>,
} }
impl<T: EthSpec> ExecutionBlockGenerator<T> { impl<T: EthSpec> ExecutionBlockGenerator<T> {
pub fn new(terminal_total_difficulty: u64, terminal_block_number: u64) -> Self { pub fn new(terminal_total_difficulty: u64, terminal_block_number: u64) -> Self {
Self { let mut gen = Self {
// PoW params blocks: <_>::default(),
seconds_since_genesis: 0, block_hashes: <_>::default(),
block_interval_secs: 1,
terminal_total_difficulty, terminal_total_difficulty,
terminal_block_number, terminal_block_number,
// PoS params
pending_payloads: <_>::default(), pending_payloads: <_>::default(),
merge_blocks: <_>::default(),
merge_block_numbers: <_>::default(),
latest_merge_block: None,
next_payload_id: 0, next_payload_id: 0,
payload_ids: <_>::default(), payload_ids: <_>::default(),
};
gen.insert_pow_block(0).unwrap();
gen
}
pub fn latest_block(&self) -> Option<Block<T>> {
let hash = *self
.block_hashes
.iter()
.max_by_key(|(number, _)| *number)
.map(|(_, hash)| hash)?;
self.block_by_hash(hash)
}
pub fn latest_execution_block(&self) -> Option<ExecutionBlock> {
self.latest_block()
.map(|block| block.as_execution_block(self.terminal_total_difficulty))
}
pub fn block_by_number(&self, number: u64) -> Option<Block<T>> {
let hash = *self.block_hashes.get(&number)?;
self.block_by_hash(hash)
}
pub fn execution_block_by_number(&self, number: u64) -> Option<ExecutionBlock> {
self.block_by_number(number)
.map(|block| block.as_execution_block(self.terminal_total_difficulty))
}
pub fn block_by_hash(&self, hash: Hash256) -> Option<Block<T>> {
self.blocks.get(&hash).cloned()
}
pub fn execution_block_by_hash(&self, hash: Hash256) -> Option<ExecutionBlock> {
self.block_by_hash(hash)
.map(|block| block.as_execution_block(self.terminal_total_difficulty))
}
pub fn insert_pow_blocks(
&mut self,
block_numbers: impl Iterator<Item = u64>,
) -> Result<(), String> {
for i in block_numbers {
self.insert_pow_block(i)?;
} }
Ok(())
} }
pub fn set_clock_for_block_number(&mut self, number: u64) { pub fn insert_pow_block(&mut self, block_number: u64) -> Result<(), String> {
self.seconds_since_genesis = number if block_number > self.terminal_block_number {
.checked_mul(self.block_interval_secs)
.expect("overflow setting clock");
}
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 sanitize_pos_block_number(&self, number: u64) -> Result<(), String> {
if number <= self.terminal_block_number {
return Err(format!( return Err(format!(
"cannot insert block {} as it is prior to terminal block {}", "{} is beyond terminal pow block {}",
number, self.terminal_block_number block_number, self.terminal_block_number
)); ));
} }
let time_based_block = self.block_number_at(self.seconds_since_genesis); let parent_hash = if block_number == 0 {
if time_based_block < self.terminal_block_number && number > time_based_block { Hash256::zero()
return Err(format!("it is too early to insert block {}", number)); } else if let Some(hash) = self.block_hashes.get(&(block_number - 1)) {
} *hash
} else {
return Err(format!(
"parent with block number {} not found",
block_number - 1
));
};
let next_block = self let increment = self
.latest_merge_block .terminal_total_difficulty
.unwrap_or(self.terminal_block_number) .checked_div(self.terminal_block_number)
+ 1; .expect("terminal block number must be non-zero");
let total_difficulty = increment
.checked_mul(block_number)
.expect("overflow computing total difficulty")
.into();
match number.cmp(&next_block) { let mut block = PoWBlock {
Ordering::Equal => Ok(()), block_number,
Ordering::Less => Err(format!( block_hash: Hash256::zero(),
"cannot insert block {} which already exists", parent_hash,
number total_difficulty,
)), };
Ordering::Greater => Err(format!(
"cannot insert block {} before inserting {}", block.block_hash = block.tree_hash_root();
number, next_block
)), self.block_hashes
} .insert(block.block_number, block.block_hash);
self.blocks.insert(block.block_hash, Block::PoW(block));
Ok(())
} }
pub fn prepare_payload_id( pub fn prepare_payload_id(
&mut self, &mut self,
payload: JsonPreparePayloadRequest, payload: JsonPreparePayloadRequest,
) -> Result<u64, String> { ) -> Result<u64, String> {
if self.block_number_at(self.seconds_since_genesis) < self.terminal_block_number { if !self
.blocks
.iter()
.any(|(_, block)| block.block_number() == self.terminal_block_number)
{
return Err("refusing to create payload id before terminal block".to_string()); return Err("refusing to create payload id before terminal block".to_string());
} }
let parent = self let parent = self
.block_by_hash(payload.parent_hash) .blocks
.get(&payload.parent_hash)
.ok_or_else(|| format!("unknown parent block {:?}", payload.parent_hash))?; .ok_or_else(|| format!("unknown parent block {:?}", payload.parent_hash))?;
let id = self.next_payload_id; let id = self.next_payload_id;
@@ -134,7 +220,7 @@ impl<T: EthSpec> ExecutionBlockGenerator<T> {
state_root: Hash256::repeat_byte(43), state_root: Hash256::repeat_byte(43),
logs_bloom: vec![0; 256].into(), logs_bloom: vec![0; 256].into(),
random: payload.random, random: payload.random,
block_number: parent.block_number + 1, block_number: parent.block_number() + 1,
gas_limit: 10, gas_limit: 10,
gas_used: 9, gas_used: 9,
timestamp: payload.timestamp, timestamp: payload.timestamp,
@@ -156,13 +242,13 @@ impl<T: EthSpec> ExecutionBlockGenerator<T> {
} }
pub fn execute_payload(&mut self, payload: ExecutionPayload<T>) -> ExecutePayloadResponse { pub fn execute_payload(&mut self, payload: ExecutionPayload<T>) -> ExecutePayloadResponse {
let parent = if let Some(parent) = self.block_by_hash(payload.parent_hash) { let parent = if let Some(parent) = self.blocks.get(&payload.parent_hash) {
parent parent
} else { } else {
return ExecutePayloadResponse::Invalid; return ExecutePayloadResponse::Invalid;
}; };
if payload.block_number != parent.block_number + 1 { if payload.block_number != parent.block_number() + 1 {
return ExecutePayloadResponse::Invalid; return ExecutePayloadResponse::Invalid;
} }
@@ -170,78 +256,6 @@ impl<T: EthSpec> ExecutionBlockGenerator<T> {
ExecutePayloadResponse::Valid ExecutePayloadResponse::Valid
} }
pub fn insert_pos_block(&mut self, number: u64) -> Result<(), String> {
self.sanitize_pos_block_number(number)?;
self.latest_merge_block = Some(number);
Ok(())
}
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<Block> {
self.block_by_number(self.latest_block_number())
}
pub fn block_by_number(&self, number: u64) -> Option<Block> {
let parent_hash = number
.checked_sub(1)
.map(block_number_to_hash)
.unwrap_or_else(Hash256::zero);
let block_hash = block_number_to_hash(number);
if number <= self.terminal_block_number {
if number <= self.latest_block_number() {
Some(Block {
block_number: number,
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(Block {
block_number: number,
block_hash,
parent_hash,
total_difficulty: Uint256::from(self.terminal_total_difficulty),
})
} else {
None
}
}
}
pub fn block_by_hash(&self, hash: Hash256) -> Option<Block> {
let block_number = block_hash_to_number(hash);
self.block_by_number(block_number)
}
}
pub fn block_number_to_hash(n: u64) -> Hash256 {
Hash256::from_low_u64_be(n + 1)
}
pub fn block_hash_to_number(hash: Hash256) -> u64 {
hash.to_low_u64_be()
.checked_sub(1)
.expect("do not query for zero hash")
} }
#[cfg(test)] #[cfg(test)]
@@ -258,26 +272,28 @@ mod test {
let mut generator: ExecutionBlockGenerator<MainnetEthSpec> = let mut generator: ExecutionBlockGenerator<MainnetEthSpec> =
ExecutionBlockGenerator::new(TERMINAL_DIFFICULTY, TERMINAL_BLOCK); ExecutionBlockGenerator::new(TERMINAL_DIFFICULTY, TERMINAL_BLOCK);
for mut i in 0..(TERMINAL_BLOCK + 5) { for i in 0..=TERMINAL_BLOCK {
i = std::cmp::min(i, TERMINAL_BLOCK); generator.insert_pow_block(i).unwrap();
/* /*
* Generate a block, inspect it. * Generate a block, inspect it.
*/ */
let block = generator.latest_block().unwrap(); let block = generator.latest_block().unwrap();
assert_eq!(block.block_hash, block_number_to_hash(i)); assert_eq!(block.block_number(), i);
assert_eq!(block_hash_to_number(block.block_hash), i);
let expected_parent = i let expected_parent = i
.checked_sub(1) .checked_sub(1)
.map(block_number_to_hash) .map(|i| generator.block_by_number(i).unwrap().block_hash())
.unwrap_or_else(Hash256::zero); .unwrap_or_else(Hash256::zero);
assert_eq!(block.parent_hash, expected_parent); assert_eq!(block.parent_hash(), expected_parent);
assert_eq!(block.total_difficulty, (i * DIFFICULTY_INCREMENT).into()); assert_eq!(
block.total_difficulty().unwrap(),
(i * DIFFICULTY_INCREMENT).into()
);
assert_eq!(generator.block_by_hash(block.block_hash).unwrap(), block); assert_eq!(generator.block_by_hash(block.block_hash()).unwrap(), block);
assert_eq!(generator.block_by_number(i).unwrap(), block); assert_eq!(generator.block_by_number(i).unwrap(), block);
/* /*
@@ -287,7 +303,7 @@ mod test {
if let Some(prev_i) = i.checked_sub(1) { if let Some(prev_i) = i.checked_sub(1) {
assert_eq!( assert_eq!(
generator.block_by_number(prev_i).unwrap(), generator.block_by_number(prev_i).unwrap(),
generator.block_by_hash(block.parent_hash).unwrap() generator.block_by_hash(block.parent_hash()).unwrap()
); );
} }
@@ -297,14 +313,10 @@ mod test {
let next_i = i + 1; let next_i = i + 1;
assert!(generator.block_by_number(next_i).is_none()); assert!(generator.block_by_number(next_i).is_none());
assert!(generator
.block_by_hash(block_number_to_hash(next_i))
.is_none());
generator.increment_seconds_since_genesis(1);
} }
} }
/*
#[test] #[test]
fn pos_blocks() { fn pos_blocks() {
const TERMINAL_DIFFICULTY: u64 = 10; const TERMINAL_DIFFICULTY: u64 = 10;
@@ -318,7 +330,7 @@ mod test {
let first_pos_block = generator.terminal_block_number + 1; let first_pos_block = generator.terminal_block_number + 1;
let second_pos_block = first_pos_block + 1; let second_pos_block = first_pos_block + 1;
generator.set_clock_for_block_number(penultimate_pow_block); generator.insert_pow_blocks(0..=penultimate_pow_block);
assert!(generator.block_by_number(last_pow_block).is_none()); assert!(generator.block_by_number(last_pow_block).is_none());
@@ -338,4 +350,5 @@ mod test {
generator.insert_pos_block(second_pos_block).unwrap(); generator.insert_pos_block(second_pos_block).unwrap();
} }
*/
} }

View File

@@ -27,7 +27,10 @@ pub async fn handle_rpc<T: EthSpec>(
match tag { match tag {
"latest" => Ok(serde_json::to_value( "latest" => Ok(serde_json::to_value(
ctx.execution_block_generator.read().await.latest_block(), ctx.execution_block_generator
.read()
.await
.latest_execution_block(),
) )
.unwrap()), .unwrap()),
other => Err(format!("The tag {} is not supported", other)), other => Err(format!("The tag {} is not supported", other)),
@@ -47,7 +50,7 @@ pub async fn handle_rpc<T: EthSpec>(
ctx.execution_block_generator ctx.execution_block_generator
.read() .read()
.await .await
.block_by_hash(hash), .execution_block_by_hash(hash),
) )
.unwrap()) .unwrap())
} }

View File

@@ -14,8 +14,6 @@ use tokio::sync::{oneshot, RwLock, RwLockWriteGuard};
use types::EthSpec; use types::EthSpec;
use warp::Filter; use warp::Filter;
pub use execution_block_generator::{block_hash_to_number, block_number_to_hash};
pub const DEFAULT_TERMINAL_DIFFICULTY: u64 = 6400; pub const DEFAULT_TERMINAL_DIFFICULTY: u64 = 6400;
pub const DEFAULT_TERMINAL_BLOCK: u64 = 64; pub const DEFAULT_TERMINAL_BLOCK: u64 = 64;