Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(l1): compute block hash once #1021

Merged
merged 3 commits into from
Oct 30, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion cmd/ef_tests/test_runner.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ pub fn run_ef_test(test_key: &str, test: &TestUnit) {

// Won't panic because test has been validated
let block: &CoreBlock = &block_fixture.block().unwrap().clone().into();
let hash = block.header.compute_block_hash();
let hash = block.hash();

// Attempt to add the block as the head of the chain
let chain_result = add_block(block, &store);
Expand Down
8 changes: 4 additions & 4 deletions cmd/ef_tests/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -175,14 +175,14 @@ impl BlockWithRLP {
}
impl From<Block> for CoreBlock {
fn from(val: Block) -> Self {
Self {
header: val.block_header.into(),
body: BlockBody {
CoreBlock::new(
val.block_header.into(),
BlockBody {
transactions: val.transactions.iter().map(|t| t.clone().into()).collect(),
ommers: val.uncle_headers.iter().map(|h| h.clone().into()).collect(),
withdrawals: val.withdrawals,
},
}
)
}
}

Expand Down
6 changes: 3 additions & 3 deletions cmd/ethereum_rust/decode.rs
Original file line number Diff line number Diff line change
Expand Up @@ -56,19 +56,19 @@ mod tests {
assert_eq!(
H256::from_str("0xac5c61edb087a51279674fe01d5c1f65eac3fd8597f9bea215058e745df8088e")
.unwrap(),
blocks.first().unwrap().header.compute_block_hash(),
blocks.first().unwrap().hash(),
"First block hash does not match"
);
assert_eq!(
H256::from_str("0xa111ce2477e1dd45173ba93cac819e62947e62a63a7d561b6f4825fb31c22645")
.unwrap(),
blocks.get(1).unwrap().header.compute_block_hash(),
blocks.get(1).unwrap().hash(),
"Second block hash does not match"
);
assert_eq!(
H256::from_str("0x8f64c4436f7213cfdf02cfb9f45d012f1774dfb329b8803de5e7479b11586902")
.unwrap(),
blocks.get(19).unwrap().header.compute_block_hash(),
blocks.get(19).unwrap().hash(),
"Last block hash does not match"
);
}
Expand Down
4 changes: 2 additions & 2 deletions cmd/ethereum_rust/ethereum_rust.rs
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,7 @@ async fn main() {
let blocks = read_chain_file(chain_rlp_path);
let size = blocks.len();
for block in &blocks {
let hash = block.header.compute_block_hash();
let hash = block.hash();
info!(
"Adding block {} with hash {:#x}.",
block.header.number, hash
Expand All @@ -139,7 +139,7 @@ async fn main() {
}
}
if let Some(last_block) = blocks.last() {
let hash = last_block.header.compute_block_hash();
let hash = last_block.hash();
apply_fork_choice(&store, hash, hash, hash).unwrap();
}
info!("Added {} blocks to blockchain", size);
Expand Down
82 changes: 41 additions & 41 deletions crates/blockchain/payload.rs
Original file line number Diff line number Diff line change
Expand Up @@ -69,51 +69,51 @@ pub fn create_payload(args: &BuildPayloadArgs, storage: &Store) -> Result<Block,
.ok_or_else(|| ChainError::ParentNotFound)?;
let chain_config = storage.get_chain_config()?;
let gas_limit = calc_gas_limit(parent_block.gas_limit, DEFAULT_BUILDER_GAS_CEIL);
let payload = Block {
header: BlockHeader {
parent_hash: args.parent,
ommers_hash: *DEFAULT_OMMERS_HASH,
coinbase: args.fee_recipient,
state_root: parent_block.state_root,
transactions_root: compute_transactions_root(&[]),
receipts_root: compute_receipts_root(&[]),
logs_bloom: Bloom::default(),
difficulty: U256::zero(),
number: parent_block.number.saturating_add(1),

let header = BlockHeader {
parent_hash: args.parent,
ommers_hash: *DEFAULT_OMMERS_HASH,
coinbase: args.fee_recipient,
state_root: parent_block.state_root,
transactions_root: compute_transactions_root(&[]),
receipts_root: compute_receipts_root(&[]),
logs_bloom: Bloom::default(),
difficulty: U256::zero(),
number: parent_block.number.saturating_add(1),
gas_limit,
gas_used: 0,
timestamp: args.timestamp,
// TODO: should use builder config's extra_data
extra_data: Bytes::new(),
prev_randao: args.random,
nonce: 0,
base_fee_per_gas: calculate_base_fee_per_gas(
gas_limit,
gas_used: 0,
timestamp: args.timestamp,
// TODO: should use builder config's extra_data
extra_data: Bytes::new(),
prev_randao: args.random,
nonce: 0,
base_fee_per_gas: calculate_base_fee_per_gas(
gas_limit,
parent_block.gas_limit,
parent_block.gas_used,
parent_block.base_fee_per_gas.unwrap_or_default(),
),
withdrawals_root: chain_config
.is_shanghai_activated(args.timestamp)
.then_some(compute_withdrawals_root(&args.withdrawals)),
blob_gas_used: Some(0),
excess_blob_gas: chain_config.is_cancun_activated(args.timestamp).then_some(
calc_excess_blob_gas(
parent_block.excess_blob_gas.unwrap_or_default(),
parent_block.blob_gas_used.unwrap_or_default(),
),
parent_block.gas_limit,
parent_block.gas_used,
parent_block.base_fee_per_gas.unwrap_or_default(),
),
withdrawals_root: chain_config
.is_shanghai_activated(args.timestamp)
.then_some(compute_withdrawals_root(&args.withdrawals)),
blob_gas_used: Some(0),
excess_blob_gas: chain_config.is_cancun_activated(args.timestamp).then_some(
calc_excess_blob_gas(
parent_block.excess_blob_gas.unwrap_or_default(),
parent_block.blob_gas_used.unwrap_or_default(),
),
parent_beacon_block_root: args.beacon_root,
},
// Empty body as we just created this payload
body: BlockBody {
transactions: Vec::new(),
ommers: Vec::new(),
withdrawals: Some(args.withdrawals.clone()),
},
),
parent_beacon_block_root: args.beacon_root,
};

let body = BlockBody {
transactions: Vec::new(),
ommers: Vec::new(),
withdrawals: Some(args.withdrawals.clone()),
};

// Delay applying withdrawals until the payload is requested and built
Ok(payload)
Ok(Block::new(header, body))
}

fn calc_gas_limit(parent_gas_limit: u64, desired_limit: u64) -> u64 {
Expand Down
24 changes: 12 additions & 12 deletions crates/blockchain/smoke_test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ mod blockchain_integration_test {

// Add first block. We'll make it canonical.
let block_1a = new_block(&store, &genesis_header);
let hash_1a = block_1a.header.compute_block_hash();
let hash_1a = block_1a.hash();
add_block(&block_1a, &store).unwrap();
store.set_canonical_block(1, hash_1a).unwrap();
let retrieved_1a = store.get_block_header(1).unwrap().unwrap();
Expand All @@ -35,7 +35,7 @@ mod blockchain_integration_test {

// Add second block at height 1. Will not be canonical.
let block_1b = new_block(&store, &genesis_header);
let hash_1b = block_1b.header.compute_block_hash();
let hash_1b = block_1b.hash();
add_block(&block_1b, &store).expect("Could not add block 1b.");
let retrieved_1b = store.get_block_header_by_hash(hash_1b).unwrap().unwrap();

Expand All @@ -44,7 +44,7 @@ mod blockchain_integration_test {

// Add a third block at height 2, child to the non canonical block.
let block_2 = new_block(&store, &block_1b.header);
let hash_2 = block_2.header.compute_block_hash();
let hash_2 = block_2.hash();
add_block(&block_2, &store).expect("Could not add block 2.");
let retrieved_2 = store.get_block_header_by_hash(hash_2).unwrap();

Expand All @@ -54,7 +54,7 @@ mod blockchain_integration_test {
// Receive block 2 as new head.
apply_fork_choice(
&store,
block_2.header.compute_block_hash(),
block_2.hash(),
genesis_header.compute_block_hash(),
genesis_header.compute_block_hash(),
)
Expand Down Expand Up @@ -104,15 +104,15 @@ mod blockchain_integration_test {

// Add first block. Not canonical.
let block_1a = new_block(&store, &genesis_header);
let hash_1a = block_1a.header.compute_block_hash();
let hash_1a = block_1a.hash();
add_block(&block_1a, &store).unwrap();
let retrieved_1a = store.get_block_header_by_hash(hash_1a).unwrap().unwrap();

assert!(!is_canonical(&store, 1, hash_1a).unwrap());

// Add second block at height 1. Canonical.
let block_1b = new_block(&store, &genesis_header);
let hash_1b = block_1b.header.compute_block_hash();
let hash_1b = block_1b.hash();
add_block(&block_1b, &store).expect("Could not add block 1b.");
apply_fork_choice(&store, hash_1b, genesis_hash, genesis_hash).unwrap();
let retrieved_1b = store.get_block_header(1).unwrap().unwrap();
Expand All @@ -124,7 +124,7 @@ mod blockchain_integration_test {

// Add a third block at height 2, child to the canonical one.
let block_2 = new_block(&store, &block_1b.header);
let hash_2 = block_2.header.compute_block_hash();
let hash_2 = block_2.hash();
add_block(&block_2, &store).expect("Could not add block 2.");
apply_fork_choice(&store, hash_2, genesis_hash, genesis_hash).unwrap();
let retrieved_2 = store.get_block_header_by_hash(hash_2).unwrap();
Expand All @@ -137,7 +137,7 @@ mod blockchain_integration_test {
// Receive block 1a as new head.
apply_fork_choice(
&store,
block_1a.header.compute_block_hash(),
block_1a.hash(),
genesis_header.compute_block_hash(),
genesis_header.compute_block_hash(),
)
Expand All @@ -159,12 +159,12 @@ mod blockchain_integration_test {

// Add block at height 1.
let block_1 = new_block(&store, &genesis_header);
let hash_1 = block_1.header.compute_block_hash();
let hash_1 = block_1.hash();
add_block(&block_1, &store).expect("Could not add block 1b.");

// Add child at height 2.
let block_2 = new_block(&store, &block_1.header);
let hash_2 = block_2.header.compute_block_hash();
let hash_2 = block_2.hash();
add_block(&block_2, &store).expect("Could not add block 2.");

assert!(!is_canonical(&store, 1, hash_1).unwrap());
Expand Down Expand Up @@ -205,7 +205,7 @@ mod blockchain_integration_test {

// Add child at height 2.
let block_2 = new_block(&store, &block_1.header);
let hash_2 = block_2.header.compute_block_hash();
let hash_2 = block_2.hash();
add_block(&block_2, &store).expect("Could not add block 2.");

assert_eq!(latest_canonical_block_hash(&store).unwrap(), genesis_hash);
Expand All @@ -217,7 +217,7 @@ mod blockchain_integration_test {

// Add a new, non canonical block, starting from genesis.
let block_1b = new_block(&store, &genesis_header);
let hash_b = block_1b.header.compute_block_hash();
let hash_b = block_1b.hash();
add_block(&block_1b, &store).expect("Could not add block b.");

// The latest block should be the same.
Expand Down
1 change: 1 addition & 0 deletions crates/common/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ secp256k1 = { version = "0.29", default-features = false, features = [
"global-context",
"recovery",
] }
once_cell = "1.20.2"
crc32fast.workspace = true
bytes.workspace = true
hex.workspace = true
Expand Down
37 changes: 34 additions & 3 deletions crates/common/types/block.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ pub type BlockNumber = u64;
pub type BlockHash = H256;

use lazy_static::lazy_static;
use once_cell::sync::OnceCell;

lazy_static! {
pub static ref DEFAULT_OMMERS_HASH: H256 = H256::from_slice(&hex::decode("1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347").unwrap()); // = Keccak256(RLP([])) as of EIP-3675
Expand All @@ -32,6 +33,22 @@ lazy_static! {
pub struct Block {
pub header: BlockHeader,
pub body: BlockBody,
#[serde(skip)]
hash: OnceCell<BlockHash>,
}

impl Block {
pub fn new(header: BlockHeader, body: BlockBody) -> Block {
Block {
header,
body,
hash: OnceCell::new(),
}
}

pub fn hash(&self) -> BlockHash {
*self.hash.get_or_init(|| self.header.compute_block_hash())
}
}

impl RLPEncode for Block {
Expand All @@ -58,7 +75,7 @@ impl RLPDecode for Block {
ommers,
withdrawals,
};
let block = Block { header, body };
let block = Block::new(header, body);
Ok((block, remaining))
}
}
Expand Down Expand Up @@ -527,13 +544,27 @@ fn calc_excess_blob_gas(parent_header: &BlockHeader) -> u64 {

#[cfg(test)]
mod test {

use std::str::FromStr;
use std::{str::FromStr, time::Instant};

use super::*;
use ethereum_types::H160;
use hex_literal::hex;

#[test]
fn compute_hash() {
let block = Block::default();

let start = Instant::now();
block.hash();
let duration = start.elapsed();

let start_2 = Instant::now();
block.hash();
let duration_2 = start_2.elapsed();

assert!(duration > 1000 * duration_2);
}

#[test]
fn test_compute_withdrawals_root() {
// Source: https://github.com/ethereum/tests/blob/9760400e667eba241265016b02644ef62ab55de2/BlockchainTests/EIPTests/bc4895-withdrawals/amountIs0.json
Expand Down
8 changes: 3 additions & 5 deletions crates/common/types/genesis.rs
Original file line number Diff line number Diff line change
Expand Up @@ -171,9 +171,7 @@ pub struct GenesisAccount {

impl Genesis {
pub fn get_block(&self) -> Block {
let header = self.get_block_header();
let body = self.get_block_body();
Block { header, body }
Block::new(self.get_block_header(), self.get_block_body())
}

fn get_block_header(&self) -> BlockHeader {
Expand Down Expand Up @@ -379,7 +377,7 @@ mod tests {
let reader = BufReader::new(file);
let genesis: Genesis =
serde_json::from_reader(reader).expect("Failed to deserialize genesis file");
let genesis_block_hash = genesis.get_block().header.compute_block_hash();
let genesis_block_hash = genesis.get_block().hash();
assert_eq!(
genesis_block_hash,
H256::from_str("0xcb5306dd861d0f2c1f9952fbfbc75a46d0b6ce4f37bea370c3471fe8410bf40b")
Expand All @@ -403,7 +401,7 @@ mod tests {
let reader = BufReader::new(file);
let genesis: Genesis =
serde_json::from_reader(reader).expect("Failed to deserialize genesis file");
let computed_block_hash = genesis.get_block().header.compute_block_hash();
let computed_block_hash = genesis.get_block().hash();
let genesis_block_hash =
H256::from_str("0x30f516e34fc173bb5fc4daddcc7532c4aca10b702c7228f3c806b4df2646fb7e")
.unwrap();
Expand Down
2 changes: 1 addition & 1 deletion crates/l2/proposer/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -155,7 +155,7 @@ impl Proposer {
};

let new_state_root_hash = store
.state_trie(block.header.compute_block_hash())
.state_trie(block.hash())
.unwrap()
.unwrap()
.hash()
Expand Down
2 changes: 1 addition & 1 deletion crates/networking/rpc/engine/payload.rs
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ impl RpcHandler for NewPayloadV3Request {
}

// Check that block_hash is valid
let actual_block_hash = block.header.compute_block_hash();
let actual_block_hash = block.hash();
if block_hash != actual_block_hash {
let result = PayloadStatus::invalid_with_err("Invalid block hash");
return serde_json::to_value(result)
Expand Down
Loading
Loading