From 29ca129b3af44f02273053695fdbf13995731c40 Mon Sep 17 00:00:00 2001 From: Kolby Moroz Liebl <31669092+KolbyML@users.noreply.github.com> Date: Thu, 14 Mar 2024 19:52:41 -0600 Subject: [PATCH] feat: add EIP-4788 parent_beacon_block_root --- ethportal-api/src/types/execution/header.rs | 161 ++++++++++++-------- trin-validation/src/accumulator.rs | 1 + 2 files changed, 98 insertions(+), 64 deletions(-) diff --git a/ethportal-api/src/types/execution/header.rs b/ethportal-api/src/types/execution/header.rs index e78b5fa50..a73b9ad51 100644 --- a/ethportal-api/src/types/execution/header.rs +++ b/ethportal-api/src/types/execution/header.rs @@ -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")] @@ -63,6 +59,8 @@ pub struct Header { pub blob_gas_used: Option, /// Excess blob gas. Introduced by EIP-4844 pub excess_blob_gas: Option, + /// The parent beacon block's root hash. Introduced by EIP-4788 + pub parent_beacon_block_root: Option, } fn se_hex(value: &[u8], serializer: S) -> Result @@ -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() { @@ -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); + } } } @@ -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) @@ -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 } } @@ -251,6 +264,7 @@ impl From
for RpcHeader { withdrawals_root, blob_gas_used, excess_blob_gas, + parent_beacon_block_root, } = header; Self { @@ -282,7 +296,8 @@ impl From
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()), } } } @@ -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 @@ -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(); @@ -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!({ @@ -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") @@ -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(); @@ -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); } } diff --git a/trin-validation/src/accumulator.rs b/trin-validation/src/accumulator.rs index 2a1581091..bc0c8c3f9 100644 --- a/trin-validation/src/accumulator.rs +++ b/trin-validation/src/accumulator.rs @@ -408,6 +408,7 @@ mod test { withdrawals_root: None, blob_gas_used: None, excess_blob_gas: None, + parent_beacon_block_root: None, } }