use crate::{Address, Header, SealedHeader, TransactionSigned, Withdrawal, B256}; use alloy_rlp::{RlpDecodable, RlpEncodable}; use reth_codecs::derive_arbitrary; use serde::{Deserialize, Serialize}; use std::ops::Deref; pub use reth_rpc_types::{ BlockHashOrNumber, BlockId, BlockNumHash, BlockNumberOrTag, ForkBlock, RpcBlockHash, }; /// Ethereum full block. /// /// Withdrawals can be optionally included at the end of the RLP encoded message. #[derive( Debug, Clone, PartialEq, Eq, Default, Serialize, Deserialize, RlpEncodable, RlpDecodable, )] #[derive_arbitrary(rlp, 25)] #[rlp(trailing)] pub struct Block { /// Block header. pub header: Header, /// Transactions in this block. pub body: Vec, /// Ommers/uncles header. pub ommers: Vec
, /// Block withdrawals. pub withdrawals: Option>, } impl Block { /// Create SealedBLock that will create all header hashes. pub fn seal_slow(self) -> SealedBlock { SealedBlock { header: self.header.seal_slow(), body: self.body, ommers: self.ommers, withdrawals: self.withdrawals, } } /// Seal the block with a known hash. /// /// WARNING: This method does not perform validation whether the hash is correct. pub fn seal(self, hash: B256) -> SealedBlock { SealedBlock { header: self.header.seal(hash), body: self.body, ommers: self.ommers, withdrawals: self.withdrawals, } } /// Transform into a [`BlockWithSenders`]. pub fn with_senders(self, senders: Vec
) -> BlockWithSenders { assert_eq!(self.body.len(), senders.len(), "Unequal number of senders"); BlockWithSenders { block: self, senders } } /// Returns whether or not the block contains any blob transactions. pub fn has_blob_transactions(&self) -> bool { self.body.iter().any(|tx| tx.is_eip4844()) } /// Calculates a heuristic for the in-memory size of the [Block]. #[inline] pub fn size(&self) -> usize { self.header.size() + // take into account capacity self.body.iter().map(TransactionSigned::size).sum::() + self.body.capacity() * std::mem::size_of::() + self.ommers.iter().map(Header::size).sum::() + self.ommers.capacity() * std::mem::size_of::
() + self.withdrawals.as_ref().map(|w| w.iter().map(Withdrawal::size).sum::() + w.capacity() * std::mem::size_of::()).unwrap_or(std::mem::size_of::>>()) } } impl Deref for Block { type Target = Header; fn deref(&self) -> &Self::Target { &self.header } } /// Sealed block with senders recovered from transactions. #[derive(Debug, Clone, PartialEq, Eq, Default)] pub struct BlockWithSenders { /// Block pub block: Block, /// List of senders that match the transactions in the block pub senders: Vec
, } impl BlockWithSenders { /// New block with senders. Return none if len of tx and senders does not match pub fn new(block: Block, senders: Vec
) -> Option { (!block.body.len() != senders.len()).then_some(Self { block, senders }) } /// Split Structure to its components pub fn into_components(self) -> (Block, Vec
) { (self.block, self.senders) } } impl Deref for BlockWithSenders { type Target = Block; fn deref(&self) -> &Self::Target { &self.block } } #[cfg(any(test, feature = "test-utils"))] impl std::ops::DerefMut for BlockWithSenders { fn deref_mut(&mut self) -> &mut Self::Target { &mut self.block } } /// Sealed Ethereum full block. /// /// Withdrawals can be optionally included at the end of the RLP encoded message. #[derive_arbitrary(rlp, 10)] #[derive( Debug, Clone, PartialEq, Eq, Default, Serialize, Deserialize, RlpEncodable, RlpDecodable, )] #[rlp(trailing)] pub struct SealedBlock { /// Locked block header. pub header: SealedHeader, /// Transactions with signatures. pub body: Vec, /// Ommer/uncle headers pub ommers: Vec
, /// Block withdrawals. pub withdrawals: Option>, } impl SealedBlock { /// Create a new sealed block instance using the sealed header and block body. #[inline] pub fn new(header: SealedHeader, body: BlockBody) -> Self { let BlockBody { transactions, ommers, withdrawals } = body; Self { header, body: transactions, ommers, withdrawals } } /// Header hash. #[inline] pub fn hash(&self) -> B256 { self.header.hash() } /// Splits the sealed block into underlying components #[inline] pub fn split(self) -> (SealedHeader, Vec, Vec
) { (self.header, self.body, self.ommers) } /// Splits the [BlockBody] and [SealedHeader] into separate components #[inline] pub fn split_header_body(self) -> (SealedHeader, BlockBody) { ( self.header, BlockBody { transactions: self.body, ommers: self.ommers, withdrawals: self.withdrawals, }, ) } /// Returns only the blob transactions, if any, from the block body. pub fn blob_transactions(&self) -> Vec<&TransactionSigned> { self.body.iter().filter(|tx| tx.is_eip4844()).collect() } /// Expensive operation that recovers transaction signer. See [SealedBlockWithSenders]. pub fn senders(&self) -> Option> { TransactionSigned::recover_signers(&self.body, self.body.len()) } /// Seal sealed block with recovered transaction senders. pub fn seal_with_senders(self) -> Option { self.try_seal_with_senders().ok() } /// Seal sealed block with recovered transaction senders. pub fn try_seal_with_senders(self) -> Result { match self.senders() { Some(senders) => Ok(SealedBlockWithSenders { block: self, senders }), None => Err(self), } } /// Unseal the block pub fn unseal(self) -> Block { Block { header: self.header.unseal(), body: self.body, ommers: self.ommers, withdrawals: self.withdrawals, } } /// Calculates a heuristic for the in-memory size of the [SealedBlock]. #[inline] pub fn size(&self) -> usize { self.header.size() + // take into account capacity self.body.iter().map(TransactionSigned::size).sum::() + self.body.capacity() * std::mem::size_of::() + self.ommers.iter().map(Header::size).sum::() + self.ommers.capacity() * std::mem::size_of::
() + self.withdrawals.as_ref().map(|w| w.iter().map(Withdrawal::size).sum::() + w.capacity() * std::mem::size_of::()).unwrap_or(std::mem::size_of::>>()) } /// Calculates the total gas used by blob transactions in the sealed block. pub fn blob_gas_used(&self) -> u64 { self.blob_transactions().iter().filter_map(|tx| tx.blob_gas_used()).sum() } } impl From for Block { fn from(block: SealedBlock) -> Self { block.unseal() } } impl Deref for SealedBlock { type Target = SealedHeader; fn deref(&self) -> &Self::Target { &self.header } } #[cfg(any(test, feature = "test-utils"))] impl std::ops::DerefMut for SealedBlock { fn deref_mut(&mut self) -> &mut Self::Target { &mut self.header } } /// Sealed block with senders recovered from transactions. #[derive(Debug, Clone, PartialEq, Eq, Default)] pub struct SealedBlockWithSenders { /// Sealed block pub block: SealedBlock, /// List of senders that match transactions from block. pub senders: Vec
, } impl SealedBlockWithSenders { /// New sealed block with sender. Return none if len of tx and senders does not match pub fn new(block: SealedBlock, senders: Vec
) -> Option { (!block.body.len() != senders.len()).then_some(Self { block, senders }) } /// Split Structure to its components pub fn into_components(self) -> (SealedBlock, Vec
) { (self.block, self.senders) } } impl Deref for SealedBlockWithSenders { type Target = SealedBlock; fn deref(&self) -> &Self::Target { &self.block } } #[cfg(any(test, feature = "test-utils"))] impl std::ops::DerefMut for SealedBlockWithSenders { fn deref_mut(&mut self) -> &mut Self::Target { &mut self.block } } /// A response to `GetBlockBodies`, containing bodies if any bodies were found. /// /// Withdrawals can be optionally included at the end of the RLP encoded message. #[derive_arbitrary(rlp, 10)] #[derive( Clone, Debug, PartialEq, Eq, Default, Serialize, Deserialize, RlpEncodable, RlpDecodable, )] #[rlp(trailing)] pub struct BlockBody { /// Transactions in the block #[cfg_attr( any(test, feature = "arbitrary"), proptest( strategy = "proptest::collection::vec(proptest::arbitrary::any::(), 0..=100)" ) )] pub transactions: Vec, /// Uncle headers for the given block #[cfg_attr( any(test, feature = "arbitrary"), proptest( strategy = "proptest::collection::vec(proptest::arbitrary::any::
(), 0..=2)" ) )] pub ommers: Vec
, /// Withdrawals in the block. #[cfg_attr( any(test, feature = "arbitrary"), proptest( strategy = "proptest::option::of(proptest::collection::vec(proptest::arbitrary::any::(), 0..=16))" ) )] pub withdrawals: Option>, } impl BlockBody { /// Create a [`Block`] from the body and its header. pub fn create_block(&self, header: Header) -> Block { Block { header, body: self.transactions.clone(), ommers: self.ommers.clone(), withdrawals: self.withdrawals.clone(), } } /// Calculate the transaction root for the block body. pub fn calculate_tx_root(&self) -> B256 { crate::proofs::calculate_transaction_root(&self.transactions) } /// Calculate the ommers root for the block body. pub fn calculate_ommers_root(&self) -> B256 { crate::proofs::calculate_ommers_root(&self.ommers) } /// Calculate the withdrawals root for the block body, if withdrawals exist. If there are no /// withdrawals, this will return `None`. pub fn calculate_withdrawals_root(&self) -> Option { self.withdrawals.as_ref().map(|w| crate::proofs::calculate_withdrawals_root(w)) } /// Calculate all roots (transaction, ommers, withdrawals) for the block body. pub fn calculate_roots(&self) -> BlockBodyRoots { BlockBodyRoots { tx_root: self.calculate_tx_root(), ommers_hash: self.calculate_ommers_root(), withdrawals_root: self.calculate_withdrawals_root(), } } /// Calculates a heuristic for the in-memory size of the [BlockBody]. #[inline] pub fn size(&self) -> usize { self.transactions.iter().map(TransactionSigned::size).sum::() + self.transactions.capacity() * std::mem::size_of::() + self.ommers.iter().map(Header::size).sum::() + self.ommers.capacity() * std::mem::size_of::
() + self.withdrawals .as_ref() .map(|w| { w.iter().map(Withdrawal::size).sum::() + w.capacity() * std::mem::size_of::() }) .unwrap_or(std::mem::size_of::>>()) } } /// A struct that represents roots associated with a block body. This can be used to correlate /// block body responses with headers. #[derive(Clone, Debug, PartialEq, Eq, Default, Serialize, Deserialize, Hash)] pub struct BlockBodyRoots { /// The transaction root for the block body. pub tx_root: B256, /// The ommers hash for the block body. pub ommers_hash: B256, /// The withdrawals root for the block body, if withdrawals exist. pub withdrawals_root: Option, } #[cfg(test)] mod test { use super::{BlockId, BlockNumberOrTag::*, *}; use crate::hex_literal::hex; use alloy_rlp::{Decodable, Encodable}; use reth_rpc_types::HexStringMissingPrefixError; use std::str::FromStr; /// Check parsing according to EIP-1898. #[test] fn can_parse_blockid_u64() { let num = serde_json::json!( {"blockNumber": "0xaf"} ); let id = serde_json::from_value::(num); assert_eq!(id.unwrap(), BlockId::from(175)); } #[test] fn can_parse_block_hash() { let block_hash = B256::from_str("0xd4e56740f876aef8c010b86a40d5f56745a118d0906a34e69aec8c0db1cb8fa3") .unwrap(); let block_hash_json = serde_json::json!( { "blockHash": "0xd4e56740f876aef8c010b86a40d5f56745a118d0906a34e69aec8c0db1cb8fa3"} ); let id = serde_json::from_value::(block_hash_json).unwrap(); assert_eq!(id, BlockId::from(block_hash,)); } #[test] fn can_parse_block_hash_with_canonical() { let block_hash = B256::from_str("0xd4e56740f876aef8c010b86a40d5f56745a118d0906a34e69aec8c0db1cb8fa3") .unwrap(); let block_id = BlockId::Hash(RpcBlockHash::from_hash(block_hash, Some(true))); let block_hash_json = serde_json::json!( { "blockHash": "0xd4e56740f876aef8c010b86a40d5f56745a118d0906a34e69aec8c0db1cb8fa3", "requireCanonical": true } ); let id = serde_json::from_value::(block_hash_json).unwrap(); assert_eq!(id, block_id) } #[test] fn can_parse_blockid_tags() { let tags = [("latest", Latest), ("finalized", Finalized), ("safe", Safe), ("pending", Pending)]; for (value, tag) in tags { let num = serde_json::json!({ "blockNumber": value }); let id = serde_json::from_value::(num); assert_eq!(id.unwrap(), BlockId::from(tag)) } } #[test] fn repeated_keys_is_err() { let num = serde_json::json!({"blockNumber": 1, "requireCanonical": true, "requireCanonical": false}); assert!(serde_json::from_value::(num).is_err()); let num = serde_json::json!({"blockNumber": 1, "requireCanonical": true, "blockNumber": 23}); assert!(serde_json::from_value::(num).is_err()); } /// Serde tests #[test] fn serde_blockid_tags() { let block_ids = [Latest, Finalized, Safe, Pending].map(BlockId::from); for block_id in &block_ids { let serialized = serde_json::to_string(&block_id).unwrap(); let deserialized: BlockId = serde_json::from_str(&serialized).unwrap(); assert_eq!(deserialized, *block_id) } } #[test] fn serde_blockid_number() { let block_id = BlockId::from(100u64); let serialized = serde_json::to_string(&block_id).unwrap(); let deserialized: BlockId = serde_json::from_str(&serialized).unwrap(); assert_eq!(deserialized, block_id) } #[test] fn serde_blockid_hash() { let block_id = BlockId::from(B256::default()); let serialized = serde_json::to_string(&block_id).unwrap(); let deserialized: BlockId = serde_json::from_str(&serialized).unwrap(); assert_eq!(deserialized, block_id) } #[test] fn serde_blockid_hash_from_str() { let val = "\"0x898753d8fdd8d92c1907ca21e68c7970abd290c647a202091181deec3f30a0b2\""; let block_hash: B256 = serde_json::from_str(val).unwrap(); let block_id: BlockId = serde_json::from_str(val).unwrap(); assert_eq!(block_id, BlockId::Hash(block_hash.into())); } #[test] fn serde_rpc_payload_block_tag() { let payload = r#"{"method":"eth_call","params":[{"to":"0xebe8efa441b9302a0d7eaecc277c09d20d684540","data":"0x45848dfc"},"latest"],"id":1,"jsonrpc":"2.0"}"#; let value: serde_json::Value = serde_json::from_str(payload).unwrap(); let block_id_param = value.pointer("/params/1").unwrap(); let block_id: BlockId = serde_json::from_value::(block_id_param.clone()).unwrap(); assert_eq!(BlockId::Number(BlockNumberOrTag::Latest), block_id); } #[test] fn serde_rpc_payload_block_object() { let example_payload = r#"{"method":"eth_call","params":[{"to":"0xebe8efa441b9302a0d7eaecc277c09d20d684540","data":"0x45848dfc"},{"blockHash": "0xd4e56740f876aef8c010b86a40d5f56745a118d0906a34e69aec8c0db1cb8fa3"}],"id":1,"jsonrpc":"2.0"}"#; let value: serde_json::Value = serde_json::from_str(example_payload).unwrap(); let block_id_param = value.pointer("/params/1").unwrap().to_string(); let block_id: BlockId = serde_json::from_str::(&block_id_param).unwrap(); let hash = B256::from_str("0xd4e56740f876aef8c010b86a40d5f56745a118d0906a34e69aec8c0db1cb8fa3") .unwrap(); assert_eq!(BlockId::from(hash), block_id); let serialized = serde_json::to_string(&BlockId::from(hash)).unwrap(); assert_eq!("{\"blockHash\":\"0xd4e56740f876aef8c010b86a40d5f56745a118d0906a34e69aec8c0db1cb8fa3\"}", serialized) } #[test] fn serde_rpc_payload_block_number() { let example_payload = r#"{"method":"eth_call","params":[{"to":"0xebe8efa441b9302a0d7eaecc277c09d20d684540","data":"0x45848dfc"},{"blockNumber": "0x0"}],"id":1,"jsonrpc":"2.0"}"#; let value: serde_json::Value = serde_json::from_str(example_payload).unwrap(); let block_id_param = value.pointer("/params/1").unwrap().to_string(); let block_id: BlockId = serde_json::from_str::(&block_id_param).unwrap(); assert_eq!(BlockId::from(0u64), block_id); let serialized = serde_json::to_string(&BlockId::from(0u64)).unwrap(); assert_eq!("\"0x0\"", serialized) } #[test] #[should_panic] fn serde_rpc_payload_block_number_duplicate_key() { let payload = r#"{"blockNumber": "0x132", "blockNumber": "0x133"}"#; let parsed_block_id = serde_json::from_str::(payload); parsed_block_id.unwrap(); } #[test] fn serde_rpc_payload_block_hash() { let payload = r#"{"blockHash": "0xd4e56740f876aef8c010b86a40d5f56745a118d0906a34e69aec8c0db1cb8fa3"}"#; let parsed = serde_json::from_str::(payload).unwrap(); let expected = BlockId::from( B256::from_str("0xd4e56740f876aef8c010b86a40d5f56745a118d0906a34e69aec8c0db1cb8fa3") .unwrap(), ); assert_eq!(parsed, expected); } #[test] fn encode_decode_raw_block() { let bytes = hex!("f90288f90218a0fe21bb173f43067a9f90cfc59bbb6830a7a2929b5de4a61f372a9db28e87f9aea01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347940000000000000000000000000000000000000000a061effbbcca94f0d3e02e5bd22e986ad57142acabf0cb3d129a6ad8d0f8752e94a0d911c25e97e27898680d242b7780b6faef30995c355a2d5de92e6b9a7212ad3aa0056b23fbba480696b65fe5a59b8f2148a1299103c4f57df839233af2cf4ca2d2b90100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008003834c4b408252081e80a00000000000000000000000000000000000000000000000000000000000000000880000000000000000842806be9da056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421f869f86702842806be9e82520894658bdf435d810c91414ec09147daa6db624063798203e880820a95a040ce7918eeb045ebf8c8b1887ca139d076bda00fa828a07881d442a72626c42da0156576a68e456e295e4c9cf67cf9f53151f329438916e0f24fc69d6bbb7fbacfc0c0"); let bytes_buf = &mut bytes.as_ref(); let block = Block::decode(bytes_buf).unwrap(); let mut encoded_buf = Vec::new(); block.encode(&mut encoded_buf); assert_eq!(bytes[..], encoded_buf); } #[test] fn serde_blocknumber_non_0xprefix() { let s = "\"2\""; let err = serde_json::from_str::(s).unwrap_err(); assert_eq!(err.to_string(), HexStringMissingPrefixError::default().to_string()); } }