diff --git a/crates/consensus/src/block.rs b/crates/consensus/src/block.rs index f540cc52dae..6c4b321a68d 100644 --- a/crates/consensus/src/block.rs +++ b/crates/consensus/src/block.rs @@ -1,6 +1,6 @@ -//! Genesic Block Type +//! Block Type -use crate::{Header, Requests}; +use crate::Header; use alloc::vec::Vec; use alloy_eips::eip4895::Withdrawal; use alloy_rlp::{Decodable, Encodable, RlpDecodable, RlpEncodable}; @@ -32,8 +32,6 @@ pub struct BlockBody { pub ommers: Vec
, /// Block withdrawals. pub withdrawals: Option>, - /// Block requests - pub requests: Option, } /// We need to implement RLP traits manually because we currently don't have a way to flatten @@ -48,7 +46,6 @@ mod block_rlp { transactions: Vec, ommers: Vec
, withdrawals: Option>, - requests: Option, } #[derive(RlpEncodable)] @@ -58,20 +55,12 @@ mod block_rlp { transactions: &'a Vec, ommers: &'a Vec
, withdrawals: Option<&'a Vec>, - requests: Option<&'a Requests>, } impl<'a, T> From<&'a Block> for HelperRef<'a, T> { fn from(block: &'a Block) -> Self { - let Block { header, body: BlockBody { transactions, ommers, withdrawals, requests } } = - block; - Self { - header, - transactions, - ommers, - withdrawals: withdrawals.as_ref(), - requests: requests.as_ref(), - } + let Block { header, body: BlockBody { transactions, ommers, withdrawals } } = block; + Self { header, transactions, ommers, withdrawals: withdrawals.as_ref() } } } @@ -89,8 +78,8 @@ mod block_rlp { impl Decodable for Block { fn decode(b: &mut &[u8]) -> alloy_rlp::Result { - let Helper { header, transactions, ommers, withdrawals, requests } = Helper::decode(b)?; - Ok(Self { header, body: BlockBody { transactions, ommers, withdrawals, requests } }) + let Helper { header, transactions, ommers, withdrawals } = Helper::decode(b)?; + Ok(Self { header, body: BlockBody { transactions, ommers, withdrawals } }) } } } diff --git a/crates/consensus/src/lib.rs b/crates/consensus/src/lib.rs index b85cab92dfb..491393c49ac 100644 --- a/crates/consensus/src/lib.rs +++ b/crates/consensus/src/lib.rs @@ -30,9 +30,6 @@ pub use receipt::{ TxReceipt, }; -mod request; -pub use request::{Request, Requests}; - pub mod transaction; #[cfg(feature = "kzg")] pub use transaction::BlobTransactionValidationError; diff --git a/crates/consensus/src/request.rs b/crates/consensus/src/request.rs deleted file mode 100644 index d234044ead8..00000000000 --- a/crates/consensus/src/request.rs +++ /dev/null @@ -1,155 +0,0 @@ -use alloc::vec::Vec; -use alloy_eips::{ - eip6110::DepositRequest, - eip7002::WithdrawalRequest, - eip7251::ConsolidationRequest, - eip7685::{Decodable7685, Eip7685Error, Encodable7685}, -}; -use alloy_primitives::{bytes, Bytes}; -use alloy_rlp::{Decodable, Encodable}; -use derive_more::{Deref, DerefMut, From, IntoIterator}; - -/// Ethereum execution layer requests. -/// -/// See also [EIP-7685](https://eips.ethereum.org/EIPS/eip-7685). -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] -#[non_exhaustive] -#[cfg_attr(any(test, feature = "arbitrary"), derive(arbitrary::Arbitrary))] -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -#[cfg_attr(feature = "serde", serde(untagged))] -pub enum Request { - /// An [EIP-6110] deposit request. - /// - /// [EIP-6110]: https://eips.ethereum.org/EIPS/eip-6110 - DepositRequest(DepositRequest), - /// An [EIP-7002] withdrawal request. - /// - /// [EIP-7002]: https://eips.ethereum.org/EIPS/eip-7002 - WithdrawalRequest(WithdrawalRequest), - /// An [EIP-7251] consolidation request. - /// - /// [EIP-7251]: https://eips.ethereum.org/EIPS/eip-7251 - ConsolidationRequest(ConsolidationRequest), -} - -impl From for Request { - fn from(v: DepositRequest) -> Self { - Self::DepositRequest(v) - } -} - -impl From for Request { - fn from(v: WithdrawalRequest) -> Self { - Self::WithdrawalRequest(v) - } -} - -impl From for Request { - fn from(v: ConsolidationRequest) -> Self { - Self::ConsolidationRequest(v) - } -} - -impl Request { - /// Whether this is a [`DepositRequest`]. - pub const fn is_deposit_request(&self) -> bool { - matches!(self, Self::DepositRequest(_)) - } - - /// Whether this is a [`WithdrawalRequest`]. - pub const fn is_withdrawal_request(&self) -> bool { - matches!(self, Self::WithdrawalRequest(_)) - } - - /// Whether this is a [`ConsolidationRequest`]. - pub const fn is_consolidation_request(&self) -> bool { - matches!(self, Self::ConsolidationRequest(_)) - } - - /// Return the inner [`DepositRequest`], or `None` of this is not a deposit request. - pub const fn as_deposit_request(&self) -> Option<&DepositRequest> { - match self { - Self::DepositRequest(req) => Some(req), - _ => None, - } - } - - /// Return the inner [`WithdrawalRequest`], or `None` if this is not a withdrawal request. - pub const fn as_withdrawal_request(&self) -> Option<&WithdrawalRequest> { - match self { - Self::WithdrawalRequest(req) => Some(req), - _ => None, - } - } - - /// Return the inner [`ConsolidationRequest`], or `None` if this is not a consolidation request. - pub const fn as_consolidation_request(&self) -> Option<&ConsolidationRequest> { - match self { - Self::ConsolidationRequest(req) => Some(req), - _ => None, - } - } -} - -impl Encodable7685 for Request { - fn request_type(&self) -> u8 { - match self { - Self::DepositRequest(_) => 0, - Self::WithdrawalRequest(_) => 1, - Self::ConsolidationRequest(_) => 2, - } - } - - fn encode_payload_7685(&self, out: &mut dyn alloy_rlp::BufMut) { - match self { - Self::DepositRequest(deposit) => deposit.encode(out), - Self::WithdrawalRequest(withdrawal) => withdrawal.encode(out), - Self::ConsolidationRequest(consolidation) => consolidation.encode(out), - } - } -} - -impl Decodable7685 for Request { - fn typed_decode(ty: u8, buf: &mut &[u8]) -> Result { - Ok(match ty { - 0 => Self::DepositRequest(DepositRequest::decode(buf)?), - 1 => Self::WithdrawalRequest(WithdrawalRequest::decode(buf)?), - 2 => Self::ConsolidationRequest(ConsolidationRequest::decode(buf)?), - ty => return Err(Eip7685Error::UnexpectedType(ty)), - }) - } -} - -/// A list of EIP-7685 requests. -#[derive(Debug, Clone, PartialEq, Eq, Default, Hash, Deref, DerefMut, From, IntoIterator)] -#[cfg_attr(any(test, feature = "arbitrary"), derive(arbitrary::Arbitrary))] -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -pub struct Requests(pub Vec); - -impl Encodable for Requests { - fn encode(&self, out: &mut dyn bytes::BufMut) { - let mut h = alloy_rlp::Header { list: true, payload_length: 0 }; - - let mut encoded = Vec::new(); - for req in &self.0 { - let encoded_req = req.encoded_7685(); - h.payload_length += encoded_req.len(); - encoded.push(Bytes::from(encoded_req)); - } - - h.encode(out); - for req in encoded { - req.encode(out); - } - } -} - -impl Decodable for Requests { - fn decode(buf: &mut &[u8]) -> alloy_rlp::Result { - Ok( as Decodable>::decode(buf)? - .into_iter() - .map(|bytes| Request::decode_7685(&mut bytes.as_ref())) - .collect::, alloy_eips::eip7685::Eip7685Error>>() - .map(Self)?) - } -} diff --git a/crates/eips/src/eip6110.rs b/crates/eips/src/eip6110.rs index cbc04497d46..98593e9d79a 100644 --- a/crates/eips/src/eip6110.rs +++ b/crates/eips/src/eip6110.rs @@ -1,136 +1,9 @@ -//! Contains Deposit types, first introduced in the [Prague hardfork](https://github.com/ethereum/execution-apis/blob/main/src/engine/prague.md). +//! Contains Deposit request constants, first introduced in the [Prague hardfork](https://github.com/ethereum/execution-apis/blob/main/src/engine/prague.md). //! //! See also [EIP-6110](https://eips.ethereum.org/EIPS/eip-6110): Supply validator deposits on chain -//! -//! Provides validator deposits as a list of deposit operations added to the Execution Layer block. -use alloy_primitives::{address, Address, FixedBytes, B256}; -use alloy_rlp::{RlpDecodable, RlpEncodable}; +use alloy_primitives::{address, Address}; /// Mainnet deposit contract address. pub const MAINNET_DEPOSIT_CONTRACT_ADDRESS: Address = address!("00000000219ab540356cbb839cbe05303d7705fa"); - -/// This structure maps onto the deposit object from [EIP-6110](https://eips.ethereum.org/EIPS/eip-6110). -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, RlpEncodable, RlpDecodable, Default)] -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))] -#[cfg_attr(feature = "ssz", derive(ssz_derive::Encode, ssz_derive::Decode))] -#[cfg_attr(any(test, feature = "arbitrary"), derive(arbitrary::Arbitrary))] -pub struct DepositRequest { - /// Validator public key - pub pubkey: FixedBytes<48>, - /// Withdrawal credentials - pub withdrawal_credentials: B256, - /// Amount of ether deposited in gwei - #[cfg_attr(feature = "serde", serde(with = "alloy_serde::quantity"))] - pub amount: u64, - /// Deposit signature - pub signature: FixedBytes<96>, - /// Deposit index - #[cfg_attr(feature = "serde", serde(with = "alloy_serde::quantity"))] - pub index: u64, -} - -#[cfg(test)] -mod tests { - use super::*; - use alloy_primitives::hex; - use alloy_rlp::{Decodable, Encodable}; - - #[test] - fn test_encode_decode_request_roundtrip() { - // Define multiple test cases as tuples containing the test data - let test_cases = vec![ - ( - // https://etherscan.io/tx/0xab9e0b47767c6172f49f691e5fd96cb257c17f2d39cf64742d71e5435308403c#eventlog - FixedBytes::<48>::from(hex!("8E01A8F21BDC38991ADA53CA86D6C78D874675A450A38431CC6AA0F12D5661E344784C56C8A211F7025224D1303EE801")), - B256::from(hex!("010000000000000000000000AF6DF504F08DDF582D604D2F0A593BC153C25DBD")), - 0x0040597307000000u64, - FixedBytes::<96>::from(hex!("B65F3DB79405544528D6D92040282F29171F4FF6E5ABB2D59F9EE1F1254ACED2A7000F87BC2684F543E913A7CC1007EA0E97289B349C553EECDF253CD3EF5814088BA3D4AC286F2634DAC3D026D9A01E4C166DC75E249D626A0F1C180DAB75CE")), - 0xB92E1A0000000000u64, - ), - // https://etherscan.io/tx/0x756a8aba9f8df9fba33519bc1ec1ad2251507f66ef65cb15eb0a80ddfd0bcbef#eventlog - ( - FixedBytes::<48>::from(hex!("85BA6057EA5100DCB0B347D545BE0B688B2AD10A029C24A9D653F18BA3AC743B3CF8E022AE487AD8C5670D9C20D101D8")), - B256::from(hex!("010000000000000000000000E839A3E9EFB32C6A56AB7128E51056585275506C")), - 0x0040597307000000u64, - FixedBytes::<96>::from(hex!("8F685E17FC36B8DE5EF6E81523995139EF59280F7D25D0C422C7BDF573217F8127B2425B87E414443430B4EE05EE5ABE19EEDB9B239BA1354FBB8133C5B068FA7B278296856F7C7592F1AF9332762AB4389B9FC224D32E209077368AE3CED710")), - 0xDD2A1A0000000000u64, - ), - ( - // https://etherscan.io/tx/0xa5759378809a22bf8072de41c155e176a74a08323e94e4167ee2692887e83859#eventlog - FixedBytes::<48>::from(hex!("A3151E4E6BE6A4002249331B60EF426F6CDE5C33B27C9F14FC6639E6888A10F54C4A44AEE7AB0690BF09A89BDC00237C")), - B256::from(hex!("01000000000000000000000006676E8584342CC8B6052CFDF381C3A281F00AC8")), - 0x0040597307000000u64, - FixedBytes::<96>::from(hex!("A3C85DF60DF11200166D49A055E96C4659D37AC630CDC6E5C3EE039478E5B558F50F390D306249CEC66ACAA09011B85300434FFB566FE599E3E1596162BC3BBCE7BEE9122DAEBF7D1F42124C0FF00BE6EE8B79E0F436044337148EB061E0B163")), - 0x85A6130000000000u64, - ), - ( - // https://etherscan.io/tx/0x132bb7c90069d9699a84cdd041ddbe7a5cc42b2d26b604a8ac282aa5c17ed218#eventlog - FixedBytes::<48>::from(hex!("AAE673FEE94E4552CFEC432DCDDF46D1F613DD48E3DFB36179C973B73BCFDE5C463C5B719DD916DA8DA3981BFD0BAE29")), - B256::from(hex!("010000000000000000000000A8C62111E4652B07110A0FC81816303C42632F64")), - 0x0040597307000000u64, - FixedBytes::<96>::from(hex!("B6BA4C2CA28E46BA0DD8757EBDD52BC3609DA3BCF17BCCEEDD181630797F2A51CA2D8B4BFBA8639074276ABA6A4B7316106AB9F1642BAB0D9A8058211F366BBEB6E42CB1C56D17155A84B2C32F61F772C41749134665CBD7EC43122691527050")), - 0xC9A8130000000000u64, - ), - ( - // https://etherscan.io/tx/0xc97358e047333933278b8ab08ec4cfff8f6cf4028e2c3d877a5a89bd9f7303c9#eventlog - FixedBytes::<48>::from(hex!("A3CF35BEC7827E666C591BE49336B19FDE0F6ADA5F7139CFEEB719F372B200EDAC11FE73EDE7BE5B4A17E43BF055C58A")), - B256::from(hex!("0100000000000000000000008B7F4F725AE240D9B28D8129D35E6580D1251852")), - 0x0040597307000000u64, - FixedBytes::<96>::from(hex!("B9844B35D0831E22DF5E8374FF6C405F98DED278E813EBF9F1A61CFC08F6019D99E3C4B975BFDE999DC2AAC8EA99C540112FFAB1557CE0ADD9D80E0F91EB2D370F3220DA04D96F6256A07288CEFDB2F27C2C3DDE26B49EBAF5801E99FDCA095D")), - 0x62301A0000000000u64, - ), - ]; - - // Iterate over each test case - for (pubkey, withdrawal_credentials, amount, signature, index) in test_cases { - let original_request = - DepositRequest { pubkey, withdrawal_credentials, amount, signature, index }; - - // Encode the request - let mut buf = Vec::new(); - original_request.encode(&mut buf); - - // Decode the request - let decoded_request = - DepositRequest::decode(&mut &buf[..]).expect("Failed to decode request"); - - // Ensure the encoded and then decoded request matches the original - assert_eq!(original_request, decoded_request); - } - } - - #[test] - fn test_serde_deposit_request() { - // Sample JSON input representing a deposit request - let json_data = r#"{"pubkey":"0x8e01a8f21bdc38991ada53ca86d6c78d874675a450a38431cc6aa0f12d5661e344784c56c8a211f7025224d1303ee801","withdrawalCredentials":"0x010000000000000000000000af6df504f08ddf582d604d2f0a593bc153c25dbd","amount":"0x40597307000000","signature":"0xb65f3db79405544528d6d92040282f29171f4ff6e5abb2d59f9ee1f1254aced2a7000f87bc2684f543e913a7cc1007ea0e97289b349c553eecdf253cd3ef5814088ba3d4ac286f2634dac3d026d9a01e4c166dc75e249d626a0f1c180dab75ce","index":"0xb92e1a0000000000"}"#; - - // Deserialize the JSON into a DepositRequest struct - let deposit_request: DepositRequest = - serde_json::from_str(json_data).expect("Failed to deserialize"); - - // Verify the deserialized content - assert_eq!( - deposit_request.pubkey, - FixedBytes::<48>::from(hex!("8E01A8F21BDC38991ADA53CA86D6C78D874675A450A38431CC6AA0F12D5661E344784C56C8A211F7025224D1303EE801")) - ); - assert_eq!( - deposit_request.withdrawal_credentials, - B256::from(hex!("010000000000000000000000AF6DF504F08DDF582D604D2F0A593BC153C25DBD")) - ); - assert_eq!(deposit_request.amount, 0x0040597307000000u64); - assert_eq!( - deposit_request.signature, - FixedBytes::<96>::from(hex!("B65F3DB79405544528D6D92040282F29171F4FF6E5ABB2D59F9EE1F1254ACED2A7000F87BC2684F543E913A7CC1007EA0E97289B349C553EECDF253CD3EF5814088BA3D4AC286F2634DAC3D026D9A01E4C166DC75E249D626A0F1C180DAB75CE")) - ); - assert_eq!(deposit_request.index, 0xB92E1A0000000000u64); - - // Serialize the struct back into JSON - let serialized_json = serde_json::to_string(&deposit_request).expect("Failed to serialize"); - - // Check if the serialized JSON matches the expected JSON structure - assert_eq!(serialized_json, json_data); - } -} diff --git a/crates/eips/src/eip7002.rs b/crates/eips/src/eip7002.rs index caa546b2ad7..b8dfef97e53 100644 --- a/crates/eips/src/eip7002.rs +++ b/crates/eips/src/eip7002.rs @@ -1,9 +1,8 @@ -//! Contains the system contract and [WithdrawalRequest] types, first introduced in the [Prague hardfork](https://github.com/ethereum/execution-apis/blob/main/src/engine/prague.md). +//! Contains the system contract, first introduced in the [Prague hardfork](https://github.com/ethereum/execution-apis/blob/main/src/engine/prague.md). //! //! See also [EIP-7002](https://eips.ethereum.org/EIPS/eip-7002): Execution layer triggerable withdrawals -use alloy_primitives::{address, bytes, Address, Bytes, FixedBytes}; -use alloy_rlp::{RlpDecodable, RlpEncodable}; +use alloy_primitives::{address, bytes, Address, Bytes}; /// The caller to be used when calling the EIP-7002 withdrawal requests contract at the end of the /// block. @@ -18,107 +17,3 @@ pub static WITHDRAWAL_REQUEST_PREDEPLOY_CODE: Bytes = bytes!(" 3373fffffffffff /// The [EIP-7685](https://eips.ethereum.org/EIPS/eip-7685) request type for withdrawal requests. pub const WITHDRAWAL_REQUEST_TYPE: u8 = 0x01; - -/// Represents an execution layer triggerable withdrawal request. -/// -/// See [EIP-7002](https://eips.ethereum.org/EIPS/eip-7002). -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, RlpEncodable, RlpDecodable, Default)] -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))] -#[cfg_attr(feature = "ssz", derive(ssz_derive::Encode, ssz_derive::Decode))] -#[cfg_attr(any(test, feature = "arbitrary"), derive(arbitrary::Arbitrary))] -pub struct WithdrawalRequest { - /// Address of the source of the exit. - pub source_address: Address, - /// Validator public key. - pub validator_pubkey: FixedBytes<48>, - /// Amount of withdrawn ether in gwei. - #[cfg_attr(feature = "serde", serde(with = "alloy_serde::quantity"))] - pub amount: u64, -} - -#[cfg(test)] -mod tests { - use super::*; - use alloy_primitives::hex; - use alloy_rlp::{Decodable, Encodable}; - use core::str::FromStr; - - #[test] - fn test_encode_decode_request_roundtrip() { - // Define multiple test cases as tuples containing the test data. - // - // Examples are randomly generated using some random validators found on Etherscan. - let test_cases = vec![ - ( - Address::from_str("0xaE0E8770147AaA6828a0D6f642504663F10F7d1E").unwrap(), - FixedBytes::<48>::from(hex!("8e8d8749f6bc79b78be7cc6e49ff640e608454840c360b344c3a4d9b7428e280e7f40d2271bad65d8cbbfdd43cb8793b")), - 10 - ), - ( - Address::from_str("0xf86f8D6A7d2AF439245c1145d88B04dAf2d7e509").unwrap(), - FixedBytes::<48>::from(hex!("a85d7a6aa90eedebe103b8d4d3dc86003aea8b6c8159d9d50f7685828bc97d211b2c512b1dcbb8d63b60a56c91dda8ea")), - 354 - ), - ( - Address::from_str("0xf86f8D6A7d2AF439245c1145d88B04dAf2d7e509").unwrap(), - FixedBytes::<48>::from(hex!("a77eec36b046fbbf088e9253aa8c6800863d882c56fc6fa04800bbed742820f1bc7eb837601322840a18bbe0d24893b2")), - 19 - ), - ( - Address::from_str("0xAFedF06777839D59eED3163cC3e0A5057b514399").unwrap(), - FixedBytes::<48>::from(hex!("a3ecb9359401bb22d00cefddf6f6879d14a2ee74d3325cc8cdff0796bd0b3b47c5f5b4d02e5a865d7b639eb8124286a5")), - 9 - ), - ]; - - // Iterate over each test case - for (source_address, validator_pubkey, amount) in test_cases { - let original_request = WithdrawalRequest { source_address, validator_pubkey, amount }; - - // Encode the request - let mut buf = Vec::new(); - original_request.encode(&mut buf); - - // Decode the request - let decoded_request = - WithdrawalRequest::decode(&mut &buf[..]).expect("Failed to decode request"); - - // Ensure the encoded and then decoded request matches the original - assert_eq!(original_request, decoded_request); - } - } - - #[test] - fn test_serde_withdrawal_request() { - // Sample JSON input representing a withdrawal request - let json_data = r#"{ - "sourceAddress":"0xAE0E8770147AaA6828a0D6f642504663F10F7d1E", - "validatorPubkey":"0x8e8d8749f6bc79b78be7cc6e49ff640e608454840c360b344c3a4d9b7428e280e7f40d2271bad65d8cbbfdd43cb8793b", - "amount":"0x1" - }"#; - - // Deserialize the JSON into a WithdrawalRequest struct - let withdrawal_request: WithdrawalRequest = - serde_json::from_str(json_data).expect("Failed to deserialize"); - - // Verify the deserialized content - assert_eq!( - withdrawal_request.source_address, - Address::from_str("0xAE0E8770147AaA6828a0D6f642504663F10F7d1E").unwrap() - ); - assert_eq!( - withdrawal_request.validator_pubkey, - FixedBytes::<48>::from(hex!("8e8d8749f6bc79b78be7cc6e49ff640e608454840c360b344c3a4d9b7428e280e7f40d2271bad65d8cbbfdd43cb8793b")) - ); - assert_eq!(withdrawal_request.amount, 1); - - // Serialize the struct back into JSON - let serialized_json = - serde_json::to_string(&withdrawal_request).expect("Failed to serialize"); - - // Check if the serialized JSON matches the expected JSON structure - let expected_json = r#"{"sourceAddress":"0xae0e8770147aaa6828a0d6f642504663f10f7d1e","validatorPubkey":"0x8e8d8749f6bc79b78be7cc6e49ff640e608454840c360b344c3a4d9b7428e280e7f40d2271bad65d8cbbfdd43cb8793b","amount":"0x1"}"#; - assert_eq!(serialized_json, expected_json); - } -} diff --git a/crates/eips/src/eip7251.rs b/crates/eips/src/eip7251.rs index 5fd343eb204..ead24920a23 100644 --- a/crates/eips/src/eip7251.rs +++ b/crates/eips/src/eip7251.rs @@ -1,9 +1,8 @@ -//! Contains consolidation types, first introduced in the [Prague hardfork](https://github.com/ethereum/execution-apis/blob/main/src/engine/prague.md). +//! Contains consolidation code, first introduced in the [Prague hardfork](https://github.com/ethereum/execution-apis/blob/main/src/engine/prague.md). //! //! See also [EIP-7251](https://eips.ethereum.org/EIPS/eip-7251): Increase the MAX_EFFECTIVE_BALANCE -use alloy_primitives::{address, bytes, Address, Bytes, FixedBytes}; -use alloy_rlp::{RlpDecodable, RlpEncodable}; +use alloy_primitives::{address, bytes, Address, Bytes}; /// The address for the EIP-7251 consolidation requests contract: /// `0x00b42dbF2194e931E80326D950320f7d9Dbeac02` @@ -15,83 +14,3 @@ pub static CONSOLIDATION_REQUEST_PREDEPLOY_CODE: Bytes = bytes!("3373fffffffffff /// The [EIP-7685](https://eips.ethereum.org/EIPS/eip-7685) request type for consolidation requests. pub const CONSOLIDATION_REQUEST_TYPE: u8 = 0x02; - -/// This structure maps onto the consolidation request object from [EIP-7251](https://eips.ethereum.org/EIPS/eip-7251). -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, RlpEncodable, RlpDecodable, Default)] -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))] -#[cfg_attr(feature = "ssz", derive(ssz_derive::Encode, ssz_derive::Decode))] -#[cfg_attr(any(test, feature = "arbitrary"), derive(arbitrary::Arbitrary))] -pub struct ConsolidationRequest { - /// Source address - pub source_address: Address, - /// Source public key - pub source_pubkey: FixedBytes<48>, - /// Target public key - pub target_pubkey: FixedBytes<48>, -} - -#[cfg(test)] -mod tests { - use super::*; - use alloy_primitives::hex; - use alloy_rlp::{Decodable, Encodable}; - use core::str::FromStr; - - #[test] - fn test_encode_decode_consolidation_request_roundtrip() { - // Generate and test multiple cases in a single pass using an iterator. - (0..4).for_each(|_| { - let original_request = ConsolidationRequest { - source_address: Address::random(), - source_pubkey: FixedBytes::<48>::random(), - target_pubkey: FixedBytes::<48>::random(), - }; - - // Encode the request - let mut buf = Vec::new(); - original_request.encode(&mut buf); - - // Decode the request and assert equality - let decoded_request = - ConsolidationRequest::decode(&mut &buf[..]).expect("Failed to decode request"); - assert_eq!(original_request, decoded_request); - }); - } - - #[test] - fn test_serde_consolidation_request() { - // Sample JSON input representing a consolidation request - let json_data = r#"{ - "sourceAddress":"0x007eABCA654E67103dF02f49EbdC5f6Cd9387a07", - "sourcePubkey":"0xb13ff174911d0137e5f2b739fbf172b22cba35a037ef1edb03683b75c9abf5b271f8d48ad279cc89c7fae91db631c1e7", - "targetPubkey":"0xd0e5be6b709f2dc02a49f6e37e0d03b7d832b79b0db1c8bbfd5b81b8e57b79a1282fb99a671b4629a0e0bfffa7cf6d4f" - }"#; - - // Deserialize the JSON into a ConsolidationRequest struct - let consolidation_request: ConsolidationRequest = - serde_json::from_str(json_data).expect("Failed to deserialize"); - - // Verify the deserialized content - assert_eq!( - consolidation_request.source_address, - Address::from_str("0x007eABCA654E67103dF02f49EbdC5f6Cd9387a07").unwrap() - ); - assert_eq!( - consolidation_request.source_pubkey, - FixedBytes::<48>::from(hex!("b13ff174911d0137e5f2b739fbf172b22cba35a037ef1edb03683b75c9abf5b271f8d48ad279cc89c7fae91db631c1e7")) - ); - assert_eq!( - consolidation_request.target_pubkey, - FixedBytes::<48>::from(hex!("d0e5be6b709f2dc02a49f6e37e0d03b7d832b79b0db1c8bbfd5b81b8e57b79a1282fb99a671b4629a0e0bfffa7cf6d4f")) - ); - - // Serialize the struct back into JSON - let serialized_json = - serde_json::to_string(&consolidation_request).expect("Failed to serialize"); - - // Check if the serialized JSON matches the expected JSON structure - let expected_json = r#"{"sourceAddress":"0x007eabca654e67103df02f49ebdc5f6cd9387a07","sourcePubkey":"0xb13ff174911d0137e5f2b739fbf172b22cba35a037ef1edb03683b75c9abf5b271f8d48ad279cc89c7fae91db631c1e7","targetPubkey":"0xd0e5be6b709f2dc02a49f6e37e0d03b7d832b79b0db1c8bbfd5b81b8e57b79a1282fb99a671b4629a0e0bfffa7cf6d4f"}"#; - assert_eq!(serialized_json, expected_json); - } -} diff --git a/crates/eips/src/eip7685.rs b/crates/eips/src/eip7685.rs index 216ca9c6683..950b9947780 100644 --- a/crates/eips/src/eip7685.rs +++ b/crates/eips/src/eip7685.rs @@ -1,136 +1,60 @@ //! [EIP-7685]: General purpose execution layer requests //! -//! Contains traits for encoding and decoding EIP-7685 requests, as well as validation functions. -//! //! [EIP-7685]: https://eips.ethereum.org/EIPS/eip-7685 -#[cfg(not(feature = "std"))] -use crate::alloc::{vec, vec::Vec}; - -use alloy_rlp::BufMut; -use core::{ - fmt, - fmt::{Display, Formatter}, -}; +use alloc::vec::Vec; +use alloy_primitives::Bytes; +use derive_more::{Deref, DerefMut, From, IntoIterator}; -/// [EIP-7685] decoding errors. -/// -/// [EIP-7685]: https://eips.ethereum.org/EIPS/eip-7685 -#[derive(Clone, Copy, Debug)] -#[non_exhaustive] -pub enum Eip7685Error { - /// Rlp error from [`alloy_rlp`]. - RlpError(alloy_rlp::Error), - /// Got an unexpected request type while decoding. - UnexpectedType(u8), - /// There was no request type in the buffer. - MissingType, -} +/// A list of opaque EIP-7685 requests. +#[derive(Debug, Clone, PartialEq, Eq, Default, Hash, Deref, DerefMut, From, IntoIterator)] +#[cfg_attr(any(test, feature = "arbitrary"), derive(arbitrary::Arbitrary))] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +pub struct Requests(Vec); -impl Display for Eip7685Error { - fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { - match self { - Self::RlpError(err) => write!(f, "{err}"), - Self::UnexpectedType(t) => write!(f, "Unexpected request type. Got {t}."), - Self::MissingType => write!(f, "There was no type flag"), - } +impl Requests { + /// Construct a new [`Requests`] container. + pub const fn new(requests: Vec) -> Self { + Self(requests) } -} -impl From for Eip7685Error { - fn from(err: alloy_rlp::Error) -> Self { - Self::RlpError(err) + /// Add a new request into the container. + pub fn push_request(&mut self, request: Bytes) { + self.0.push(request); } -} -impl From for alloy_rlp::Error { - fn from(err: Eip7685Error) -> Self { - match err { - Eip7685Error::RlpError(err) => err, - Eip7685Error::MissingType => Self::Custom("eip7685 decoding failed: missing type"), - Eip7685Error::UnexpectedType(_) => { - Self::Custom("eip7685 decoding failed: unexpected type") - } - } + /// Consumes [`Requests`] and returns the inner raw opaque requests. + pub fn take(self) -> Vec { + self.0 } -} -/// Decoding trait for [EIP-7685] requests. The trait should be implemented for an envelope that -/// wraps each possible request type. -/// -/// [EIP-7685]: https://eips.ethereum.org/EIPS/eip-7685 -pub trait Decodable7685: Sized { - /// Extract the type byte from the buffer, if any. The type byte is the - /// first byte. - fn extract_type_byte(buf: &mut &[u8]) -> Option { - buf.first().copied() + /// Get an iterator over the Requests. + pub fn iter(&self) -> core::slice::Iter<'_, Bytes> { + self.0.iter() } - /// Decode the appropriate variant, based on the request type. - /// - /// This function is invoked by [`Self::decode_7685`] with the type byte, and the tail of the - /// buffer. - /// - /// ## Note - /// - /// This should be a simple match block that invokes an inner type's decoder. The decoder is - /// request type dependent. - fn typed_decode(ty: u8, buf: &mut &[u8]) -> Result; - - /// Decode an EIP-7685 request into a concrete instance - fn decode_7685(buf: &mut &[u8]) -> Result { - Self::extract_type_byte(buf) - .map(|ty| Self::typed_decode(ty, &mut &buf[1..])) - .unwrap_or(Err(Eip7685Error::MissingType)) - } -} - -/// Encoding trait for [EIP-7685] requests. The trait should be implemented for an envelope that -/// wraps each possible request type. -/// -/// [EIP-7685]: https://eips.ethereum.org/EIPS/eip-7685 -pub trait Encodable7685: Sized + Send + Sync + 'static { - /// Return the request type. - fn request_type(&self) -> u8; - - /// Encode the request according to [EIP-7685] rules. - /// - /// First a 1-byte flag specifying the request type, then the encoded payload. - /// - /// The encoding of the payload is request-type dependent. - /// - /// [EIP-7685]: https://eips.ethereum.org/EIPS/eip-7685 - fn encode_7685(&self, out: &mut dyn BufMut) { - out.put_u8(self.request_type()); - self.encode_payload_7685(out); + /// Calculate the requests hash as defined in EIP-7685 for the requests. + /// + /// The requests hash is defined as + /// + /// ```text + /// sha256(sha256(requests_0) ++ sha256(requests_1) ++ ...) + /// ``` + #[cfg(feature = "sha2")] + pub fn requests_hash(&self) -> alloy_primitives::B256 { + use sha2::{Digest, Sha256}; + let mut hash = sha2::Sha256::new(); + for (ty, req) in self.0.iter().enumerate() { + let mut req_hash = Sha256::new(); + req_hash.update([ty as u8]); + req_hash.update(req); + hash.update(req_hash.finalize()); + } + alloy_primitives::B256::from(hash.finalize().as_ref()) } - /// Encode the request payload. - /// - /// The encoding for the payload is request type dependent. - fn encode_payload_7685(&self, out: &mut dyn BufMut); - - /// Encode the request according to [EIP-7685] rules. - /// - /// First a 1-byte flag specifying the request type, then the encoded payload. - /// - /// The encoding of the payload is request-type dependent. - /// - /// This is a convenience method for encoding into a vec, and returning the - /// vec. - /// - /// [EIP-7685]: https://eips.ethereum.org/EIPS/eip-7685 - fn encoded_7685(&self) -> Vec { - let mut out = vec![]; - self.encode_7685(&mut out); - out + /// Extend this container with requests from another container. + pub fn extend(&mut self, other: Self) { + self.0.extend(other.take()); } } - -/// An [EIP-7685] request envelope, blanket implemented for types that impl -/// [`Encodable7685`] and [`Decodable7685`]. This envelope is a wrapper around -/// a request, differentiated by the request type. -/// -/// [EIP-7685]: https://eips.ethereum.org/EIPS/eip-7685 -pub trait Eip7685RequestEnvelope: Decodable7685 + Encodable7685 {} -impl Eip7685RequestEnvelope for T where T: Decodable7685 + Encodable7685 {} diff --git a/crates/rpc-types-engine/src/lib.rs b/crates/rpc-types-engine/src/lib.rs index e545137b490..cbc044a8adc 100644 --- a/crates/rpc-types-engine/src/lib.rs +++ b/crates/rpc-types-engine/src/lib.rs @@ -31,12 +31,6 @@ pub use payload::*; mod transition; pub use transition::*; -#[doc(inline)] -pub use alloy_eips::eip6110::DepositRequest as DepositRequestV1; - -#[doc(inline)] -pub use alloy_eips::eip7002::WithdrawalRequest as WithdrawalRequestV1; - #[doc(inline)] pub use alloy_eips::eip4844::BlobAndProofV1;