Skip to content

Commit

Permalink
Merge pull request #19 from pnetwork-association/eip4844-block-verifi…
Browse files Browse the repository at this point in the history
…cation

feat(ethereum) <- add eip4844 block header verification
  • Loading branch information
Oghma authored Feb 19, 2024
2 parents e05dd3b + 63881eb commit 1c8a1ea
Show file tree
Hide file tree
Showing 7 changed files with 183 additions and 1 deletion.
37 changes: 37 additions & 0 deletions common/ethereum/src/eip_4844.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
use derive_more::Constructor;

use crate::EthBlock;

#[derive(Clone, Constructor)]
pub struct Eip4844 {}

impl Eip4844 {
pub fn is_active(&self, block: &EthBlock) -> bool {
block.blob_gas_used.is_some()
}
}

#[cfg(test)]
mod tests {
use super::*;
use crate::test_utils::get_sample_eip4844_sepolia_submission_material;

#[test]
fn eip4844_should_be_active() {
let eip_4844 = Eip4844::new();
let block = get_sample_eip4844_sepolia_submission_material();
let result = eip_4844.is_active(&block.block.unwrap());
assert!(result);
}

#[test]
fn eip_4844_should_not_be_active() {
let eip_4844 = Eip4844::new();

let block = get_sample_eip4844_sepolia_submission_material();
let mut block = block.block.unwrap();
block.blob_gas_used = None;
let result = eip_4844.is_active(&block);
assert!(!result);
}
}
98 changes: 97 additions & 1 deletion common/ethereum/src/eth_block.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ use serde_json::{json, Value as JsonValue};

