Skip to content

Commit

Permalink
feat: add EIP-4788 parent_beacon_block_root
Browse files Browse the repository at this point in the history
  • Loading branch information
KolbyML committed Mar 15, 2024
1 parent e0dc410 commit 29ca129
Show file tree
Hide file tree
Showing 2 changed files with 98 additions and 64 deletions.
161 changes: 97 additions & 64 deletions ethportal-api/src/types/execution/header.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,6 @@ use crate::{
utils::bytes::{hex_decode, hex_encode},
};

const LONDON_BLOCK_NUMBER: u64 = 12965000;
const SHANGHAI_BLOCK_NUMBER: u64 = 17034871;
const DENCUN_BLOCK_NUMBER: u64 = 19426589; // double-check

/// A block header.
#[derive(Debug, Clone, Eq, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
Expand Down Expand Up @@ -63,6 +59,8 @@ pub struct Header {
pub blob_gas_used: Option<U64>,
/// Excess blob gas. Introduced by EIP-4844
pub excess_blob_gas: Option<U64>,
/// The parent beacon block's root hash. Introduced by EIP-4788
pub parent_beacon_block_root: Option<H256>,
}

fn se_hex<S>(value: &[u8], serializer: S) -> Result<S::Ok, S::Error>
Expand Down Expand Up @@ -98,7 +96,9 @@ impl Header {

/// Append header to RLP stream `s`, optionally `with_seal`.
fn stream_rlp(&self, s: &mut RlpStream, with_seal: bool) {
let stream_length_without_seal = if self.excess_blob_gas.is_some() {
let stream_length_without_seal = if self.parent_beacon_block_root.is_some() {
18
} else if self.excess_blob_gas.is_some() {
// add two for excess_blob_gas and blob_gas_used
17
} else if self.withdrawals_root.is_some() {
Expand Down Expand Up @@ -149,6 +149,10 @@ impl Header {
if let Some(val) = self.excess_blob_gas {
s.append(&val);
}

if let Some(val) = self.parent_beacon_block_root {
s.append(&val);
}
}
}

Expand All @@ -175,19 +179,27 @@ impl Decodable for Header {
withdrawals_root: None,
blob_gas_used: None,
excess_blob_gas: None,
parent_beacon_block_root: None,
};

if header.number >= LONDON_BLOCK_NUMBER {
header.base_fee_per_gas = Some(rlp.val_at(15)?);
if let Ok(val) = rlp.val_at(15) {
header.base_fee_per_gas = Some(val)
}

if header.number >= SHANGHAI_BLOCK_NUMBER {
header.withdrawals_root = Some(rlp.val_at(16)?);
if let Ok(val) = rlp.val_at(16) {
header.withdrawals_root = Some(val)
}

if header.number >= DENCUN_BLOCK_NUMBER {
header.blob_gas_used = Some(rlp.val_at(17)?);
header.excess_blob_gas = Some(rlp.val_at(18)?);
if let Ok(val) = rlp.val_at(17) {
header.blob_gas_used = Some(val)
}

if let Ok(val) = rlp.val_at(18) {
header.excess_blob_gas = Some(val)
}

if let Ok(val) = rlp.val_at(19) {
header.parent_beacon_block_root = Some(val)
}

Ok(header)
Expand Down Expand Up @@ -221,6 +233,7 @@ impl PartialEq for Header {
&& self.withdrawals_root == other.withdrawals_root
&& self.blob_gas_used == other.blob_gas_used
&& self.excess_blob_gas == other.excess_blob_gas
&& self.parent_beacon_block_root == other.parent_beacon_block_root
}
}

Expand Down Expand Up @@ -251,6 +264,7 @@ impl From<Header> for RpcHeader {
withdrawals_root,
blob_gas_used,
excess_blob_gas,
parent_beacon_block_root,
} = header;

Self {
Expand Down Expand Up @@ -282,7 +296,8 @@ impl From<Header> for RpcHeader {
blob_gas_used,
excess_blob_gas,
hash,
parent_beacon_block_root: None,
parent_beacon_block_root: parent_beacon_block_root
.map(|h264| h264.as_fixed_bytes().into()),
}
}
}
Expand Down Expand Up @@ -471,13 +486,12 @@ impl ssz::Encode for SszNone {
#[allow(clippy::unwrap_used)]
mod tests {
use super::*;
use std::fs;
use std::{fs, str::FromStr};

use serde_json::{json, Value};
use ssz::Decode;
use test_log::test;

#[test]
#[test_log::test]
fn decode_and_encode_header() {
// Mainnet block #1 rlp encoded header
// sourced from mainnetMM data dump
Expand All @@ -499,7 +513,7 @@ mod tests {
assert_eq!(header_rlp, encoded_header);
}

#[test]
#[test_log::test]
fn decode_and_encode_header_after_1559() {
// RLP encoded block header #14037611
let header_rlp = hex_decode("0xf90214a02320c9ca606618919c2a4cf5c6012cfac99399446c60a07f084334dea25f69eca01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d4934794ea674fdde714fd979de3edf0f56aa9716b898ec8a0604a0ab7fe0d434943fbf2c525c4086818b8305349d91d6f4b205aca0759a2b8a0fdfe28e250fb15f7cb360d36ebb7dafa6da4f74543ce593baa96c27891ccac83a0cb9f9e60fb971068b76a8dece4202dde6b4075ebd90e7b2cd21c7fd8e121bba1b9010082e01d13f40116b1e1a0244090289b6920c51418685a0855031b988aef1b494313054c4002584928380267bc11cec18b0b30c456ca30651d9b06c931ea78aa0c40849859c7e0432df944341b489322b0450ce12026cafa1ba590f20af8051024fb8722a43610800381a531aa92042dd02448b1549052d6f06e4005b1000e063035c0220402a09c0124daab9028836209c446240d652c927bc7e4004b849256db5ba8d08b4a2321fd1e25c4d1dc480d18465d8600a41e864001cae44f38609d1c7414a8d62b5869d5a8001180d87228d788e852119c8a03df162471a317832622153da12fc21d828710062c7103534eb119714280201341ce6889ae926e025067872b68048d94e1ed83d6326b8401caa84183b062808461e859a88c617369612d65617374322d32a03472320df4ea70d29b89afdf195c3aa2289560a453957eea5058b57b80b908bf88d6450793e6dcec1c8532ff3f048d").unwrap();
Expand All @@ -519,7 +533,7 @@ mod tests {
assert_eq!(header_rlp, encoded_header);
}

#[test]
#[test_log::test]
fn decode_infura_jsonrpc_response() {
// https://etherscan.io/block/6008149
let val = json!({
Expand Down Expand Up @@ -556,7 +570,7 @@ mod tests {
assert_eq!(header.base_fee_per_gas, None);
}

#[test]
#[test_log::test]
fn decode_encode_header_with_proofs() {
let file =
fs::read_to_string("../trin-validation/src/assets/fluffy/header_with_proofs.json")
Expand All @@ -574,7 +588,7 @@ mod tests {
}
}

#[test]
#[test_log::test]
fn post_shanghai_header() {
let body =
std::fs::read_to_string("../test_assets/mainnet/block_17034871_value.json").unwrap();
Expand All @@ -588,59 +602,78 @@ mod tests {
assert_eq!(header.hash(), expected_hash);
}

#[test]
fn dencun_rlp() {
let body =
std::fs::read_to_string("../test_assets/mainnet/block_19433903_value.json").unwrap();
let response: Value = serde_json::from_str(&body).unwrap();
let header: Header = serde_json::from_value(response["result"].clone()).unwrap();
let encoded = rlp::encode(&header);
let decoded: Header = rlp::decode(&encoded).unwrap();
assert_eq!(header, decoded);
let re_encoded = rlp::encode(&decoded);
assert_eq!(encoded, re_encoded);
let body =
std::fs::read_to_string("../test_assets/mainnet/block_19433902_value.json").unwrap();
let response: Value = serde_json::from_str(&body).unwrap();
let header: Header = serde_json::from_value(response["result"].clone()).unwrap();
let encoded = rlp::encode(&header);
let decoded: Header = rlp::decode(&encoded).unwrap();
assert_eq!(header, decoded);
let re_encoded = rlp::encode(&decoded);
assert_eq!(encoded, re_encoded);
}

#[test]
fn post_dencun_header_without_blob_txs() {
let body =
std::fs::read_to_string("../test_assets/mainnet/block_19433902_value.json").unwrap();
let response: Value = serde_json::from_str(&body).unwrap();
let header: Header = serde_json::from_value(response["result"].clone()).unwrap();
let etherscan_hash = H256::from_slice(
&hex_decode("0x8ec7bd37afa247fde16aa96317c77055b7a633aa8dc9ae27d0d5c776b58fef04")
// Test vector from: https://github.com/ethereum/tests/blob/7e9e0940c0fcdbead8af3078ede70f969109bd85/BlockchainTests/ValidBlocks/bcExample/cancunExample.json
#[test_log::test]
fn dencun_rlp_ethereum_tests_example() {
let data = hex_decode("0xf90221a03a9b485972e7353edd9152712492f0c58d89ef80623686b6bf947a4a6dce6cb6a01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347942adc25665018aa1fe0e6bc666dac8fc2697ff9baa03c837fc158e3e93eafcaf2e658a02f5d8f99abc9f1c4c66cdea96c0ca26406aea04409cc4b699384ba5f8248d92b784713610c5ff9c1de51e9239da0dac76de9cea046cab26abf1047b5b119ecc2dda1296b071766c8b1307e1381fcecc90d513d86b90100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008001887fffffffffffffff8302a86582079e42a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b42188000000000000000009a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b4218302000080").unwrap();
let decoded: Header = rlp::decode(&data).unwrap();
let expected: Header = Header {
parent_hash: H256::from_str(
"0x3a9b485972e7353edd9152712492f0c58d89ef80623686b6bf947a4a6dce6cb6",
)
.unwrap(),
uncles_hash: H256::from_str(
"0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347",
)
.unwrap(),
author: H160::from_str("0x2adc25665018aa1fe0e6bc666dac8fc2697ff9ba").unwrap(),
state_root: H256::from_str(
"0x3c837fc158e3e93eafcaf2e658a02f5d8f99abc9f1c4c66cdea96c0ca26406ae",
)
.unwrap(),
transactions_root: H256::from_str(
"0x4409cc4b699384ba5f8248d92b784713610c5ff9c1de51e9239da0dac76de9ce",
)
.unwrap(),
receipts_root: H256::from_str(
"0x46cab26abf1047b5b119ecc2dda1296b071766c8b1307e1381fcecc90d513d86",
)
.unwrap(),
logs_bloom: Bloom::default(),
difficulty: U256::from_str("0x0").unwrap(),
number: 0x1,
gas_limit: U256::from_str("0x7fffffffffffffff").unwrap(),
gas_used: U256::from_str("0x02a865").unwrap(),
timestamp: 0x079e,
extra_data: vec![0x42],
mix_hash: Some(
H256::from_str(
"0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421",
)
.unwrap(),
);
),
nonce: Some(H64::zero()),
base_fee_per_gas: Some(U256::from_str("0x9").unwrap()),
withdrawals_root: Some(
H256::from_str(
"0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421",
)
.unwrap(),
),
blob_gas_used: Some(U64::from_str("0x020000").unwrap()),
excess_blob_gas: Some(U64::from_str("0x0").unwrap()),
parent_beacon_block_root: None,
};
assert_eq!(decoded, expected);

let expected_hash =
H256::from_slice(&hex_decode(response["result"]["hash"].as_str().unwrap()).unwrap());
assert_eq!(etherscan_hash, expected_hash);
assert_eq!(header.number, 19433902);
assert_eq!(header.hash(), expected_hash);
H256::from_str("0x10aca3ebb4cf6ddd9e945a5db19385f9c105ede7374380c50d56384c3d233785")
.unwrap();
assert_eq!(decoded.hash(), expected_hash);
assert_eq!(data, rlp::encode(&expected));
}

#[test]
fn post_dencun_header_with_blob_txs() {
#[rstest::rstest]
#[case("19433902")]
#[case("19433903")]
fn post_dencun_header(#[case] case: &str) {
let body =
std::fs::read_to_string("../test_assets/mainnet/block_19433903_value.json").unwrap();
std::fs::read_to_string(format!("../test_assets/mainnet/block_{case}_value.json"))
.unwrap();
let response: Value = serde_json::from_str(&body).unwrap();
let header: Header = serde_json::from_value(response["result"].clone()).unwrap();
let etherscan_hash = H256::from_slice(
&hex_decode("0xb39d99cc81a35570c4d5765d9273cb282e424324cd5aae059cc00de7d59afb69")
.unwrap(),
);
let expected_hash =
H256::from_slice(&hex_decode(response["result"]["hash"].as_str().unwrap()).unwrap());
assert_eq!(etherscan_hash, expected_hash);
assert_eq!(header.number, 19433903);
assert_eq!(header.hash(), expected_hash);
}
}
1 change: 1 addition & 0 deletions trin-validation/src/accumulator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -408,6 +408,7 @@ mod test {
withdrawals_root: None,
blob_gas_used: None,
excess_blob_gas: None,
parent_beacon_block_root: None,
}
}

Expand Down

0 comments on commit 29ca129

Please sign in to comment.