Skip to content

Commit b34be1e

Browse files
avilagaston9fkrause98
authored andcommitted
feat(l1): implement engine_NewPayloadV4 (#2016)
**Motivation** Implements endpoint `engine_newPayloadV4`. The endpoint was tested by running the `eest` simulator. **Description** - Performs validation on the received `Requests` following: https://github.com/ethereum/execution-apis/blob/c710097abda52b5a190d831eb8b1eddd3d28c603/src/engine/prague.md?plain=1#L50 - Computes the hash of the received `Requests` and adds it to the block header for validation. - Modifies `GenesisAccounts` to use `U256` as storage keys to be able to parse the genesis of the `eest` simulator. - Removes the `requests_hash` from the `Payload` structure since it is not a payload field. > [!NOTE] > The `eest` suite was not added to the CI because some tests (unrelated to this implementation) are still failing.
1 parent 25635f6 commit b34be1e

File tree

12 files changed

+211
-83
lines changed

12 files changed

+211
-83
lines changed

cmd/ef_tests/blockchain/types.rs

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -471,11 +471,7 @@ impl From<Account> for GenesisAccount {
471471
fn from(val: Account) -> Self {
472472
GenesisAccount {
473473
code: val.code,
474-
storage: val
475-
.storage
476-
.into_iter()
477-
.map(|(k, v)| (H256(k.to_big_endian()), v))
478-
.collect(),
474+
storage: val.storage,
479475
balance: val.balance,
480476
nonce: val.nonce.as_u64(),
481477
}

cmd/ef_tests/state/types.rs

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -203,11 +203,7 @@ impl From<&EFTestPreValue> for GenesisAccount {
203203
fn from(value: &EFTestPreValue) -> Self {
204204
Self {
205205
code: value.code.clone(),
206-
storage: value
207-
.storage
208-
.iter()
209-
.map(|(k, v)| (H256::from_slice(&k.to_big_endian()), *v))
210-
.collect(),
206+
storage: value.storage.clone(),
211207
balance: value.balance,
212208
nonce: value.nonce.as_u64(),
213209
}

crates/blockchain/blockchain.rs

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,11 @@ mod smoke_test;
77

88
use error::{ChainError, InvalidBlockError};
99
use ethrex_common::constants::GAS_PER_BLOB;
10-
use ethrex_common::types::requests::Requests;
10+
use ethrex_common::types::requests::{compute_requests_hash, EncodedRequests, Requests};
1111
use ethrex_common::types::{
12-
compute_receipts_root, compute_requests_hash, validate_block_header,
13-
validate_cancun_header_fields, validate_prague_header_fields,
14-
validate_pre_cancun_header_fields, Block, BlockHash, BlockHeader, BlockNumber, ChainConfig,
15-
EIP4844Transaction, Receipt, Transaction,
12+
compute_receipts_root, validate_block_header, validate_cancun_header_fields,
13+
validate_prague_header_fields, validate_pre_cancun_header_fields, Block, BlockHash,
14+
BlockHeader, BlockNumber, ChainConfig, EIP4844Transaction, Receipt, Transaction,
1615
};
1716
use ethrex_common::H256;
1817

@@ -82,7 +81,8 @@ pub fn validate_requests_hash(
8281
return Ok(());
8382
}
8483

85-
let computed_requests_hash = compute_requests_hash(requests);
84+
let encoded_requests: Vec<EncodedRequests> = requests.iter().map(|r| r.encode()).collect();
85+
let computed_requests_hash = compute_requests_hash(&encoded_requests);
8686
let valid = header
8787
.requests_hash
8888
.map(|requests_hash| requests_hash == computed_requests_hash)

crates/blockchain/payload.rs

Lines changed: 17 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,10 @@ use ethrex_common::{
77
constants::GAS_PER_BLOB,
88
types::{
99
calculate_base_fee_per_blob_gas, calculate_base_fee_per_gas, compute_receipts_root,
10-
compute_requests_hash, compute_transactions_root, compute_withdrawals_root,
11-
requests::Requests, BlobsBundle, Block, BlockBody, BlockHash, BlockHeader, BlockNumber,
12-
ChainConfig, MempoolTransaction, Receipt, Transaction, Withdrawal, DEFAULT_OMMERS_HASH,
10+
compute_transactions_root, compute_withdrawals_root,
11+
requests::{compute_requests_hash, EncodedRequests, Requests},
12+
BlobsBundle, Block, BlockBody, BlockHash, BlockHeader, BlockNumber, ChainConfig,
13+
MempoolTransaction, Receipt, Transaction, Withdrawal, DEFAULT_OMMERS_HASH,
1314
DEFAULT_REQUESTS_HASH,
1415
},
1516
Address, Bloom, Bytes, H256, U256,
@@ -178,6 +179,7 @@ pub struct PayloadBuildContext<'a> {
178179
pub remaining_gas: u64,
179180
pub receipts: Vec<Receipt>,
180181
pub requests: Vec<Requests>,
182+
pub requests_hash: Option<H256>,
181183
pub block_value: U256,
182184
base_fee_per_blob_gas: U256,
183185
pub blobs_bundle: BlobsBundle,
@@ -198,6 +200,7 @@ impl<'a> PayloadBuildContext<'a> {
198200
remaining_gas: payload.header.gas_limit,
199201
receipts: vec![],
200202
requests: vec![],
203+
requests_hash: None,
201204
block_value: U256::zero(),
202205
base_fee_per_blob_gas: U256::from(base_fee_per_blob_gas),
203206
payload,
@@ -491,13 +494,23 @@ fn apply_plain_transaction(
491494
}
492495

493496
pub fn extract_requests(context: &mut PayloadBuildContext) -> Result<(), EvmError> {
497+
if !context
498+
.chain_config()?
499+
.is_prague_activated(context.payload.header.timestamp)
500+
{
501+
return Ok(());
502+
};
503+
494504
let requests = get_evm_backend_or_default().extract_requests(
495505
&context.receipts,
496506
context.evm_state,
497507
&context.payload.header,
498508
&mut context.block_cache,
499509
);
500510
context.requests = requests?;
511+
let encoded_requests: Vec<EncodedRequests> =
512+
context.requests.iter().map(|r| r.encode()).collect();
513+
context.requests_hash = Some(compute_requests_hash(&encoded_requests));
501514

502515
Ok(())
503516
}
@@ -517,10 +530,7 @@ fn finalize_payload(context: &mut PayloadBuildContext) -> Result<(), ChainError>
517530
context.payload.header.transactions_root =
518531
compute_transactions_root(&context.payload.body.transactions);
519532
context.payload.header.receipts_root = compute_receipts_root(&context.receipts);
520-
context.payload.header.requests_hash = context
521-
.chain_config()?
522-
.is_prague_activated(context.payload.header.timestamp)
523-
.then_some(compute_requests_hash(&context.requests));
533+
context.payload.header.requests_hash = context.requests_hash;
524534
context.payload.header.gas_used = context.payload.header.gas_limit - context.remaining_gas;
525535
Ok(())
526536
}

crates/common/types/account.rs

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,11 @@ impl From<GenesisAccount> for Account {
8282
nonce: genesis.nonce,
8383
},
8484
code: genesis.code,
85-
storage: genesis.storage,
85+
storage: genesis
86+
.storage
87+
.iter()
88+
.map(|(k, v)| (H256(k.to_big_endian()), *v))
89+
.collect(),
8690
}
8791
}
8892
}
@@ -144,9 +148,12 @@ impl RLPDecode for AccountState {
144148
}
145149
}
146150

147-
pub fn compute_storage_root(storage: &HashMap<H256, U256>) -> H256 {
151+
pub fn compute_storage_root(storage: &HashMap<U256, U256>) -> H256 {
148152
let iter = storage.iter().filter_map(|(k, v)| {
149-
(!v.is_zero()).then_some((Keccak256::digest(k).to_vec(), v.encode_to_vec()))
153+
(!v.is_zero()).then_some((
154+
Keccak256::digest(k.to_big_endian()).to_vec(),
155+
v.encode_to_vec(),
156+
))
150157
});
151158
Trie::compute_hash_from_unsorted_iter(iter)
152159
}

crates/common/types/block.rs

Lines changed: 1 addition & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ use super::{
44
};
55
use crate::{
66
constants::MIN_BASE_FEE_PER_BLOB_GAS,
7-
types::{requests::Requests, Receipt, Transaction},
7+
types::{Receipt, Transaction},
88
Address, H256, U256,
99
};
1010
use bytes::Bytes;
@@ -16,7 +16,6 @@ use ethrex_rlp::{
1616
structs::{Decoder, Encoder},
1717
};
1818
use ethrex_trie::Trie;
19-
use k256::sha2::{Digest, Sha256};
2019
use keccak_hash::keccak;
2120
use serde::{Deserialize, Serialize};
2221

@@ -262,18 +261,6 @@ pub fn compute_withdrawals_root(withdrawals: &[Withdrawal]) -> H256 {
262261
Trie::compute_hash_from_unsorted_iter(iter)
263262
}
264263

265-
// See https://github.com/ethereum/EIPs/blob/2a6b6965e64787815f7fffb9a4c27660d9683846/EIPS/eip-7685.md?plain=1#L62.
266-
pub fn compute_requests_hash(requests: &[Requests]) -> H256 {
267-
let mut hasher = Sha256::new();
268-
for request in requests {
269-
let request_bytes = request.to_bytes();
270-
if request_bytes.len() > 1 {
271-
hasher.update(Sha256::digest(request_bytes));
272-
}
273-
}
274-
H256::from_slice(&hasher.finalize())
275-
}
276-
277264
impl RLPEncode for BlockBody {
278265
fn encode(&self, buf: &mut dyn bytes::BufMut) {
279266
Encoder::new(buf)

crates/common/types/genesis.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -296,7 +296,7 @@ pub struct GenesisAccount {
296296
#[serde(default, with = "crate::serde_utils::bytes")]
297297
pub code: Bytes,
298298
#[serde(default)]
299-
pub storage: HashMap<H256, U256>,
299+
pub storage: HashMap<U256, U256>,
300300
#[serde(deserialize_with = "crate::serde_utils::u256::deser_hex_or_dec_str")]
301301
pub balance: U256,
302302
#[serde(default, with = "crate::serde_utils::u64::hex_str")]
@@ -450,7 +450,7 @@ mod tests {
450450
let addr_b_storage = &genesis.alloc[&addr_b].storage;
451451
assert_eq!(
452452
addr_b_storage.get(
453-
&H256::from_str(
453+
&U256::from_str(
454454
"0x0000000000000000000000000000000000000000000000000000000000000022"
455455
)
456456
.unwrap()
@@ -464,7 +464,7 @@ mod tests {
464464
);
465465
assert_eq!(
466466
addr_b_storage.get(
467-
&H256::from_str(
467+
&U256::from_str(
468468
"0x0000000000000000000000000000000000000000000000000000000000000038"
469469
)
470470
.unwrap()

crates/common/types/requests.rs

Lines changed: 48 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,13 @@
1+
use bytes::Bytes;
12
use ethereum_types::Address;
3+
use k256::sha2::Sha256;
4+
use keccak_hash::H256;
5+
use serde::{Deserialize, Serialize};
6+
use sha3::Digest;
27
use tracing::error;
38

9+
use crate::serde_utils;
10+
411
use super::{Bytes48, Receipt};
512

613
pub type Bytes32 = [u8; 32];
@@ -9,15 +16,38 @@ const DEPOSIT_TYPE: u8 = 0x00;
916
const WITHDRAWAL_TYPE: u8 = 0x01;
1017
const CONSOLIDATION_TYPE: u8 = 0x02;
1118

19+
#[derive(Clone, Debug)]
20+
pub struct EncodedRequests(pub Bytes);
21+
22+
impl<'de> Deserialize<'de> for EncodedRequests {
23+
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
24+
where
25+
D: serde::Deserializer<'de>,
26+
{
27+
Ok(EncodedRequests(serde_utils::bytes::deserialize(
28+
deserializer,
29+
)?))
30+
}
31+
}
32+
33+
impl Serialize for EncodedRequests {
34+
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
35+
where
36+
S: serde::Serializer,
37+
{
38+
serde_utils::bytes::serialize(&self.0, serializer)
39+
}
40+
}
41+
1242
pub enum Requests {
1343
Deposit(Vec<Deposit>),
1444
Withdrawal(Vec<u8>),
1545
Consolidation(Vec<u8>),
1646
}
1747

1848
impl Requests {
19-
pub fn to_bytes(&self) -> Vec<u8> {
20-
match self {
49+
pub fn encode(&self) -> EncodedRequests {
50+
let bytes: Vec<u8> = match self {
2151
Requests::Deposit(deposits) => {
2252
let deposit_data = deposits.iter().flat_map(|d| d.to_summarized_byte_array());
2353
std::iter::once(DEPOSIT_TYPE).chain(deposit_data).collect()
@@ -28,8 +58,11 @@ impl Requests {
2858
Requests::Consolidation(data) => std::iter::once(CONSOLIDATION_TYPE)
2959
.chain(data.iter().cloned())
3060
.collect(),
31-
}
61+
};
62+
63+
EncodedRequests(Bytes::from(bytes))
3264
}
65+
3366
pub fn from_deposit_receipts(
3467
deposit_contract_address: Address,
3568
receipts: &[Receipt],
@@ -131,3 +164,15 @@ fn fixed_bytes<const N: usize>(data: &[u8], offset: usize) -> [u8; N] {
131164
.try_into()
132165
.expect("Couldn't convert to fixed bytes")
133166
}
167+
168+
// See https://github.com/ethereum/EIPs/blob/2a6b6965e64787815f7fffb9a4c27660d9683846/EIPS/eip-7685.md?plain=1#L62.
169+
pub fn compute_requests_hash(requests: &[EncodedRequests]) -> H256 {
170+
let mut hasher = Sha256::new();
171+
for request in requests {
172+
let request_bytes = request.0.as_ref();
173+
if request_bytes.len() > 1 {
174+
hasher.update(Sha256::digest(request_bytes));
175+
}
176+
}
177+
H256::from_slice(&hasher.finalize())
178+
}

0 commit comments

Comments
 (0)