use crate::{
eip_1559::Eip1559,
eip_4844::Eip4844,
eth_utils::{
convert_dec_str_to_u256,
convert_hex_strings_to_h256s,
Expand Down Expand Up @@ -44,6 +45,13 @@ pub struct EthBlock {
pub transactions_root: EthHash,
pub uncles: Vec<EthHash>,
pub base_fee_per_gas: Option<U256>,
// NOTE: The following new fields are EIP-4844 specific and hence they are
// optional. Non EIP-4844 blocks or layer 2s or other forks may not have
// these fields.
pub withdrawals_root: Option<EthHash>,
pub blob_gas_used: Option<U256>,
pub excess_blob_gas: Option<U256>,
pub parent_beacon_block_root: Option<EthHash>,
}

impl EthBlock {
Expand All @@ -56,6 +64,26 @@ impl EthBlock {
.ok_or(NoneError("Could not unwrap 'base_fee' from ETH block!"))
}

pub fn get_withdrawals_root(&self) -> Result<EthHash> {
self.withdrawals_root
.ok_or(NoneError("Could not unwrap 'withdrawals_root' from ETH block!"))
}

pub fn get_blob_gas_used(&self) -> Result<U256> {
self.blob_gas_used
.ok_or(NoneError("Could not unwrap 'blob_gas_used' from ETH block!"))
}

pub fn get_excess_blob_gas(&self) -> Result<U256> {
self.excess_blob_gas
.ok_or(NoneError("Could not unwrap 'excess_blob_gas' from ETH block!"))
}

pub fn get_parent_beacon_block_root(&self) -> Result<EthHash> {
self.parent_beacon_block_root
.ok_or(NoneError("Could not unwrap 'parent_beacon_block_root' from ETH block!"))
}

pub fn to_json(&self) -> Result<JsonValue> {
let encoded_transactions = self
.transactions
Expand Down Expand Up @@ -92,6 +120,8 @@ impl EthBlock {
}

pub fn from_json(json: &EthBlockJson) -> Result<Self> {
let radix = 16;

Ok(EthBlock {
size: U256::from(json.size),
number: U256::from(json.number),
Expand All @@ -114,6 +144,22 @@ impl EthBlock {
total_difficulty: convert_dec_str_to_u256(&json.total_difficulty)?,
base_fee_per_gas: Self::parse_base_fee_per_gas(&json.base_fee_per_gas)?,
logs_bloom: Bloom::from_slice(&convert_hex_to_bytes(&json.logs_bloom)?[..]),
withdrawals_root: match json.withdrawals_root.as_ref() {
None => None,
Some(hex) => Some(convert_hex_to_h256(hex)?),
},
blob_gas_used: match json.blob_gas_used.as_ref() {
None => None,
Some(hex) => Some(U256::from_str_radix(&strip_hex_prefix(hex), radix)?),
},
excess_blob_gas: match json.excess_blob_gas.as_ref() {
None => None,
Some(hex) => Some(U256::from_str_radix(&strip_hex_prefix(hex), radix)?),
},
parent_beacon_block_root: match json.parent_beacon_block_root.as_ref() {
None => None,
Some(hex) => Some(convert_hex_to_h256(hex)?),
},
})
}

Expand All @@ -133,9 +179,18 @@ impl EthBlock {

pub fn rlp_encode(&self, chain_id: &EthChainId) -> Result<Bytes> {
let mut rlp_stream = RlpStream::new();
let mut num_items = 15;
let eip_1559_is_active = Eip1559::new().is_active(chain_id, self.number)?;
let eip_4844_is_active = Eip4844::new().is_active(self);

if eip_1559_is_active {
num_items += 1;
}
if eip_4844_is_active {
num_items += 4;
}
rlp_stream
.begin_list(if eip_1559_is_active { 16 } else { 15 })
.begin_list(num_items)
.append(&self.parent_hash)
.append(&self.sha3_uncles)
.append(&self.miner)
Expand All @@ -154,6 +209,13 @@ impl EthBlock {
if eip_1559_is_active {
rlp_stream.append(&self.get_base_fee_per_gas()?);
};
if eip_4844_is_active {
rlp_stream.append(&self.get_withdrawals_root()?);
rlp_stream.append(&self.get_blob_gas_used()?);
rlp_stream.append(&self.get_excess_blob_gas()?);
rlp_stream.append(&self.get_parent_beacon_block_root()?);
}

Ok(rlp_stream.out().to_vec())
}

Expand Down Expand Up @@ -195,6 +257,10 @@ pub struct EthBlockJson {
pub transactions_root: String,
pub uncles: Vec<String>,
pub base_fee_per_gas: Option<JsonValue>,
pub withdrawals_root: Option<String>,
pub blob_gas_used: Option<String>,
pub excess_blob_gas: Option<String>,
pub parent_beacon_block_root: Option<String>,
}

#[cfg(test)]
Expand All @@ -204,6 +270,7 @@ mod tests {
get_expected_block,
get_sample_eip1559_mainnet_submission_material,
get_sample_eip1559_ropsten_submission_material,
get_sample_eip4844_sepolia_submission_material,
get_sample_eth_submission_material,
get_sample_eth_submission_material_json,
get_sample_invalid_block,
Expand Down Expand Up @@ -354,4 +421,33 @@ mod tests {
assert!(r.is_err() || matches!(r, Ok(false)))
});
}

#[test]
fn eip_4844_block_should_have_blob_fields() {
let block = get_sample_eip4844_sepolia_submission_material().block.unwrap();
let blob_gas = block.blob_gas_used.unwrap();
let excess_blob_gas = block.excess_blob_gas.unwrap();
let expected_blob_gas = U256::from(786432);
let expected_excess_blob_gas = U256::from(79953920);
assert_eq!(blob_gas, expected_blob_gas);
assert_eq!(excess_blob_gas, expected_excess_blob_gas);
}

#[test]
fn sepolia_eip4844_block_should_be_valid() {
let block = get_sample_eip4844_sepolia_submission_material().block.unwrap();
let chain_id = EthChainId::Sepolia;
let result = block.is_valid(&chain_id).unwrap();
assert!(result);
}

#[test]
fn invalid_sepolia_eip4844_block_should_not_be_valid() {
let mut block = get_sample_eip4844_sepolia_submission_material().block.unwrap();
// NOTE: Alter the new EIP4844 block header additional field to render the block invalid.
block.blob_gas_used = Some(block.blob_gas_used.unwrap() - 1);
let chain_id = EthChainId::Sepolia;
let result = block.is_valid(&chain_id).unwrap();
assert!(!result);
}
}
20 changes: 20 additions & 0 deletions common/ethereum/src/eth_block_from_json_rpc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,10 @@ pub struct EthBlockJsonFromRpc {
transactions_root: String,
transactions: Vec<String>,
base_fee_per_gas: Option<String>,
withdrawals_root: Option<String>,
blob_gas_used: Option<String>,
excess_blob_gas: Option<String>,
parent_beacon_block_root: Option<String>,
}

impl EthSubmissionMaterial {
Expand Down Expand Up @@ -91,6 +95,22 @@ impl EthBlock {
None => None,
Some(hex) => Some(U256::from_str_radix(&strip_hex_prefix(hex), radix)?),
},
withdrawals_root: match json.withdrawals_root.as_ref() {
None => None,
Some(hex) => Some(convert_hex_to_h256(hex)?),
},
blob_gas_used: match json.blob_gas_used.as_ref() {
None => None,
Some(hex) => Some(U256::from_str_radix(&strip_hex_prefix(hex), radix)?),
},
excess_blob_gas: match json.excess_blob_gas.as_ref() {
None => None,
Some(hex) => Some(U256::from_str_radix(&strip_hex_prefix(hex), radix)?),
},
parent_beacon_block_root: match json.parent_beacon_block_root.as_ref() {
None => None,
Some(hex) => Some(convert_hex_to_h256(hex)?),
},
})
}
}
Expand Down
1 change: 1 addition & 0 deletions common/ethereum/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ mod check_parent_exists;
mod core_initialization;
mod default_block_parameter;
mod eip_1559;
mod eip_4844;
mod eth_block;
mod eth_block_from_json_rpc;
mod eth_constants;
Expand Down
7 changes: 7 additions & 0 deletions common/ethereum/src/test_utils/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,8 @@ pub const SAMPLE_BLOCK_AND_RECEIPT_JSON_18: &str = "src/test_utils/rpc-block-0xf

pub const SAMPLE_BLOCK_AND_RECEIPT_JSON_19: &str = "src/test_utils/host-sub-mat-num-16640614.json";

pub const SAMPLE_BLOCK_AND_RECEIPT_JSON_20: &str = "src/test_utils/sepolia-sub-mat-block-5301643-with-eip-4844.json";

pub fn get_sample_block_from_rpc() -> String {
get_sample_eth_submission_material_string(18).unwrap()
}
Expand Down Expand Up @@ -149,6 +151,7 @@ pub fn get_sample_eth_submission_material_string(num: usize) -> Result<String> {
17 => Ok(SAMPLE_BLOCK_AND_RECEIPT_JSON_17),
18 => Ok(SAMPLE_BLOCK_AND_RECEIPT_JSON_18),
19 => Ok(SAMPLE_BLOCK_AND_RECEIPT_JSON_19),
20 => Ok(SAMPLE_BLOCK_AND_RECEIPT_JSON_20),
_ => Err(AppError::Custom(format!("Cannot find sample block num: {}", num))),
}?;
match Path::new(&path).exists() {
Expand Down Expand Up @@ -181,6 +184,10 @@ pub fn get_sample_eip1559_mainnet_submission_material() -> EthSubmissionMaterial
get_sample_eth_submission_material_n(12).unwrap()
}

pub fn get_sample_eip4844_sepolia_submission_material() -> EthSubmissionMaterial {
get_sample_eth_submission_material_n(20).unwrap()
}

pub fn get_sample_receipt_n(sample_block_num: usize, receipt_index: usize) -> Result<EthReceipt> {
get_sample_eth_submission_material_n(sample_block_num).map(|block| block.receipts.0[receipt_index].clone())
}
Expand Down

Large diffs are not rendered by default.

20 changes: 20 additions & 0 deletions common/sentinel/src/eth_rpc_calls/get_quicknode_sub_mat.rs
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,10 @@ pub struct QuicknodeBlockFromRpc {
transactions_root: String,
base_fee_per_gas: Option<String>,
transactions: Vec<EthReceiptJsonFromRpc>,
withdrawals_root: Option<String>,
blob_gas_used: Option<String>,
excess_blob_gas: Option<String>,
parent_beacon_block_root: Option<String>,
}

impl TryFrom<QuicknodeBlockFromRpc> for EthBlock {
Expand Down Expand Up @@ -81,6 +85,22 @@ impl TryFrom<QuicknodeBlockFromRpc> for EthBlock {
None => None,
Some(hex) => Some(U256::from_str_radix(&strip_hex_prefix(hex), radix)?),
},
withdrawals_root: match json.withdrawals_root.as_ref() {
None => None,
Some(hex) => Some(convert_hex_to_h256(hex)?),
},
blob_gas_used: match json.blob_gas_used.as_ref() {
None => None,
Some(hex) => Some(U256::from_str_radix(&strip_hex_prefix(hex), radix)?),
},
excess_blob_gas: match json.excess_blob_gas.as_ref() {
None => None,
Some(hex) => Some(U256::from_str_radix(&strip_hex_prefix(hex), radix)?),
},
parent_beacon_block_root: match json.parent_beacon_block_root.as_ref() {
None => None,
Some(hex) => Some(convert_hex_to_h256(hex)?),
},
})
}
}
Expand Down

0 comments on commit 1c8a1ea

Please sign in to comment.