From 09f04a0f6cb51a5308ff04872c65300a137bdbb5 Mon Sep 17 00:00:00 2001 From: evalir Date: Thu, 4 Jan 2024 13:11:49 -0400 Subject: [PATCH] feat(`rpc`): use `alloy-rpc-types` for ethereum-related types (#5947) --- Cargo.lock | 22 + Cargo.toml | 1 + crates/primitives/src/transaction/sidecar.rs | 2 +- crates/rpc/rpc-testing-util/src/debug.rs | 2 +- crates/rpc/rpc-types-compat/Cargo.toml | 2 + crates/rpc/rpc-types-compat/src/block.rs | 2 + .../rpc-types-compat/src/engine/payload.rs | 7 +- .../rpc-types-compat/src/transaction/mod.rs | 7 +- crates/rpc/rpc-types/Cargo.toml | 5 +- crates/rpc/rpc-types/src/beacon/payload.rs | 4 +- crates/rpc/rpc-types/src/eth/account.rs | 88 -- crates/rpc/rpc-types/src/eth/block.rs | 962 ------------ crates/rpc/rpc-types/src/eth/call.rs | 274 ---- crates/rpc/rpc-types/src/eth/error.rs | 32 - crates/rpc/rpc-types/src/eth/fee.rs | 55 - crates/rpc/rpc-types/src/eth/filter.rs | 1354 ----------------- crates/rpc/rpc-types/src/eth/index.rs | 104 -- crates/rpc/rpc-types/src/eth/log.rs | 55 - crates/rpc/rpc-types/src/eth/mod.rs | 30 - crates/rpc/rpc-types/src/eth/pubsub.rs | 169 -- crates/rpc/rpc-types/src/eth/raw_log.rs | 30 - crates/rpc/rpc-types/src/eth/state.rs | 69 - crates/rpc/rpc-types/src/eth/syncing.rs | 161 -- crates/rpc/rpc-types/src/eth/trace/common.rs | 36 - crates/rpc/rpc-types/src/eth/trace/filter.rs | 184 --- .../rpc/rpc-types/src/eth/trace/geth/call.rs | 118 -- .../rpc-types/src/eth/trace/geth/four_byte.rs | 34 - .../rpc/rpc-types/src/eth/trace/geth/mod.rs | 579 ------- .../rpc/rpc-types/src/eth/trace/geth/noop.rs | 32 - .../rpc-types/src/eth/trace/geth/pre_state.rs | 348 ----- crates/rpc/rpc-types/src/eth/trace/mod.rs | 7 - crates/rpc/rpc-types/src/eth/trace/parity.rs | 759 --------- .../rpc-types/src/eth/trace/tracerequest.rs | 84 - .../src/eth/transaction/access_list.rs | 89 -- .../rpc-types/src/eth/transaction/common.rs | 19 - .../rpc/rpc-types/src/eth/transaction/kzg.rs | 12 - .../rpc/rpc-types/src/eth/transaction/mod.rs | 181 +-- .../rpc-types/src/eth/transaction/receipt.rs | 74 - .../rpc-types/src/eth/transaction/request.rs | 9 +- .../src/eth/transaction/signature.rs | 190 --- .../rpc-types/src/eth/transaction/typed.rs | 8 +- crates/rpc/rpc-types/src/eth/txpool.rs | 520 ------- crates/rpc/rpc-types/src/eth/withdrawal.rs | 57 - crates/rpc/rpc-types/src/eth/work.rs | 69 - crates/rpc/rpc-types/src/lib.rs | 15 +- crates/rpc/rpc-types/src/relay/mod.rs | 5 +- crates/rpc/rpc/src/eth/api/transactions.rs | 30 +- crates/transaction-pool/benches/reorder.rs | 6 +- deny.toml | 2 +- 49 files changed, 91 insertions(+), 6813 deletions(-) delete mode 100644 crates/rpc/rpc-types/src/eth/account.rs delete mode 100644 crates/rpc/rpc-types/src/eth/block.rs delete mode 100644 crates/rpc/rpc-types/src/eth/call.rs delete mode 100644 crates/rpc/rpc-types/src/eth/error.rs delete mode 100644 crates/rpc/rpc-types/src/eth/fee.rs delete mode 100644 crates/rpc/rpc-types/src/eth/filter.rs delete mode 100644 crates/rpc/rpc-types/src/eth/index.rs delete mode 100644 crates/rpc/rpc-types/src/eth/log.rs delete mode 100644 crates/rpc/rpc-types/src/eth/pubsub.rs delete mode 100644 crates/rpc/rpc-types/src/eth/raw_log.rs delete mode 100644 crates/rpc/rpc-types/src/eth/state.rs delete mode 100644 crates/rpc/rpc-types/src/eth/syncing.rs delete mode 100644 crates/rpc/rpc-types/src/eth/trace/common.rs delete mode 100644 crates/rpc/rpc-types/src/eth/trace/filter.rs delete mode 100644 crates/rpc/rpc-types/src/eth/trace/geth/call.rs delete mode 100644 crates/rpc/rpc-types/src/eth/trace/geth/four_byte.rs delete mode 100644 crates/rpc/rpc-types/src/eth/trace/geth/mod.rs delete mode 100644 crates/rpc/rpc-types/src/eth/trace/geth/noop.rs delete mode 100644 crates/rpc/rpc-types/src/eth/trace/geth/pre_state.rs delete mode 100644 crates/rpc/rpc-types/src/eth/trace/mod.rs delete mode 100644 crates/rpc/rpc-types/src/eth/trace/parity.rs delete mode 100644 crates/rpc/rpc-types/src/eth/trace/tracerequest.rs delete mode 100644 crates/rpc/rpc-types/src/eth/transaction/access_list.rs delete mode 100644 crates/rpc/rpc-types/src/eth/transaction/common.rs delete mode 100644 crates/rpc/rpc-types/src/eth/transaction/kzg.rs delete mode 100644 crates/rpc/rpc-types/src/eth/transaction/receipt.rs delete mode 100644 crates/rpc/rpc-types/src/eth/transaction/signature.rs delete mode 100644 crates/rpc/rpc-types/src/eth/txpool.rs delete mode 100644 crates/rpc/rpc-types/src/eth/withdrawal.rs delete mode 100644 crates/rpc/rpc-types/src/eth/work.rs diff --git a/Cargo.lock b/Cargo.lock index b0ead9b67..1f9792270 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -216,6 +216,25 @@ dependencies = [ "syn 2.0.43", ] +[[package]] +name = "alloy-rpc-types" +version = "0.1.0" +source = "git+https://github.com/alloy-rs/alloy#e64c1c2b9e4f80fe13ac4f65504a4c30fe786454" +dependencies = [ + "alloy-primitives", + "alloy-rlp", + "arbitrary", + "ethereum_ssz", + "ethereum_ssz_derive", + "itertools 0.12.0", + "jsonrpsee-types", + "proptest", + "proptest-derive", + "serde", + "serde_json", + "thiserror", +] + [[package]] name = "alloy-sol-macro" version = "0.5.4" @@ -6562,6 +6581,7 @@ version = "0.1.0-alpha.13" dependencies = [ "alloy-primitives", "alloy-rlp", + "alloy-rpc-types", "arbitrary", "bytes", "ethereum_ssz", @@ -6585,8 +6605,10 @@ name = "reth-rpc-types-compat" version = "0.1.0-alpha.13" dependencies = [ "alloy-rlp", + "alloy-rpc-types", "reth-primitives", "reth-rpc-types", + "serde_json", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index 6734ecabe..1c75295c0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -160,6 +160,7 @@ alloy-primitives = "0.5" alloy-dyn-abi = "0.5" alloy-sol-types = "0.5" alloy-rlp = "0.3" +alloy-rpc-types = { git = "https://github.com/alloy-rs/alloy", features = ["jsonrpsee-types"] } ethers-core = { version = "2.0", default-features = false } ethers-providers = { version = "2.0", default-features = false } ethers-signers = { version = "2.0", default-features = false } diff --git a/crates/primitives/src/transaction/sidecar.rs b/crates/primitives/src/transaction/sidecar.rs index 0a2963365..2c21371b8 100644 --- a/crates/primitives/src/transaction/sidecar.rs +++ b/crates/primitives/src/transaction/sidecar.rs @@ -400,7 +400,7 @@ const _: [(); std::mem::size_of::()] = [(); std::mem::size_of::()]; const _: [(); std::mem::size_of::()] = - [(); std::mem::size_of::()]; + [(); std::mem::size_of::()]; impl BlobTransactionSidecarRlp { fn wrap_ref(other: &BlobTransactionSidecar) -> &Self { diff --git a/crates/rpc/rpc-testing-util/src/debug.rs b/crates/rpc/rpc-testing-util/src/debug.rs index 14a6b8690..7e40e7778 100644 --- a/crates/rpc/rpc-testing-util/src/debug.rs +++ b/crates/rpc/rpc-testing-util/src/debug.rs @@ -85,7 +85,7 @@ where BlockId::Number(tag) => self.block_by_number(tag, false).await, }? .ok_or_else(|| RpcError::Custom("block not found".to_string()))?; - let hashes = block.transactions.iter().map(|tx| (tx, opts.clone())).collect::>(); + let hashes = block.transactions.hashes().map(|tx| (*tx, opts.clone())).collect::>(); let stream = futures::stream::iter(hashes.into_iter().map(move |(tx, opts)| async move { match self.debug_trace_transaction_json(tx, opts).await { Ok(result) => Ok((result, tx)), diff --git a/crates/rpc/rpc-types-compat/Cargo.toml b/crates/rpc/rpc-types-compat/Cargo.toml index e9d0b8622..6dc225ffa 100644 --- a/crates/rpc/rpc-types-compat/Cargo.toml +++ b/crates/rpc/rpc-types-compat/Cargo.toml @@ -15,6 +15,8 @@ workspace = true reth-primitives.workspace = true reth-rpc-types.workspace = true alloy-rlp.workspace = true +alloy-rpc-types.workspace = true +serde_json.workspace = true [features] optimism = ["reth-primitives/optimism", "reth-rpc-types/optimism"] diff --git a/crates/rpc/rpc-types-compat/src/block.rs b/crates/rpc/rpc-types-compat/src/block.rs index c0cf0bae4..33c580dfc 100644 --- a/crates/rpc/rpc-types-compat/src/block.rs +++ b/crates/rpc/rpc-types-compat/src/block.rs @@ -179,6 +179,7 @@ fn from_block_with_transactions( total_difficulty: Some(total_difficulty), size: Some(U256::from(block_length)), withdrawals, + other: Default::default(), } } @@ -196,5 +197,6 @@ pub fn uncle_block_from_header(header: PrimitiveHeader) -> Block { withdrawals: Some(vec![]), size, total_difficulty: None, + other: Default::default(), } } diff --git a/crates/rpc/rpc-types-compat/src/engine/payload.rs b/crates/rpc/rpc-types-compat/src/engine/payload.rs index 7bcd49524..b9b51f00f 100644 --- a/crates/rpc/rpc-types-compat/src/engine/payload.rs +++ b/crates/rpc/rpc-types-compat/src/engine/payload.rs @@ -65,11 +65,8 @@ pub fn try_payload_v2_to_block(payload: ExecutionPayloadV2) -> Result = payload - .withdrawals - .iter() - .map(|w| convert_standalone_withdraw_to_withdrawal(w.clone())) - .collect(); + let withdrawals: Vec<_> = + payload.withdrawals.iter().map(|w| convert_standalone_withdraw_to_withdrawal(*w)).collect(); let withdrawals_root = proofs::calculate_withdrawals_root(&withdrawals); base_sealed_block.withdrawals = Some(withdrawals); base_sealed_block.header.withdrawals_root = Some(withdrawals_root); diff --git a/crates/rpc/rpc-types-compat/src/transaction/mod.rs b/crates/rpc/rpc-types-compat/src/transaction/mod.rs index 414dc8063..7f65c5476 100644 --- a/crates/rpc/rpc-types-compat/src/transaction/mod.rs +++ b/crates/rpc/rpc-types-compat/src/transaction/mod.rs @@ -138,11 +138,14 @@ fn fill( blob_versioned_hashes, // Optimism fields #[cfg(feature = "optimism")] - optimism: reth_rpc_types::OptimismTransactionFields { + other: reth_rpc_types::OptimismTransactionFields { source_hash: signed_tx.source_hash(), mint: signed_tx.mint().map(U128::from), is_system_tx: signed_tx.is_deposit().then_some(signed_tx.is_system_transaction()), - }, + } + .into(), + #[cfg(not(feature = "optimism"))] + other: Default::default(), } } diff --git a/crates/rpc/rpc-types/Cargo.toml b/crates/rpc/rpc-types/Cargo.toml index 89f972e23..62f93e7bf 100644 --- a/crates/rpc/rpc-types/Cargo.toml +++ b/crates/rpc/rpc-types/Cargo.toml @@ -15,6 +15,7 @@ workspace = true # ethereum alloy-rlp = { workspace = true, features = ["arrayvec", "derive"] } alloy-primitives = { workspace = true, features = ["rand", "rlp", "serde"] } +alloy-rpc-types.workspace = true ethereum_ssz_derive = { version = "0.5", optional = true } ethereum_ssz = { version = "0.5", optional = true } @@ -38,8 +39,8 @@ proptest-derive = { workspace = true, optional = true } [features] default = ["jsonrpsee-types"] -arbitrary = ["dep:arbitrary", "dep:proptest-derive", "dep:proptest", "alloy-primitives/arbitrary"] -ssz = ["dep:ethereum_ssz" ,"dep:ethereum_ssz_derive", "alloy-primitives/ssz"] +arbitrary = ["dep:arbitrary", "dep:proptest-derive", "dep:proptest", "alloy-primitives/arbitrary", "alloy-rpc-types/arbitrary"] +ssz = ["dep:ethereum_ssz" ,"dep:ethereum_ssz_derive", "alloy-primitives/ssz", "alloy-rpc-types/ssz"] optimism = [] diff --git a/crates/rpc/rpc-types/src/beacon/payload.rs b/crates/rpc/rpc-types/src/beacon/payload.rs index 225d590cc..d660bbbcf 100644 --- a/crates/rpc/rpc-types/src/beacon/payload.rs +++ b/crates/rpc/rpc-types/src/beacon/payload.rs @@ -12,8 +12,8 @@ use crate::{ beacon::{withdrawals::BeaconWithdrawal, BlsPublicKey}, - engine::ExecutionPayloadV3, - ExecutionPayload, ExecutionPayloadV1, ExecutionPayloadV2, Withdrawal, + engine::{ExecutionPayload, ExecutionPayloadV1, ExecutionPayloadV2, ExecutionPayloadV3}, + Withdrawal, }; use alloy_primitives::{Address, Bloom, Bytes, B256, U256}; use serde::{Deserialize, Deserializer, Serialize, Serializer}; diff --git a/crates/rpc/rpc-types/src/eth/account.rs b/crates/rpc/rpc-types/src/eth/account.rs deleted file mode 100644 index 755eff7b1..000000000 --- a/crates/rpc/rpc-types/src/eth/account.rs +++ /dev/null @@ -1,88 +0,0 @@ -#![allow(missing_docs)] - -use crate::serde_helpers::storage::JsonStorageKey; -use alloy_primitives::{Address, Bytes, B256, B512, U256, U64}; -use serde::{Deserialize, Serialize}; - -/// Account information. -#[derive(Clone, Debug, Default, Eq, PartialEq, Serialize, Deserialize)] -pub struct AccountInfo { - /// Account name - pub name: String, -} - -/// Data structure with proof for one single storage-entry -#[derive(Clone, Debug, Default, Eq, PartialEq, Serialize, Deserialize)] -#[serde(rename_all = "camelCase")] -pub struct EIP1186StorageProof { - /// Storage key. - pub key: JsonStorageKey, - /// Value that the key holds - pub value: U256, - /// proof for the pair - pub proof: Vec, -} - -/// Response for EIP-1186 account proof `eth_getProof` -#[derive(Debug, Default, Clone, PartialEq, Eq, Serialize, Deserialize)] -#[serde(rename_all = "camelCase")] -pub struct EIP1186AccountProofResponse { - pub address: Address, - pub balance: U256, - pub code_hash: B256, - pub nonce: U64, - pub storage_hash: B256, - pub account_proof: Vec, - pub storage_proof: Vec, -} - -/// Extended account information (used by `parity_allAccountInfo`). -#[derive(Clone, Debug, Default, Eq, PartialEq, Serialize, Deserialize)] -pub struct ExtAccountInfo { - /// Account name - pub name: String, - /// Account meta JSON - pub meta: String, - /// Account UUID (`None` for address book entries) - #[serde(skip_serializing_if = "Option::is_none")] - pub uuid: Option, -} - -/// account derived from a signature -/// as well as information that tells if it is valid for -/// the current chain -#[derive(Debug, Clone, Serialize, Deserialize)] -#[serde(rename_all = "camelCase")] -pub struct RecoveredAccount { - /// address of the recovered account - pub address: Address, - /// public key of the recovered account - pub public_key: B512, - /// If the signature contains chain replay protection, - /// And the chain_id encoded within the signature - /// matches the current chain this would be true, otherwise false. - pub is_valid_for_current_chain: bool, -} - -#[test] -fn test_eip_1186_account_without_storage_proof() { - let response = r#"{ - "address":"0xc36442b4a4522e871399cd717abdd847ab11fe88", - "accountProof":["0xf90211a0a3deb2d4417de23e3c64a80ab58fa1cf4b62d7f193e36e507c8cf3794477b5fba0fc7ce8769dcfa9ae8d9d9537098c5cc5477b5920ed494e856049f5783c843c50a0f7d083f1e79a4c0ba1686b97a0e27c79c3a49432d333dc3574d5879cad1ca897a0cd36cf391201df64a786187d99013bdbaf5f0da6bfb8f5f2d6f0f60504f76ad9a03a9f09c92c3cefe87840938dc15fe68a3586d3b28b0f47c7037b6413c95a9feda0decb7e1969758d401af2d1cab14c0951814c094a3da108dd9f606a96840bae2ba060bf0c44ccc3ccbb5ab674841858cc5ea16495529442061295f1cecefd436659a039f8b307e0a295d6d03df089ee8211b52c5ae510d071f17ae5734a7055858002a0508040aef23dfe9c8ab16813258d95c4e765b4a557c2987fb7f3751693f34f4fa0c07e58aa6cd257695cdf147acd800c6197c235e2b5242c22e9da5d86b169d56aa00f2e89ddd874d28e62326ba365fd4f26a86cbd9f867ec0b3de69441ef8870f4ea06c1eb5455e43a36ec41a0372bde915f889cee070b8c8b8a78173d4d7df3ccebaa0cee4848c4119ed28e165e963c5b46ffa6dbeb0b14c8c51726124e7d26ff3f27aa0fc5b82dce2ee5a1691aa92b91dbeec7b2ba94df8116ea985dd7d3f4d5b8292c0a03675e148c987494e22a9767b931611fb1b7c7c287af128ea23aa70b88a1c458ba04f269f556f0f8d9cb2a9a6de52d35cf5a9098f7bb8badb1dc1d496096236aed880", - "0xf90211a0715ed9b0b002d050084eaecb878f457a348ccd47c7a597134766a7d705303de9a0c49f0fe23b0ca61892d75aebaf7277f00fdfd2022e746bab94de5d049a96edfca0b01f9c91f2bc1373862d7936198a5d11efaf370e2b9bb1dac2134b8e256ecdafa0888395aa7e0f699bb632215f08cdf92840b01e5d8e9a61d18355098cdfd50283a0ba748d609b0018667d311527a2302267209a38b08378f7d833fdead048de0defa098878e5d1461ceddeddf62bd8277586b120b5097202aa243607bc3fc8f30fc0ba0ad4111ee1952b6db0939a384986ee3fb34e0a5fc522955588fc22e159949196fa00fc948964dff427566bad468d62b0498c59df7ca7ae799ab29555d5d829d3742a0766922a88ebc6db7dfb06b03a5b17d0773094e46e42e7f2ba6a0b8567d9f1000a0db25676c4a36591f37c5e16f7199ab16559d82a2bed8c0c6a35f528a3c166bfda0149a5d50d238722e7d44c555169ed32a7f182fcb487ea378b4410a46a63a4e66a06b2298bbfe4972113e7e18cac0a8a39792c1a940ea128218343b8f88057d90aea096b2adb84105ae2aca8a7edf937e91e40872070a8641a74891e64db94d059df0a0ddbb162125ecfbd42edad8d8ef5d5e97ca7c72f54ddc404a61ae318bad0d2108a00e9a68f3e2b0c793d5fcd607edc5c55226d53fdfacd713077d6e01cb38d00d5ba05dc099f1685b2a4b7308e063e8e7905994f5c36969b1c6bfe3780c9878a4d85c80", - "0xf90211a05fc921be4d63ee07fe47a509e1abf2d69b00b6ea582a755467bf4371c2d2bd1fa0d552faa477e95f4631e2f7247aeb58693d90b03b2eee57e3fe8a9ddbd19ee42da028682c15041aa6ced1a5306aff311f5dbb8bbf7e77615994305ab3132e7842b5a0e5e0316b5046bde22d09676210885c5bea6a71703bf3b4dbac2a7199910f54faa0527fccccef17df926ccfb608f76d3c259848ed43cd24857a59c2a9352b6f1fa4a02b3863355b927b78c80ca379a4f7165bbe1644aaefed8a0bfa2001ae6284b392a09964c73eccc3d12e44dba112e31d8bd3eacbc6a42b4f17985d5b99dff968f24ea0cc426479c7ff0573629dcb2872e57f7438a28bd112a5c3fb2241bdda8031432ba04987fe755f260c2f7218640078af5f6ac4d98c2d0c001e398debc30221b14668a0e811d046c21c6cbaee464bf55553cbf88e70c2bda6951800c75c3896fdeb8e13a04aa8d0ab4946ac86e784e29000a0842cd6eebddaf8a82ece8aa69b72c98cfff5a0dfc010051ddceeec55e4146027c0eb4c72d7c242a103bf1977033ebe00a57b5da039e4da79576281284bf46ce6ca90d47832e4aefea4846615d7a61a7b976c8e3ea0dad1dfff731f7dcf37c499f4afbd5618247289c2e8c14525534b826a13b0a5a6a025f356cbc0469cb4dc326d98479e3b756e4418a67cbbb8ffb2d1abab6b1910e9a03f4082bf1da27b2a76f6bdc930eaaaf1e3f0e4d3135c2a9fb85e301f47f5174d80", - "0xf90211a0df6448f21c4e19da33f9c64c90bbcc02a499866d344c73576f63e3b4cbd4c000a010efb3b0f1d6365e2e4a389965e114e2a508ef8901f7d6c7564ba88793ff974aa0295bef2313a4f603614a5d5af3c659f63edfaa5b59a6ea2ac1da05f69ff4657ba0d8f16d5ddf4ba09616008148d2993dc50658accc2edf9111b6f464112db5d369a084604d9e06ddb53aeb7b13bb70fbe91f60df6bdc30f59bc7dc57ff37b6fe3325a04c64bd1dbeaecc54f18b23ab1ade2200970757f437e75e285f79a8c405315a14a0868075fc7f73b13863fc653c806f9a20f8e52dce44c15d2c4f94d6711021b985a01e85c49da7a8c91068468779e79b267d93d4fad01f44183353a381207304723ea05fcf186d55c53413f6988b16aa34721f0539f1cf0917f02e9d1a6ec8d3e191ffa00ad581842eab665351913e0afb3bfc070b9e4fad4d354c073f44c4f2a0c425c9a0000cb2066d81bf07f80703a40a5c5012e2c4b387bc53d381d37ee1d0f0a6643ba061f221d01c98721e79c525af5fc2eb9cc648c2ca54bb70520b868e2bdc037967a0e580f297c477df46362eb8e20371d8f0528091454bb5ad00d40368ca3ffdbd1fa079a13d35f79699f9e51d4fa07d03cd9b9dec4de9906559c0470629a663181652a0dbb402183633dbaa73e6e6a6b66bfffc4570763b264d3a702de165032298b858a065d5321015531309bb3abe0235f825d5be4270d2e511dca3b984d1e70ef308d880", - "0xf90211a06d0adafe89896724704275a42a8a63f0910dce83188add0073f621b8ca1167aaa00de7d4efad36d08f5a0320cdfd964484eba803d9933efae12c292d3ff2d06a20a083341fc12fffccf4b11df314b14f7bcead154525a097493fdf15dde4ec0c0d2aa088b7759fe3aef617828e7abd9e554add2e84ef3e2e024b1a0e2f537fce7d37f9a01e73c28722d825063304c6b51be3a8c7b6312ba8be4c6e99602e623993c014c0a0e50fbe12ddbaf184f3ba0cda971675a55abbf44c73f771bc5824b393262e5255a0b1a937d4c50528cb6aeb80aa5fe83bcfa8c294124a086302caf42cead1f99f96a04c4376b13859af218b5b09ffb33e3465288837c37fa254a46f8d0e75afecae10a0f158c0171bdb454eab6bb6dc5e276e749b6aa550f53b497492c0a392425035c3a0ac496050db1fbb1d34180ee7fd7bed18efa4cf43299390a72dcf530cc3422630a02cacb30ac3b4bab293d31833be4865cd1d1de8db8630edac4af056979cc903aea090cbb538f0f4601289db4cf49485ab3a178044daeae325c525bc3978714a7219a0542021427adbe890896fcc888418a747a555b2a7121fe3c683e07dcf5012e96ca006569c5e3715f52f62dd856dec2136e60c49bbadc1cf9fb625930da3e8f1c16ea0a2539ebb66a2c10c3809626181a2389f043e0b54867cd356eb5f20daaeb521b4a0ab49972dced10010275f2604e6182722dbc426ca1b0ae128defe80c0baefd3c080", - "0xf90211a006c1d8a7c5deeb435ea0b080aea8b7acb58d2d898e12e3560d399594a77863a1a088105243bc96e1f10baa73d670929a834c51eb7f695cf43f4fab94e73c9a5b8da0fce3a21f09b62d65607bbdabb8d675d58a5f3bfb19ae46510a4ea2205070aa03a0039ae7a999ed83bfdb49b6df7074589059ba6c2eed22bfc6dac8ff5241c71bd7a09feca6f7331b6c147f4fd7bd94de496144b85543d868f47be6345330b3f8ccd3a00e55c30d16438567979c92d387a2b99e51a4026192ccfda2ac87a190c3aee511a0a86c5bb52651e490203c63670b569b2337e838e4d80d455cc83e64571e2552f1a0cfb31ae59b691c15ffd97658bab646ff4b90dbc72a81ec52731b3fbd38d0dd5ba0d83936fc4143cc885be5fa420ef22fb97f6a8dd24e9ece9af965792565a7b2c8a0abb179481f4b29578adb8768aa4f6ba6ed6bd43c7572d7c3405c879a362f1ab1a0506651daa07d44901dfd76c12d302b2242e5ceac385f95ea928f20a0336eccf6a010e8a7f461231438987fb26adc4c5004721dc401dc2b77e9b79d26b1308d0079a09174afa82e6d27dfdde74f556d0e782ae6222dc66104d84ea0f1e21e093578c4a0391e24ed0033cc58f149af753b485de3c8b9e4b3c8e145c308db60e51cabbefca03b0991359019197dd53e3798e55a14c8795d655b0693efd37404cf8f8d979cfba0594d95bbfe8e2ea5040b571010549a233bc33bf959792e1e41c515c65abac14480", - "0xf90151a0e8ed81735d358657020dd6bc4bc58cf751cc037fa57e1d0c668bf24049e720d280a03e8bf7abdd8a4190a0ee5f92a78bf1dba529312ed66dd7ead7c9be55c81a2db480a006312425a007cda585740355f52db74d0ae43c21d562c599112546e3ffe22f01a023bbbb0ffb33c7a5477ab514c0f4f3c94ba1748a5ea1dc3edc7c4b5330cd70fe80a03ed45ab6045a10fa00b2fba662914f4dedbf3f3a5f2ce1e6e53a12ee3ea21235a01e02c98684cea92a7c0b04a01658530a09d268b395840a66263923e44b93d2b5a0a585db4a911fe6452a4540bf7dc143981ca31035ccb2c51d02eccd021a6163a480a06032919dcb44e22852b6367473bbc3f43311226ac28991a90b9c9da669f9e08a80a0146aee58a46c30bc84f6e99cd76bf29b3bd238053102679498a3ea15d4ff6d53a04cf57cfdc046c135004b9579059c84b2d902a51fb6feaed51ea272f0ca1cdc648080", - "0xf871a059ce2e1f470580853d88511bf8672f9ffaefadd80bc07b2e3d5a18c3d7812007a0867e978faf3461d2238ccf8d6a138406cb6d8bd36dfa60caddb62af14447a6f880808080a0fc6209fdaa57d224ee35f73e96469a7f95760a54d5de3da07953430b001aee6980808080808080808080", - "0xf8669d20852b2b985cd8c252fddae2acb4f798d0fecdcb1e2da53726332eb559b846f8440180a079fe22fe88fc4b45db10ce94d975e02e8a42b57dc190f8ae15e321f72bbc08eaa0692e658b31cbe3407682854806658d315d61a58c7e4933a2f91d383dc00736c6"], - "balance":"0x0", - "codeHash":"0x692e658b31cbe3407682854806658d315d61a58c7e4933a2f91d383dc00736c6", - "nonce":"0x1", - "storageHash":"0x79fe22fe88fc4b45db10ce94d975e02e8a42b57dc190f8ae15e321f72bbc08ea", - "storageProof":[] - }"#; - let val = serde_json::from_str::(response).unwrap(); - serde_json::to_value(val).unwrap(); -} diff --git a/crates/rpc/rpc-types/src/eth/block.rs b/crates/rpc/rpc-types/src/eth/block.rs deleted file mode 100644 index 91370b7cf..000000000 --- a/crates/rpc/rpc-types/src/eth/block.rs +++ /dev/null @@ -1,962 +0,0 @@ -//! Contains types that represent ethereum types when used in RPC - -use crate::{Transaction, Withdrawal}; -use alloy_primitives::{Address, BlockHash, BlockNumber, Bloom, Bytes, B256, B64, U256, U64}; -use alloy_rlp::{bytes, Decodable, Encodable, Error as RlpError}; -use serde::{ - de::{MapAccess, Visitor}, - ser::{Error, SerializeStruct}, - Deserialize, Deserializer, Serialize, Serializer, -}; -use std::{ - collections::BTreeMap, - fmt::{self, Formatter}, - num::ParseIntError, - ops::Deref, - str::FromStr, -}; -/// Block Transactions depending on the boolean attribute of `eth_getBlockBy*`, -/// or if used by `eth_getUncle*` -#[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize)] -#[serde(untagged)] -pub enum BlockTransactions { - /// Only hashes - Hashes(Vec), - /// Full transactions - Full(Vec), - /// Special case for uncle response. - Uncle, -} - -impl BlockTransactions { - /// Check if the enum variant is - /// used for an uncle response. - pub fn is_uncle(&self) -> bool { - matches!(self, Self::Uncle) - } - - /// Returns an iterator over the transaction hashes. - pub fn iter(&self) -> BlockTransactionsHashIterator<'_> { - BlockTransactionsHashIterator::new(self) - } -} - -/// An Iterator over the transaction hashes of a block. -#[derive(Debug, Clone)] -pub struct BlockTransactionsHashIterator<'a> { - txs: &'a BlockTransactions, - idx: usize, -} - -impl<'a> BlockTransactionsHashIterator<'a> { - fn new(txs: &'a BlockTransactions) -> Self { - Self { txs, idx: 0 } - } -} - -impl<'a> Iterator for BlockTransactionsHashIterator<'a> { - type Item = B256; - - fn next(&mut self) -> Option { - match self.txs { - BlockTransactions::Full(txs) => { - let tx = txs.get(self.idx); - self.idx += 1; - tx.map(|tx| tx.hash) - } - BlockTransactions::Hashes(txs) => { - let tx = txs.get(self.idx).copied(); - self.idx += 1; - tx - } - BlockTransactions::Uncle => None, - } - } -} - -/// Determines how the `transactions` field of [Block] should be filled. -/// -/// This essentially represents the `full:bool` argument in RPC calls that determine whether the -/// response should include full transaction objects or just the hashes. -#[derive(Debug, Clone, Eq, PartialEq)] -pub enum BlockTransactionsKind { - /// Only include hashes: [BlockTransactions::Hashes] - Hashes, - /// Include full transaction objects: [BlockTransactions::Full] - Full, -} - -impl From for BlockTransactionsKind { - fn from(is_full: bool) -> Self { - if is_full { - BlockTransactionsKind::Full - } else { - BlockTransactionsKind::Hashes - } - } -} - -/// Error that can occur when converting other types to blocks -#[derive(Debug, thiserror::Error)] -pub enum BlockError { - /// A transaction failed sender recovery - #[error("transaction failed sender recovery")] - InvalidSignature, - /// A raw block failed to decode - #[error("failed to decode raw block {0}")] - RlpDecodeRawBlock(alloy_rlp::Error), -} - -/// Block representation -#[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize)] -#[serde(rename_all = "camelCase")] -pub struct Block { - /// Header of the block - #[serde(flatten)] - pub header: Header, - /// Total difficulty, this field is None only if representing - /// an Uncle block. - #[serde(skip_serializing_if = "Option::is_none")] - pub total_difficulty: Option, - /// Uncles' hashes - pub uncles: Vec, - /// Transactions - #[serde(skip_serializing_if = "BlockTransactions::is_uncle")] - pub transactions: BlockTransactions, - /// Integer the size of this block in bytes. - pub size: Option, - /// Withdrawals in the block - #[serde(default, skip_serializing_if = "Option::is_none")] - pub withdrawals: Option>, -} - -/// Block header representation. -#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Hash)] -#[serde(rename_all = "camelCase")] -pub struct Header { - /// Hash of the block - pub hash: Option, - /// Hash of the parent - pub parent_hash: B256, - /// Hash of the uncles - #[serde(rename = "sha3Uncles")] - pub uncles_hash: B256, - /// Alias of `author` - pub miner: Address, - /// State root hash - pub state_root: B256, - /// Transactions root hash - pub transactions_root: B256, - /// Transactions receipts root hash - pub receipts_root: B256, - /// Logs bloom - pub logs_bloom: Bloom, - /// Difficulty - pub difficulty: U256, - /// Block number - pub number: Option, - /// Gas Limit - pub gas_limit: U256, - /// Gas Used - pub gas_used: U256, - /// Timestamp - pub timestamp: U256, - /// Extra data - pub extra_data: Bytes, - /// Mix Hash - /// - /// Before the merge this proves, combined with the nonce, that a sufficient amount of - /// computation has been carried out on this block: the Proof-of-Work (PoF). - /// - /// After the merge this is `prevRandao`: Randomness value for the generated payload. - /// - /// This is an Option because it is not always set by non-ethereum networks. - /// - /// See also - /// And - #[serde(skip_serializing_if = "Option::is_none")] - pub mix_hash: Option, - /// Nonce - pub nonce: Option, - /// Base fee per unit of gas (if past London) - #[serde(skip_serializing_if = "Option::is_none")] - pub base_fee_per_gas: Option, - /// Withdrawals root hash added by EIP-4895 and is ignored in legacy headers. - #[serde(skip_serializing_if = "Option::is_none")] - pub withdrawals_root: Option, - /// Blob gas used - #[serde(skip_serializing_if = "Option::is_none")] - pub blob_gas_used: Option, - /// Excess blob gas - #[serde(skip_serializing_if = "Option::is_none")] - pub excess_blob_gas: Option, - /// Parent beacon block root - #[serde(skip_serializing_if = "Option::is_none")] - pub parent_beacon_block_root: Option, -} - -/// A block hash which may have -/// a boolean requireCanonical field. -/// If false, an RPC call should raise if a block -/// matching the hash is not found. -/// If true, an RPC call should additionaly raise if -/// the block is not in the canonical chain. -/// -#[derive(Copy, Clone, Debug, PartialEq, Eq, Serialize)] -pub struct RpcBlockHash { - /// A block hash - pub block_hash: B256, - /// Whether the block must be a canonical block - pub require_canonical: Option, -} - -impl RpcBlockHash { - /// Returns an [RpcBlockHash] from a [B256]. - pub const fn from_hash(block_hash: B256, require_canonical: Option) -> Self { - RpcBlockHash { block_hash, require_canonical } - } -} - -impl From for RpcBlockHash { - fn from(value: B256) -> Self { - Self::from_hash(value, None) - } -} - -impl From for B256 { - fn from(value: RpcBlockHash) -> Self { - value.block_hash - } -} - -impl AsRef for RpcBlockHash { - fn as_ref(&self) -> &B256 { - &self.block_hash - } -} - -/// A block Number (or tag - "latest", "earliest", "pending") -#[derive(Copy, Clone, Debug, Default, PartialEq, Eq, Hash)] -pub enum BlockNumberOrTag { - /// Latest block - #[default] - Latest, - /// Finalized block accepted as canonical - Finalized, - /// Safe head block - Safe, - /// Earliest block (genesis) - Earliest, - /// Pending block (not yet part of the blockchain) - Pending, - /// Block by number from canon chain - Number(u64), -} - -impl BlockNumberOrTag { - /// Returns the numeric block number if explicitly set - pub const fn as_number(&self) -> Option { - match *self { - BlockNumberOrTag::Number(num) => Some(num), - _ => None, - } - } - - /// Returns `true` if a numeric block number is set - pub const fn is_number(&self) -> bool { - matches!(self, BlockNumberOrTag::Number(_)) - } - - /// Returns `true` if it's "latest" - pub const fn is_latest(&self) -> bool { - matches!(self, BlockNumberOrTag::Latest) - } - - /// Returns `true` if it's "finalized" - pub const fn is_finalized(&self) -> bool { - matches!(self, BlockNumberOrTag::Finalized) - } - - /// Returns `true` if it's "safe" - pub const fn is_safe(&self) -> bool { - matches!(self, BlockNumberOrTag::Safe) - } - - /// Returns `true` if it's "pending" - pub const fn is_pending(&self) -> bool { - matches!(self, BlockNumberOrTag::Pending) - } - - /// Returns `true` if it's "earliest" - pub const fn is_earliest(&self) -> bool { - matches!(self, BlockNumberOrTag::Earliest) - } -} - -impl From for BlockNumberOrTag { - fn from(num: u64) -> Self { - BlockNumberOrTag::Number(num) - } -} - -impl From for BlockNumberOrTag { - fn from(num: U64) -> Self { - num.to::().into() - } -} - -impl Serialize for BlockNumberOrTag { - fn serialize(&self, serializer: S) -> Result - where - S: Serializer, - { - match *self { - BlockNumberOrTag::Number(ref x) => serializer.serialize_str(&format!("0x{x:x}")), - BlockNumberOrTag::Latest => serializer.serialize_str("latest"), - BlockNumberOrTag::Finalized => serializer.serialize_str("finalized"), - BlockNumberOrTag::Safe => serializer.serialize_str("safe"), - BlockNumberOrTag::Earliest => serializer.serialize_str("earliest"), - BlockNumberOrTag::Pending => serializer.serialize_str("pending"), - } - } -} - -impl<'de> Deserialize<'de> for BlockNumberOrTag { - fn deserialize(deserializer: D) -> Result - where - D: Deserializer<'de>, - { - let s = String::deserialize(deserializer)?.to_lowercase(); - s.parse().map_err(serde::de::Error::custom) - } -} - -impl FromStr for BlockNumberOrTag { - type Err = ParseBlockNumberError; - - fn from_str(s: &str) -> Result { - let block = match s { - "latest" => Self::Latest, - "finalized" => Self::Finalized, - "safe" => Self::Safe, - "earliest" => Self::Earliest, - "pending" => Self::Pending, - _number => { - if let Some(hex_val) = s.strip_prefix("0x") { - let number = u64::from_str_radix(hex_val, 16); - BlockNumberOrTag::Number(number?) - } else { - return Err(HexStringMissingPrefixError::default().into()) - } - } - }; - Ok(block) - } -} - -impl fmt::Display for BlockNumberOrTag { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self { - BlockNumberOrTag::Number(ref x) => format!("0x{x:x}").fmt(f), - BlockNumberOrTag::Latest => f.write_str("latest"), - BlockNumberOrTag::Finalized => f.write_str("finalized"), - BlockNumberOrTag::Safe => f.write_str("safe"), - BlockNumberOrTag::Earliest => f.write_str("earliest"), - BlockNumberOrTag::Pending => f.write_str("pending"), - } - } -} - -/// Error variants when parsing a [BlockNumberOrTag] -#[derive(Debug, thiserror::Error)] -pub enum ParseBlockNumberError { - /// Failed to parse hex value - #[error(transparent)] - ParseIntErr(#[from] ParseIntError), - /// Block numbers should be 0x-prefixed - #[error(transparent)] - MissingPrefix(#[from] HexStringMissingPrefixError), -} - -/// Thrown when a 0x-prefixed hex string was expected -#[derive(Copy, Clone, Debug, Default, thiserror::Error)] -#[non_exhaustive] -#[error("hex string without 0x prefix")] -pub struct HexStringMissingPrefixError; - -/// A Block Identifier -/// -#[derive(Copy, Clone, Debug, PartialEq, Eq)] -pub enum BlockId { - /// A block hash and an optional bool that defines if it's canonical - Hash(RpcBlockHash), - /// A block number - Number(BlockNumberOrTag), -} - -// === impl BlockId === - -impl BlockId { - /// Returns the block hash if it is [BlockId::Hash] - pub const fn as_block_hash(&self) -> Option { - match self { - BlockId::Hash(hash) => Some(hash.block_hash), - BlockId::Number(_) => None, - } - } - - /// Returns true if this is [BlockNumberOrTag::Latest] - pub const fn is_latest(&self) -> bool { - matches!(self, BlockId::Number(BlockNumberOrTag::Latest)) - } - - /// Returns true if this is [BlockNumberOrTag::Pending] - pub const fn is_pending(&self) -> bool { - matches!(self, BlockId::Number(BlockNumberOrTag::Pending)) - } -} - -impl From for BlockId { - fn from(num: u64) -> Self { - BlockNumberOrTag::Number(num).into() - } -} - -impl From for BlockId { - fn from(value: U64) -> Self { - BlockNumberOrTag::Number(value.to()).into() - } -} - -impl From for BlockId { - fn from(num: BlockNumberOrTag) -> Self { - BlockId::Number(num) - } -} - -impl From for BlockId { - fn from(block_hash: B256) -> Self { - BlockId::Hash(RpcBlockHash { block_hash, require_canonical: None }) - } -} - -impl From<(B256, Option)> for BlockId { - fn from(hash_can: (B256, Option)) -> Self { - BlockId::Hash(RpcBlockHash { block_hash: hash_can.0, require_canonical: hash_can.1 }) - } -} - -impl Serialize for BlockId { - fn serialize(&self, serializer: S) -> Result - where - S: Serializer, - { - match *self { - BlockId::Hash(RpcBlockHash { ref block_hash, ref require_canonical }) => { - let mut s = serializer.serialize_struct("BlockIdEip1898", 1)?; - s.serialize_field("blockHash", block_hash)?; - if let Some(require_canonical) = require_canonical { - s.serialize_field("requireCanonical", require_canonical)?; - } - s.end() - } - BlockId::Number(ref num) => num.serialize(serializer), - } - } -} - -impl<'de> Deserialize<'de> for BlockId { - fn deserialize(deserializer: D) -> Result - where - D: Deserializer<'de>, - { - struct BlockIdVisitor; - - impl<'de> Visitor<'de> for BlockIdVisitor { - type Value = BlockId; - - fn expecting(&self, formatter: &mut Formatter<'_>) -> std::fmt::Result { - formatter.write_str("Block identifier following EIP-1898") - } - - fn visit_str(self, v: &str) -> Result - where - E: serde::de::Error, - { - // Since there is no way to clearly distinguish between a DATA parameter and a QUANTITY parameter. A str is therefor deserialized into a Block Number: - // However, since the hex string should be a QUANTITY, we can safely assume that if the len is 66 bytes, it is in fact a hash, ref - if v.len() == 66 { - Ok(BlockId::Hash(v.parse::().map_err(serde::de::Error::custom)?.into())) - } else { - // quantity hex string or tag - Ok(BlockId::Number(v.parse().map_err(serde::de::Error::custom)?)) - } - } - - fn visit_map(self, mut map: A) -> Result - where - A: MapAccess<'de>, - { - let mut number = None; - let mut block_hash = None; - let mut require_canonical = None; - while let Some(key) = map.next_key::()? { - match key.as_str() { - "blockNumber" => { - if number.is_some() || block_hash.is_some() { - return Err(serde::de::Error::duplicate_field("blockNumber")) - } - if require_canonical.is_some() { - return Err(serde::de::Error::custom( - "Non-valid require_canonical field", - )) - } - number = Some(map.next_value::()?) - } - "blockHash" => { - if number.is_some() || block_hash.is_some() { - return Err(serde::de::Error::duplicate_field("blockHash")) - } - - block_hash = Some(map.next_value::()?); - } - "requireCanonical" => { - if number.is_some() || require_canonical.is_some() { - return Err(serde::de::Error::duplicate_field("requireCanonical")) - } - - require_canonical = Some(map.next_value::()?) - } - key => { - return Err(serde::de::Error::unknown_field( - key, - &["blockNumber", "blockHash", "requireCanonical"], - )) - } - } - } - - if let Some(number) = number { - Ok(BlockId::Number(number)) - } else if let Some(block_hash) = block_hash { - Ok(BlockId::Hash(RpcBlockHash { block_hash, require_canonical })) - } else { - Err(serde::de::Error::custom( - "Expected `blockNumber` or `blockHash` with `requireCanonical` optionally", - )) - } - } - } - - deserializer.deserialize_any(BlockIdVisitor) - } -} - -/// Block number and hash. -#[derive(Clone, Copy, Hash, Default, PartialEq, Eq)] -pub struct BlockNumHash { - /// Block number - pub number: BlockNumber, - /// Block hash - pub hash: BlockHash, -} - -/// Block number and hash of the forked block. -pub type ForkBlock = BlockNumHash; - -impl std::fmt::Debug for BlockNumHash { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - f.debug_tuple("").field(&self.number).field(&self.hash).finish() - } -} - -impl BlockNumHash { - /// Creates a new `BlockNumHash` from a block number and hash. - pub fn new(number: BlockNumber, hash: BlockHash) -> Self { - Self { number, hash } - } - - /// Consumes `Self` and returns [`BlockNumber`], [`BlockHash`] - pub fn into_components(self) -> (BlockNumber, BlockHash) { - (self.number, self.hash) - } - - /// Returns whether or not the block matches the given [BlockHashOrNumber]. - pub fn matches_block_or_num(&self, block: &BlockHashOrNumber) -> bool { - match block { - BlockHashOrNumber::Hash(hash) => self.hash == *hash, - BlockHashOrNumber::Number(number) => self.number == *number, - } - } -} - -impl From<(BlockNumber, BlockHash)> for BlockNumHash { - fn from(val: (BlockNumber, BlockHash)) -> Self { - BlockNumHash { number: val.0, hash: val.1 } - } -} - -impl From<(BlockHash, BlockNumber)> for BlockNumHash { - fn from(val: (BlockHash, BlockNumber)) -> Self { - BlockNumHash { hash: val.0, number: val.1 } - } -} - -/// Either a block hash _or_ a block number -#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] -#[cfg_attr( - any(test, feature = "arbitrary"), - derive(proptest_derive::Arbitrary, arbitrary::Arbitrary) -)] -pub enum BlockHashOrNumber { - /// A block hash - Hash(B256), - /// A block number - Number(u64), -} - -// === impl BlockHashOrNumber === - -impl BlockHashOrNumber { - /// Returns the block number if it is a [`BlockHashOrNumber::Number`]. - #[inline] - pub fn as_number(self) -> Option { - match self { - BlockHashOrNumber::Hash(_) => None, - BlockHashOrNumber::Number(num) => Some(num), - } - } -} - -impl From for BlockHashOrNumber { - fn from(value: B256) -> Self { - BlockHashOrNumber::Hash(value) - } -} - -impl From for BlockHashOrNumber { - fn from(value: u64) -> Self { - BlockHashOrNumber::Number(value) - } -} - -impl From for BlockHashOrNumber { - fn from(value: U64) -> Self { - value.to::().into() - } -} - -impl Encodable for BlockHashOrNumber { - /// RLP encodes either the block hash or block number, depending on the variant. - fn encode(&self, out: &mut dyn bytes::BufMut) { - match self { - Self::Hash(block_hash) => block_hash.encode(out), - Self::Number(block_number) => block_number.encode(out), - } - } - fn length(&self) -> usize { - match self { - Self::Hash(block_hash) => block_hash.length(), - Self::Number(block_number) => block_number.length(), - } - } -} - -impl Decodable for BlockHashOrNumber { - /// RLP decodes the data into a block hash or number. - /// If the data is exactly 32 bytes and a RLP string, it will be decoded into a block hash. - /// Otherwise, this will try to decode a `u64` from the data as a block number. - fn decode(buf: &mut &[u8]) -> alloy_rlp::Result { - let header: u8 = *buf.first().ok_or(RlpError::InputTooShort)?; - // if the byte string is exactly 32 bytes, decode it into a Hash - // 0xa0 = 0x80 (start of string) + 0x20 (32, length of string) - if header == 0xa0 { - // strip the first byte, parsing the rest of the string. - // If the rest of the string fails to decode into 32 bytes, we'll bubble up the - // decoding error. - let hash = B256::decode(buf)?; - Ok(Self::Hash(hash)) - } else { - // a block number when encoded as bytes ranges from 0 to any number of bytes - we're - // going to accept numbers which fit in less than 64 bytes. - // Any data larger than this which is not caught by the Hash decoding should error and - // is considered an invalid block number. - Ok(Self::Number(u64::decode(buf)?)) - } - } -} - -/// Error thrown when parsing a [BlockHashOrNumber] from a string. -#[derive(Debug, thiserror::Error)] -#[error("failed to parse {input:?} as a number: {parse_int_error} or hash: {hex_error}")] -pub struct ParseBlockHashOrNumberError { - input: String, - parse_int_error: ParseIntError, - hex_error: alloy_primitives::hex::FromHexError, -} - -impl FromStr for BlockHashOrNumber { - type Err = ParseBlockHashOrNumberError; - - fn from_str(s: &str) -> Result { - match u64::from_str(s) { - Ok(val) => Ok(val.into()), - Err(pares_int_error) => match B256::from_str(s) { - Ok(val) => Ok(val.into()), - Err(hex_error) => Err(ParseBlockHashOrNumberError { - input: s.to_string(), - parse_int_error: pares_int_error, - hex_error, - }), - }, - } - } -} - -/// A Block representation that allows to include additional fields -pub type RichBlock = Rich; - -impl From for RichBlock { - fn from(block: Block) -> Self { - Rich { inner: block, extra_info: Default::default() } - } -} - -/// Header representation with additional info. -pub type RichHeader = Rich
; - -impl From
for RichHeader { - fn from(header: Header) -> Self { - Rich { inner: header, extra_info: Default::default() } - } -} - -/// Value representation with additional info -#[derive(Debug, Clone, PartialEq, Eq, Deserialize)] -pub struct Rich { - /// Standard value. - #[serde(flatten)] - pub inner: T, - /// Additional fields that should be serialized into the `Block` object - #[serde(flatten)] - pub extra_info: BTreeMap, -} - -impl Deref for Rich { - type Target = T; - fn deref(&self) -> &Self::Target { - &self.inner - } -} - -impl Serialize for Rich { - fn serialize(&self, serializer: S) -> Result - where - S: Serializer, - { - if self.extra_info.is_empty() { - return self.inner.serialize(serializer) - } - - let inner = serde_json::to_value(&self.inner); - let extras = serde_json::to_value(&self.extra_info); - - if let (Ok(serde_json::Value::Object(mut value)), Ok(serde_json::Value::Object(extras))) = - (inner, extras) - { - value.extend(extras); - value.serialize(serializer) - } else { - Err(S::Error::custom("Unserializable structures: expected objects")) - } - } -} - -/// BlockOverrides is a set of header fields to override. -#[derive(Clone, Debug, PartialEq, Eq, Default, Serialize, Deserialize)] -#[serde(default, rename_all = "camelCase", deny_unknown_fields)] -pub struct BlockOverrides { - /// Overrides the block number. - /// - /// For `eth_callMany` this will be the block number of the first simulated block. Each - /// following block increments its block number by 1 - // Note: geth uses `number`, erigon uses `blockNumber` - #[serde(default, skip_serializing_if = "Option::is_none", alias = "blockNumber")] - pub number: Option, - /// Overrides the difficulty of the block. - #[serde(default, skip_serializing_if = "Option::is_none")] - pub difficulty: Option, - /// Overrides the timestamp of the block. - // Note: geth uses `time`, erigon uses `timestamp` - #[serde(default, skip_serializing_if = "Option::is_none", alias = "timestamp")] - pub time: Option, - /// Overrides the gas limit of the block. - #[serde(default, skip_serializing_if = "Option::is_none")] - pub gas_limit: Option, - /// Overrides the coinbase address of the block. - #[serde(default, skip_serializing_if = "Option::is_none")] - pub coinbase: Option
, - /// Overrides the prevrandao of the block. - #[serde(default, skip_serializing_if = "Option::is_none")] - pub random: Option, - /// Overrides the basefee of the block. - #[serde(default, skip_serializing_if = "Option::is_none")] - pub base_fee: Option, - /// A dictionary that maps blockNumber to a user-defined hash. It could be queried from the - /// solidity opcode BLOCKHASH. - #[serde(default, skip_serializing_if = "Option::is_none")] - pub block_hash: Option>, -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn test_full_conversion() { - let full = true; - assert_eq!(BlockTransactionsKind::Full, full.into()); - - let full = false; - assert_eq!(BlockTransactionsKind::Hashes, full.into()); - } - - #[test] - #[cfg(feature = "jsonrpsee-types")] - fn serde_json_header() { - use jsonrpsee_types::SubscriptionResponse; - let resp = r#"{"jsonrpc":"2.0","method":"eth_subscribe","params":{"subscription":"0x7eef37ff35d471f8825b1c8f67a5d3c0","result":{"hash":"0x7a7ada12e140961a32395059597764416499f4178daf1917193fad7bd2cc6386","parentHash":"0xdedbd831f496e705e7f2ec3c8dcb79051040a360bf1455dbd7eb8ea6ad03b751","sha3Uncles":"0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347","miner":"0x0000000000000000000000000000000000000000","stateRoot":"0x0000000000000000000000000000000000000000000000000000000000000000","transactionsRoot":"0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421","receiptsRoot":"0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421","number":"0x8","gasUsed":"0x0","gasLimit":"0x1c9c380","extraData":"0x","logsBloom":"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000","timestamp":"0x642aa48f","difficulty":"0x0","mixHash":"0x0000000000000000000000000000000000000000000000000000000000000000","nonce":"0x0000000000000000"}}}"#; - let _header: SubscriptionResponse<'_, Header> = serde_json::from_str(resp).unwrap(); - - let resp = r#"{"jsonrpc":"2.0","method":"eth_subscription","params":{"subscription":"0x1a14b6bdcf4542fabf71c4abee244e47","result":{"author":"0x000000568b9b5a365eaa767d42e74ed88915c204","difficulty":"0x1","extraData":"0x4e65746865726d696e6420312e392e32322d302d6463373666616366612d32308639ad8ff3d850a261f3b26bc2a55e0f3a718de0dd040a19a4ce37e7b473f2d7481448a1e1fd8fb69260825377c0478393e6055f471a5cf839467ce919a6ad2700","gasLimit":"0x7a1200","gasUsed":"0x0","hash":"0xa4856602944fdfd18c528ef93cc52a681b38d766a7e39c27a47488c8461adcb0","logsBloom":"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000","miner":"0x0000000000000000000000000000000000000000","mixHash":"0x0000000000000000000000000000000000000000000000000000000000000000","nonce":"0x0000000000000000","number":"0x434822","parentHash":"0x1a9bdc31fc785f8a95efeeb7ae58f40f6366b8e805f47447a52335c95f4ceb49","receiptsRoot":"0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421","sha3Uncles":"0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347","size":"0x261","stateRoot":"0xf38c4bf2958e541ec6df148e54ce073dc6b610f8613147ede568cb7b5c2d81ee","totalDifficulty":"0x633ebd","timestamp":"0x604726b0","transactions":[],"transactionsRoot":"0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421","uncles":[]}}}"#; - let _header: SubscriptionResponse<'_, Header> = serde_json::from_str(resp).unwrap(); - } - - #[test] - fn serde_block() { - let block = Block { - header: Header { - hash: Some(B256::with_last_byte(1)), - parent_hash: B256::with_last_byte(2), - uncles_hash: B256::with_last_byte(3), - miner: Address::with_last_byte(4), - state_root: B256::with_last_byte(5), - transactions_root: B256::with_last_byte(6), - receipts_root: B256::with_last_byte(7), - withdrawals_root: Some(B256::with_last_byte(8)), - number: Some(U256::from(9)), - gas_used: U256::from(10), - gas_limit: U256::from(11), - extra_data: Bytes::from(vec![1, 2, 3]), - logs_bloom: Bloom::default(), - timestamp: U256::from(12), - difficulty: U256::from(13), - mix_hash: Some(B256::with_last_byte(14)), - nonce: Some(B64::with_last_byte(15)), - base_fee_per_gas: Some(U256::from(20)), - blob_gas_used: None, - excess_blob_gas: None, - parent_beacon_block_root: None, - }, - total_difficulty: Some(U256::from(100000)), - uncles: vec![B256::with_last_byte(17)], - transactions: BlockTransactions::Hashes(vec![B256::with_last_byte(18)]), - size: Some(U256::from(19)), - withdrawals: Some(vec![]), - }; - let serialized = serde_json::to_string(&block).unwrap(); - assert_eq!( - serialized, - r#"{"hash":"0x0000000000000000000000000000000000000000000000000000000000000001","parentHash":"0x0000000000000000000000000000000000000000000000000000000000000002","sha3Uncles":"0x0000000000000000000000000000000000000000000000000000000000000003","miner":"0x0000000000000000000000000000000000000004","stateRoot":"0x0000000000000000000000000000000000000000000000000000000000000005","transactionsRoot":"0x0000000000000000000000000000000000000000000000000000000000000006","receiptsRoot":"0x0000000000000000000000000000000000000000000000000000000000000007","logsBloom":"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000","difficulty":"0xd","number":"0x9","gasLimit":"0xb","gasUsed":"0xa","timestamp":"0xc","extraData":"0x010203","mixHash":"0x000000000000000000000000000000000000000000000000000000000000000e","nonce":"0x000000000000000f","baseFeePerGas":"0x14","withdrawalsRoot":"0x0000000000000000000000000000000000000000000000000000000000000008","totalDifficulty":"0x186a0","uncles":["0x0000000000000000000000000000000000000000000000000000000000000011"],"transactions":["0x0000000000000000000000000000000000000000000000000000000000000012"],"size":"0x13","withdrawals":[]}"# - ); - let deserialized: Block = serde_json::from_str(&serialized).unwrap(); - assert_eq!(block, deserialized); - } - - #[test] - fn serde_block_with_withdrawals_set_as_none() { - let block = Block { - header: Header { - hash: Some(B256::with_last_byte(1)), - parent_hash: B256::with_last_byte(2), - uncles_hash: B256::with_last_byte(3), - miner: Address::with_last_byte(4), - state_root: B256::with_last_byte(5), - transactions_root: B256::with_last_byte(6), - receipts_root: B256::with_last_byte(7), - withdrawals_root: None, - number: Some(U256::from(9)), - gas_used: U256::from(10), - gas_limit: U256::from(11), - extra_data: Bytes::from(vec![1, 2, 3]), - logs_bloom: Bloom::default(), - timestamp: U256::from(12), - difficulty: U256::from(13), - mix_hash: Some(B256::with_last_byte(14)), - nonce: Some(B64::with_last_byte(15)), - base_fee_per_gas: Some(U256::from(20)), - blob_gas_used: None, - excess_blob_gas: None, - parent_beacon_block_root: None, - }, - total_difficulty: Some(U256::from(100000)), - uncles: vec![B256::with_last_byte(17)], - transactions: BlockTransactions::Hashes(vec![B256::with_last_byte(18)]), - size: Some(U256::from(19)), - withdrawals: None, - }; - let serialized = serde_json::to_string(&block).unwrap(); - assert_eq!( - serialized, - r#"{"hash":"0x0000000000000000000000000000000000000000000000000000000000000001","parentHash":"0x0000000000000000000000000000000000000000000000000000000000000002","sha3Uncles":"0x0000000000000000000000000000000000000000000000000000000000000003","miner":"0x0000000000000000000000000000000000000004","stateRoot":"0x0000000000000000000000000000000000000000000000000000000000000005","transactionsRoot":"0x0000000000000000000000000000000000000000000000000000000000000006","receiptsRoot":"0x0000000000000000000000000000000000000000000000000000000000000007","logsBloom":"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000","difficulty":"0xd","number":"0x9","gasLimit":"0xb","gasUsed":"0xa","timestamp":"0xc","extraData":"0x010203","mixHash":"0x000000000000000000000000000000000000000000000000000000000000000e","nonce":"0x000000000000000f","baseFeePerGas":"0x14","totalDifficulty":"0x186a0","uncles":["0x0000000000000000000000000000000000000000000000000000000000000011"],"transactions":["0x0000000000000000000000000000000000000000000000000000000000000012"],"size":"0x13"}"# - ); - let deserialized: Block = serde_json::from_str(&serialized).unwrap(); - assert_eq!(block, deserialized); - } - - #[test] - fn block_overrides() { - let s = r#"{"blockNumber": "0xe39dd0"}"#; - let _overrides = serde_json::from_str::(s).unwrap(); - } - - #[test] - fn serde_rich_block() { - let s = r#"{ - "hash": "0xb25d0e54ca0104e3ebfb5a1dcdf9528140854d609886a300946fd6750dcb19f4", - "parentHash": "0x9400ec9ef59689c157ac89eeed906f15ddd768f94e1575e0e27d37c241439a5d", - "sha3Uncles": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347", - "miner": "0x829bd824b016326a401d083b33d092293333a830", - "stateRoot": "0x546e330050c66d02923e7f1f3e925efaf64e4384eeecf2288f40088714a77a84", - "transactionsRoot": "0xd5eb3ad6d7c7a4798cc5fb14a6820073f44a941107c5d79dac60bd16325631fe", - "receiptsRoot": "0xb21c41cbb3439c5af25304e1405524c885e733b16203221900cb7f4b387b62f0", - "logsBloom": "0x1f304e641097eafae088627298685d20202004a4a59e4d8900914724e2402b028c9d596660581f361240816e82d00fa14250c9ca89840887a381efa600288283d170010ab0b2a0694c81842c2482457e0eb77c2c02554614007f42aaf3b4dc15d006a83522c86a240c06d241013258d90540c3008888d576a02c10120808520a2221110f4805200302624d22092b2c0e94e849b1e1aa80bc4cc3206f00b249d0a603ee4310216850e47c8997a20aa81fe95040a49ca5a420464600e008351d161dc00d620970b6a801535c218d0b4116099292000c08001943a225d6485528828110645b8244625a182c1a88a41087e6d039b000a180d04300d0680700a15794", - "difficulty": "0xc40faff9c737d", - "number": "0xa9a230", - "gasLimit": "0xbe5a66", - "gasUsed": "0xbe0fcc", - "timestamp": "0x5f93b749", - "extraData": "0x7070796520e4b883e5bda9e7a59ee4bb99e9b1bc0103", - "mixHash": "0xd5e2b7b71fbe4ddfe552fb2377bf7cddb16bbb7e185806036cee86994c6e97fc", - "nonce": "0x4722f2acd35abe0f", - "totalDifficulty": "0x3dc957fd8167fb2684a", - "uncles": [], - "transactions": [ - "0xf435a26acc2a9ef73ac0b73632e32e29bd0e28d5c4f46a7e18ed545c93315916" - ], - "size": "0xaeb6" -}"#; - - let block = serde_json::from_str::(s).unwrap(); - let serialized = serde_json::to_string(&block).unwrap(); - let block2 = serde_json::from_str::(&serialized).unwrap(); - assert_eq!(block, block2); - } - - #[test] - fn compact_block_number_serde() { - let num: BlockNumberOrTag = 1u64.into(); - let serialized = serde_json::to_string(&num).unwrap(); - assert_eq!(serialized, "\"0x1\""); - } -} diff --git a/crates/rpc/rpc-types/src/eth/call.rs b/crates/rpc/rpc-types/src/eth/call.rs deleted file mode 100644 index 66d2e28cb..000000000 --- a/crates/rpc/rpc-types/src/eth/call.rs +++ /dev/null @@ -1,274 +0,0 @@ -use crate::{AccessList, BlockId, BlockOverrides}; -use alloy_primitives::{Address, Bytes, B256, U256, U64, U8}; -use serde::{Deserialize, Deserializer, Serialize, Serializer}; - -/// Bundle of transactions -#[derive(Debug, Clone, Default, Eq, PartialEq, Serialize, Deserialize)] -#[serde(default, rename_all = "camelCase")] -pub struct Bundle { - /// All transactions to execute - pub transactions: Vec, - /// Block overrides to apply - pub block_override: Option, -} - -/// State context for callMany -#[derive(Debug, Clone, Default, Eq, PartialEq, Serialize, Deserialize)] -#[serde(default, rename_all = "camelCase")] -pub struct StateContext { - /// Block Number - pub block_number: Option, - /// Inclusive number of tx to replay in block. -1 means replay all - pub transaction_index: Option, -} - -/// CallResponse for eth_callMany -#[derive(Debug, Clone, Default, Eq, PartialEq, Serialize, Deserialize)] -#[serde(default, rename_all = "camelCase")] -pub struct EthCallResponse { - /// eth_call output (if no error) - #[serde(skip_serializing_if = "Option::is_none")] - pub value: Option, - /// eth_call output (if error) - #[serde(skip_serializing_if = "Option::is_none")] - pub error: Option, -} - -impl EthCallResponse { - /// Returns the value if present, otherwise returns the error. - pub fn ensure_ok(self) -> Result { - match self.value { - Some(output) => Ok(output), - None => Err(self.error.unwrap_or_else(|| "Unknown error".to_string())), - } - } -} - -/// Represents a transaction index where -1 means all transactions -#[derive(Debug, Copy, Clone, Eq, PartialEq, Default)] -pub enum TransactionIndex { - /// -1 means all transactions - #[default] - All, - /// Transaction index - Index(usize), -} - -impl TransactionIndex { - /// Returns true if this is the all variant - pub fn is_all(&self) -> bool { - matches!(self, TransactionIndex::All) - } - - /// Returns the index if this is the index variant - pub fn index(&self) -> Option { - match self { - TransactionIndex::All => None, - TransactionIndex::Index(idx) => Some(*idx), - } - } -} - -impl Serialize for TransactionIndex { - fn serialize(&self, serializer: S) -> Result - where - S: Serializer, - { - match self { - TransactionIndex::All => serializer.serialize_i8(-1), - TransactionIndex::Index(idx) => idx.serialize(serializer), - } - } -} - -impl<'de> Deserialize<'de> for TransactionIndex { - fn deserialize(deserializer: D) -> Result - where - D: Deserializer<'de>, - { - match isize::deserialize(deserializer)? { - -1 => Ok(TransactionIndex::All), - idx if idx < -1 => Err(serde::de::Error::custom(format!( - "Invalid transaction index, expected -1 or positive integer, got {}", - idx - ))), - idx => Ok(TransactionIndex::Index(idx as usize)), - } - } -} - -/// Call request for `eth_call` and adjacent methods. -#[derive(Debug, Clone, Default, Eq, PartialEq, Serialize, Deserialize)] -#[serde(default, rename_all = "camelCase")] -pub struct CallRequest { - /// From - pub from: Option
, - /// To - pub to: Option
, - /// Gas Price - pub gas_price: Option, - /// EIP-1559 Max base fee the caller is willing to pay - pub max_fee_per_gas: Option, - /// EIP-1559 Priority fee the caller is paying to the block author - pub max_priority_fee_per_gas: Option, - /// Gas - pub gas: Option, - /// Value - pub value: Option, - /// Transaction input data - #[serde(default, flatten)] - pub input: CallInput, - /// Nonce - pub nonce: Option, - /// chain id - pub chain_id: Option, - /// AccessList - pub access_list: Option, - /// Max Fee per Blob gas for EIP-4844 transactions - #[serde(skip_serializing_if = "Option::is_none")] - pub max_fee_per_blob_gas: Option, - /// Blob Versioned Hashes for EIP-4844 transactions - #[serde(skip_serializing_if = "Option::is_none")] - pub blob_versioned_hashes: Option>, - /// EIP-2718 type - #[serde(rename = "type", skip_serializing_if = "Option::is_none")] - pub transaction_type: Option, -} - -impl CallRequest { - /// Returns the configured fee cap, if any. - /// - /// The returns `gas_price` (legacy) if set or `max_fee_per_gas` (EIP1559) - #[inline] - pub fn fee_cap(&self) -> Option { - self.gas_price.or(self.max_fee_per_gas) - } - - /// Returns true if the request has a `blobVersionedHashes` field but it is empty. - #[inline] - pub fn has_empty_blob_hashes(&self) -> bool { - self.blob_versioned_hashes.as_ref().map(|blobs| blobs.is_empty()).unwrap_or(false) - } -} - -/// Helper type that supports both `data` and `input` fields that map to transaction input data. -/// -/// This is done for compatibility reasons where older implementations used `data` instead of the -/// newer, recommended `input` field. -/// -/// If both fields are set, it is expected that they contain the same value, otherwise an error is -/// returned. -#[derive(Debug, Clone, Default, Eq, PartialEq, Serialize, Deserialize)] -pub struct CallInput { - /// Transaction data - #[serde(skip_serializing_if = "Option::is_none")] - pub input: Option, - /// Transaction data - /// - /// This is the same as `input` but is used for backwards compatibility: - #[serde(skip_serializing_if = "Option::is_none")] - pub data: Option, -} - -impl CallInput { - /// Creates a new instance with the given input data. - pub fn new(data: Bytes) -> Self { - Self::maybe_input(Some(data)) - } - - /// Creates a new instance with the given input data. - pub fn maybe_input(input: Option) -> Self { - Self { input, data: None } - } - - /// Consumes the type and returns the optional input data. - /// - /// Returns an error if both `data` and `input` fields are set and not equal. - pub fn try_into_unique_input(self) -> Result, CallInputError> { - let Self { input, data } = self; - match (input, data) { - (Some(input), Some(data)) if input == data => Ok(Some(input)), - (Some(_), Some(_)) => Err(CallInputError::default()), - (Some(input), None) => Ok(Some(input)), - (None, Some(data)) => Ok(Some(data)), - (None, None) => Ok(None), - } - } - - /// Consumes the type and returns the optional input data. - /// - /// Returns an error if both `data` and `input` fields are set and not equal. - pub fn unique_input(&self) -> Result, CallInputError> { - let Self { input, data } = self; - match (input, data) { - (Some(input), Some(data)) if input == data => Ok(Some(input)), - (Some(_), Some(_)) => Err(CallInputError::default()), - (Some(input), None) => Ok(Some(input)), - (None, Some(data)) => Ok(Some(data)), - (None, None) => Ok(None), - } - } -} - -impl From for CallInput { - fn from(input: Bytes) -> Self { - Self { input: Some(input), data: None } - } -} - -impl From> for CallInput { - fn from(input: Option) -> Self { - Self { input, data: None } - } -} - -/// Error thrown when both `data` and `input` fields are set and not equal. -#[derive(Debug, Default, thiserror::Error)] -#[error("both \"data\" and \"input\" are set and not equal. Please use \"input\" to pass transaction call data")] -#[non_exhaustive] -pub struct CallInputError; - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn transaction_index() { - let s = "-1"; - let idx = serde_json::from_str::(s).unwrap(); - assert_eq!(idx, TransactionIndex::All); - - let s = "5"; - let idx = serde_json::from_str::(s).unwrap(); - assert_eq!(idx, TransactionIndex::Index(5)); - - let s = "-2"; - let res = serde_json::from_str::(s); - assert!(res.is_err()); - } - - #[test] - fn serde_call_request() { - let s = r#"{"accessList":[],"data":"0x0902f1ac","to":"0xa478c2975ab1ea89e8196811f51a7b7ade33eb11","type":"0x02"}"#; - let _req = serde_json::from_str::(s).unwrap(); - } - - #[test] - fn serde_unique_call_input() { - let s = r#"{"accessList":[],"data":"0x0902f1ac", "input":"0x0902f1ac","to":"0xa478c2975ab1ea89e8196811f51a7b7ade33eb11","type":"0x02"}"#; - let req = serde_json::from_str::(s).unwrap(); - assert!(req.input.try_into_unique_input().unwrap().is_some()); - - let s = r#"{"accessList":[],"data":"0x0902f1ac","to":"0xa478c2975ab1ea89e8196811f51a7b7ade33eb11","type":"0x02"}"#; - let req = serde_json::from_str::(s).unwrap(); - assert!(req.input.try_into_unique_input().unwrap().is_some()); - - let s = r#"{"accessList":[],"input":"0x0902f1ac","to":"0xa478c2975ab1ea89e8196811f51a7b7ade33eb11","type":"0x02"}"#; - let req = serde_json::from_str::(s).unwrap(); - assert!(req.input.try_into_unique_input().unwrap().is_some()); - - let s = r#"{"accessList":[],"data":"0x0902f1ac", "input":"0x0902f1","to":"0xa478c2975ab1ea89e8196811f51a7b7ade33eb11","type":"0x02"}"#; - let req = serde_json::from_str::(s).unwrap(); - assert!(req.input.try_into_unique_input().is_err()); - } -} diff --git a/crates/rpc/rpc-types/src/eth/error.rs b/crates/rpc/rpc-types/src/eth/error.rs deleted file mode 100644 index f580a4dbe..000000000 --- a/crates/rpc/rpc-types/src/eth/error.rs +++ /dev/null @@ -1,32 +0,0 @@ -//! Commonly used errors for the `eth_` namespace. - -/// List of JSON-RPC error codes -#[derive(Debug, Copy, PartialEq, Eq, Clone)] -pub enum EthRpcErrorCode { - /// Failed to send transaction, See also - TransactionRejected, - /// Custom geth error code, - ExecutionError, - /// - InvalidInput, - /// Thrown when a block wasn't found - /// > If the block is not found, the callee SHOULD raise a JSON-RPC error (the recommended - /// > error code is -32001: Resource not found). - ResourceNotFound, - /// Thrown when querying for `finalized` or `safe` block before the merge transition is - /// finalized, - UnknownBlock, -} - -impl EthRpcErrorCode { - /// Returns the error code as `i32` - pub const fn code(&self) -> i32 { - match *self { - EthRpcErrorCode::TransactionRejected => -32003, - EthRpcErrorCode::ExecutionError => 3, - EthRpcErrorCode::InvalidInput => -32000, - EthRpcErrorCode::ResourceNotFound => -32001, - EthRpcErrorCode::UnknownBlock => -39001, - } - } -} diff --git a/crates/rpc/rpc-types/src/eth/fee.rs b/crates/rpc/rpc-types/src/eth/fee.rs deleted file mode 100644 index 5fb145ab6..000000000 --- a/crates/rpc/rpc-types/src/eth/fee.rs +++ /dev/null @@ -1,55 +0,0 @@ -use alloy_primitives::U256; -use serde::{Deserialize, Serialize}; - -/// Internal struct to calculate reward percentiles -#[derive(Clone, Debug, PartialEq, Eq)] -pub struct TxGasAndReward { - /// Gas used by the transaction - pub gas_used: u64, - /// The effective gas tip by the transaction - pub reward: u128, -} - -impl PartialOrd for TxGasAndReward { - fn partial_cmp(&self, other: &Self) -> Option { - Some(self.cmp(other)) - } -} - -impl Ord for TxGasAndReward { - fn cmp(&self, other: &Self) -> std::cmp::Ordering { - // compare only the reward - // see: - // - self.reward.cmp(&other.reward) - } -} - -/// Response type for `eth_feeHistory` -#[derive(Debug, Clone, Default, PartialEq, Deserialize, Serialize)] -#[serde(rename_all = "camelCase")] -pub struct FeeHistory { - /// An array of block base fees per gas. - /// This includes the next block after the newest of the returned range, - /// because this value can be derived from the newest block. Zeroes are - /// returned for pre-EIP-1559 blocks. - /// - /// # Note - /// - /// Empty list is skipped only for compatibility with Erigon and Geth. - #[serde(default, skip_serializing_if = "Vec::is_empty")] - pub base_fee_per_gas: Vec, - /// An array of block gas used ratios. These are calculated as the ratio - /// of `gasUsed` and `gasLimit`. - /// - /// # Note - /// - /// The `Option` is only for compatability with Erigon and Geth. - pub gas_used_ratio: Vec, - /// Lowest number block of the returned range. - pub oldest_block: U256, - /// An (optional) array of effective priority fee per gas data points from a single - /// block. All zeroes are returned if the block is empty. - #[serde(skip_serializing_if = "Option::is_none")] - pub reward: Option>>, -} diff --git a/crates/rpc/rpc-types/src/eth/filter.rs b/crates/rpc/rpc-types/src/eth/filter.rs deleted file mode 100644 index b6c65bd19..000000000 --- a/crates/rpc/rpc-types/src/eth/filter.rs +++ /dev/null @@ -1,1354 +0,0 @@ -use crate::{eth::log::Log as RpcLog, BlockNumberOrTag, Log, Transaction}; -use alloy_primitives::{keccak256, Address, Bloom, BloomInput, B256, U256, U64}; -use itertools::{EitherOrBoth::*, Itertools}; -use serde::{ - de::{DeserializeOwned, MapAccess, Visitor}, - ser::SerializeStruct, - Deserialize, Deserializer, Serialize, Serializer, -}; -use std::{ - collections::HashSet, - hash::Hash, - ops::{Range, RangeFrom, RangeTo}, -}; - -/// Helper type to represent a bloom filter used for matching logs. -#[derive(Default, Debug)] -pub struct BloomFilter(Vec); - -impl From> for BloomFilter { - fn from(src: Vec) -> Self { - BloomFilter(src) - } -} - -impl BloomFilter { - /// Returns whether the given bloom matches the list of Blooms in the current filter. - /// If the filter is empty (the list is empty), then any bloom matches - /// Otherwise, there must be at least one matche for the BloomFilter to match. - pub fn matches(&self, bloom: Bloom) -> bool { - self.0.is_empty() || self.0.iter().any(|a| bloom.contains(a)) - } -} - -#[derive(Default, Debug, PartialEq, Eq, Clone, Deserialize)] -/// FilterSet is a set of values that will be used to filter logs -pub struct FilterSet(HashSet); - -impl From for FilterSet { - fn from(src: T) -> Self { - FilterSet(HashSet::from([src])) - } -} - -impl From> for FilterSet { - fn from(src: Vec) -> Self { - FilterSet(HashSet::from_iter(src.into_iter().map(Into::into))) - } -} - -impl From> for FilterSet { - fn from(src: ValueOrArray) -> Self { - match src { - ValueOrArray::Value(val) => val.into(), - ValueOrArray::Array(arr) => arr.into(), - } - } -} - -impl From>> for FilterSet { - fn from(src: ValueOrArray>) -> Self { - match src { - ValueOrArray::Value(None) => FilterSet(HashSet::new()), - ValueOrArray::Value(Some(val)) => val.into(), - ValueOrArray::Array(arr) => { - // If the array contains at least one `null` (ie. None), as it's considered - // a "wildcard" value, the whole filter should be treated as matching everything, - // thus is empty. - if arr.iter().contains(&None) { - FilterSet(HashSet::new()) - } else { - // Otherwise, we flatten the array, knowing there are no `None` values - arr.into_iter().flatten().collect::>().into() - } - } - } - } -} - -impl FilterSet { - /// Returns wheter the filter is empty - pub fn is_empty(&self) -> bool { - self.0.is_empty() - } - - /// Returns whether the given value matches the filter. It the filter is empty - /// any value matches. Otherwise, the filter must include the value - pub fn matches(&self, value: &T) -> bool { - self.is_empty() || self.0.contains(value) - } -} - -impl + Eq + Hash> FilterSet { - /// Returns a list of Bloom (BloomFilter) corresponding to the filter's values - pub fn to_bloom_filter(&self) -> BloomFilter { - self.0.iter().map(|a| BloomInput::Raw(a.as_ref()).into()).collect::>().into() - } -} - -impl FilterSet { - /// Returns a ValueOrArray inside an Option, so that: - /// - If the filter is empty, it returns None - /// - If the filter has only 1 value, it returns the single value - /// - Otherwise it returns an array of values - /// This should be useful for serialization - pub fn to_value_or_array(&self) -> Option> { - let mut values = self.0.iter().cloned().collect::>(); - match values.len() { - 0 => None, - 1 => Some(ValueOrArray::Value(values.pop().expect("values length is one"))), - _ => Some(ValueOrArray::Array(values)), - } - } -} - -/// A single topic -pub type Topic = FilterSet; - -impl From for Topic { - fn from(src: U256) -> Self { - Into::::into(src).into() - } -} - -/// Represents the target range of blocks for the filter -#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] -pub enum FilterBlockOption { - /// Represents a range of blocks with optional from and to blocks - /// - /// Note: ranges are considered to be __inclusive__ - Range { - /// The block number or tag this filter should start at. - from_block: Option, - /// The block number or that this filter should end at. - to_block: Option, - }, - /// The hash of the block if the filter only targets a single block - AtBlockHash(B256), -} - -impl FilterBlockOption { - /// Returns the `fromBlock` value, if any - pub fn get_to_block(&self) -> Option<&BlockNumberOrTag> { - match self { - FilterBlockOption::Range { to_block, .. } => to_block.as_ref(), - FilterBlockOption::AtBlockHash(_) => None, - } - } - - /// Returns the `toBlock` value, if any - pub fn get_from_block(&self) -> Option<&BlockNumberOrTag> { - match self { - FilterBlockOption::Range { from_block, .. } => from_block.as_ref(), - FilterBlockOption::AtBlockHash(_) => None, - } - } - - /// Returns the range (`fromBlock`, `toBlock`) if this is a range filter. - pub fn as_range(&self) -> (Option<&BlockNumberOrTag>, Option<&BlockNumberOrTag>) { - match self { - FilterBlockOption::Range { from_block, to_block } => { - (from_block.as_ref(), to_block.as_ref()) - } - FilterBlockOption::AtBlockHash(_) => (None, None), - } - } -} - -impl From for FilterBlockOption { - fn from(block: BlockNumberOrTag) -> Self { - let block = Some(block); - FilterBlockOption::Range { from_block: block, to_block: block } - } -} - -impl From for FilterBlockOption { - fn from(block: U64) -> Self { - BlockNumberOrTag::from(block).into() - } -} - -impl From for FilterBlockOption { - fn from(block: u64) -> Self { - BlockNumberOrTag::from(block).into() - } -} - -impl> From> for FilterBlockOption { - fn from(r: Range) -> Self { - let from_block = Some(r.start.into()); - let to_block = Some(r.end.into()); - FilterBlockOption::Range { from_block, to_block } - } -} - -impl> From> for FilterBlockOption { - fn from(r: RangeTo) -> Self { - let to_block = Some(r.end.into()); - FilterBlockOption::Range { from_block: Some(BlockNumberOrTag::Earliest), to_block } - } -} - -impl> From> for FilterBlockOption { - fn from(r: RangeFrom) -> Self { - let from_block = Some(r.start.into()); - FilterBlockOption::Range { from_block, to_block: Some(BlockNumberOrTag::Latest) } - } -} - -impl From for FilterBlockOption { - fn from(hash: B256) -> Self { - FilterBlockOption::AtBlockHash(hash) - } -} - -impl Default for FilterBlockOption { - fn default() -> Self { - FilterBlockOption::Range { from_block: None, to_block: None } - } -} - -impl FilterBlockOption { - /// Sets the block number this range filter should start at. - #[must_use] - pub fn set_from_block(&self, block: BlockNumberOrTag) -> Self { - let to_block = - if let FilterBlockOption::Range { to_block, .. } = self { *to_block } else { None }; - - FilterBlockOption::Range { from_block: Some(block), to_block } - } - - /// Sets the block number this range filter should end at. - #[must_use] - pub fn set_to_block(&self, block: BlockNumberOrTag) -> Self { - let from_block = - if let FilterBlockOption::Range { from_block, .. } = self { *from_block } else { None }; - - FilterBlockOption::Range { from_block, to_block: Some(block) } - } - - /// Pins the block hash this filter should target. - #[must_use] - pub fn set_hash(&self, hash: B256) -> Self { - FilterBlockOption::AtBlockHash(hash) - } -} - -/// Filter for -#[derive(Default, Debug, PartialEq, Eq, Clone)] -pub struct Filter { - /// Filter block options, specifying on which blocks the filter should - /// match. - // https://eips.ethereum.org/EIPS/eip-234 - pub block_option: FilterBlockOption, - /// Address - pub address: FilterSet
, - /// Topics (maxmimum of 4) - pub topics: [Topic; 4], -} - -impl Filter { - /// Creates a new, empty filter - pub fn new() -> Self { - Self::default() - } - - /// Sets the inner filter object - /// - /// *NOTE:* ranges are always inclusive - /// - /// # Examples - /// - /// Match only a specific block - /// - /// ```rust - /// # use reth_rpc_types::Filter; - /// # fn main() { - /// let filter = Filter::new().select(69u64); - /// # } - /// ``` - /// This is the same as `Filter::new().from_block(1337u64).to_block(1337u64)` - /// - /// Match the latest block only - /// - /// ```rust - /// # use reth_rpc_types::BlockNumberOrTag; - /// # use reth_rpc_types::Filter; - /// # fn main() { - /// let filter = Filter::new().select(BlockNumberOrTag::Latest); - /// # } - /// ``` - /// - /// Match a block by its hash - /// - /// ```rust - /// # use alloy_primitives::B256; - /// # use reth_rpc_types::Filter; - /// # fn main() { - /// let filter = Filter::new().select(B256::ZERO); - /// # } - /// ``` - /// This is the same as `at_block_hash` - /// - /// Match a range of blocks - /// - /// ```rust - /// # use reth_rpc_types::Filter; - /// # fn main() { - /// let filter = Filter::new().select(0u64..100u64); - /// # } - /// ``` - /// - /// Match all blocks in range `(1337..BlockNumberOrTag::Latest)` - /// - /// ```rust - /// # use reth_rpc_types::Filter; - /// # fn main() { - /// let filter = Filter::new().select(1337u64..); - /// # } - /// ``` - /// - /// Match all blocks in range `(BlockNumberOrTag::Earliest..1337)` - /// - /// ```rust - /// # use reth_rpc_types::Filter; - /// # fn main() { - /// let filter = Filter::new().select(..1337u64); - /// # } - /// ``` - #[must_use] - pub fn select(mut self, filter: impl Into) -> Self { - self.block_option = filter.into(); - self - } - - /// Sets the from block number - #[allow(clippy::wrong_self_convention)] - #[must_use] - pub fn from_block>(mut self, block: T) -> Self { - self.block_option = self.block_option.set_from_block(block.into()); - self - } - - /// Sets the to block number - #[allow(clippy::wrong_self_convention)] - #[must_use] - pub fn to_block>(mut self, block: T) -> Self { - self.block_option = self.block_option.set_to_block(block.into()); - self - } - - /// Pins the block hash for the filter - #[must_use] - pub fn at_block_hash>(mut self, hash: T) -> Self { - self.block_option = self.block_option.set_hash(hash.into()); - self - } - /// Sets the inner filter object - /// - /// *NOTE:* ranges are always inclusive - /// - /// # Examples - /// - /// Match only a specific address `("0xAc4b3DacB91461209Ae9d41EC517c2B9Cb1B7DAF")` - /// - /// ```rust - /// # use alloy_primitives::Address; - /// # use reth_rpc_types::Filter; - /// # fn main() { - /// let filter = Filter::new() - /// .address("0xAc4b3DacB91461209Ae9d41EC517c2B9Cb1B7DAF".parse::
().unwrap()); - /// # } - /// ``` - /// - /// Match all addresses in array `(vec!["0xAc4b3DacB91461209Ae9d41EC517c2B9Cb1B7DAF", - /// "0x8ad599c3A0ff1De082011EFDDc58f1908eb6e6D8"])` - /// - /// ```rust - /// # use alloy_primitives::Address; - /// # use reth_rpc_types::Filter; - /// # fn main() { - /// let addresses = vec![ - /// "0xAc4b3DacB91461209Ae9d41EC517c2B9Cb1B7DAF".parse::
().unwrap(), - /// "0x8ad599c3A0ff1De082011EFDDc58f1908eb6e6D8".parse::
().unwrap(), - /// ]; - /// let filter = Filter::new().address(addresses); - /// # } - /// ``` - #[must_use] - pub fn address>>(mut self, address: T) -> Self { - self.address = address.into().into(); - self - } - - /// Given the event signature in string form, it hashes it and adds it to the topics to monitor - #[must_use] - pub fn event(self, event_name: &str) -> Self { - let hash = keccak256(event_name.as_bytes()); - self.event_signature(hash) - } - - /// Hashes all event signatures and sets them as array to event_signature(topic0) - #[must_use] - pub fn events(self, events: impl IntoIterator>) -> Self { - let events = events.into_iter().map(|e| keccak256(e.as_ref())).collect::>(); - self.event_signature(events) - } - - /// Sets event_signature(topic0) (the event name for non-anonymous events) - #[must_use] - pub fn event_signature>(mut self, topic: T) -> Self { - self.topics[0] = topic.into(); - self - } - - /// Sets topic0 (the event name for non-anonymous events) - #[must_use] - #[deprecated(note = "use `event_signature` instead")] - pub fn topic0>(mut self, topic: T) -> Self { - self.topics[0] = topic.into(); - self - } - - /// Sets the 1st indexed topic - #[must_use] - pub fn topic1>(mut self, topic: T) -> Self { - self.topics[1] = topic.into(); - self - } - - /// Sets the 2nd indexed topic - #[must_use] - pub fn topic2>(mut self, topic: T) -> Self { - self.topics[2] = topic.into(); - self - } - - /// Sets the 3rd indexed topic - #[must_use] - pub fn topic3>(mut self, topic: T) -> Self { - self.topics[3] = topic.into(); - self - } - - /// Returns true if this is a range filter and has a from block - pub fn is_paginatable(&self) -> bool { - self.get_from_block().is_some() - } - - /// Returns the numeric value of the `toBlock` field - pub fn get_to_block(&self) -> Option { - self.block_option.get_to_block().and_then(|b| b.as_number()) - } - - /// Returns the numeric value of the `fromBlock` field - pub fn get_from_block(&self) -> Option { - self.block_option.get_from_block().and_then(|b| b.as_number()) - } - - /// Returns the numeric value of the `fromBlock` field - pub fn get_block_hash(&self) -> Option { - match self.block_option { - FilterBlockOption::AtBlockHash(hash) => Some(hash), - FilterBlockOption::Range { .. } => None, - } - } - - /// Returns true if at least one topic is set - pub fn has_topics(&self) -> bool { - self.topics.iter().any(|t| !t.is_empty()) - } -} - -impl Serialize for Filter { - fn serialize(&self, serializer: S) -> Result - where - S: Serializer, - { - let mut s = serializer.serialize_struct("Filter", 5)?; - match self.block_option { - FilterBlockOption::Range { from_block, to_block } => { - if let Some(ref from_block) = from_block { - s.serialize_field("fromBlock", from_block)?; - } - - if let Some(ref to_block) = to_block { - s.serialize_field("toBlock", to_block)?; - } - } - - FilterBlockOption::AtBlockHash(ref h) => s.serialize_field("blockHash", h)?, - } - - if let Some(address) = self.address.to_value_or_array() { - s.serialize_field("address", &address)?; - } - - let mut filtered_topics = Vec::new(); - let mut filtered_topics_len = 0; - for (i, topic) in self.topics.iter().enumerate() { - if !topic.is_empty() { - filtered_topics_len = i + 1; - } - filtered_topics.push(topic.to_value_or_array()); - } - filtered_topics.truncate(filtered_topics_len); - s.serialize_field("topics", &filtered_topics)?; - - s.end() - } -} - -type RawAddressFilter = ValueOrArray>; -type RawTopicsFilter = Vec>>>; - -impl<'de> Deserialize<'de> for Filter { - fn deserialize(deserializer: D) -> Result - where - D: Deserializer<'de>, - { - struct FilterVisitor; - - impl<'de> Visitor<'de> for FilterVisitor { - type Value = Filter; - - fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - formatter.write_str("Filter object") - } - - fn visit_map(self, mut map: A) -> Result - where - A: MapAccess<'de>, - { - let mut from_block: Option> = None; - let mut to_block: Option> = None; - let mut block_hash: Option> = None; - let mut address: Option> = None; - let mut topics: Option> = None; - - while let Some(key) = map.next_key::()? { - match key.as_str() { - "fromBlock" => { - if from_block.is_some() { - return Err(serde::de::Error::duplicate_field("fromBlock")) - } - if block_hash.is_some() { - return Err(serde::de::Error::custom( - "fromBlock not allowed with blockHash", - )) - } - from_block = Some(map.next_value()?) - } - "toBlock" => { - if to_block.is_some() { - return Err(serde::de::Error::duplicate_field("toBlock")) - } - if block_hash.is_some() { - return Err(serde::de::Error::custom( - "toBlock not allowed with blockHash", - )) - } - to_block = Some(map.next_value()?) - } - "blockHash" => { - if block_hash.is_some() { - return Err(serde::de::Error::duplicate_field("blockHash")) - } - if from_block.is_some() || to_block.is_some() { - return Err(serde::de::Error::custom( - "fromBlock,toBlock not allowed with blockHash", - )) - } - block_hash = Some(map.next_value()?) - } - "address" => { - if address.is_some() { - return Err(serde::de::Error::duplicate_field("address")) - } - address = Some(map.next_value()?) - } - "topics" => { - if topics.is_some() { - return Err(serde::de::Error::duplicate_field("topics")) - } - topics = Some(map.next_value()?) - } - - key => { - return Err(serde::de::Error::unknown_field( - key, - &["fromBlock", "toBlock", "address", "topics", "blockHash"], - )) - } - } - } - - let from_block = from_block.unwrap_or_default(); - let to_block = to_block.unwrap_or_default(); - let block_hash = block_hash.unwrap_or_default(); - let address = address.flatten().map(|a| a.into()).unwrap_or_default(); - let topics_vec = topics.flatten().unwrap_or_default(); - - // maximum allowed filter len - if topics_vec.len() > 4 { - return Err(serde::de::Error::custom("exceeded maximum topics len")) - } - let mut topics: [Topic; 4] = [ - Default::default(), - Default::default(), - Default::default(), - Default::default(), - ]; - for (idx, topic) in topics_vec.into_iter().enumerate() { - topics[idx] = topic.map(|t| t.into()).unwrap_or_default(); - } - - let block_option = if let Some(block_hash) = block_hash { - FilterBlockOption::AtBlockHash(block_hash) - } else { - FilterBlockOption::Range { from_block, to_block } - }; - - Ok(Filter { block_option, address, topics }) - } - } - - deserializer.deserialize_any(FilterVisitor) - } -} - -/// Union type for representing a single value or a vector of values inside a filter -#[derive(Debug, PartialEq, Eq, Clone, Hash)] -pub enum ValueOrArray { - /// A single value - Value(T), - /// A vector of values - Array(Vec), -} - -impl From
for ValueOrArray
{ - fn from(src: Address) -> Self { - ValueOrArray::Value(src) - } -} - -impl From> for ValueOrArray
{ - fn from(src: Vec
) -> Self { - ValueOrArray::Array(src) - } -} - -impl From> for ValueOrArray { - fn from(src: Vec) -> Self { - ValueOrArray::Array(src) - } -} - -impl Serialize for ValueOrArray -where - T: Serialize, -{ - fn serialize(&self, serializer: S) -> Result - where - S: Serializer, - { - match self { - ValueOrArray::Value(inner) => inner.serialize(serializer), - ValueOrArray::Array(inner) => inner.serialize(serializer), - } - } -} - -impl<'a, T> Deserialize<'a> for ValueOrArray -where - T: DeserializeOwned, -{ - fn deserialize(deserializer: D) -> Result, D::Error> - where - D: Deserializer<'a>, - { - let value = serde_json::Value::deserialize(deserializer)?; - - if value.is_null() { - return Ok(ValueOrArray::Array(Vec::new())) - } - - #[derive(Deserialize)] - #[serde(untagged)] - enum Variadic { - Value(T), - Array(Vec), - } - - match serde_json::from_value::>(value).map_err(|err| { - serde::de::Error::custom(format!("Invalid variadic value or array type: {err}")) - })? { - Variadic::Value(val) => Ok(ValueOrArray::Value(val)), - Variadic::Array(arr) => Ok(ValueOrArray::Array(arr)), - } - } -} - -/// Support for matching [Filter]s -#[derive(Debug, Default)] -pub struct FilteredParams { - /// The original filter, if any - pub filter: Option, -} - -impl FilteredParams { - /// Creates a new wrapper type for a [Filter], if any with flattened topics, that can be used - /// for matching - pub fn new(filter: Option) -> Self { - if let Some(filter) = filter { - FilteredParams { filter: Some(filter) } - } else { - Default::default() - } - } - - /// Returns the [BloomFilter] for the given address - pub fn address_filter(address: &FilterSet
) -> BloomFilter { - address.to_bloom_filter() - } - - /// Returns the [BloomFilter] for the given topics - pub fn topics_filter(topics: &[FilterSet]) -> Vec { - topics.iter().map(|t| t.to_bloom_filter()).collect() - } - - /// Returns `true` if the bloom matches the topics - pub fn matches_topics(bloom: Bloom, topic_filters: &[BloomFilter]) -> bool { - if topic_filters.is_empty() { - return true - } - - // for each filter, iterate through the list of filter blooms. for each set of filter - // (each BloomFilter), the given `bloom` must match at least one of them, unless the list is - // empty (no filters). - for filter in topic_filters.iter() { - if !filter.matches(bloom) { - return false - } - } - true - } - - /// Returns `true` if the bloom contains one of the address blooms, or the address blooms - /// list is empty (thus, no filters) - pub fn matches_address(bloom: Bloom, address_filter: &BloomFilter) -> bool { - address_filter.matches(bloom) - } - - /// Returns true if the filter matches the given block number - pub fn filter_block_range(&self, block_number: u64) -> bool { - if self.filter.is_none() { - return true - } - let filter = self.filter.as_ref().unwrap(); - let mut res = true; - - if let Some(BlockNumberOrTag::Number(num)) = filter.block_option.get_from_block() { - if *num > block_number { - res = false; - } - } - - if let Some(to) = filter.block_option.get_to_block() { - match to { - BlockNumberOrTag::Number(num) => { - if *num < block_number { - res = false; - } - } - BlockNumberOrTag::Earliest => { - res = false; - } - _ => {} - } - } - res - } - - /// Returns `true` if the filter matches the given block hash. - pub fn filter_block_hash(&self, block_hash: B256) -> bool { - if let Some(h) = self.filter.as_ref().and_then(|f| f.get_block_hash()) { - if h != block_hash { - return false - } - } - true - } - - /// Returns `true` if the filter matches the given log. - pub fn filter_address(&self, log: &Log) -> bool { - self.filter.as_ref().map(|f| f.address.matches(&log.address)).unwrap_or(true) - } - - /// Returns `true` if the log matches the filter's topics - pub fn filter_topics(&self, log: &Log) -> bool { - let topics = match self.filter.as_ref() { - None => return true, - Some(f) => &f.topics, - }; - for topic_tuple in topics.iter().zip_longest(log.topics.iter()) { - match topic_tuple { - // We exhausted the `log.topics`, so if there's a filter set for - // this topic index, there is no match. Otherwise (empty filter), continue. - Left(filter_topic) => { - if !filter_topic.is_empty() { - return false - } - } - // We exhausted the filter topics, therefore any subsequent log topic - // will match. - Right(_) => return true, - // Check that `log_topic` is included in `filter_topic` - Both(filter_topic, log_topic) => { - if !filter_topic.matches(log_topic) { - return false - } - } - } - } - true - } -} -/// Response of the `eth_getFilterChanges` RPC. -#[derive(Clone, Debug, Eq, PartialEq)] -pub enum FilterChanges { - /// New logs. - Logs(Vec), - /// New hashes (block or transactions) - Hashes(Vec), - /// New transactions. - Transactions(Vec), - /// Empty result, - Empty, -} - -impl Serialize for FilterChanges { - fn serialize(&self, s: S) -> Result - where - S: Serializer, - { - match self { - FilterChanges::Logs(logs) => logs.serialize(s), - FilterChanges::Hashes(hashes) => hashes.serialize(s), - FilterChanges::Transactions(transactions) => transactions.serialize(s), - FilterChanges::Empty => (&[] as &[serde_json::Value]).serialize(s), - } - } -} - -impl<'de> Deserialize<'de> for FilterChanges { - fn deserialize(deserializer: D) -> Result - where - D: Deserializer<'de>, - { - #[derive(Deserialize)] - #[serde(untagged)] - enum Changes { - Logs(Vec), - Hashes(Vec), - } - - let changes = Changes::deserialize(deserializer)?; - let changes = match changes { - Changes::Logs(vals) => { - if vals.is_empty() { - FilterChanges::Empty - } else { - FilterChanges::Logs(vals) - } - } - Changes::Hashes(vals) => { - if vals.is_empty() { - FilterChanges::Empty - } else { - FilterChanges::Hashes(vals) - } - } - }; - Ok(changes) - } -} - -/// Owned equivalent of a `SubscriptionId` -#[derive(Debug, PartialEq, Clone, Hash, Eq, Deserialize, Serialize)] -#[serde(deny_unknown_fields)] -#[serde(untagged)] -pub enum FilterId { - /// Numeric id - Num(u64), - /// String id - Str(String), -} - -#[cfg(feature = "jsonrpsee-types")] -impl From for jsonrpsee_types::SubscriptionId<'_> { - fn from(value: FilterId) -> Self { - match value { - FilterId::Num(n) => jsonrpsee_types::SubscriptionId::Num(n), - FilterId::Str(s) => jsonrpsee_types::SubscriptionId::Str(s.into()), - } - } -} - -#[cfg(feature = "jsonrpsee-types")] -impl From> for FilterId { - fn from(value: jsonrpsee_types::SubscriptionId<'_>) -> Self { - match value { - jsonrpsee_types::SubscriptionId::Num(n) => FilterId::Num(n), - jsonrpsee_types::SubscriptionId::Str(s) => FilterId::Str(s.into_owned()), - } - } -} -/// Specifies the kind of information you wish to receive from the `eth_newPendingTransactionFilter` -/// RPC endpoint. -/// -/// When this type is used in a request, it determines whether the client wishes to receive: -/// - Only the transaction hashes (`Hashes` variant), or -/// - Full transaction details (`Full` variant). -#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)] -pub enum PendingTransactionFilterKind { - /// Receive only the hashes of the transactions. - #[default] - Hashes, - /// Receive full details of the transactions. - Full, -} - -impl Serialize for PendingTransactionFilterKind { - /// Serializes the `PendingTransactionFilterKind` into a boolean value: - /// - `false` for `Hashes` - /// - `true` for `Full` - fn serialize(&self, serializer: S) -> Result - where - S: Serializer, - { - match self { - PendingTransactionFilterKind::Hashes => false.serialize(serializer), - PendingTransactionFilterKind::Full => true.serialize(serializer), - } - } -} - -impl<'a> Deserialize<'a> for PendingTransactionFilterKind { - /// Deserializes a boolean value into `PendingTransactionFilterKind`: - /// - `false` becomes `Hashes` - /// - `true` becomes `Full` - fn deserialize(deserializer: D) -> Result - where - D: Deserializer<'a>, - { - let val = Option::::deserialize(deserializer)?; - match val { - Some(true) => Ok(PendingTransactionFilterKind::Full), - _ => Ok(PendingTransactionFilterKind::Hashes), - } - } -} - -#[cfg(test)] -mod tests { - use super::*; - use alloy_primitives::U256; - use serde_json::json; - - fn serialize(t: &T) -> serde_json::Value { - serde_json::to_value(t).expect("Failed to serialize value") - } - - #[test] - fn test_empty_filter_topics_list() { - let s = r#"{"fromBlock": "0xfc359e", "toBlock": "0xfc359e", "topics": [["0x8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925"], [], ["0x0000000000000000000000000c17e776cd218252adfca8d4e761d3fe757e9778"]]}"#; - let filter = serde_json::from_str::(s).unwrap(); - similar_asserts::assert_eq!( - filter.topics, - [ - "0x8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925" - .parse::() - .unwrap() - .into(), - Default::default(), - "0x0000000000000000000000000c17e776cd218252adfca8d4e761d3fe757e9778" - .parse::() - .unwrap() - .into(), - Default::default(), - ] - ); - } - - #[test] - fn test_block_hash() { - let s = - r#"{"blockHash":"0x58dc57ab582b282c143424bd01e8d923cddfdcda9455bad02a29522f6274a948"}"#; - let filter = serde_json::from_str::(s).unwrap(); - similar_asserts::assert_eq!( - filter.block_option, - FilterBlockOption::AtBlockHash( - "0x58dc57ab582b282c143424bd01e8d923cddfdcda9455bad02a29522f6274a948" - .parse() - .unwrap() - ) - ); - } - - #[test] - fn test_filter_topics_middle_wildcard() { - let s = r#"{"fromBlock": "0xfc359e", "toBlock": "0xfc359e", "topics": [["0x8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925"], [], [null, "0x0000000000000000000000000c17e776cd218252adfca8d4e761d3fe757e9778"]]}"#; - let filter = serde_json::from_str::(s).unwrap(); - similar_asserts::assert_eq!( - filter.topics, - [ - "0x8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925" - .parse::() - .unwrap() - .into(), - Default::default(), - Default::default(), - Default::default(), - ] - ); - } - - #[test] - fn can_serde_value_or_array() { - #[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)] - struct Item { - value: ValueOrArray, - } - - let item = Item { value: ValueOrArray::Value(U256::from(1u64)) }; - let json = serde_json::to_value(item.clone()).unwrap(); - let deserialized: Item = serde_json::from_value(json).unwrap(); - assert_eq!(item, deserialized); - - let item = Item { value: ValueOrArray::Array(vec![U256::from(1u64), U256::ZERO]) }; - let json = serde_json::to_value(item.clone()).unwrap(); - let deserialized: Item = serde_json::from_value(json).unwrap(); - assert_eq!(item, deserialized); - } - - #[test] - fn filter_serialization_test() { - let t1 = "0000000000000000000000009729a6fbefefc8f6005933898b13dc45c3a2c8b7" - .parse::() - .unwrap(); - let t2 = B256::from([0; 32]); - let t3 = U256::from(123); - - let t1_padded = t1; - let t3_padded = B256::from({ - let mut x = [0; 32]; - x[31] = 123; - x - }); - - let event = "ValueChanged(address,string,string)"; - let t0 = keccak256(event.as_bytes()); - let addr: Address = "f817796F60D268A36a57b8D2dF1B97B14C0D0E1d".parse().unwrap(); - let filter = Filter::new(); - - let ser = serialize(&filter); - assert_eq!(ser, json!({ "topics": [] })); - - let filter = filter.address(ValueOrArray::Value(addr)); - - let ser = serialize(&filter); - assert_eq!(ser, json!({"address" : addr, "topics": []})); - - let filter = filter.event(event); - - // 0 - let ser = serialize(&filter); - assert_eq!(ser, json!({ "address" : addr, "topics": [t0]})); - - // 1 - let ser = serialize(&filter.clone().topic1(t1)); - assert_eq!(ser, json!({ "address" : addr, "topics": [t0, t1_padded]})); - - // 2 - let ser = serialize(&filter.clone().topic2(t2)); - assert_eq!(ser, json!({ "address" : addr, "topics": [t0, null, t2]})); - - // 3 - let ser = serialize(&filter.clone().topic3(t3)); - assert_eq!(ser, json!({ "address" : addr, "topics": [t0, null, null, t3_padded]})); - - // 1 & 2 - let ser = serialize(&filter.clone().topic1(t1).topic2(t2)); - assert_eq!(ser, json!({ "address" : addr, "topics": [t0, t1_padded, t2]})); - - // 1 & 3 - let ser = serialize(&filter.clone().topic1(t1).topic3(t3)); - assert_eq!(ser, json!({ "address" : addr, "topics": [t0, t1_padded, null, t3_padded]})); - - // 2 & 3 - let ser = serialize(&filter.clone().topic2(t2).topic3(t3)); - assert_eq!(ser, json!({ "address" : addr, "topics": [t0, null, t2, t3_padded]})); - - // 1 & 2 & 3 - let ser = serialize(&filter.topic1(t1).topic2(t2).topic3(t3)); - assert_eq!(ser, json!({ "address" : addr, "topics": [t0, t1_padded, t2, t3_padded]})); - } - - fn build_bloom(address: Address, topic1: B256, topic2: B256) -> Bloom { - let mut block_bloom = Bloom::default(); - block_bloom.accrue(BloomInput::Raw(&address[..])); - block_bloom.accrue(BloomInput::Raw(&topic1[..])); - block_bloom.accrue(BloomInput::Raw(&topic2[..])); - block_bloom - } - - fn topic_filter(topic1: B256, topic2: B256, topic3: B256) -> Filter { - Filter { - block_option: Default::default(), - address: Default::default(), - topics: [ - topic1.into(), - vec![topic2, topic3].into(), - Default::default(), - Default::default(), - ], - } - } - - #[test] - fn can_detect_different_topics() { - let topic1 = B256::random(); - let topic2 = B256::random(); - let topic3 = B256::random(); - - let topics = topic_filter(topic1, topic2, topic3).topics; - let topics_bloom = FilteredParams::topics_filter(&topics); - assert!(!FilteredParams::matches_topics( - build_bloom(Address::random(), B256::random(), B256::random()), - &topics_bloom - )); - } - - #[test] - fn can_match_topic() { - let topic1 = B256::random(); - let topic2 = B256::random(); - let topic3 = B256::random(); - - let topics = topic_filter(topic1, topic2, topic3).topics; - let _topics_bloom = FilteredParams::topics_filter(&topics); - - let topics_bloom = FilteredParams::topics_filter(&topics); - assert!(FilteredParams::matches_topics( - build_bloom(Address::random(), topic1, topic2), - &topics_bloom - )); - } - - #[test] - fn can_match_empty_topics() { - let filter = Filter { - block_option: Default::default(), - address: Default::default(), - topics: Default::default(), - }; - let topics = filter.topics; - - let topics_bloom = FilteredParams::topics_filter(&topics); - assert!(FilteredParams::matches_topics( - build_bloom(Address::random(), B256::random(), B256::random()), - &topics_bloom - )); - } - - #[test] - fn can_match_address_and_topics() { - let rng_address = Address::random(); - let topic1 = B256::random(); - let topic2 = B256::random(); - let topic3 = B256::random(); - - let filter = Filter { - block_option: Default::default(), - address: rng_address.into(), - topics: [ - topic1.into(), - vec![topic2, topic3].into(), - Default::default(), - Default::default(), - ], - }; - let topics = filter.topics; - - let address_filter = FilteredParams::address_filter(&filter.address); - let topics_filter = FilteredParams::topics_filter(&topics); - assert!( - FilteredParams::matches_address( - build_bloom(rng_address, topic1, topic2), - &address_filter - ) && FilteredParams::matches_topics( - build_bloom(rng_address, topic1, topic2), - &topics_filter - ) - ); - } - - #[test] - fn can_match_topics_wildcard() { - let topic1 = B256::random(); - let topic2 = B256::random(); - let topic3 = B256::random(); - - let filter = Filter { - block_option: Default::default(), - address: Default::default(), - topics: [ - Default::default(), - vec![topic2, topic3].into(), - Default::default(), - Default::default(), - ], - }; - let topics = filter.topics; - - let topics_bloom = FilteredParams::topics_filter(&topics); - assert!(FilteredParams::matches_topics( - build_bloom(Address::random(), topic1, topic2), - &topics_bloom - )); - } - - #[test] - fn can_match_topics_wildcard_mismatch() { - let filter = Filter { - block_option: Default::default(), - address: Default::default(), - topics: [ - Default::default(), - vec![B256::random(), B256::random()].into(), - Default::default(), - Default::default(), - ], - }; - let topics_input = filter.topics; - - let topics_bloom = FilteredParams::topics_filter(&topics_input); - assert!(!FilteredParams::matches_topics( - build_bloom(Address::random(), B256::random(), B256::random()), - &topics_bloom - )); - } - - #[test] - fn can_match_address_filter() { - let rng_address = Address::random(); - let filter = Filter { - block_option: Default::default(), - address: rng_address.into(), - topics: Default::default(), - }; - let address_bloom = FilteredParams::address_filter(&filter.address); - assert!(FilteredParams::matches_address( - build_bloom(rng_address, B256::random(), B256::random(),), - &address_bloom - )); - } - - #[test] - fn can_detect_different_address() { - let bloom_address = Address::random(); - let rng_address = Address::random(); - let filter = Filter { - block_option: Default::default(), - address: rng_address.into(), - topics: Default::default(), - }; - let address_bloom = FilteredParams::address_filter(&filter.address); - assert!(!FilteredParams::matches_address( - build_bloom(bloom_address, B256::random(), B256::random(),), - &address_bloom - )); - } - - #[test] - fn can_convert_to_ethers_filter() { - let json = json!( - { - "fromBlock": "0x429d3b", - "toBlock": "0x429d3b", - "address": "0xb59f67a8bff5d8cd03f6ac17265c550ed8f33907", - "topics": [ - "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef", - "0x00000000000000000000000000b46c2526e227482e2ebb8f4c69e4674d262e75", - "0x00000000000000000000000054a2d42a40f51259dedd1978f6c118a0f0eff078" - ] - } - ); - - let filter: Filter = serde_json::from_value(json).unwrap(); - assert_eq!( - filter, - Filter { - block_option: FilterBlockOption::Range { - from_block: Some(4365627u64.into()), - to_block: Some(4365627u64.into()), - }, - address: "0xb59f67a8bff5d8cd03f6ac17265c550ed8f33907" - .parse::
() - .unwrap() - .into(), - topics: [ - "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef" - .parse::() - .unwrap() - .into(), - "0x00000000000000000000000000b46c2526e227482e2ebb8f4c69e4674d262e75" - .parse::() - .unwrap() - .into(), - "0x00000000000000000000000054a2d42a40f51259dedd1978f6c118a0f0eff078" - .parse::() - .unwrap() - .into(), - Default::default(), - ], - } - ); - } - - #[test] - fn can_convert_to_ethers_filter_with_null_fields() { - let json = json!( - { - "fromBlock": "0x429d3b", - "toBlock": "0x429d3b", - "address": null, - "topics": null - } - ); - - let filter: Filter = serde_json::from_value(json).unwrap(); - assert_eq!( - filter, - Filter { - block_option: FilterBlockOption::Range { - from_block: Some(4365627u64.into()), - to_block: Some(4365627u64.into()), - }, - address: Default::default(), - topics: Default::default(), - } - ); - } -} diff --git a/crates/rpc/rpc-types/src/eth/index.rs b/crates/rpc/rpc-types/src/eth/index.rs deleted file mode 100644 index 151352171..000000000 --- a/crates/rpc/rpc-types/src/eth/index.rs +++ /dev/null @@ -1,104 +0,0 @@ -use alloy_primitives::U256; -use serde::{ - de::{Error, Visitor}, - Deserialize, Deserializer, Serialize, Serializer, -}; -use std::fmt; - -/// A hex encoded or decimal index that's intended to be used as a rust index, hence it's -/// deserialized into a `usize`. -#[derive(Debug, PartialEq, Eq, Hash, Clone, Copy, Default)] -pub struct Index(usize); - -impl From for usize { - fn from(idx: Index) -> Self { - idx.0 - } -} - -impl From for U256 { - fn from(idx: Index) -> Self { - U256::from(idx.0) - } -} - -impl From for Index { - fn from(idx: usize) -> Self { - Index(idx) - } -} - -impl Serialize for Index { - fn serialize(&self, serializer: S) -> Result - where - S: Serializer, - { - serializer.serialize_str(&format!("0x{:x}", self.0)) - } -} - -impl<'a> Deserialize<'a> for Index { - fn deserialize(deserializer: D) -> Result - where - D: Deserializer<'a>, - { - struct IndexVisitor; - - impl<'a> Visitor<'a> for IndexVisitor { - type Value = Index; - - fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(formatter, "hex-encoded or decimal index") - } - - fn visit_u64(self, value: u64) -> Result - where - E: Error, - { - Ok(Index(value as usize)) - } - - fn visit_str(self, value: &str) -> Result - where - E: Error, - { - if let Some(val) = value.strip_prefix("0x") { - usize::from_str_radix(val, 16).map(Index).map_err(|e| { - Error::custom(format!("Failed to parse hex encoded index value: {e}")) - }) - } else { - value - .parse::() - .map(Index) - .map_err(|e| Error::custom(format!("Failed to parse numeric index: {e}"))) - } - } - - fn visit_string(self, value: String) -> Result - where - E: Error, - { - self.visit_str(value.as_ref()) - } - } - - deserializer.deserialize_any(IndexVisitor) - } -} - -#[cfg(test)] -mod tests { - use super::*; - use rand::{thread_rng, Rng}; - - #[test] - fn test_serde_index_rand() { - let mut rng = thread_rng(); - for _ in 0..100 { - let index = Index(rng.gen()); - let val = serde_json::to_string(&index).unwrap(); - let de: Index = serde_json::from_str(&val).unwrap(); - assert_eq!(index, de); - } - } -} diff --git a/crates/rpc/rpc-types/src/eth/log.rs b/crates/rpc/rpc-types/src/eth/log.rs deleted file mode 100644 index 199af2497..000000000 --- a/crates/rpc/rpc-types/src/eth/log.rs +++ /dev/null @@ -1,55 +0,0 @@ -use alloy_primitives::{Address, Bytes, B256, U256}; -use serde::{Deserialize, Serialize}; - -/// Ethereum Log emitted by a transaction -#[derive(Debug, Clone, Eq, PartialEq, Hash, Serialize, Deserialize)] -#[serde(rename_all = "camelCase")] -pub struct Log { - /// Address - pub address: Address, - /// All topics of the log - pub topics: Vec, - /// Additional data fields of the log - pub data: Bytes, - /// Hash of the block the transaction that emitted this log was mined in - pub block_hash: Option, - /// Number of the block the transaction that emitted this log was mined in - pub block_number: Option, - /// Transaction Hash - pub transaction_hash: Option, - /// Index of the Transaction in the block - pub transaction_index: Option, - /// Log Index in Block - pub log_index: Option, - /// Geth Compatibility Field: whether this log was removed - #[serde(default)] - pub removed: bool, -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn serde_log() { - let log = Log { - address: Address::with_last_byte(0x69), - topics: vec![B256::with_last_byte(0x69)], - data: Bytes::from_static(&[0x69]), - block_hash: Some(B256::with_last_byte(0x69)), - block_number: Some(U256::from(0x69)), - transaction_hash: Some(B256::with_last_byte(0x69)), - transaction_index: Some(U256::from(0x69)), - log_index: Some(U256::from(0x69)), - removed: false, - }; - let serialized = serde_json::to_string(&log).unwrap(); - assert_eq!( - serialized, - r#"{"address":"0x0000000000000000000000000000000000000069","topics":["0x0000000000000000000000000000000000000000000000000000000000000069"],"data":"0x69","blockHash":"0x0000000000000000000000000000000000000000000000000000000000000069","blockNumber":"0x69","transactionHash":"0x0000000000000000000000000000000000000000000000000000000000000069","transactionIndex":"0x69","logIndex":"0x69","removed":false}"# - ); - - let deserialized: Log = serde_json::from_str(&serialized).unwrap(); - assert_eq!(log, deserialized); - } -} diff --git a/crates/rpc/rpc-types/src/eth/mod.rs b/crates/rpc/rpc-types/src/eth/mod.rs index a1a45d7b2..e313abb39 100644 --- a/crates/rpc/rpc-types/src/eth/mod.rs +++ b/crates/rpc/rpc-types/src/eth/mod.rs @@ -1,34 +1,4 @@ //! Ethereum related types -mod account; -mod block; -mod call; pub mod engine; -pub mod error; -mod fee; -mod filter; -mod index; -mod log; -pub mod pubsub; -pub mod raw_log; -pub mod state; -mod syncing; -pub mod trace; pub mod transaction; -pub mod txpool; -pub mod withdrawal; -mod work; - -pub use account::*; -pub use block::*; -pub use call::{Bundle, CallInput, CallInputError, CallRequest, EthCallResponse, StateContext}; -pub use engine::{ExecutionPayload, ExecutionPayloadV1, ExecutionPayloadV2, PayloadError}; -pub use fee::{FeeHistory, TxGasAndReward}; -pub use filter::*; -pub use index::Index; -pub use log::Log; -pub use raw_log::{logs_bloom, Log as RawLog}; -pub use syncing::*; -pub use transaction::*; -pub use withdrawal::Withdrawal; -pub use work::Work; diff --git a/crates/rpc/rpc-types/src/eth/pubsub.rs b/crates/rpc/rpc-types/src/eth/pubsub.rs deleted file mode 100644 index 66a8266fb..000000000 --- a/crates/rpc/rpc-types/src/eth/pubsub.rs +++ /dev/null @@ -1,169 +0,0 @@ -//! Ethereum types for pub-sub - -use crate::{ - eth::{Filter, Transaction}, - Log, RichHeader, -}; -use alloy_primitives::B256; -use serde::{de::Error, Deserialize, Deserializer, Serialize, Serializer}; - -/// Subscription result. -#[derive(Debug, Clone, PartialEq, Eq, Deserialize)] -#[serde(untagged)] -pub enum SubscriptionResult { - /// New block header. - Header(Box), - /// Log - Log(Box), - /// Transaction hash - TransactionHash(B256), - /// Full Transaction - FullTransaction(Box), - /// SyncStatus - SyncState(PubSubSyncStatus), -} - -/// Response type for a SyncStatus subscription -#[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize)] -#[serde(untagged)] -pub enum PubSubSyncStatus { - /// If not currently syncing, this should always be `false` - Simple(bool), - /// Current Stats about syncing - Detailed(SyncStatusMetadata), -} - -/// Sync status infos -#[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize)] -#[serde(rename_all = "camelCase")] -#[allow(missing_docs)] -pub struct SyncStatusMetadata { - pub syncing: bool, - pub starting_block: u64, - pub current_block: u64, - #[serde(default, skip_serializing_if = "Option::is_none")] - pub highest_block: Option, -} - -impl Serialize for SubscriptionResult { - fn serialize(&self, serializer: S) -> Result - where - S: Serializer, - { - match *self { - SubscriptionResult::Header(ref header) => header.serialize(serializer), - SubscriptionResult::Log(ref log) => log.serialize(serializer), - SubscriptionResult::TransactionHash(ref hash) => hash.serialize(serializer), - SubscriptionResult::FullTransaction(ref tx) => tx.serialize(serializer), - SubscriptionResult::SyncState(ref sync) => sync.serialize(serializer), - } - } -} - -/// Subscription kind. -#[derive(Debug, Serialize, Deserialize, PartialEq, Eq, Hash, Clone)] -#[serde(deny_unknown_fields)] -#[serde(rename_all = "camelCase")] -pub enum SubscriptionKind { - /// New block headers subscription. - /// - /// Fires a notification each time a new header is appended to the chain, including chain - /// reorganizations. In case of a chain reorganization the subscription will emit all new - /// headers for the new chain. Therefore the subscription can emit multiple headers on the same - /// height. - NewHeads, - /// Logs subscription. - /// - /// Returns logs that are included in new imported blocks and match the given filter criteria. - /// In case of a chain reorganization previous sent logs that are on the old chain will be - /// resent with the removed property set to true. Logs from transactions that ended up in the - /// new chain are emitted. Therefore, a subscription can emit logs for the same transaction - /// multiple times. - Logs, - /// New Pending Transactions subscription. - /// - /// Returns the hash or full tx for all transactions that are added to the pending state and - /// are signed with a key that is available in the node. When a transaction that was - /// previously part of the canonical chain isn't part of the new canonical chain after a - /// reorganization its again emitted. - NewPendingTransactions, - /// Node syncing status subscription. - /// - /// Indicates when the node starts or stops synchronizing. The result can either be a boolean - /// indicating that the synchronization has started (true), finished (false) or an object with - /// various progress indicators. - Syncing, -} - -/// Any additional parameters for a subscription. -#[derive(Debug, Clone, PartialEq, Eq, Default)] -pub enum Params { - /// No parameters passed. - #[default] - None, - /// Log parameters. - Logs(Box), - /// Boolean parameter for new pending transactions. - Bool(bool), -} - -impl Params { - /// Returns true if it's a bool parameter. - #[inline] - pub fn is_bool(&self) -> bool { - matches!(self, Params::Bool(_)) - } - - /// Returns true if it's a log parameter. - #[inline] - pub fn is_logs(&self) -> bool { - matches!(self, Params::Logs(_)) - } -} - -impl Serialize for Params { - fn serialize(&self, serializer: S) -> Result - where - S: Serializer, - { - match self { - Params::None => (&[] as &[serde_json::Value]).serialize(serializer), - Params::Logs(logs) => logs.serialize(serializer), - Params::Bool(full) => full.serialize(serializer), - } - } -} - -impl<'a> Deserialize<'a> for Params { - fn deserialize(deserializer: D) -> Result - where - D: Deserializer<'a>, - { - let v = serde_json::Value::deserialize(deserializer)?; - - if v.is_null() { - return Ok(Params::None) - } - - if let Some(val) = v.as_bool() { - return Ok(Params::Bool(val)) - } - - serde_json::from_value(v) - .map(|f| Params::Logs(Box::new(f))) - .map_err(|e| D::Error::custom(format!("Invalid Pub-Sub parameters: {e}"))) - } -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn params_serde() { - let s: Params = serde_json::from_str("true").unwrap(); - assert_eq!(s, Params::Bool(true)); - let s: Params = serde_json::from_str("null").unwrap(); - assert_eq!(s, Params::None); - } -} diff --git a/crates/rpc/rpc-types/src/eth/raw_log.rs b/crates/rpc/rpc-types/src/eth/raw_log.rs deleted file mode 100644 index 2a12903ec..000000000 --- a/crates/rpc/rpc-types/src/eth/raw_log.rs +++ /dev/null @@ -1,30 +0,0 @@ -//! Ethereum log object. - -use alloy_primitives::{Address, Bloom, Bytes, B256}; -use alloy_rlp::{RlpDecodable, RlpEncodable}; - -/// Ethereum Log -#[derive(Clone, Debug, PartialEq, Eq, RlpDecodable, RlpEncodable, Default)] -pub struct Log { - /// Contract that emitted this log. - pub address: Address, - /// Topics of the log. The number of logs depend on what `LOG` opcode is used. - pub topics: Vec, - /// Arbitrary length data. - pub data: Bytes, -} - -/// Calculate receipt logs bloom. -pub fn logs_bloom<'a, It>(logs: It) -> Bloom -where - It: IntoIterator, -{ - let mut bloom = Bloom::ZERO; - for log in logs { - bloom.m3_2048(log.address.as_slice()); - for topic in &log.topics { - bloom.m3_2048(topic.as_slice()); - } - } - bloom -} diff --git a/crates/rpc/rpc-types/src/eth/state.rs b/crates/rpc/rpc-types/src/eth/state.rs deleted file mode 100644 index dc9339322..000000000 --- a/crates/rpc/rpc-types/src/eth/state.rs +++ /dev/null @@ -1,69 +0,0 @@ -//! bindings for state overrides in eth_call - -use alloy_primitives::{Address, Bytes, B256, U256, U64}; -use serde::{Deserialize, Serialize}; -use std::collections::HashMap; - -/// A set of account overrides -pub type StateOverride = HashMap; - -/// Custom account override used in call -#[derive(Clone, Debug, PartialEq, Eq, Default, Serialize, Deserialize)] -#[serde(default, rename_all = "camelCase", deny_unknown_fields)] -#[allow(missing_docs)] -pub struct AccountOverride { - /// Fake balance to set for the account before executing the call. - #[serde(default, skip_serializing_if = "Option::is_none")] - pub balance: Option, - /// Fake nonce to set for the account before executing the call. - #[serde(default, skip_serializing_if = "Option::is_none")] - pub nonce: Option, - /// Fake EVM bytecode to inject into the account before executing the call. - #[serde(default, skip_serializing_if = "Option::is_none")] - pub code: Option, - /// Fake key-value mapping to override all slots in the account storage before executing the - /// call. - #[serde(default, skip_serializing_if = "Option::is_none")] - pub state: Option>, - /// Fake key-value mapping to override individual slots in the account storage before executing - /// the call. - #[serde(default, skip_serializing_if = "Option::is_none")] - pub state_diff: Option>, -} - -#[cfg(test)] -mod tests { - use super::*; - use alloy_primitives::address; - - #[test] - fn test_state_override() { - let s = r#"{ - "0x0000000000000000000000000000000000000124": { - "code": "0x6080604052348015600e575f80fd5b50600436106026575f3560e01c80632096525514602a575b5f80fd5b60306044565b604051901515815260200160405180910390f35b5f604e600242605e565b5f0360595750600190565b505f90565b5f82607757634e487b7160e01b5f52601260045260245ffd5b50069056fea2646970667358221220287f77a4262e88659e3fb402138d2ee6a7ff9ba86bae487a95aa28156367d09c64736f6c63430008140033" - } - }"#; - let state_override: StateOverride = serde_json::from_str(s).unwrap(); - let acc = - state_override.get(&address!("0000000000000000000000000000000000000124")).unwrap(); - assert!(acc.code.is_some()); - } - #[test] - fn test_state_override_state_diff() { - let s = r#"{ - "0x1b5212AF6b76113afD94cD2B5a78a73B7d7A8222": { - "balance": "0x39726378b58c400000", - "stateDiff": {} - }, - "0xdAC17F958D2ee523a2206206994597C13D831ec7": { - "stateDiff": { - "0xede27e4e7f3676edbf125879f17a896d6507958df3d57bda6219f1880cae8a41": "0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff" - } - } - }"#; - let state_override: StateOverride = serde_json::from_str(s).unwrap(); - let acc = - state_override.get(&address!("1b5212AF6b76113afD94cD2B5a78a73B7d7A8222")).unwrap(); - assert!(acc.state_diff.is_some()); - } -} diff --git a/crates/rpc/rpc-types/src/eth/syncing.rs b/crates/rpc/rpc-types/src/eth/syncing.rs deleted file mode 100644 index 2ad0c8be0..000000000 --- a/crates/rpc/rpc-types/src/eth/syncing.rs +++ /dev/null @@ -1,161 +0,0 @@ -use alloy_primitives::{B512, U256, U64}; -use serde::{Deserialize, Deserializer, Serialize, Serializer}; -use std::collections::BTreeMap; - -/// Syncing info -#[derive(Debug, Clone, Default, Serialize, Deserialize, Eq, PartialEq)] -#[serde(rename_all = "camelCase")] -pub struct SyncInfo { - /// Starting block - pub starting_block: U256, - /// Current block - pub current_block: U256, - /// Highest block seen so far - pub highest_block: U256, - /// Warp sync snapshot chunks total. - pub warp_chunks_amount: Option, - /// Warp sync snapshot chunks processed. - pub warp_chunks_processed: Option, -} - -/// Peers info -#[derive(Debug, Clone, Default, Serialize)] -pub struct Peers { - /// Number of active peers - pub active: usize, - /// Number of connected peers - pub connected: usize, - /// Max number of peers - pub max: u32, - /// Detailed information on peers - pub peers: Vec, -} - -/// Number of peers connected to. -#[derive(Clone, Debug, Serialize, Deserialize)] -#[serde(untagged)] -pub enum PeerCount { - /// Peer count as integer - Number(u32), - /// Peer count as hex - Hex(U64), -} - -/// Peer connection information -#[derive(Debug, Clone, Default, Serialize, Deserialize)] -pub struct PeerInfo { - /// Public node id - pub id: Option, - /// Node client ID - pub name: String, - /// Capabilities - pub caps: Vec, - /// Network information - pub network: PeerNetworkInfo, - /// Protocols information - pub protocols: PeerProtocolsInfo, -} - -/// Peer network information -#[derive(Debug, Clone, Default, Serialize, Deserialize)] -#[serde(rename_all = "camelCase")] -pub struct PeerNetworkInfo { - /// Remote endpoint address - pub remote_address: String, - /// Local endpoint address - pub local_address: String, -} - -/// Peer protocols information -#[derive(Debug, Clone, Default, Serialize, Deserialize)] -pub struct PeerProtocolsInfo { - /// Ethereum protocol information - pub eth: Option, - /// PIP protocol information. - #[serde(default, skip_serializing_if = "Option::is_none")] - pub pip: Option, -} - -/// Peer Ethereum protocol information -#[derive(Debug, Clone, Default, Serialize, Deserialize)] -pub struct PeerEthProtocolInfo { - /// Negotiated ethereum protocol version - pub version: u32, - /// Peer total difficulty if known - pub difficulty: Option, - /// SHA3 of peer best block hash - pub head: String, -} - -/// Peer PIP protocol information -#[derive(Debug, Clone, Default, Serialize, Deserialize)] -pub struct PipProtocolInfo { - /// Negotiated PIP protocol version - pub version: u32, - /// Peer total difficulty - pub difficulty: U256, - /// SHA3 of peer best block hash - pub head: String, -} - -/// Sync status -#[derive(Clone, Debug, Eq, PartialEq)] -pub enum SyncStatus { - /// Info when syncing - Info(SyncInfo), - /// Not syncing - None, -} - -impl<'de> Deserialize<'de> for SyncStatus { - fn deserialize(deserializer: D) -> Result - where - D: Deserializer<'de>, - { - #[derive(Deserialize)] - #[serde(untagged)] - enum Syncing { - /// When client is synced to the highest block, eth_syncing with return "false" - None(bool), - IsSyncing(SyncInfo), - } - - match Syncing::deserialize(deserializer)? { - Syncing::None(false) => Ok(SyncStatus::None), - Syncing::None(true) => Err(serde::de::Error::custom( - "eth_syncing returned `true` that is undefined value.", - )), - Syncing::IsSyncing(sync) => Ok(SyncStatus::Info(sync)), - } - } -} - -impl Serialize for SyncStatus { - fn serialize(&self, serializer: S) -> Result - where - S: Serializer, - { - match self { - SyncStatus::Info(info) => info.serialize(serializer), - SyncStatus::None => serializer.serialize_bool(false), - } - } -} - -/// Propagation statistics for pending transaction. -#[derive(Debug, Clone, Default, Serialize)] -#[serde(rename_all = "camelCase")] -pub struct TransactionStats { - /// Block no this transaction was first seen. - pub first_seen: u64, - /// Peers this transaction was propagated to with count. - pub propagated_to: BTreeMap, -} - -/// Chain status. -#[derive(Debug, Clone, Default, Serialize)] -#[serde(rename_all = "camelCase")] -pub struct ChainStatus { - /// Describes the gap in the blockchain, if there is one: (first, last) - pub block_gap: Option<(U256, U256)>, -} diff --git a/crates/rpc/rpc-types/src/eth/trace/common.rs b/crates/rpc/rpc-types/src/eth/trace/common.rs deleted file mode 100644 index 617b44850..000000000 --- a/crates/rpc/rpc-types/src/eth/trace/common.rs +++ /dev/null @@ -1,36 +0,0 @@ -//! Types used by tracing backends - -use alloy_primitives::TxHash; -use serde::{Deserialize, Serialize}; - -/// The result of a single transaction trace. -#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)] -#[serde(untagged, rename_all = "camelCase")] -pub enum TraceResult { - /// Untagged success variant - Success { - /// Trace results produced by the tracer - result: Ok, - /// transaction hash - #[serde(skip_serializing_if = "Option::is_none")] - tx_hash: Option, - }, - /// Untagged error variant - Error { - /// Trace failure produced by the tracer - error: Err, - /// transaction hash - #[serde(skip_serializing_if = "Option::is_none")] - tx_hash: Option, - }, -} - -impl TraceResult { - /// Returns the hash of the transaction that was traced. - pub fn tx_hash(&self) -> Option { - *match self { - TraceResult::Success { tx_hash, .. } => tx_hash, - TraceResult::Error { tx_hash, .. } => tx_hash, - } - } -} diff --git a/crates/rpc/rpc-types/src/eth/trace/filter.rs b/crates/rpc/rpc-types/src/eth/trace/filter.rs deleted file mode 100644 index a19da2254..000000000 --- a/crates/rpc/rpc-types/src/eth/trace/filter.rs +++ /dev/null @@ -1,184 +0,0 @@ -//! `trace_filter` types and support - -use crate::serde_helpers::num::u64_hex_or_decimal_opt; -use alloy_primitives::Address; -use serde::{Deserialize, Serialize}; -use std::collections::HashSet; - -/// Trace filter. -#[derive(Debug, PartialEq, Eq, Serialize, Deserialize, Clone, Default)] -#[serde(deny_unknown_fields)] -#[serde(rename_all = "camelCase")] -pub struct TraceFilter { - /// From block - #[serde(with = "u64_hex_or_decimal_opt")] - pub from_block: Option, - /// To block - #[serde(with = "u64_hex_or_decimal_opt")] - pub to_block: Option, - /// From address - #[serde(default)] - pub from_address: Vec
, - /// To address - #[serde(default)] - pub to_address: Vec
, - /// How to apply `from_address` and `to_address` filters. - #[serde(default)] - pub mode: TraceFilterMode, - /// Output offset - pub after: Option, - /// Output amount - pub count: Option, -} - -// === impl TraceFilter === - -impl TraceFilter { - /// Returns a `TraceFilterMatcher` for this filter. - pub fn matcher(&self) -> TraceFilterMatcher { - let from_addresses = self.from_address.iter().cloned().collect(); - let to_addresses = self.to_address.iter().cloned().collect(); - TraceFilterMatcher { mode: self.mode, from_addresses, to_addresses } - } -} - -/// How to apply `from_address` and `to_address` filters. -#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Serialize, Deserialize)] -#[serde(rename_all = "camelCase")] -pub enum TraceFilterMode { - /// Return traces for transactions with matching `from` OR `to` addresses. - #[default] - Union, - /// Only return traces for transactions with matching `from` _and_ `to` addresses. - Intersection, -} - -/// Helper type for matching `from` and `to` addresses. Empty sets match all addresses. -#[derive(Debug, Clone, PartialEq, Eq)] -pub struct TraceFilterMatcher { - mode: TraceFilterMode, - from_addresses: HashSet
, - to_addresses: HashSet
, -} - -impl TraceFilterMatcher { - /// Returns `true` if the given `from` and `to` addresses match this filter. - pub fn matches(&self, from: Address, to: Option
) -> bool { - match (self.from_addresses.is_empty(), self.to_addresses.is_empty()) { - (true, true) => true, - (false, true) => self.from_addresses.contains(&from), - (true, false) => to.map_or(false, |to_addr| self.to_addresses.contains(&to_addr)), - (false, false) => match self.mode { - TraceFilterMode::Union => { - self.from_addresses.contains(&from) || - to.map_or(false, |to_addr| self.to_addresses.contains(&to_addr)) - } - TraceFilterMode::Intersection => { - self.from_addresses.contains(&from) && - to.map_or(false, |to_addr| self.to_addresses.contains(&to_addr)) - } - }, - } - } -} - -#[cfg(test)] -mod tests { - use super::*; - use serde_json::json; - - #[test] - fn test_parse_filter() { - let s = r#"{"fromBlock": "0x3","toBlock": "0x5"}"#; - let filter: TraceFilter = serde_json::from_str(s).unwrap(); - assert_eq!(filter.from_block, Some(3)); - assert_eq!(filter.to_block, Some(5)); - } - - #[test] - fn test_filter_matcher_addresses_unspecified() { - let test_addr_d8 = "0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045".parse().unwrap(); - let test_addr_16 = "0x160f5f00288e9e1cc8655b327e081566e580a71d".parse().unwrap(); - let filter_json = json!({ - "fromBlock": "0x3", - "toBlock": "0x5", - }); - let filter: TraceFilter = - serde_json::from_value(filter_json).expect("Failed to parse filter"); - let matcher = filter.matcher(); - assert!(matcher.matches(test_addr_d8, None)); - assert!(matcher.matches(test_addr_16, None)); - assert!(matcher.matches(test_addr_d8, Some(test_addr_16))); - assert!(matcher.matches(test_addr_16, Some(test_addr_d8))); - } - - #[test] - fn test_filter_matcher_from_address() { - let test_addr_d8 = "0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045".parse().unwrap(); - let test_addr_16 = "0x160f5f00288e9e1cc8655b327e081566e580a71d".parse().unwrap(); - let filter_json = json!({ - "fromBlock": "0x3", - "toBlock": "0x5", - "fromAddress": [test_addr_d8] - }); - let filter: TraceFilter = serde_json::from_value(filter_json).unwrap(); - let matcher = filter.matcher(); - assert!(matcher.matches(test_addr_d8, None)); - assert!(!matcher.matches(test_addr_16, None)); - assert!(matcher.matches(test_addr_d8, Some(test_addr_16))); - assert!(!matcher.matches(test_addr_16, Some(test_addr_d8))); - } - - #[test] - fn test_filter_matcher_to_address() { - let test_addr_d8 = "0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045".parse().unwrap(); - let test_addr_16 = "0x160f5f00288e9e1cc8655b327e081566e580a71d".parse().unwrap(); - let filter_json = json!({ - "fromBlock": "0x3", - "toBlock": "0x5", - "toAddress": [test_addr_d8], - }); - let filter: TraceFilter = serde_json::from_value(filter_json).unwrap(); - let matcher = filter.matcher(); - assert!(matcher.matches(test_addr_16, Some(test_addr_d8))); - assert!(!matcher.matches(test_addr_16, None)); - assert!(!matcher.matches(test_addr_d8, Some(test_addr_16))); - } - - #[test] - fn test_filter_matcher_both_addresses_union() { - let test_addr_d8 = "0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045".parse().unwrap(); - let test_addr_16 = "0x160f5f00288e9e1cc8655b327e081566e580a71d".parse().unwrap(); - let filter_json = json!({ - "fromBlock": "0x3", - "toBlock": "0x5", - "fromAddress": [test_addr_16], - "toAddress": [test_addr_d8], - }); - let filter: TraceFilter = serde_json::from_value(filter_json).unwrap(); - let matcher = filter.matcher(); - assert!(matcher.matches(test_addr_16, Some(test_addr_d8))); - assert!(matcher.matches(test_addr_16, None)); - assert!(matcher.matches(test_addr_d8, Some(test_addr_d8))); - assert!(!matcher.matches(test_addr_d8, Some(test_addr_16))); - } - - #[test] - fn test_filter_matcher_both_addresses_intersection() { - let test_addr_d8 = "0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045".parse().unwrap(); - let test_addr_16 = "0x160f5f00288e9e1cc8655b327e081566e580a71d".parse().unwrap(); - let filter_json = json!({ - "fromBlock": "0x3", - "toBlock": "0x5", - "fromAddress": [test_addr_16], - "toAddress": [test_addr_d8], - "mode": "intersection", - }); - let filter: TraceFilter = serde_json::from_value(filter_json).unwrap(); - let matcher = filter.matcher(); - assert!(matcher.matches(test_addr_16, Some(test_addr_d8))); - assert!(!matcher.matches(test_addr_16, None)); - assert!(!matcher.matches(test_addr_d8, Some(test_addr_d8))); - assert!(!matcher.matches(test_addr_d8, Some(test_addr_16))); - } -} diff --git a/crates/rpc/rpc-types/src/eth/trace/geth/call.rs b/crates/rpc/rpc-types/src/eth/trace/geth/call.rs deleted file mode 100644 index bf6910cf1..000000000 --- a/crates/rpc/rpc-types/src/eth/trace/geth/call.rs +++ /dev/null @@ -1,118 +0,0 @@ -use crate::serde_helpers::num::from_int_or_hex; -use alloy_primitives::{Address, Bytes, B256, U256}; -use serde::{Deserialize, Serialize}; - -/// The response object for `debug_traceTransaction` with `"tracer": "callTracer"` -/// -/// -#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)] -pub struct CallFrame { - /// The address of that initiated the call. - pub from: Address, - /// How much gas was left before the call - #[serde(default, deserialize_with = "from_int_or_hex")] - pub gas: U256, - /// How much gas was used by the call - #[serde(default, deserialize_with = "from_int_or_hex", rename = "gasUsed")] - pub gas_used: U256, - /// The address of the contract that was called. - #[serde(default, skip_serializing_if = "Option::is_none")] - pub to: Option
, - /// Calldata input - pub input: Bytes, - /// Output of the call, if any. - #[serde(default, skip_serializing_if = "Option::is_none")] - pub output: Option, - /// Error message, if any. - #[serde(default, skip_serializing_if = "Option::is_none")] - pub error: Option, - /// Why this call reverted, if it reverted. - #[serde(default, rename = "revertReason", skip_serializing_if = "Option::is_none")] - pub revert_reason: Option, - /// Recorded child calls. - #[serde(default, skip_serializing_if = "Vec::is_empty")] - pub calls: Vec, - /// Logs emitted by this call - #[serde(default, skip_serializing_if = "Vec::is_empty")] - pub logs: Vec, - /// Value transferred - #[serde(default, skip_serializing_if = "Option::is_none")] - pub value: Option, - /// The type of the call - #[serde(rename = "type")] - pub typ: String, -} - -/// Represents a recorded call -#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)] -pub struct CallLogFrame { - #[serde(default, skip_serializing_if = "Option::is_none")] - pub address: Option
, - #[serde(default, skip_serializing_if = "Option::is_none")] - pub topics: Option>, - #[serde(default, skip_serializing_if = "Option::is_none")] - pub data: Option, -} - -#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)] -#[serde(rename_all = "camelCase")] -pub struct CallConfig { - /// When set to true, this will only trace the primary (top-level) call and not any sub-calls. - /// It eliminates the additional processing for each call frame - #[serde(default, skip_serializing_if = "Option::is_none")] - pub only_top_call: Option, - #[serde(default, skip_serializing_if = "Option::is_none")] - pub with_log: Option, -} - -impl CallConfig { - /// Sets the only top call flag - pub fn only_top_call(mut self) -> Self { - self.only_top_call = Some(true); - self - } - - /// Sets the with log flag - pub fn with_log(mut self) -> Self { - self.with_log = Some(true); - self - } -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::trace::geth::*; - - // See - const DEFAULT: &str = include_str!("../../../../test_data/call_tracer/default.json"); - const LEGACY: &str = include_str!("../../../../test_data/call_tracer/legacy.json"); - const ONLY_TOP_CALL: &str = - include_str!("../../../../test_data/call_tracer/only_top_call.json"); - const WITH_LOG: &str = include_str!("../../../../test_data/call_tracer/with_log.json"); - - #[test] - fn test_serialize_call_trace() { - let mut opts = GethDebugTracingCallOptions::default(); - opts.tracing_options.config.disable_storage = Some(false); - opts.tracing_options.tracer = - Some(GethDebugTracerType::BuiltInTracer(GethDebugBuiltInTracerType::CallTracer)); - opts.tracing_options.tracer_config = - serde_json::to_value(CallConfig { only_top_call: Some(true), with_log: Some(true) }) - .unwrap() - .into(); - - assert_eq!( - serde_json::to_string(&opts).unwrap(), - r#"{"disableStorage":false,"tracer":"callTracer","tracerConfig":{"onlyTopCall":true,"withLog":true}}"# - ); - } - - #[test] - fn test_deserialize_call_trace() { - let _trace: CallFrame = serde_json::from_str(DEFAULT).unwrap(); - let _trace: CallFrame = serde_json::from_str(LEGACY).unwrap(); - let _trace: CallFrame = serde_json::from_str(ONLY_TOP_CALL).unwrap(); - let _trace: CallFrame = serde_json::from_str(WITH_LOG).unwrap(); - } -} diff --git a/crates/rpc/rpc-types/src/eth/trace/geth/four_byte.rs b/crates/rpc/rpc-types/src/eth/trace/geth/four_byte.rs deleted file mode 100644 index 927df7b76..000000000 --- a/crates/rpc/rpc-types/src/eth/trace/geth/four_byte.rs +++ /dev/null @@ -1,34 +0,0 @@ -use serde::{Deserialize, Serialize}; -use std::collections::BTreeMap; - -/// -#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)] -pub struct FourByteFrame(pub BTreeMap); - -#[cfg(test)] -mod tests { - use super::*; - use crate::trace::geth::*; - - const DEFAULT: &str = r#"{ - "0x27dc297e-128": 1, - "0x38cc4831-0": 2, - "0x524f3889-96": 1, - "0xadf59f99-288": 1, - "0xc281d19e-0": 1 - }"#; - - #[test] - fn test_serialize_four_byte_trace() { - let mut opts = GethDebugTracingCallOptions::default(); - opts.tracing_options.tracer = - Some(GethDebugTracerType::BuiltInTracer(GethDebugBuiltInTracerType::FourByteTracer)); - - assert_eq!(serde_json::to_string(&opts).unwrap(), r#"{"tracer":"4byteTracer"}"#); - } - - #[test] - fn test_deserialize_four_byte_trace() { - let _trace: FourByteFrame = serde_json::from_str(DEFAULT).unwrap(); - } -} diff --git a/crates/rpc/rpc-types/src/eth/trace/geth/mod.rs b/crates/rpc/rpc-types/src/eth/trace/geth/mod.rs deleted file mode 100644 index f6b4979da..000000000 --- a/crates/rpc/rpc-types/src/eth/trace/geth/mod.rs +++ /dev/null @@ -1,579 +0,0 @@ -//! Geth tracing types. - -#![allow(missing_docs)] - -use crate::{state::StateOverride, BlockOverrides}; -use alloy_primitives::{Bytes, B256, U256}; -use serde::{de::DeserializeOwned, ser::SerializeMap, Deserialize, Serialize, Serializer}; -use std::{collections::BTreeMap, time::Duration}; - -// re-exports -pub use self::{ - call::{CallConfig, CallFrame, CallLogFrame}, - four_byte::FourByteFrame, - noop::NoopFrame, - pre_state::{ - AccountChangeKind, AccountState, DiffMode, DiffStateKind, PreStateConfig, PreStateFrame, - PreStateMode, - }, -}; - -mod call; -mod four_byte; -mod noop; -mod pre_state; - -/// Result type for geth style transaction trace -pub type TraceResult = crate::trace::common::TraceResult; - -/// blockTraceResult represents the results of tracing a single block when an entire chain is being -/// traced. ref -#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)] -pub struct BlockTraceResult { - /// Block number corresponding to the trace task - pub block: U256, - /// Block hash corresponding to the trace task - pub hash: B256, - /// Trace results produced by the trace task - pub traces: Vec, -} - -/// Geth Default struct log trace frame -/// -/// -#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)] -#[serde(rename_all = "camelCase")] -pub struct DefaultFrame { - /// Whether the transaction failed - pub failed: bool, - /// How much gas was used. - pub gas: u64, - /// Output of the transaction - #[serde(serialize_with = "crate::serde_helpers::serialize_hex_string_no_prefix")] - pub return_value: Bytes, - /// Recorded traces of the transaction - pub struct_logs: Vec, -} - -/// Represents a struct log entry in a trace -/// -/// -#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)] -pub struct StructLog { - /// program counter - pub pc: u64, - /// opcode to be executed - pub op: String, - /// remaining gas - pub gas: u64, - /// cost for executing op - #[serde(rename = "gasCost")] - pub gas_cost: u64, - /// ref - #[serde(default, skip_serializing_if = "Option::is_none")] - pub memory: Option>, - /// Size of memory. - #[serde(default, rename = "memSize", skip_serializing_if = "Option::is_none")] - pub memory_size: Option, - /// EVM stack - #[serde(default, skip_serializing_if = "Option::is_none")] - pub stack: Option>, - /// Last call's return data. Enabled via enableReturnData - #[serde(default, rename = "returnData", skip_serializing_if = "Option::is_none")] - pub return_data: Option, - /// Storage slots of current contract read from and written to. Only emitted for SLOAD and - /// SSTORE. Disabled via disableStorage - #[serde( - default, - skip_serializing_if = "Option::is_none", - serialize_with = "serialize_string_storage_map_opt" - )] - pub storage: Option>, - /// Current call depth - pub depth: u64, - /// Refund counter - #[serde(default, rename = "refund", skip_serializing_if = "Option::is_none")] - pub refund_counter: Option, - /// Error message if any - #[serde(default, skip_serializing_if = "Option::is_none")] - pub error: Option, -} - -/// Tracing response objects -/// -/// Note: This deserializes untagged, so it's possible that a custom javascript tracer response -/// matches another variant, for example a js tracer that returns `{}` would be deserialized as -/// [GethTrace::NoopTracer] -#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize)] -#[serde(untagged)] -pub enum GethTrace { - /// The response for the default struct log tracer - Default(DefaultFrame), - /// The response for call tracer - CallTracer(CallFrame), - /// The response for four byte tracer - FourByteTracer(FourByteFrame), - /// The response for pre-state byte tracer - PreStateTracer(PreStateFrame), - /// An empty json response - NoopTracer(NoopFrame), - /// Any other trace response, such as custom javascript response objects - JS(serde_json::Value), -} - -impl From for GethTrace { - fn from(value: DefaultFrame) -> Self { - GethTrace::Default(value) - } -} - -impl From for GethTrace { - fn from(value: FourByteFrame) -> Self { - GethTrace::FourByteTracer(value) - } -} - -impl From for GethTrace { - fn from(value: CallFrame) -> Self { - GethTrace::CallTracer(value) - } -} - -impl From for GethTrace { - fn from(value: PreStateFrame) -> Self { - GethTrace::PreStateTracer(value) - } -} - -impl From for GethTrace { - fn from(value: NoopFrame) -> Self { - GethTrace::NoopTracer(value) - } -} - -/// Available built-in tracers -/// -/// See -#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize)] -pub enum GethDebugBuiltInTracerType { - /// The 4byteTracer collects the function selectors of every function executed in the lifetime - /// of a transaction, along with the size of the supplied call data. The result is a - /// [FourByteFrame] where the keys are SELECTOR-CALLDATASIZE and the values are number of - /// occurrences of this key. - #[serde(rename = "4byteTracer")] - FourByteTracer, - /// The callTracer tracks all the call frames executed during a transaction, including depth 0. - /// The result will be a nested list of call frames, resembling how EVM works. They form a tree - /// with the top-level call at root and sub-calls as children of the higher levels. - #[serde(rename = "callTracer")] - CallTracer, - /// The prestate tracer has two modes: prestate and diff. The prestate mode returns the - /// accounts necessary to execute a given transaction. diff mode returns the differences - /// between the transaction's pre and post-state (i.e. what changed because the transaction - /// happened). The prestateTracer defaults to prestate mode. It reexecutes the given - /// transaction and tracks every part of state that is touched. This is similar to the concept - /// of a stateless witness, the difference being this tracer doesn't return any cryptographic - /// proof, rather only the trie leaves. The result is an object. The keys are addresses of - /// accounts. - #[serde(rename = "prestateTracer")] - PreStateTracer, - /// This tracer is noop. It returns an empty object and is only meant for testing the setup. - #[serde(rename = "noopTracer")] - NoopTracer, -} - -/// Available tracers -/// -/// See and -#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize)] -#[serde(untagged)] -pub enum GethDebugTracerType { - /// built-in tracer - BuiltInTracer(GethDebugBuiltInTracerType), - /// custom JS tracer - JsTracer(String), -} - -impl From for GethDebugTracerType { - fn from(value: GethDebugBuiltInTracerType) -> Self { - GethDebugTracerType::BuiltInTracer(value) - } -} - -/// Configuration of the tracer -/// -/// This is a simple wrapper around serde_json::Value. -/// with helpers for deserializing tracer configs. -#[derive(Debug, PartialEq, Eq, Clone, Default, Deserialize, Serialize)] -#[serde(transparent)] -pub struct GethDebugTracerConfig(pub serde_json::Value); - -// === impl GethDebugTracerConfig === - -impl GethDebugTracerConfig { - /// Returns if this is a null object - pub fn is_null(&self) -> bool { - self.0.is_null() - } - - /// Consumes the config and tries to deserialize it into the given type. - pub fn from_value(self) -> Result { - serde_json::from_value(self.0) - } - - /// Returns the [CallConfig] if it is a call config. - pub fn into_call_config(self) -> Result { - if self.0.is_null() { - return Ok(Default::default()) - } - self.from_value() - } - - /// Returns the raw json value - pub fn into_json(self) -> serde_json::Value { - self.0 - } - - /// Returns the [PreStateConfig] if it is a call config. - pub fn into_pre_state_config(self) -> Result { - if self.0.is_null() { - return Ok(Default::default()) - } - self.from_value() - } -} - -impl From for GethDebugTracerConfig { - fn from(value: serde_json::Value) -> Self { - GethDebugTracerConfig(value) - } -} - -/// Bindings for additional `debug_traceTransaction` options -/// -/// See -#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, Default)] -#[serde(rename_all = "camelCase")] -pub struct GethDebugTracingOptions { - /// The common tracing options - #[serde(default, flatten)] - pub config: GethDefaultTracingOptions, - /// The custom tracer to use. - /// - /// If `None` then the default structlog tracer is used. - #[serde(default, skip_serializing_if = "Option::is_none")] - pub tracer: Option, - /// Config specific to given `tracer`. - /// - /// Note default struct logger config are historically embedded in main object. - /// - /// tracerConfig is slated for Geth v1.11.0 - /// See - /// - /// This could be [CallConfig] or [PreStateConfig] depending on the tracer. - #[serde(default, skip_serializing_if = "GethDebugTracerConfig::is_null")] - pub tracer_config: GethDebugTracerConfig, - /// A string of decimal integers that overrides the JavaScript-based tracing calls default - /// timeout of 5 seconds. - #[serde(default, skip_serializing_if = "Option::is_none")] - pub timeout: Option, -} - -impl GethDebugTracingOptions { - /// Sets the tracer to use - pub fn with_tracer(mut self, tracer: GethDebugTracerType) -> Self { - self.tracer = Some(tracer); - self - } - - /// Sets the timeout to use for tracing - pub fn with_timeout(mut self, duration: Duration) -> Self { - self.timeout = Some(format!("{}ms", duration.as_millis())); - self - } - - /// Configures a [CallConfig] - pub fn call_config(mut self, config: CallConfig) -> Self { - self.tracer_config = - GethDebugTracerConfig(serde_json::to_value(config).expect("is serializable")); - self - } - - /// Configures a [PreStateConfig] - pub fn prestate_config(mut self, config: PreStateConfig) -> Self { - self.tracer_config = - GethDebugTracerConfig(serde_json::to_value(config).expect("is serializable")); - self - } -} - -/// Default tracing options for the struct looger. -/// -/// These are all known general purpose tracer options that may or not be supported by a given -/// tracer. For example, the `enableReturnData` option is a noop on regular -/// `debug_trace{Transaction,Block}` calls. -#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, Default)] -#[serde(rename_all = "camelCase")] -pub struct GethDefaultTracingOptions { - /// enable memory capture - #[serde(default, skip_serializing_if = "Option::is_none")] - pub enable_memory: Option, - /// Disable memory capture - /// - /// This is the opposite of `enable_memory`. - /// - /// Note: memory capture used to be enabled by default on geth, but has since been flipped and is now disabled by default. - /// However, at the time of writing this, erigon still defaults to enabled and supports the - /// `disableMemory` option. So we keep this option for compatibility, but if it's missing - /// OR `enableMemory` is present `enableMemory` takes precedence. - /// - /// See also - #[serde(default, skip_serializing_if = "Option::is_none")] - pub disable_memory: Option, - /// disable stack capture - #[serde(default, skip_serializing_if = "Option::is_none")] - pub disable_stack: Option, - /// Disable storage capture - #[serde(default, skip_serializing_if = "Option::is_none")] - pub disable_storage: Option, - /// Enable return data capture - #[serde(default, skip_serializing_if = "Option::is_none")] - pub enable_return_data: Option, - /// Disable return data capture - /// - /// This is the opposite of `enable_return_data`, and only supported for compatibility reasons. - /// See also `disable_memory`. - /// - /// If `enable_return_data` is present, `enable_return_data` always takes precedence. - #[serde(default, skip_serializing_if = "Option::is_none")] - pub disable_return_data: Option, - /// print output during capture end - #[serde(default, skip_serializing_if = "Option::is_none")] - pub debug: Option, - /// maximum length of output, but zero means unlimited - #[serde(default, skip_serializing_if = "Option::is_none")] - pub limit: Option, -} - -impl GethDefaultTracingOptions { - /// Enables memory capture. - pub fn enable_memory(self) -> Self { - self.with_enable_memory(true) - } - - /// Disables memory capture. - pub fn disable_memory(self) -> Self { - self.with_disable_memory(true) - } - - /// Disables stack capture. - pub fn disable_stack(self) -> Self { - self.with_disable_stack(true) - } - - /// Disables storage capture. - pub fn disable_storage(self) -> Self { - self.with_disable_storage(true) - } - - /// Enables return data capture. - pub fn enable_return_data(self) -> Self { - self.with_enable_return_data(true) - } - - /// Disables return data capture. - pub fn disable_return_data(self) -> Self { - self.with_disable_return_data(true) - } - - /// Enables debug mode. - pub fn debug(self) -> Self { - self.with_debug(true) - } - - /// Sets the enable_memory field. - pub fn with_enable_memory(mut self, enable: bool) -> Self { - self.enable_memory = Some(enable); - self - } - - /// Sets the disable_memory field. - pub fn with_disable_memory(mut self, disable: bool) -> Self { - self.disable_memory = Some(disable); - self - } - - /// Sets the disable_stack field. - pub fn with_disable_stack(mut self, disable: bool) -> Self { - self.disable_stack = Some(disable); - self - } - - /// Sets the disable_storage field. - pub fn with_disable_storage(mut self, disable: bool) -> Self { - self.disable_storage = Some(disable); - self - } - - /// Sets the enable_return_data field. - pub fn with_enable_return_data(mut self, enable: bool) -> Self { - self.enable_return_data = Some(enable); - self - } - - /// Sets the disable_return_data field. - pub fn with_disable_return_data(mut self, disable: bool) -> Self { - self.disable_return_data = Some(disable); - self - } - - /// Sets the debug field. - pub fn with_debug(mut self, debug: bool) -> Self { - self.debug = Some(debug); - self - } - - /// Sets the limit field. - pub fn with_limit(mut self, limit: u64) -> Self { - self.limit = Some(limit); - self - } - /// Returns `true` if return data capture is enabled - pub fn is_return_data_enabled(&self) -> bool { - self.enable_return_data - .or_else(|| self.disable_return_data.map(|disable| !disable)) - .unwrap_or(false) - } - - /// Returns `true` if memory capture is enabled - pub fn is_memory_enabled(&self) -> bool { - self.enable_memory.or_else(|| self.disable_memory.map(|disable| !disable)).unwrap_or(false) - } - - /// Returns `true` if stack capture is enabled - pub fn is_stack_enabled(&self) -> bool { - !self.disable_stack.unwrap_or(false) - } - - /// Returns `true` if storage capture is enabled - pub fn is_storage_enabled(&self) -> bool { - !self.disable_storage.unwrap_or(false) - } -} -/// Bindings for additional `debug_traceCall` options -/// -/// See -#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, Default)] -#[serde(rename_all = "camelCase")] -pub struct GethDebugTracingCallOptions { - /// All the options - #[serde(flatten)] - pub tracing_options: GethDebugTracingOptions, - /// The state overrides to apply - #[serde(default, skip_serializing_if = "Option::is_none")] - pub state_overrides: Option, - /// The block overrides to apply - #[serde(default, skip_serializing_if = "Option::is_none")] - pub block_overrides: Option, -} - -/// Serializes a storage map as a list of key-value pairs _without_ 0x-prefix -fn serialize_string_storage_map_opt( - storage: &Option>, - s: S, -) -> Result { - match storage { - None => s.serialize_none(), - Some(storage) => { - let mut m = s.serialize_map(Some(storage.len()))?; - for (key, val) in storage.iter() { - let key = format!("{:?}", key); - let val = format!("{:?}", val); - // skip the 0x prefix - m.serialize_entry(&key.as_str()[2..], &val.as_str()[2..])?; - } - m.end() - } - } -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn test_tracer_config() { - let s = "{\"tracer\": \"callTracer\"}"; - let opts = serde_json::from_str::(s).unwrap(); - assert_eq!( - opts.tracer, - Some(GethDebugTracerType::BuiltInTracer(GethDebugBuiltInTracerType::CallTracer)) - ); - let _call_config = opts.tracer_config.clone().into_call_config().unwrap(); - let _prestate_config = opts.tracer_config.into_pre_state_config().unwrap(); - } - - #[test] - fn test_memory_capture() { - let mut config = GethDefaultTracingOptions::default(); - - // by default false - assert!(!config.is_memory_enabled()); - - config.disable_memory = Some(false); - // disable == false -> enable - assert!(config.is_memory_enabled()); - - config.enable_memory = Some(false); - // enable == false -> disable - assert!(!config.is_memory_enabled()); - } - - #[test] - fn test_return_data_capture() { - let mut config = GethDefaultTracingOptions::default(); - - // by default false - assert!(!config.is_return_data_enabled()); - - config.disable_return_data = Some(false); - // disable == false -> enable - assert!(config.is_return_data_enabled()); - - config.enable_return_data = Some(false); - // enable == false -> disable - assert!(!config.is_return_data_enabled()); - } - - // - #[test] - fn serde_default_frame() { - let input = include_str!("../../../../test_data/default/structlogs_01.json"); - let _frame: DefaultFrame = serde_json::from_str(input).unwrap(); - } - - #[test] - fn test_serialize_storage_map() { - let s = r#"{"pc":3349,"op":"SLOAD","gas":23959,"gasCost":2100,"depth":1,"stack":[],"memory":[],"storage":{"6693dabf5ec7ab1a0d1c5bc58451f85d5e44d504c9ffeb75799bfdb61aa2997a":"0000000000000000000000000000000000000000000000000000000000000000"}}"#; - let log: StructLog = serde_json::from_str(s).unwrap(); - let val = serde_json::to_value(&log).unwrap(); - let input = serde_json::from_str::(s).unwrap(); - similar_asserts::assert_eq!(input, val); - } - - #[test] - fn test_trace_result_serde() { - let s = r#" { - "result": { - "from": "0xccc5499e15fedaaeaba68aeb79b95b20f725bc56", - "gas": "0x186a0", - "gasUsed": "0xdb91", - "to": "0xdac17f958d2ee523a2206206994597c13d831ec7", - "input": "0xa9059cbb000000000000000000000000e3f85a274c1edbea2f2498cf5978f41961cf8b5b0000000000000000000000000000000000000000000000000000000068c8f380", - "value": "0x0", - "type": "CALL" - } - }"#; - let _result: TraceResult = serde_json::from_str(s).unwrap(); - } -} diff --git a/crates/rpc/rpc-types/src/eth/trace/geth/noop.rs b/crates/rpc/rpc-types/src/eth/trace/geth/noop.rs deleted file mode 100644 index a9db36735..000000000 --- a/crates/rpc/rpc-types/src/eth/trace/geth/noop.rs +++ /dev/null @@ -1,32 +0,0 @@ -use serde::{Deserialize, Serialize}; -use std::collections::BTreeMap; - -/// An empty frame response that's only an empty json object `{}` -/// -#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)] -pub struct NoopFrame(BTreeMap); - -#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize, PartialOrd, Ord)] -struct Null; - -#[cfg(test)] -mod tests { - use super::*; - use crate::trace::geth::*; - - const DEFAULT: &str = r"{}"; - - #[test] - fn test_serialize_noop_trace() { - let mut opts = GethDebugTracingCallOptions::default(); - opts.tracing_options.tracer = - Some(GethDebugTracerType::BuiltInTracer(GethDebugBuiltInTracerType::NoopTracer)); - - assert_eq!(serde_json::to_string(&opts).unwrap(), r#"{"tracer":"noopTracer"}"#); - } - - #[test] - fn test_deserialize_noop_trace() { - let _trace: NoopFrame = serde_json::from_str(DEFAULT).unwrap(); - } -} diff --git a/crates/rpc/rpc-types/src/eth/trace/geth/pre_state.rs b/crates/rpc/rpc-types/src/eth/trace/geth/pre_state.rs deleted file mode 100644 index 04cb7c812..000000000 --- a/crates/rpc/rpc-types/src/eth/trace/geth/pre_state.rs +++ /dev/null @@ -1,348 +0,0 @@ -use crate::serde_helpers::num::from_int_or_hex_opt; -use alloy_primitives::{Address, Bytes, B256, U256}; -use serde::{Deserialize, Serialize}; -use std::collections::{btree_map, BTreeMap}; - -/// A tracer that records [AccountState]s. -/// The prestate tracer has two modes: prestate and diff -/// -/// -#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize)] -#[serde(untagged)] -pub enum PreStateFrame { - /// The default mode returns the accounts necessary to execute a given transaction. - /// - /// It re-executes the given transaction and tracks every part of state that is touched. - Default(PreStateMode), - /// Diff mode returns the differences between the transaction's pre and post-state (i.e. what - /// changed because the transaction happened). - Diff(DiffMode), -} - -impl PreStateFrame { - /// Returns true if this trace was requested without diffmode. - pub fn is_default(&self) -> bool { - matches!(self, PreStateFrame::Default(_)) - } - - /// Returns true if this trace was requested with diffmode. - pub fn is_diff(&self) -> bool { - matches!(self, PreStateFrame::Diff(_)) - } - - /// Returns the account states after the transaction is executed if this trace was requested - /// without diffmode. - pub fn as_default(&self) -> Option<&PreStateMode> { - match self { - PreStateFrame::Default(mode) => Some(mode), - _ => None, - } - } - - /// Returns the account states before and after the transaction is executed if this trace was - /// requested with diffmode. - pub fn as_diff(&self) -> Option<&DiffMode> { - match self { - PreStateFrame::Diff(mode) => Some(mode), - _ => None, - } - } -} - -#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)] -pub struct PreStateMode(pub BTreeMap); - -/// Represents the account states before and after the transaction is executed. -/// -/// This corresponds to the [DiffMode] of the [PreStateConfig]. -/// -/// This will only contain changed [AccountState]s, created accounts will not be included in the pre -/// state and selfdestructed accounts will not be included in the post state. -#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)] -#[serde(deny_unknown_fields)] -pub struct DiffMode { - /// The account states after the transaction is executed. - pub post: BTreeMap, - /// The account states before the transaction is executed. - pub pre: BTreeMap, -} - -// === impl DiffMode === - -impl DiffMode { - /// The sets of the [DiffMode] should only contain changed [AccountState]s. - /// - /// This will remove all unchanged [AccountState]s from the sets. - /// - /// In other words it removes entries that are equal (unchanged) in both the pre and post sets. - pub fn retain_changed(&mut self) -> &mut Self { - self.pre.retain(|address, pre| { - if let btree_map::Entry::Occupied(entry) = self.post.entry(*address) { - if entry.get() == pre { - // remove unchanged account state from both sets - entry.remove(); - return false - } - } - - true - }); - self - } - - /// Removes all zero values from the storage of the [AccountState]s. - pub fn remove_zero_storage_values(&mut self) { - self.pre.values_mut().for_each(|state| { - state.storage.retain(|_, value| *value != B256::ZERO); - }); - self.post.values_mut().for_each(|state| { - state.storage.retain(|_, value| *value != B256::ZERO); - }); - } -} - -/// Helper type for [DiffMode] to represent a specific set -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub enum DiffStateKind { - /// Corresponds to the pre state of the [DiffMode] - Pre, - /// Corresponds to the post state of the [DiffMode] - Post, -} - -impl DiffStateKind { - /// Returns true if this is the pre state of the [DiffMode] - pub fn is_pre(&self) -> bool { - matches!(self, DiffStateKind::Pre) - } - - /// Returns true if this is the post state of the [DiffMode] - pub fn is_post(&self) -> bool { - matches!(self, DiffStateKind::Post) - } -} - -/// Represents the state of an account -#[derive(Debug, Clone, Default, PartialEq, Eq, Hash, Serialize, Deserialize)] -pub struct AccountState { - #[serde( - default, - deserialize_with = "from_int_or_hex_opt", - skip_serializing_if = "Option::is_none" - )] - pub balance: Option, - #[serde(default, skip_serializing_if = "Option::is_none")] - pub code: Option, - #[serde(default, skip_serializing_if = "Option::is_none")] - pub nonce: Option, - #[serde(default, skip_serializing_if = "BTreeMap::is_empty")] - pub storage: BTreeMap, -} - -impl AccountState { - /// Creates a new `AccountState` with the given account info. - /// - /// If balance is zero, it will be omitted. - /// If nonce is zero, it will be omitted. - /// If code is empty, it will be omitted. - pub fn from_account_info(nonce: u64, balance: U256, code: Option) -> Self { - Self { - balance: Some(balance), - code: code.filter(|code| !code.is_empty()), - nonce: (nonce != 0).then_some(nonce), - storage: Default::default(), - } - } - - /// Removes balance,nonce or code if they match the given account info. - /// - /// This is useful for comparing pre vs post state and only keep changed values in post state. - pub fn remove_matching_account_info(&mut self, other: &AccountState) { - if self.balance == other.balance { - self.balance = None; - } - if self.nonce == other.nonce { - self.nonce = None; - } - if self.code == other.code { - self.code = None; - } - } -} - -/// Helper type to track the kind of change of an [AccountState]. -#[derive(Debug, Clone, Default, PartialEq, Eq, Hash, Serialize, Deserialize)] -pub enum AccountChangeKind { - #[default] - Modify, - Create, - SelfDestruct, -} - -impl AccountChangeKind { - /// Returns true if the account was created - pub fn is_created(&self) -> bool { - matches!(self, AccountChangeKind::Create) - } - - /// Returns true the account was modified - pub fn is_modified(&self) -> bool { - matches!(self, AccountChangeKind::Modify) - } - - /// Returns true the account was modified - pub fn is_selfdestruct(&self) -> bool { - matches!(self, AccountChangeKind::SelfDestruct) - } -} - -/// The config for the prestate tracer. -/// -/// If `diffMode` is set to true, the response frame includes all the account and storage diffs for -/// the transaction. If it's missing or set to false it only returns the accounts and storage -/// necessary to execute the transaction. -#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)] -#[serde(rename_all = "camelCase")] -pub struct PreStateConfig { - #[serde(default, skip_serializing_if = "Option::is_none")] - pub diff_mode: Option, -} - -impl PreStateConfig { - /// Returns true if this trace was requested with diffmode. - #[inline] - pub fn is_diff_mode(&self) -> bool { - self.diff_mode.unwrap_or_default() - } - - /// Is default mode if diff_mode is not set - #[inline] - pub fn is_default_mode(&self) -> bool { - !self.is_diff_mode() - } -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::trace::geth::*; - - // See - const DEFAULT: &str = include_str!("../../../../test_data/pre_state_tracer/default.json"); - const LEGACY: &str = include_str!("../../../../test_data/pre_state_tracer/legacy.json"); - const DIFF_MODE: &str = include_str!("../../../../test_data/pre_state_tracer/diff_mode.json"); - - #[test] - fn test_serialize_pre_state_trace() { - let mut opts = GethDebugTracingCallOptions::default(); - opts.tracing_options.config.disable_storage = Some(false); - opts.tracing_options.tracer = - Some(GethDebugTracerType::BuiltInTracer(GethDebugBuiltInTracerType::PreStateTracer)); - opts.tracing_options.tracer_config = - serde_json::to_value(PreStateConfig { diff_mode: Some(true) }).unwrap().into(); - - assert_eq!( - serde_json::to_string(&opts).unwrap(), - r#"{"disableStorage":false,"tracer":"prestateTracer","tracerConfig":{"diffMode":true}}"# - ); - } - - #[test] - fn test_deserialize_pre_state_trace() { - let trace: PreStateFrame = serde_json::from_str(DEFAULT).unwrap(); - match trace { - PreStateFrame::Default(PreStateMode(_)) => {} - _ => unreachable!(), - } - let _trace: PreStateFrame = serde_json::from_str(LEGACY).unwrap(); - let trace: PreStateFrame = serde_json::from_str(DIFF_MODE).unwrap(); - match trace { - PreStateFrame::Diff(DiffMode { pre: _pre, post: _post }) => {} - _ => unreachable!(), - } - } - - #[test] - fn test_is_diff_mode() { - assert!(PreStateConfig { diff_mode: Some(true) }.is_diff_mode()); - assert!(!PreStateConfig { diff_mode: Some(false) }.is_diff_mode()); - assert!(!PreStateConfig { diff_mode: None }.is_diff_mode()); - } - - #[test] - fn parse_prestate_default_resp() { - let s = r#"{ - "0x0000000000000000000000000000000000000002": { - "balance": "0x0" - }, - "0x008b3b2f992c0e14edaa6e2c662bec549caa8df1": { - "balance": "0x2638035a26d133809" - }, - "0x35a9f94af726f07b5162df7e828cc9dc8439e7d0": { - "balance": "0x7a48734599f7284", - "nonce": 1133 - }, - "0xc8ba32cab1757528daf49033e3673fae77dcf05d": { - "balance": "0x0", - "code": "0x", - "nonce": 1, - "storage": { - "0x0000000000000000000000000000000000000000000000000000000000000000": "0x000000000000000000000000000000000000000000000000000000000024aea6", - "0x59fb7853eb21f604d010b94c123acbeae621f09ce15ee5d7616485b1e78a72e9": "0x00000000000000c42b56a52aedf18667c8ae258a0280a8912641c80c48cd9548", - "0x8d8ebb65ec00cb973d4fe086a607728fd1b9de14aa48208381eed9592f0dee9a": "0x00000000000000784ae4881e40b1f5ebb4437905fbb8a5914454123b0293b35f", - "0xff896b09014882056009dedb136458f017fcef9a4729467d0d00b4fd413fb1f1": "0x000000000000000e78ac39cb1c20e9edc753623b153705d0ccc487e31f9d6749" - } - } -} -"#; - let pre_state: PreStateFrame = serde_json::from_str(s).unwrap(); - assert!(pre_state.is_default()); - } - #[test] - fn parse_prestate_diff_resp() { - let s = r#"{ - "post": { - "0x35a9f94af726f07b5162df7e828cc9dc8439e7d0": { - "nonce": 1135 - } - }, - "pre": { - "0x35a9f94af726f07b5162df7e828cc9dc8439e7d0": { - "balance": "0x7a48429e177130a", - "nonce": 1134 - } - } -} -"#; - let pre_state: PreStateFrame = serde_json::from_str(s).unwrap(); - assert!(pre_state.is_diff()); - } - - #[test] - fn test_retain_changed_accounts() { - let s = r#"{ - "post": { - "0x35a9f94af726f07b5162df7e828cc9dc8439e7d0": { - "nonce": 1135 - } - }, - "pre": { - "0x35a9f94af726f07b5162df7e828cc9dc8439e7d0": { - "balance": "0x7a48429e177130a", - "nonce": 1134 - } - } -} -"#; - let diff: DiffMode = serde_json::from_str(s).unwrap(); - let mut diff_changed = diff.clone(); - diff_changed.retain_changed(); - // different entries - assert_eq!(diff_changed, diff); - - diff_changed.pre = diff_changed.post.clone(); - diff_changed.retain_changed(); - assert!(diff_changed.post.is_empty()); - assert!(diff_changed.pre.is_empty()); - } -} diff --git a/crates/rpc/rpc-types/src/eth/trace/mod.rs b/crates/rpc/rpc-types/src/eth/trace/mod.rs deleted file mode 100644 index 8cd79eb2c..000000000 --- a/crates/rpc/rpc-types/src/eth/trace/mod.rs +++ /dev/null @@ -1,7 +0,0 @@ -//! Types for tracing - -pub mod common; -pub mod filter; -pub mod geth; -pub mod parity; -pub mod tracerequest; diff --git a/crates/rpc/rpc-types/src/eth/trace/parity.rs b/crates/rpc/rpc-types/src/eth/trace/parity.rs deleted file mode 100644 index 2fbc36137..000000000 --- a/crates/rpc/rpc-types/src/eth/trace/parity.rs +++ /dev/null @@ -1,759 +0,0 @@ -//! Types for trace module. -//! -//! See - -use alloy_primitives::{Address, Bytes, B256, U256, U64}; -use serde::{ser::SerializeStruct, Deserialize, Serialize, Serializer}; -use std::{ - collections::BTreeMap, - ops::{Deref, DerefMut}, -}; - -/// Different Trace diagnostic targets. -#[derive(Clone, Debug, Eq, PartialEq, Hash, Serialize, Deserialize)] -#[serde(rename_all = "camelCase")] -pub enum TraceType { - /// Default trace - Trace, - /// Provides a full trace of the VM’s state throughout the execution of the transaction, - /// including for any subcalls. - VmTrace, - /// Provides information detailing all altered portions of the Ethereum state made due to the - /// execution of the transaction. - StateDiff, -} - -/// The Outcome of a traced transaction with optional settings -#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)] -#[serde(rename_all = "camelCase")] -pub struct TraceResults { - /// Output of the trace - pub output: Bytes, - /// Enabled if [TraceType::StateDiff] is provided - pub state_diff: Option, - /// Enabled if [TraceType::Trace] is provided, otherwise an empty vec - #[serde(default)] - pub trace: Vec, - /// Enabled if [TraceType::VmTrace] is provided - pub vm_trace: Option, -} - -// === impl TraceResults === - -impl TraceResults { - /// Sets the gas used of the root trace. - /// - /// The root trace's gasUsed should mirror the actual gas used by the transaction. - /// - /// This allows setting it manually by consuming the execution result's gas for example. - pub fn set_root_trace_gas_used(&mut self, gas_used: u64) { - if let Some(r) = self.trace.first_mut().and_then(|t| t.result.as_mut()) { - r.set_gas_used(gas_used) - } - } -} - -/// A `FullTrace` with an additional transaction hash -#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)] -#[serde(rename_all = "camelCase")] -pub struct TraceResultsWithTransactionHash { - /// The recorded trace. - #[serde(flatten)] - pub full_trace: TraceResults, - /// Hash of the traced transaction. - pub transaction_hash: B256, -} - -/// A changed value -#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)] -pub struct ChangedType { - /// Original value - pub from: T, - /// New value - pub to: T, -} - -/// Represents how a value changed. -/// -/// This is used for statediff. -#[derive(Clone, Debug, Default, Eq, PartialEq, Serialize, Deserialize)] -pub enum Delta { - /// Existing value didn't change. - #[default] - #[serde(rename = "=")] - Unchanged, - /// New storage value added. - #[serde(rename = "+")] - Added(T), - /// Existing storage value removed. - #[serde(rename = "-")] - Removed(T), - /// Existing storage value changed. - #[serde(rename = "*")] - Changed(ChangedType), -} - -// === impl Delta === - -impl Delta { - /// Creates a new [Delta::Changed] variant - pub fn changed(from: T, to: T) -> Self { - Self::Changed(ChangedType { from, to }) - } - - /// Returns true if the value is unchanged - pub fn is_unchanged(&self) -> bool { - matches!(self, Delta::Unchanged) - } - - /// Returns true if the value is added - pub fn is_added(&self) -> bool { - matches!(self, Delta::Added(_)) - } - - /// Returns true if the value is removed - pub fn is_removed(&self) -> bool { - matches!(self, Delta::Removed(_)) - } - - /// Returns true if the value is changed - pub fn is_changed(&self) -> bool { - matches!(self, Delta::Changed(_)) - } -} - -/// The diff of an account after a transaction -#[derive(Clone, Debug, Default, Eq, PartialEq, Serialize, Deserialize)] -#[serde(rename_all = "camelCase")] -pub struct AccountDiff { - /// How the balance changed, if at all - pub balance: Delta, - /// How the code changed, if at all - pub code: Delta, - /// How the nonce changed, if at all - pub nonce: Delta, - /// All touched/changed storage values - pub storage: BTreeMap>, -} - -/// New-type for list of account diffs -#[derive(Clone, Debug, Eq, PartialEq, Default, Serialize, Deserialize)] -#[serde(transparent)] -pub struct StateDiff(pub BTreeMap); - -impl Deref for StateDiff { - type Target = BTreeMap; - - fn deref(&self) -> &Self::Target { - &self.0 - } -} - -impl DerefMut for StateDiff { - fn deref_mut(&mut self) -> &mut Self::Target { - &mut self.0 - } -} - -/// Represents the various types of actions recorded during tracing -#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)] -#[serde(rename_all = "camelCase", tag = "type", content = "action")] -pub enum Action { - /// Regular call - Call(CallAction), - /// A CREATE call - Create(CreateAction), - /// Parity style traces never renamed suicide to selfdestruct: - /// - /// For compatibility reasons, this is serialized as `suicide`: - #[serde(rename = "suicide", alias = "selfdestruct")] - Selfdestruct(SelfdestructAction), - /// Rewards if any (pre POS) - Reward(RewardAction), -} - -impl Action { - /// Returns true if this is a call action - pub fn is_call(&self) -> bool { - matches!(self, Action::Call(_)) - } - - /// Returns true if this is a create action - pub fn is_create(&self) -> bool { - matches!(self, Action::Call(_)) - } - - /// Returns true if this is a selfdestruct action - pub fn is_selfdestruct(&self) -> bool { - matches!(self, Action::Selfdestruct(_)) - } - /// Returns true if this is a reward action - pub fn is_reward(&self) -> bool { - matches!(self, Action::Reward(_)) - } - - /// Returns what kind of action this is - pub fn kind(&self) -> ActionType { - match self { - Action::Call(_) => ActionType::Call, - Action::Create(_) => ActionType::Create, - Action::Selfdestruct(_) => ActionType::Selfdestruct, - Action::Reward(_) => ActionType::Reward, - } - } -} - -/// An external action type. -/// -/// Used as enum identifier for [Action] -#[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize)] -#[serde(rename_all = "lowercase")] -pub enum ActionType { - /// Contract call. - Call, - /// Contract creation. - Create, - /// Contract suicide/selfdestruct. - #[serde(rename = "suicide", alias = "selfdestruct")] - Selfdestruct, - /// A block reward. - Reward, -} - -/// Call type. -#[derive(Clone, Debug, Default, Eq, PartialEq, Serialize, Deserialize)] -#[serde(rename_all = "lowercase")] -pub enum CallType { - /// None - #[default] - None, - /// Call - Call, - /// Call code - CallCode, - /// Delegate call - DelegateCall, - /// Static call - StaticCall, -} - -/// Represents a certain [CallType] of a _call_ or message transaction. -#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)] -#[serde(rename_all = "camelCase")] -pub struct CallAction { - /// Address of the sending account. - pub from: Address, - /// The type of the call. - pub call_type: CallType, - /// The gas available for executing the call. - pub gas: U64, - /// The input data provided to the call. - pub input: Bytes, - /// Address of the destination/target account. - pub to: Address, - /// Value transferred to the destination account. - pub value: U256, -} - -/// Represents a _create_ action, either a `CREATE` operation or a CREATE transaction. -#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)] -#[serde(rename_all = "camelCase")] -pub struct CreateAction { - /// The address of the creator. - pub from: Address, - /// The gas available for the creation init code. - pub gas: U64, - /// The init code. - pub init: Bytes, - /// The value with which the new account is endowed. - pub value: U256, -} - -/// What kind of reward. -#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)] -#[serde(rename_all = "camelCase")] -pub enum RewardType { - /// Block rewards - Block, - /// Reward for uncle block - Uncle, -} - -/// Recorded reward of a block. -#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)] -#[serde(rename_all = "camelCase")] -pub struct RewardAction { - /// Author's address. - pub author: Address, - /// Reward type. - pub reward_type: RewardType, - /// Reward amount. - pub value: U256, -} - -/// Represents a _selfdestruct_ action fka `suicide`. -#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)] -#[serde(rename_all = "camelCase")] -pub struct SelfdestructAction { - /// destroyed/suicided address. - pub address: Address, - /// Balance of the contract just before it was destroyed. - pub balance: U256, - /// destroyed contract heir. - pub refund_address: Address, -} - -/// Outcome of a CALL. -#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)] -#[serde(rename_all = "camelCase")] -pub struct CallOutput { - /// Gas used by the call. - pub gas_used: U64, - /// The output data of the call. - pub output: Bytes, -} - -/// Outcome of a CREATE. -#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)] -#[serde(rename_all = "camelCase")] -pub struct CreateOutput { - /// Address of the created contract. - pub address: Address, - /// Contract code. - pub code: Bytes, - /// Gas used by the call. - pub gas_used: U64, -} - -/// Represents the output of a trace. -#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)] -#[serde(untagged)] -pub enum TraceOutput { - /// Output of a regular call transaction. - Call(CallOutput), - /// Output of a CREATE transaction. - Create(CreateOutput), -} - -// === impl TraceOutput === - -impl TraceOutput { - /// Returns the gas used by this trace. - pub fn gas_used(&self) -> U64 { - match self { - TraceOutput::Call(call) => call.gas_used, - TraceOutput::Create(create) => create.gas_used, - } - } - - /// Sets the gas used by this trace. - pub fn set_gas_used(&mut self, gas_used: u64) { - match self { - TraceOutput::Call(call) => call.gas_used = U64::from(gas_used), - TraceOutput::Create(create) => create.gas_used = U64::from(gas_used), - } - } -} - -/// A parity style trace of a transaction. -#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)] -#[serde(rename_all = "camelCase")] -pub struct TransactionTrace { - /// Represents what kind of trace this is - #[serde(flatten)] - pub action: Action, - /// The error message if the transaction failed. - #[serde(skip_serializing_if = "Option::is_none")] - pub error: Option, - /// Output of the trace, can be CALL or CREATE - pub result: Option, - /// How many subtraces this trace has. - pub subtraces: usize, - /// The identifier of this transaction trace in the set. - /// - /// This gives the exact location in the call trace - /// [index in root CALL, index in first CALL, index in second CALL, …]. - pub trace_address: Vec, -} - -/// A wrapper for [TransactionTrace] that includes additional information about the transaction. -#[derive(Clone, Debug, Eq, PartialEq, Deserialize)] -#[serde(rename_all = "camelCase")] -pub struct LocalizedTransactionTrace { - /// Trace of the transaction and its result. - #[serde(flatten)] - pub trace: TransactionTrace, - /// Hash of the block, if not pending. - /// - /// Note: this deviates from which always returns a block number - pub block_hash: Option, - /// Block number the transaction is included in, None if pending. - /// - /// Note: this deviates from which always returns a block number - pub block_number: Option, - /// Hash of the transaction - pub transaction_hash: Option, - /// Transaction index within the block, None if pending. - pub transaction_position: Option, -} - -// Implement Serialize manually to ensure consistent ordering of fields to match other client's -// format -impl Serialize for LocalizedTransactionTrace { - fn serialize(&self, serializer: S) -> Result - where - S: Serializer, - { - let mut s = serializer.serialize_struct("LocalizedTransactionTrace", 9)?; - - let TransactionTrace { action, error, result, subtraces, trace_address } = &self.trace; - - match action { - Action::Call(call_action) => { - s.serialize_field("action", call_action)?; - } - Action::Create(create_action) => { - s.serialize_field("action", create_action)?; - } - Action::Selfdestruct(selfdestruct_action) => { - s.serialize_field("action", selfdestruct_action)?; - } - Action::Reward(reward_action) => { - s.serialize_field("action", reward_action)?; - } - } - if let Some(block_hash) = self.block_hash { - s.serialize_field("blockHash", &block_hash)?; - } - if let Some(block_number) = self.block_number { - s.serialize_field("blockNumber", &block_number)?; - } - - if let Some(error) = error { - s.serialize_field("error", error)?; - } - - match result { - Some(TraceOutput::Call(call)) => { - s.serialize_field("result", call)?; - } - Some(TraceOutput::Create(create)) => { - s.serialize_field("result", create)?; - } - None => {} - } - - s.serialize_field("subtraces", &subtraces)?; - s.serialize_field("traceAddress", &trace_address)?; - - if let Some(transaction_hash) = &self.transaction_hash { - s.serialize_field("transactionHash", transaction_hash)?; - } - if let Some(transaction_position) = &self.transaction_position { - s.serialize_field("transactionPosition", transaction_position)?; - } - - s.serialize_field("type", &action.kind())?; - - s.end() - } -} - -/// A record of a full VM trace for a CALL/CREATE. -#[derive(Clone, Debug, Default, Eq, PartialEq, Serialize, Deserialize)] -#[serde(rename_all = "camelCase")] -pub struct VmTrace { - /// The code to be executed. - pub code: Bytes, - /// All executed instructions. - pub ops: Vec, -} - -/// A record of a single VM instruction, opcode level. -#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)] -#[serde(rename_all = "camelCase")] -pub struct VmInstruction { - /// The gas cost for this instruction. - pub cost: u64, - /// Information concerning the execution of the operation. - pub ex: Option, - /// The program counter. - pub pc: usize, - /// Subordinate trace of the CALL/CREATE if applicable. - pub sub: Option, - /// Stringified opcode. - #[serde(skip_serializing_if = "Option::is_none")] - pub op: Option, - /// Index of the instruction in the set. - #[serde(skip_serializing_if = "Option::is_none")] - pub idx: Option, -} - -/// A record of an executed VM operation. -#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)] -#[serde(rename_all = "camelCase")] -pub struct VmExecutedOperation { - /// The total gas used. - pub used: u64, - /// The stack item placed, if any. - pub push: Vec, - /// If altered, the memory delta. - pub mem: Option, - /// The altered storage value, if any. - pub store: Option, -} - -/// A diff of some chunk of memory. -#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)] -#[serde(rename_all = "camelCase")] -pub struct MemoryDelta { - /// Offset into memory the change begins. - pub off: usize, - /// The changed data. - pub data: Bytes, -} - -/// A diff of some storage value. -#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)] -#[serde(rename_all = "camelCase")] -pub struct StorageDelta { - /// Storage key. - pub key: U256, - /// Storage value belonging to the key. - pub val: U256, -} - -#[cfg(test)] -mod tests { - use super::*; - use serde_json::{json, Value}; - use std::str::FromStr; - - #[test] - fn test_transaction_trace() { - let s = r#"{ - "action": { - "from": "0x66e29f0b6b1b07071f2fde4345d512386cb66f5f", - "callType": "call", - "gas": "0x10bfc", - "input": "0xf6cd1e8d0000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000011c37937e080000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ec6952892271c8ee13f12e118484e03149281c9600000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000010480862479000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000002000000000000000000000000160f5f00288e9e1cc8655b327e081566e580a71d00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000011c37937e080000fffffffffffffffffffffffffffffffffffffffffffffffffee3c86c81f8000000000000000000000000000000000000000000000000000000000000", - "to": "0x160f5f00288e9e1cc8655b327e081566e580a71d", - "value": "0x244b" - }, - "error": "Reverted", - "result": { - "gasUsed": "0x9daf", - "output": "0x000000000000000000000000000000000000000000000000011c37937e080000" - }, - "subtraces": 3, - "traceAddress": [], - "type": "call" - }"#; - let val = serde_json::from_str::(s).unwrap(); - serde_json::to_value(val).unwrap(); - } - - #[test] - fn test_selfdestruct_suicide() { - let input = r#"{ - "action": { - "address": "0x66e29f0b6b1b07071f2fde4345d512386cb66f5f", - "refundAddress": "0x66e29f0b6b1b07071f2fde4345d512386cb66f5f", - "balance": "0x244b" - }, - "error": "Reverted", - "result": { - "gasUsed": "0x9daf", - "output": "0x000000000000000000000000000000000000000000000000011c37937e080000" - }, - "subtraces": 3, - "traceAddress": [], - "type": "suicide" - }"#; - let val = serde_json::from_str::(input).unwrap(); - assert!(val.action.is_selfdestruct()); - - let json = serde_json::to_value(val.clone()).unwrap(); - let expect = serde_json::from_str::(input).unwrap(); - similar_asserts::assert_eq!(json, expect); - let s = serde_json::to_string(&val).unwrap(); - let json = serde_json::from_str::(&s).unwrap(); - similar_asserts::assert_eq!(json, expect); - - let input = input.replace("suicide", "selfdestruct"); - let val = serde_json::from_str::(&input).unwrap(); - assert!(val.action.is_selfdestruct()); - } - - #[derive(Debug)] - struct TraceTestCase { - trace: LocalizedTransactionTrace, - expected_json: Value, - } - - #[test] - fn test_serialization_order() { - let test_cases = vec![ - TraceTestCase { - trace: LocalizedTransactionTrace { - trace: TransactionTrace { - action: Action::Call(CallAction { - from: "0x4f4495243837681061c4743b74b3eedf548d56a5".parse::
().unwrap(), - call_type: CallType::DelegateCall, - gas: U64::from(3148955), - input: Bytes::from_str("0x585a9fd40000000000000000000000000000000000000000000000000000000000000040a47c5ad9a4af285720eae6cc174a9c75c5bbaf973b00f1a0c191327445b6581000000000000000000000000000000000000000000000000000000000000001e000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000140000000000000000000000000ce16f69375520ab01377ce7b88f5ba8c48f8d666f61490331372e432315cd97447e3bc452d6c73a6e0536260a88ddab46f85c88d00000000000000000000000000000000000000000000000000000000000001a0000000000000000000000000000000000000000000000000000000000aab8cf0fbfb038751339cb61161fa11789b41a78f1b7b0e12cf8e467d403590b7a5f26f0000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000646616e746f6d0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002a3078636531364636393337353532306162303133373763653742383866354241384334384638443636360000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000045553444300000000000000000000000000000000000000000000000000000000").unwrap(), - to: "0x99b5fa03a5ea4315725c43346e55a6a6fbd94098".parse::
().unwrap(), - value: U256::from(0), - }), - error: None, - result: Some(TraceOutput::Call(CallOutput { gas_used: U64::from(32364), output: Bytes::new() })), - subtraces: 0, - trace_address: vec![0, 10, 0], - }, - block_hash: Some(B256::ZERO), - block_number: Some(18557272), - transaction_hash: Some(B256::from_str("0x54160ddcdbfaf98a43a43c328ebd44aa99faa765e0daa93e61145b06815a4071").unwrap()), - transaction_position: Some(102), - }, - expected_json: json!({ - "action": { - "from": "0x4f4495243837681061c4743b74b3eedf548d56a5", - "callType": "delegatecall", - "gas": "0x300c9b", - "input": "0x585a9fd40000000000000000000000000000000000000000000000000000000000000040a47c5ad9a4af285720eae6cc174a9c75c5bbaf973b00f1a0c191327445b6581000000000000000000000000000000000000000000000000000000000000001e000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000140000000000000000000000000ce16f69375520ab01377ce7b88f5ba8c48f8d666f61490331372e432315cd97447e3bc452d6c73a6e0536260a88ddab46f85c88d00000000000000000000000000000000000000000000000000000000000001a0000000000000000000000000000000000000000000000000000000000aab8cf0fbfb038751339cb61161fa11789b41a78f1b7b0e12cf8e467d403590b7a5f26f0000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000646616e746f6d0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002a3078636531364636393337353532306162303133373763653742383866354241384334384638443636360000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000045553444300000000000000000000000000000000000000000000000000000000", - "to": "0x99b5fa03a5ea4315725c43346e55a6a6fbd94098", - "value": "0x0" - }, - "blockHash": "0x0000000000000000000000000000000000000000000000000000000000000000", - "blockNumber": 18557272, - "result": { - "gasUsed": "0x7e6c", - "output": "0x" - }, - "subtraces": 0, - "traceAddress": [ - 0, - 10, - 0 - ], - "transactionHash": "0x54160ddcdbfaf98a43a43c328ebd44aa99faa765e0daa93e61145b06815a4071", - "transactionPosition": 102, - "type": "call" - }), - }, - TraceTestCase { - trace: LocalizedTransactionTrace { - trace: TransactionTrace { - action: Action::Create(CreateAction{ - from: "0x4f4495243837681061c4743b74b3eedf548d56a5".parse::
().unwrap(), - gas: U64::from(3438907), - init: Bytes::from_str("0x6080604052600160005534801561001557600080fd5b50610324806100256000396000f3fe608060405234801561001057600080fd5b50600436106100355760003560e01c8062f55d9d1461003a5780631cff79cd1461004f575b600080fd5b61004d6100483660046101da565b610079565b005b61006261005d3660046101fc565b6100bb565b60405161007092919061027f565b60405180910390f35b6002600054141561009d5760405163caa30f5560e01b815260040160405180910390fd5b600260005573ffffffffffffffffffffffffffffffffffffffff8116ff5b60006060600260005414156100e35760405163caa30f5560e01b815260040160405180910390fd5b600260005573ffffffffffffffffffffffffffffffffffffffff85163b610136576040517f6f7c43f100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8473ffffffffffffffffffffffffffffffffffffffff16848460405161015d9291906102de565b6000604051808303816000865af19150503d806000811461019a576040519150601f19603f3d011682016040523d82523d6000602084013e61019f565b606091505b50600160005590969095509350505050565b803573ffffffffffffffffffffffffffffffffffffffff811681146101d557600080fd5b919050565b6000602082840312156101ec57600080fd5b6101f5826101b1565b9392505050565b60008060006040848603121561021157600080fd5b61021a846101b1565b9250602084013567ffffffffffffffff8082111561023757600080fd5b818601915086601f83011261024b57600080fd5b81358181111561025a57600080fd5b87602082850101111561026c57600080fd5b6020830194508093505050509250925092565b821515815260006020604081840152835180604085015260005b818110156102b557858101830151858201606001528201610299565b818111156102c7576000606083870101525b50601f01601f191692909201606001949350505050565b818382376000910190815291905056fea264697066735822122032cb5e746816b7fac95205c068b30da37bd40119a57265be331c162cae74712464736f6c63430008090033").unwrap(), - value: U256::from(0), - }), - error: None, - result: Some(TraceOutput::Create(CreateOutput { gas_used: U64::from(183114), address: "0x7eb6c6c1db08c0b9459a68cfdcedab64f319c138".parse::
().unwrap(), code: Bytes::from_str("0x608060405234801561001057600080fd5b50600436106100355760003560e01c8062f55d9d1461003a5780631cff79cd1461004f575b600080fd5b61004d6100483660046101da565b610079565b005b61006261005d3660046101fc565b6100bb565b60405161007092919061027f565b60405180910390f35b6002600054141561009d5760405163caa30f5560e01b815260040160405180910390fd5b600260005573ffffffffffffffffffffffffffffffffffffffff8116ff5b60006060600260005414156100e35760405163caa30f5560e01b815260040160405180910390fd5b600260005573ffffffffffffffffffffffffffffffffffffffff85163b610136576040517f6f7c43f100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8473ffffffffffffffffffffffffffffffffffffffff16848460405161015d9291906102de565b6000604051808303816000865af19150503d806000811461019a576040519150601f19603f3d011682016040523d82523d6000602084013e61019f565b606091505b50600160005590969095509350505050565b803573ffffffffffffffffffffffffffffffffffffffff811681146101d557600080fd5b919050565b6000602082840312156101ec57600080fd5b6101f5826101b1565b9392505050565b60008060006040848603121561021157600080fd5b61021a846101b1565b9250602084013567ffffffffffffffff8082111561023757600080fd5b818601915086601f83011261024b57600080fd5b81358181111561025a57600080fd5b87602082850101111561026c57600080fd5b6020830194508093505050509250925092565b821515815260006020604081840152835180604085015260005b818110156102b557858101830151858201606001528201610299565b818111156102c7576000606083870101525b50601f01601f191692909201606001949350505050565b818382376000910190815291905056fea264697066735822122032cb5e746816b7fac95205c068b30da37bd40119a57265be331c162cae74712464736f6c63430008090033").unwrap() })), - subtraces: 0, - trace_address: vec![0, 7, 0, 0], - }, - block_hash: Some(B256::from_str("0xd5ac5043011d4f16dba7841fa760c4659644b78f663b901af4673b679605ed0d").unwrap()), - block_number: Some(18557272), - transaction_hash: Some(B256::from_str("0x54160ddcdbfaf98a43a43c328ebd44aa99faa765e0daa93e61145b06815a4071").unwrap()), - transaction_position: Some(102), - }, - expected_json: json!({ - "action": { - "from": "0x4f4495243837681061c4743b74b3eedf548d56a5", - "gas": "0x34793b", - "init": "0x6080604052600160005534801561001557600080fd5b50610324806100256000396000f3fe608060405234801561001057600080fd5b50600436106100355760003560e01c8062f55d9d1461003a5780631cff79cd1461004f575b600080fd5b61004d6100483660046101da565b610079565b005b61006261005d3660046101fc565b6100bb565b60405161007092919061027f565b60405180910390f35b6002600054141561009d5760405163caa30f5560e01b815260040160405180910390fd5b600260005573ffffffffffffffffffffffffffffffffffffffff8116ff5b60006060600260005414156100e35760405163caa30f5560e01b815260040160405180910390fd5b600260005573ffffffffffffffffffffffffffffffffffffffff85163b610136576040517f6f7c43f100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8473ffffffffffffffffffffffffffffffffffffffff16848460405161015d9291906102de565b6000604051808303816000865af19150503d806000811461019a576040519150601f19603f3d011682016040523d82523d6000602084013e61019f565b606091505b50600160005590969095509350505050565b803573ffffffffffffffffffffffffffffffffffffffff811681146101d557600080fd5b919050565b6000602082840312156101ec57600080fd5b6101f5826101b1565b9392505050565b60008060006040848603121561021157600080fd5b61021a846101b1565b9250602084013567ffffffffffffffff8082111561023757600080fd5b818601915086601f83011261024b57600080fd5b81358181111561025a57600080fd5b87602082850101111561026c57600080fd5b6020830194508093505050509250925092565b821515815260006020604081840152835180604085015260005b818110156102b557858101830151858201606001528201610299565b818111156102c7576000606083870101525b50601f01601f191692909201606001949350505050565b818382376000910190815291905056fea264697066735822122032cb5e746816b7fac95205c068b30da37bd40119a57265be331c162cae74712464736f6c63430008090033", - "value": "0x0" - }, - "blockHash": "0xd5ac5043011d4f16dba7841fa760c4659644b78f663b901af4673b679605ed0d", - "blockNumber": 18557272, - "result": { - "address": "0x7eb6c6c1db08c0b9459a68cfdcedab64f319c138", - "code": "0x608060405234801561001057600080fd5b50600436106100355760003560e01c8062f55d9d1461003a5780631cff79cd1461004f575b600080fd5b61004d6100483660046101da565b610079565b005b61006261005d3660046101fc565b6100bb565b60405161007092919061027f565b60405180910390f35b6002600054141561009d5760405163caa30f5560e01b815260040160405180910390fd5b600260005573ffffffffffffffffffffffffffffffffffffffff8116ff5b60006060600260005414156100e35760405163caa30f5560e01b815260040160405180910390fd5b600260005573ffffffffffffffffffffffffffffffffffffffff85163b610136576040517f6f7c43f100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8473ffffffffffffffffffffffffffffffffffffffff16848460405161015d9291906102de565b6000604051808303816000865af19150503d806000811461019a576040519150601f19603f3d011682016040523d82523d6000602084013e61019f565b606091505b50600160005590969095509350505050565b803573ffffffffffffffffffffffffffffffffffffffff811681146101d557600080fd5b919050565b6000602082840312156101ec57600080fd5b6101f5826101b1565b9392505050565b60008060006040848603121561021157600080fd5b61021a846101b1565b9250602084013567ffffffffffffffff8082111561023757600080fd5b818601915086601f83011261024b57600080fd5b81358181111561025a57600080fd5b87602082850101111561026c57600080fd5b6020830194508093505050509250925092565b821515815260006020604081840152835180604085015260005b818110156102b557858101830151858201606001528201610299565b818111156102c7576000606083870101525b50601f01601f191692909201606001949350505050565b818382376000910190815291905056fea264697066735822122032cb5e746816b7fac95205c068b30da37bd40119a57265be331c162cae74712464736f6c63430008090033", - "gasUsed": "0x2cb4a" - }, - "subtraces": 0, - "traceAddress": [ - 0, - 7, - 0, - 0 - ], - "transactionHash": "0x54160ddcdbfaf98a43a43c328ebd44aa99faa765e0daa93e61145b06815a4071", - "transactionPosition": 102, - "type": "create" - }), - } - ]; - - for (i, test_case) in test_cases.iter().enumerate() { - let serialized = serde_json::to_string(&test_case.trace).unwrap(); - let actual_json: Value = serde_json::from_str(&serialized).unwrap(); - - assert_eq!( - actual_json, test_case.expected_json, - "Test case {} failed; Trace: {:?}", - i, test_case.trace - ); - } - } - - #[test] - fn test_deserialize_serialize() { - let reference_data = r#"{ - "action": { - "from": "0xc77820eef59629fc8d88154977bc8de8a1b2f4ae", - "callType": "call", - "gas": "0x4a0d00", - "input": "0x12", - "to": "0x4f4495243837681061c4743b74b3eedf548d56a5", - "value": "0x0" - }, - "blockHash": "0xd5ac5043011d4f16dba7841fa760c4659644b78f663b901af4673b679605ed0d", - "blockNumber": 18557272, - "result": { - "gasUsed": "0x17d337", - "output": "0x" - }, - "subtraces": 1, - "traceAddress": [], - "transactionHash": "0x54160ddcdbfaf98a43a43c328ebd44aa99faa765e0daa93e61145b06815a4071", - "transactionPosition": 102, - "type": "call" -}"#; - - let trace: LocalizedTransactionTrace = serde_json::from_str(reference_data).unwrap(); - assert!(trace.trace.action.is_call()); - let serialized = serde_json::to_string_pretty(&trace).unwrap(); - similar_asserts::assert_eq!(serialized, reference_data); - } - - #[test] - fn test_deserialize_serialize_selfdestruct() { - let reference_data = r#"{ - "action": { - "address": "0xc77820eef59629fc8d88154977bc8de8a1b2f4ae", - "balance": "0x0", - "refundAddress": "0x4f4495243837681061c4743b74b3eedf548d56a5" - }, - "blockHash": "0xd5ac5043011d4f16dba7841fa760c4659644b78f663b901af4673b679605ed0d", - "blockNumber": 18557272, - "result": { - "gasUsed": "0x17d337", - "output": "0x" - }, - "subtraces": 1, - "traceAddress": [], - "transactionHash": "0x54160ddcdbfaf98a43a43c328ebd44aa99faa765e0daa93e61145b06815a4071", - "transactionPosition": 102, - "type": "suicide" -}"#; - - let trace: LocalizedTransactionTrace = serde_json::from_str(reference_data).unwrap(); - assert!(trace.trace.action.is_selfdestruct()); - let serialized = serde_json::to_string_pretty(&trace).unwrap(); - similar_asserts::assert_eq!(serialized, reference_data); - } -} diff --git a/crates/rpc/rpc-types/src/eth/trace/tracerequest.rs b/crates/rpc/rpc-types/src/eth/trace/tracerequest.rs deleted file mode 100644 index 8345006a0..000000000 --- a/crates/rpc/rpc-types/src/eth/trace/tracerequest.rs +++ /dev/null @@ -1,84 +0,0 @@ -//! Builder style functions for `trace_call` - -use crate::{ - eth::block::BlockId, state::StateOverride, trace::parity::TraceType, BlockOverrides, - CallRequest, -}; -use serde::{Deserialize, Serialize}; -use std::collections::HashSet; - -/// Container type for `trace_call` arguments -#[derive(Debug, Serialize, Deserialize, Default)] -pub struct TraceCallRequest { - /// call request object - pub call: CallRequest, - /// trace types - pub trace_types: HashSet, - /// Optional: blockId - pub block_id: Option, - /// Optional: StateOverride - pub state_overrides: Option, - /// Optional: BlockOverrides - pub block_overrides: Option>, -} - -impl TraceCallRequest { - /// Returns a new [`TraceCallRequest`] given a [`CallRequest`] and [`HashSet`] - pub fn new(call: CallRequest) -> Self { - Self { - call, - trace_types: HashSet::new(), - block_id: None, - state_overrides: None, - block_overrides: None, - } - } - - /// Sets the [`BlockId`] - /// Note: this is optional - pub fn with_block_id(mut self, block_id: BlockId) -> Self { - self.block_id = Some(block_id); - self - } - - /// Sets the [`StateOverride`] - /// Note: this is optional - pub fn with_state_override(mut self, state_overrides: StateOverride) -> Self { - self.state_overrides = Some(state_overrides); - self - } - - /// Sets the [`BlockOverrides`] - /// Note: this is optional - pub fn with_block_overrides(mut self, block_overrides: Box) -> Self { - self.block_overrides = Some(block_overrides); - self - } - - /// Inserts a single trace type. - pub fn with_trace_type(mut self, trace_type: TraceType) -> Self { - self.trace_types.insert(trace_type); - self - } - - /// Inserts multiple trace types from an iterator. - pub fn with_trace_types>(mut self, trace_types: I) -> Self { - self.trace_types.extend(trace_types); - self - } - - /// Inserts [`TraceType::Trace`] - pub fn with_trace(self) -> Self { - self.with_trace_type(TraceType::Trace) - } - - /// Inserts [`TraceType::VmTrace`] - pub fn with_vm_trace(self) -> Self { - self.with_trace_type(TraceType::VmTrace) - } - - /// Inserts [`TraceType::StateDiff`] - pub fn with_statediff(self) -> Self { - self.with_trace_type(TraceType::StateDiff) - } -} diff --git a/crates/rpc/rpc-types/src/eth/transaction/access_list.rs b/crates/rpc/rpc-types/src/eth/transaction/access_list.rs deleted file mode 100644 index f6fd892a9..000000000 --- a/crates/rpc/rpc-types/src/eth/transaction/access_list.rs +++ /dev/null @@ -1,89 +0,0 @@ -use alloy_primitives::{Address, B256, U256}; -use serde::{Deserialize, Serialize}; - -/// A list of addresses and storage keys that the transaction plans to access. -/// Accesses outside the list are possible, but become more expensive. -#[derive(Deserialize, Serialize, Clone, Debug, PartialEq, Eq, Hash, Default)] -#[serde(rename_all = "camelCase")] -pub struct AccessListItem { - /// Account addresses that would be loaded at the start of execution - pub address: Address, - /// Keys of storage that would be loaded at the start of execution - pub storage_keys: Vec, -} - -/// AccessList as defined in EIP-2930 -#[derive(Deserialize, Serialize, Clone, Debug, PartialEq, Eq, Hash, Default)] -pub struct AccessList(pub Vec); - -impl AccessList { - /// Converts the list into a vec, expected by revm - pub fn flattened(&self) -> Vec<(Address, Vec)> { - self.flatten().collect() - } - - /// Consumes the type and converts the list into a vec, expected by revm - pub fn into_flattened(self) -> Vec<(Address, Vec)> { - self.into_flatten().collect() - } - - /// Consumes the type and returns an iterator over the list's addresses and storage keys. - pub fn into_flatten(self) -> impl Iterator)> { - self.0.into_iter().map(|item| { - ( - item.address, - item.storage_keys.into_iter().map(|slot| U256::from_be_bytes(slot.0)).collect(), - ) - }) - } - - /// Returns an iterator over the list's addresses and storage keys. - pub fn flatten(&self) -> impl Iterator)> + '_ { - self.0.iter().map(|item| { - ( - item.address, - item.storage_keys.iter().map(|slot| U256::from_be_bytes(slot.0)).collect(), - ) - }) - } -} - -/// Access list with gas used appended. -#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, Default)] -#[serde(rename_all = "camelCase")] -pub struct AccessListWithGasUsed { - /// List with accounts accessed during transaction. - pub access_list: AccessList, - /// Estimated gas used with access list. - pub gas_used: U256, -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn access_list_serde() { - let list = AccessList(vec![ - AccessListItem { address: Address::ZERO, storage_keys: vec![B256::ZERO] }, - AccessListItem { address: Address::ZERO, storage_keys: vec![B256::ZERO] }, - ]); - let json = serde_json::to_string(&list).unwrap(); - let list2 = serde_json::from_str::(&json).unwrap(); - assert_eq!(list, list2); - } - - #[test] - fn access_list_with_gas_used() { - let list = AccessListWithGasUsed { - access_list: AccessList(vec![ - AccessListItem { address: Address::ZERO, storage_keys: vec![B256::ZERO] }, - AccessListItem { address: Address::ZERO, storage_keys: vec![B256::ZERO] }, - ]), - gas_used: U256::from(100), - }; - let json = serde_json::to_string(&list).unwrap(); - let list2 = serde_json::from_str::(&json).unwrap(); - assert_eq!(list, list2); - } -} diff --git a/crates/rpc/rpc-types/src/eth/transaction/common.rs b/crates/rpc/rpc-types/src/eth/transaction/common.rs deleted file mode 100644 index b6217bdb8..000000000 --- a/crates/rpc/rpc-types/src/eth/transaction/common.rs +++ /dev/null @@ -1,19 +0,0 @@ -//! Commonly used additional types that are not part of the JSON RPC spec but are often required -//! when working with RPC types, such as [Transaction](crate::Transaction) - -use alloy_primitives::{TxHash, B256}; - -/// Additional fields in the context of a block that contains this transaction. -#[derive(Debug, Clone, Copy, Default, Eq, PartialEq)] -pub struct TransactionInfo { - /// Hash of the transaction. - pub hash: Option, - /// Index of the transaction in the block - pub index: Option, - /// Hash of the block. - pub block_hash: Option, - /// Number of the block. - pub block_number: Option, - /// Base fee of the block. - pub base_fee: Option, -} diff --git a/crates/rpc/rpc-types/src/eth/transaction/kzg.rs b/crates/rpc/rpc-types/src/eth/transaction/kzg.rs deleted file mode 100644 index 209fcb7a2..000000000 --- a/crates/rpc/rpc-types/src/eth/transaction/kzg.rs +++ /dev/null @@ -1,12 +0,0 @@ -//! KZG type bindings - -use alloy_primitives::FixedBytes; - -/// How many bytes are in a blob -pub const BYTES_PER_BLOB: usize = 131072; - -/// A Blob serialized as 0x-prefixed hex string -pub type Blob = FixedBytes; - -/// A commitment/proof serialized as 0x-prefixed hex string -pub type Bytes48 = FixedBytes<48>; diff --git a/crates/rpc/rpc-types/src/eth/transaction/mod.rs b/crates/rpc/rpc-types/src/eth/transaction/mod.rs index d3d20a9f6..b7a9ab73f 100644 --- a/crates/rpc/rpc-types/src/eth/transaction/mod.rs +++ b/crates/rpc/rpc-types/src/eth/transaction/mod.rs @@ -1,182 +1,5 @@ //! RPC types for transactions - -pub use access_list::{AccessList, AccessListItem, AccessListWithGasUsed}; -use alloy_primitives::{Address, Bytes, B256, U128, U256, U64}; -pub use common::TransactionInfo; -pub use receipt::TransactionReceipt; -pub use request::TransactionRequest; -use serde::{Deserialize, Serialize}; -pub use signature::{Parity, Signature}; -pub use typed::*; - -mod access_list; -mod common; -pub mod kzg; -mod receipt; mod request; -mod signature; mod typed; - -/// Transaction object used in RPC -#[derive(Debug, Default, Clone, PartialEq, Eq, Serialize, Deserialize)] -#[serde(rename_all = "camelCase")] -pub struct Transaction { - /// Hash - pub hash: B256, - /// Nonce - pub nonce: U64, - /// Block hash - pub block_hash: Option, - /// Block number - pub block_number: Option, - /// Transaction Index - pub transaction_index: Option, - /// Sender - pub from: Address, - /// Recipient - pub to: Option
, - /// Transferred value - pub value: U256, - /// Gas Price - #[serde(skip_serializing_if = "Option::is_none")] - pub gas_price: Option, - /// Gas amount - pub gas: U256, - /// Max BaseFeePerGas the user is willing to pay. - #[serde(skip_serializing_if = "Option::is_none")] - pub max_fee_per_gas: Option, - /// The miner's tip. - #[serde(skip_serializing_if = "Option::is_none")] - pub max_priority_fee_per_gas: Option, - /// Configured max fee per blob gas for eip-4844 transactions - #[serde(skip_serializing_if = "Option::is_none")] - pub max_fee_per_blob_gas: Option, - /// Data - pub input: Bytes, - /// All _flattened_ fields of the transaction signature. - /// - /// Note: this is an option so special transaction types without a signature (e.g. ) can be supported. - #[serde(flatten, skip_serializing_if = "Option::is_none")] - pub signature: Option, - /// The chain id of the transaction, if any. - pub chain_id: Option, - /// Contains the blob hashes for eip-4844 transactions. - #[serde(default, skip_serializing_if = "Vec::is_empty")] - pub blob_versioned_hashes: Vec, - /// EIP2930 - /// - /// Pre-pay to warm storage access. - #[serde(skip_serializing_if = "Option::is_none")] - pub access_list: Option>, - /// EIP2718 - /// - /// Transaction type, Some(2) for EIP-1559 transaction, - /// Some(1) for AccessList transaction, None for Legacy - #[serde(rename = "type", skip_serializing_if = "Option::is_none")] - pub transaction_type: Option, - - /// Optimism specific transaction fields - #[cfg(feature = "optimism")] - #[serde(flatten)] - pub optimism: OptimismTransactionFields, -} - -/// Optimism specific transaction fields -#[cfg(feature = "optimism")] -#[derive(Debug, Default, Clone, PartialEq, Eq, Serialize, Deserialize)] -pub struct OptimismTransactionFields { - /// Hash that uniquely identifies the source of the deposit. - #[serde(rename = "sourceHash", skip_serializing_if = "Option::is_none")] - pub source_hash: Option, - /// The ETH value to mint on L2 - #[serde(rename = "mint", skip_serializing_if = "Option::is_none")] - pub mint: Option, - /// Field indicating whether the transaction is a system transaction, and therefore - /// exempt from the L2 gas limit. - #[serde(rename = "isSystemTx", skip_serializing_if = "Option::is_none")] - pub is_system_tx: Option, -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::eth::transaction::signature::Parity; - - #[test] - fn serde_transaction() { - let transaction = Transaction { - hash: B256::with_last_byte(1), - nonce: U64::from(2), - block_hash: Some(B256::with_last_byte(3)), - block_number: Some(U256::from(4)), - transaction_index: Some(U256::from(5)), - from: Address::with_last_byte(6), - to: Some(Address::with_last_byte(7)), - value: U256::from(8), - gas_price: Some(U128::from(9)), - gas: U256::from(10), - input: Bytes::from(vec![11, 12, 13]), - signature: Some(Signature { - v: U256::from(14), - r: U256::from(14), - s: U256::from(14), - y_parity: None, - }), - chain_id: Some(U64::from(17)), - blob_versioned_hashes: vec![], - access_list: None, - transaction_type: Some(U64::from(20)), - max_fee_per_gas: Some(U128::from(21)), - max_priority_fee_per_gas: Some(U128::from(22)), - max_fee_per_blob_gas: None, - #[cfg(feature = "optimism")] - optimism: Default::default(), - }; - let serialized = serde_json::to_string(&transaction).unwrap(); - assert_eq!( - serialized, - r#"{"hash":"0x0000000000000000000000000000000000000000000000000000000000000001","nonce":"0x2","blockHash":"0x0000000000000000000000000000000000000000000000000000000000000003","blockNumber":"0x4","transactionIndex":"0x5","from":"0x0000000000000000000000000000000000000006","to":"0x0000000000000000000000000000000000000007","value":"0x8","gasPrice":"0x9","gas":"0xa","maxFeePerGas":"0x15","maxPriorityFeePerGas":"0x16","input":"0x0b0c0d","r":"0xe","s":"0xe","v":"0xe","chainId":"0x11","type":"0x14"}"# - ); - let deserialized: Transaction = serde_json::from_str(&serialized).unwrap(); - assert_eq!(transaction, deserialized); - } - - #[test] - fn serde_transaction_with_parity_bit() { - let transaction = Transaction { - hash: B256::with_last_byte(1), - nonce: U64::from(2), - block_hash: Some(B256::with_last_byte(3)), - block_number: Some(U256::from(4)), - transaction_index: Some(U256::from(5)), - from: Address::with_last_byte(6), - to: Some(Address::with_last_byte(7)), - value: U256::from(8), - gas_price: Some(U128::from(9)), - gas: U256::from(10), - input: Bytes::from(vec![11, 12, 13]), - signature: Some(Signature { - v: U256::from(14), - r: U256::from(14), - s: U256::from(14), - y_parity: Some(Parity(true)), - }), - chain_id: Some(U64::from(17)), - blob_versioned_hashes: vec![], - access_list: None, - transaction_type: Some(U64::from(20)), - max_fee_per_gas: Some(U128::from(21)), - max_priority_fee_per_gas: Some(U128::from(22)), - max_fee_per_blob_gas: None, - #[cfg(feature = "optimism")] - optimism: Default::default(), - }; - let serialized = serde_json::to_string(&transaction).unwrap(); - assert_eq!( - serialized, - r#"{"hash":"0x0000000000000000000000000000000000000000000000000000000000000001","nonce":"0x2","blockHash":"0x0000000000000000000000000000000000000000000000000000000000000003","blockNumber":"0x4","transactionIndex":"0x5","from":"0x0000000000000000000000000000000000000006","to":"0x0000000000000000000000000000000000000007","value":"0x8","gasPrice":"0x9","gas":"0xa","maxFeePerGas":"0x15","maxPriorityFeePerGas":"0x16","input":"0x0b0c0d","r":"0xe","s":"0xe","v":"0xe","yParity":"0x1","chainId":"0x11","type":"0x14"}"# - ); - let deserialized: Transaction = serde_json::from_str(&serialized).unwrap(); - assert_eq!(transaction, deserialized); - } -} +pub use request::TransactionRequest; +pub use typed::*; diff --git a/crates/rpc/rpc-types/src/eth/transaction/receipt.rs b/crates/rpc/rpc-types/src/eth/transaction/receipt.rs deleted file mode 100644 index c780aa216..000000000 --- a/crates/rpc/rpc-types/src/eth/transaction/receipt.rs +++ /dev/null @@ -1,74 +0,0 @@ -use crate::Log; -use alloy_primitives::{Address, Bloom, B256, U128, U256, U64, U8}; -use serde::{Deserialize, Serialize}; - -/// Transaction receipt -#[derive(Clone, Default, Debug, Eq, PartialEq, Serialize, Deserialize)] -#[serde(rename_all = "camelCase")] -pub struct TransactionReceipt { - /// Transaction Hash. - pub transaction_hash: Option, - /// Index within the block. - pub transaction_index: U64, - /// Hash of the block this transaction was included within. - pub block_hash: Option, - /// Number of the block this transaction was included within. - pub block_number: Option, - /// Cumulative gas used within the block after this was executed. - pub cumulative_gas_used: U256, - /// Gas used by this transaction alone. - pub gas_used: Option, - /// The price paid post-execution by the transaction (i.e. base fee + priority fee). Both - /// fields in 1559-style transactions are maximums (max fee + max priority fee), the amount - /// that's actually paid by users can only be determined post-execution - pub effective_gas_price: U128, - /// Blob gas used by the eip-4844 transaction - /// - /// This is None for non eip-4844 transactions - #[serde(skip_serializing_if = "Option::is_none")] - pub blob_gas_used: Option, - /// The price paid by the eip-4844 transaction per blob gas. - #[serde(skip_serializing_if = "Option::is_none")] - pub blob_gas_price: Option, - /// Address of the sender - pub from: Address, - /// Address of the receiver. null when its a contract creation transaction. - pub to: Option
, - /// Contract address created, or None if not a deployment. - pub contract_address: Option
, - /// Logs emitted by this transaction. - pub logs: Vec, - /// Logs bloom - pub logs_bloom: Bloom, - /// The post-transaction stateroot (pre Byzantium) - /// - /// EIP98 makes this optional field, if it's missing then skip serializing it - #[serde(skip_serializing_if = "Option::is_none", rename = "root")] - pub state_root: Option, - /// Status: either 1 (success) or 0 (failure). Only present after activation of EIP-658 - #[serde(skip_serializing_if = "Option::is_none", rename = "status")] - pub status_code: Option, - /// EIP-2718 Transaction type, Some(1) for AccessList transaction, None for Legacy - #[serde(rename = "type")] - pub transaction_type: U8, - /// Deposit nonce for deposit transactions post-regolith - #[cfg(feature = "optimism")] - #[serde(skip_serializing_if = "Option::is_none")] - pub deposit_nonce: Option, - /// L1 fee for the transaction - #[cfg(feature = "optimism")] - #[serde(skip_serializing_if = "Option::is_none")] - pub l1_fee: Option, - /// L1 fee scalar for the transaction - #[cfg(feature = "optimism")] - #[serde(skip_serializing_if = "Option::is_none")] - pub l1_fee_scalar: Option, - /// L1 gas price for the transaction - #[cfg(feature = "optimism")] - #[serde(skip_serializing_if = "Option::is_none")] - pub l1_gas_price: Option, - /// L1 gas used for the transaction - #[cfg(feature = "optimism")] - #[serde(skip_serializing_if = "Option::is_none")] - pub l1_gas_used: Option, -} diff --git a/crates/rpc/rpc-types/src/eth/transaction/request.rs b/crates/rpc/rpc-types/src/eth/transaction/request.rs index ebb9dbaf2..72cc85bd8 100644 --- a/crates/rpc/rpc-types/src/eth/transaction/request.rs +++ b/crates/rpc/rpc-types/src/eth/transaction/request.rs @@ -1,7 +1,8 @@ -use crate::eth::transaction::{ - typed::{ +use crate::{ + eth::transaction::typed::{ BlobTransactionSidecar, EIP1559TransactionRequest, EIP2930TransactionRequest, - LegacyTransactionRequest, TransactionKind, TypedTransactionRequest, + EIP4844TransactionRequest, LegacyTransactionRequest, TransactionKind, + TypedTransactionRequest, }, AccessList, }; @@ -141,7 +142,7 @@ impl TransactionRequest { Some(sidecar), ) => { // As per the EIP, we follow the same semantics as EIP-1559. - Some(TypedTransactionRequest::EIP4844(crate::EIP4844TransactionRequest { + Some(TypedTransactionRequest::EIP4844(EIP4844TransactionRequest { chain_id: 0, nonce: nonce.unwrap_or_default(), max_priority_fee_per_gas: max_priority_fee_per_gas.unwrap_or_default(), diff --git a/crates/rpc/rpc-types/src/eth/transaction/signature.rs b/crates/rpc/rpc-types/src/eth/transaction/signature.rs deleted file mode 100644 index 7ffe64a4d..000000000 --- a/crates/rpc/rpc-types/src/eth/transaction/signature.rs +++ /dev/null @@ -1,190 +0,0 @@ -//! Signature related RPC values - -use alloy_primitives::U256; -use serde::{Deserialize, Serialize}; - -/// Container type for all signature fields in RPC -#[derive(Debug, Clone, PartialEq, Eq, Hash, Default, Serialize, Deserialize)] -pub struct Signature { - /// The R field of the signature; the point on the curve. - pub r: U256, - /// The S field of the signature; the point on the curve. - pub s: U256, - // TODO: change these fields to an untagged enum for `v` XOR `y_parity` if/when CLs support it. - // See for more information - /// For EIP-155, EIP-2930 and Blob transactions this is set to the parity (0 for even, 1 for - /// odd) of the y-value of the secp256k1 signature. - /// - /// For legacy transactions, this is the recovery id - /// - /// See also and - pub v: U256, - /// The y parity of the signature. This is only used for typed (non-legacy) transactions. - #[serde(default, rename = "yParity", skip_serializing_if = "Option::is_none")] - pub y_parity: Option, -} - -/// Type that represents the signature parity byte, meant for use in RPC. -/// -/// This will be serialized as "0x0" if false, and "0x1" if true. -#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)] -pub struct Parity( - #[serde(serialize_with = "serialize_parity", deserialize_with = "deserialize_parity")] pub bool, -); - -fn serialize_parity(parity: &bool, serializer: S) -> Result -where - S: serde::Serializer, -{ - serializer.serialize_str(if *parity { "0x1" } else { "0x0" }) -} - -/// This implementation disallows serialization of the y parity bit that are not `"0x0"` or `"0x1"`. -fn deserialize_parity<'de, D>(deserializer: D) -> Result -where - D: serde::Deserializer<'de>, -{ - let s = String::deserialize(deserializer)?; - match s.as_str() { - "0x0" => Ok(false), - "0x1" => Ok(true), - _ => Err(serde::de::Error::custom(format!( - "invalid parity value, parity should be either \"0x0\" or \"0x1\": {}", - s - ))), - } -} - -#[cfg(test)] -mod tests { - use std::str::FromStr; - - use super::*; - - #[test] - fn deserialize_without_parity() { - let raw_signature_without_y_parity = r#"{ - "r":"0xc569c92f176a3be1a6352dd5005bfc751dcb32f57623dd2a23693e64bf4447b0", - "s":"0x1a891b566d369e79b7a66eecab1e008831e22daa15f91a0a0cf4f9f28f47ee05", - "v":"0x1" - }"#; - - let signature: Signature = serde_json::from_str(raw_signature_without_y_parity).unwrap(); - let expected = Signature { - r: U256::from_str("0xc569c92f176a3be1a6352dd5005bfc751dcb32f57623dd2a23693e64bf4447b0") - .unwrap(), - s: U256::from_str("0x1a891b566d369e79b7a66eecab1e008831e22daa15f91a0a0cf4f9f28f47ee05") - .unwrap(), - v: U256::from_str("1").unwrap(), - y_parity: None, - }; - - assert_eq!(signature, expected); - } - - #[test] - fn deserialize_with_parity() { - let raw_signature_with_y_parity = r#"{ - "r":"0xc569c92f176a3be1a6352dd5005bfc751dcb32f57623dd2a23693e64bf4447b0", - "s":"0x1a891b566d369e79b7a66eecab1e008831e22daa15f91a0a0cf4f9f28f47ee05", - "v":"0x1", - "yParity": "0x1" - }"#; - - let signature: Signature = serde_json::from_str(raw_signature_with_y_parity).unwrap(); - let expected = Signature { - r: U256::from_str("0xc569c92f176a3be1a6352dd5005bfc751dcb32f57623dd2a23693e64bf4447b0") - .unwrap(), - s: U256::from_str("0x1a891b566d369e79b7a66eecab1e008831e22daa15f91a0a0cf4f9f28f47ee05") - .unwrap(), - v: U256::from_str("1").unwrap(), - y_parity: Some(Parity(true)), - }; - - assert_eq!(signature, expected); - } - - #[test] - fn serialize_both_parity() { - // this test should be removed if the struct moves to an enum based on tx type - let signature = Signature { - r: U256::from_str("0xc569c92f176a3be1a6352dd5005bfc751dcb32f57623dd2a23693e64bf4447b0") - .unwrap(), - s: U256::from_str("0x1a891b566d369e79b7a66eecab1e008831e22daa15f91a0a0cf4f9f28f47ee05") - .unwrap(), - v: U256::from_str("1").unwrap(), - y_parity: Some(Parity(true)), - }; - - let serialized = serde_json::to_string(&signature).unwrap(); - assert_eq!( - serialized, - r#"{"r":"0xc569c92f176a3be1a6352dd5005bfc751dcb32f57623dd2a23693e64bf4447b0","s":"0x1a891b566d369e79b7a66eecab1e008831e22daa15f91a0a0cf4f9f28f47ee05","v":"0x1","yParity":"0x1"}"# - ); - } - - #[test] - fn serialize_v_only() { - // this test should be removed if the struct moves to an enum based on tx type - let signature = Signature { - r: U256::from_str("0xc569c92f176a3be1a6352dd5005bfc751dcb32f57623dd2a23693e64bf4447b0") - .unwrap(), - s: U256::from_str("0x1a891b566d369e79b7a66eecab1e008831e22daa15f91a0a0cf4f9f28f47ee05") - .unwrap(), - v: U256::from_str("1").unwrap(), - y_parity: None, - }; - - let expected = r#"{"r":"0xc569c92f176a3be1a6352dd5005bfc751dcb32f57623dd2a23693e64bf4447b0","s":"0x1a891b566d369e79b7a66eecab1e008831e22daa15f91a0a0cf4f9f28f47ee05","v":"0x1"}"#; - - let serialized = serde_json::to_string(&signature).unwrap(); - assert_eq!(serialized, expected); - } - - #[test] - fn serialize_parity() { - let parity = Parity(true); - let serialized = serde_json::to_string(&parity).unwrap(); - assert_eq!(serialized, r#""0x1""#); - - let parity = Parity(false); - let serialized = serde_json::to_string(&parity).unwrap(); - assert_eq!(serialized, r#""0x0""#); - } - - #[test] - fn deserialize_parity() { - let raw_parity = r#""0x1""#; - let parity: Parity = serde_json::from_str(raw_parity).unwrap(); - assert_eq!(parity, Parity(true)); - - let raw_parity = r#""0x0""#; - let parity: Parity = serde_json::from_str(raw_parity).unwrap(); - assert_eq!(parity, Parity(false)); - } - - #[test] - fn deserialize_parity_invalid() { - let raw_parity = r#""0x2""#; - let parity: Result = serde_json::from_str(raw_parity); - assert!(parity.is_err()); - - let raw_parity = r#""0x""#; - let parity: Result = serde_json::from_str(raw_parity); - assert!(parity.is_err()); - - // In the spec this is defined as a uint, which requires 0x - // yParity: - // - // - // uint: - // - let raw_parity = r#""1""#; - let parity: Result = serde_json::from_str(raw_parity); - assert!(parity.is_err()); - - let raw_parity = r#""0""#; - let parity: Result = serde_json::from_str(raw_parity); - assert!(parity.is_err()); - } -} diff --git a/crates/rpc/rpc-types/src/eth/transaction/typed.rs b/crates/rpc/rpc-types/src/eth/transaction/typed.rs index 65c577680..ce10976bf 100644 --- a/crates/rpc/rpc-types/src/eth/transaction/typed.rs +++ b/crates/rpc/rpc-types/src/eth/transaction/typed.rs @@ -4,12 +4,12 @@ #![allow(missing_docs)] -use crate::{ - eth::transaction::AccessList, - kzg::{Blob, Bytes48}, -}; use alloy_primitives::{Address, Bytes, B256, U128, U256, U64}; use alloy_rlp::{BufMut, Decodable, Encodable, Error as RlpError}; +use alloy_rpc_types::{ + kzg::{Blob, Bytes48}, + AccessList, +}; use serde::{Deserialize, Serialize}; /// Container type for various Ethereum transaction requests diff --git a/crates/rpc/rpc-types/src/eth/txpool.rs b/crates/rpc/rpc-types/src/eth/txpool.rs deleted file mode 100644 index e78e56069..000000000 --- a/crates/rpc/rpc-types/src/eth/txpool.rs +++ /dev/null @@ -1,520 +0,0 @@ -//! Types for the `txpool` namespace: - -use crate::Transaction; -use alloy_primitives::{Address, U256, U64}; -use serde::{ - de::{self, Deserializer, Visitor}, - Deserialize, Serialize, -}; -use std::{collections::BTreeMap, fmt, str::FromStr}; - -/// Transaction summary as found in the Txpool Inspection property. -#[derive(Debug, Clone, PartialEq, Eq)] -pub struct TxpoolInspectSummary { - /// Recipient (None when contract creation) - pub to: Option
, - /// Transferred value - pub value: U256, - /// Gas amount - pub gas: U256, - /// Gas Price - pub gas_price: U256, -} - -/// Visitor struct for TxpoolInspectSummary. -struct TxpoolInspectSummaryVisitor; - -/// Walk through the deserializer to parse a txpool inspection summary into the -/// `TxpoolInspectSummary` struct. -impl<'de> Visitor<'de> for TxpoolInspectSummaryVisitor { - type Value = TxpoolInspectSummary; - - fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { - formatter.write_str("to: value wei + gasLimit gas × gas_price wei") - } - - fn visit_str(self, value: &str) -> Result - where - E: de::Error, - { - let addr_split: Vec<&str> = value.split(": ").collect(); - if addr_split.len() != 2 { - return Err(de::Error::custom("invalid format for TxpoolInspectSummary: to")) - } - let value_split: Vec<&str> = addr_split[1].split(" wei + ").collect(); - if value_split.len() != 2 { - return Err(de::Error::custom("invalid format for TxpoolInspectSummary: gasLimit")) - } - let gas_split: Vec<&str> = value_split[1].split(" gas × ").collect(); - if gas_split.len() != 2 { - return Err(de::Error::custom("invalid format for TxpoolInspectSummary: gas")) - } - let gas_price_split: Vec<&str> = gas_split[1].split(" wei").collect(); - if gas_price_split.len() != 2 { - return Err(de::Error::custom("invalid format for TxpoolInspectSummary: gas_price")) - } - let addr = match addr_split[0] { - "" => None, - "0x" => None, - "contract creation" => None, - addr => { - Some(Address::from_str(addr.trim_start_matches("0x")).map_err(de::Error::custom)?) - } - }; - let value = U256::from_str(value_split[0]).map_err(de::Error::custom)?; - let gas = U256::from_str(gas_split[0]).map_err(de::Error::custom)?; - let gas_price = U256::from_str(gas_price_split[0]).map_err(de::Error::custom)?; - - Ok(TxpoolInspectSummary { to: addr, value, gas, gas_price }) - } - - fn visit_string(self, value: String) -> Result - where - E: de::Error, - { - self.visit_str(&value) - } -} - -/// Implement the `Deserialize` trait for `TxpoolInspectSummary` struct. -impl<'de> Deserialize<'de> for TxpoolInspectSummary { - fn deserialize(deserializer: D) -> Result - where - D: Deserializer<'de>, - { - deserializer.deserialize_str(TxpoolInspectSummaryVisitor) - } -} - -/// Implement the `Serialize` trait for `TxpoolInspectSummary` struct so that the -/// format matches the one from geth. -impl Serialize for TxpoolInspectSummary { - fn serialize(&self, serializer: S) -> Result - where - S: serde::Serializer, - { - let formatted_to = if let Some(to) = self.to { - format!("{to:?}") - } else { - "contract creation".to_string() - }; - let formatted = format!( - "{}: {} wei + {} gas × {} wei", - formatted_to, self.value, self.gas, self.gas_price - ); - serializer.serialize_str(&formatted) - } -} - -/// Transaction Pool Content -/// -/// The content inspection property can be queried to list the exact details of all -/// the transactions currently pending for inclusion in the next block(s), as well -/// as the ones that are being scheduled for future execution only. -/// -/// See [here](https://geth.ethereum.org/docs/rpc/ns-txpool#txpool_content) for more details -#[derive(Debug, Default, Clone, PartialEq, Eq, Serialize, Deserialize)] -pub struct TxpoolContent { - /// pending tx - pub pending: BTreeMap>, - /// queued tx - pub queued: BTreeMap>, -} - -impl TxpoolContent { - /// Removes the transactions from the given sender - pub fn remove_from(&mut self, sender: &Address) -> TxpoolContentFrom { - TxpoolContentFrom { - pending: self.pending.remove(sender).unwrap_or_default(), - queued: self.queued.remove(sender).unwrap_or_default(), - } - } -} - -/// Transaction Pool Content From -/// -/// Same as [TxpoolContent] but for a specific address. -/// -/// See [here](https://geth.ethereum.org/docs/rpc/ns-txpool#txpool_contentFrom) for more details -#[derive(Debug, Default, Clone, PartialEq, Eq, Serialize, Deserialize)] -pub struct TxpoolContentFrom { - /// pending tx - pub pending: BTreeMap, - /// queued tx - pub queued: BTreeMap, -} - -/// Transaction Pool Inspect -/// -/// The inspect inspection property can be queried to list a textual summary -/// of all the transactions currently pending for inclusion in the next block(s), -/// as well as the ones that are being scheduled for future execution only. -/// This is a method specifically tailored to developers to quickly see the -/// transactions in the pool and find any potential issues. -/// -/// See [here](https://geth.ethereum.org/docs/rpc/ns-txpool#txpool_inspect) for more details -#[derive(Debug, Default, Clone, PartialEq, Eq, Serialize, Deserialize)] -pub struct TxpoolInspect { - /// pending tx - pub pending: BTreeMap>, - /// queued tx - pub queued: BTreeMap>, -} - -/// Transaction Pool Status -/// -/// The status inspection property can be queried for the number of transactions -/// currently pending for inclusion in the next block(s), as well as the ones that -/// are being scheduled for future execution only. -/// -/// See [here](https://geth.ethereum.org/docs/rpc/ns-txpool#txpool_status) for more details -#[derive(Debug, Default, Clone, PartialEq, Eq, Deserialize, Serialize)] -pub struct TxpoolStatus { - /// number of pending tx - pub pending: U64, - /// number of queued tx - pub queued: U64, -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn serde_txpool_content() { - // Gathered from geth v1.10.23-stable-d901d853/linux-amd64/go1.18.5 - // Addresses used as keys in `pending` and `queued` have been manually lowercased to - // simplify the test. - let txpool_content_json = r#" -{ - "pending": { - "0x00000000863b56a3c1f0f1be8bc4f8b7bd78f57a": { - "29": { - "blockHash": null, - "blockNumber": null, - "from": "0x00000000863b56a3c1f0f1be8bc4f8b7bd78f57a", - "gas": "0x2af9e", - "gasPrice": "0x218711a00", - "maxFeePerGas": "0x218711a00", - "maxPriorityFeePerGas": "0x3b9aca00", - "hash": "0xfbc6fd04ba1c4114f06574263f04099b4fb2da72acc6f9709f0a3d2361308344", - "input": "0x5ae401dc00000000000000000000000000000000000000000000000000000000636c757700000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000e404e45aaf000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb480000000000000000000000006b175474e89094c44da98b954eedeac495271d0f000000000000000000000000000000000000000000000000000000000000006400000000000000000000000000000000863b56a3c1f0f1be8bc4f8b7bd78f57a000000000000000000000000000000000000000000000000000000007781df4000000000000000000000000000000000000000000000006c240454bf9c87cd84000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", - "nonce": "0x1d", - "to": "0x68b3465833fb72a70ecdf485e0e4c7bd8665fc45", - "transactionIndex": null, - "value": "0x0", - "type": "0x2", - "accessList": [], - "chainId": "0x1", - "v": "0x0", - "r": "0xbb809ae71b03319ba2811ebd581c85665169143ffade86e07d2eb4cd03b544dc", - "s": "0x65a2aa7e0e70356f765205a611d580de8e84fa79086f117fd9ab4765f5cf1339" - } - }, - "0x000042429c09de5881f05a0c2a068222f4f5b091": { - "38": { - "blockHash": null, - "blockNumber": null, - "from": "0x000042429c09de5881f05a0c2a068222f4f5b091", - "gas": "0x61a80", - "gasPrice": "0x2540be400", - "hash": "0x054ad1ccf5917139a9b1952f62048f742255a7c11100f593c4f18c1ed49b8dfd", - "input": "0x27dc297e800332e506f28f49a13c1edf087bdd6482d6cb3abdf2a4c455642aef1e98fc240000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000002d7b22444149223a313439332e37342c2254555344223a313438392e36362c2255534443223a313439322e34387d00000000000000000000000000000000000000", - "nonce": "0x26", - "to": "0xabd279febe00c93fb0c9e683c6919ec4f107241f", - "transactionIndex": null, - "value": "0x0", - "type": "0x0", - "chainId": "0x1", - "v": "0x26", - "r": "0xaf46b2c0f067f7d1d63ac19daa349c0e1eb83f019ee00542ffa7095e05352e92", - "s": "0x21d6d24d58ec361379ffffe4cc17bec8ce2b9f5f9759a91afc9a54dfdfa519c2" - } - }, - "0x000fab888651fbceb55de230493562159ead0340": { - "12": { - "blockHash": null, - "blockNumber": null, - "from": "0x000fab888651fbceb55de230493562159ead0340", - "gas": "0x12fed", - "gasPrice": "0x1a13b8600", - "maxFeePerGas": "0x1a13b8600", - "maxPriorityFeePerGas": "0x59682f00", - "hash": "0xfae0cffdae6774abe11662a2cdbea019fce48fca87ba9ebf5e9e7c2454c01715", - "input": "0xa9059cbb00000000000000000000000050272a56ef9aff7238e8b40347da62e87c1f69e200000000000000000000000000000000000000000000000000000000428d3dfc", - "nonce": "0xc", - "to": "0x8e8d6ab093905c400d583efd37fbeeb1ee1c0c39", - "transactionIndex": null, - "value": "0x0", - "type": "0x2", - "accessList": [], - "chainId": "0x1", - "v": "0x0", - "r": "0x7b717e689d1bd045ee7afd79b97219f2e36bd22a6a14e07023902194bca96fbf", - "s": "0x7b0ba462c98e7b0f95a53f047cf568ee0443839628dfe4ab294bfab88fa8e251" - } - } - }, - "queued": { - "0x00b846f07f5e7c61569437ca16f88a9dfa00f1bf": { - "143": { - "blockHash": null, - "blockNumber": null, - "from": "0x00b846f07f5e7c61569437ca16f88a9dfa00f1bf", - "gas": "0x33c3b", - "gasPrice": "0x218711a00", - "maxFeePerGas": "0x218711a00", - "maxPriorityFeePerGas": "0x77359400", - "hash": "0x68959706857f7a58d752ede0a5118a5f55f4ae40801f31377e1af201944720b2", - "input": "0x03a9ea6d00000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000f2ff840000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000041d0c4694374d7893d63605625687be2f01028a5b49eca00f72901e773ad8ba7906e58d43e114a28353efaf8abd6a2675de83a3a07af579b8b268e6b714376610d1c00000000000000000000000000000000000000000000000000000000000000", - "nonce": "0x8f", - "to": "0xfbddadd80fe7bda00b901fbaf73803f2238ae655", - "transactionIndex": null, - "value": "0x1f58a57c1794eb", - "type": "0x2", - "accessList": [], - "chainId": "0x1", - "v": "0x0", - "r": "0x77d149add2b1b84af9408af55661b05b21e2a436f9bfcaa844584905a0f8f1ac", - "s": "0x358d79063d702f0c3fb46ad0f6ce5db61f5fdb0b20359c8da2e72a11988db283" - } - }, - "0x025276ec2de8ee570cfd4c1010319f14a6d9f0dd": { - "1": { - "blockHash": null, - "blockNumber": null, - "from": "0x025276ec2de8ee570cfd4c1010319f14a6d9f0dd", - "gas": "0x7918", - "gasPrice": "0x12e531724e", - "maxFeePerGas": "0x12e531724e", - "maxPriorityFeePerGas": "0x59682f00", - "hash": "0x35109918ab6129a4d69480514ebec0ea08dc4a4de032fec59003ea66718828c4", - "input": "0x", - "nonce": "0x1", - "to": "0x025276ec2de8ee570cfd4c1010319f14a6d9f0dd", - "transactionIndex": null, - "value": "0x0", - "type": "0x2", - "accessList": [], - "chainId": "0x1", - "v": "0x0", - "r": "0x863ed0413a14f3f1695fd9728f1500a2b46e69d6f4c82408af15354cc5a667d6", - "s": "0x2d503050aa1c9ecbb6df9957459c296f2f6190bc07aa09047d541233100b1c7a" - }, - "4": { - "blockHash": null, - "blockNumber": null, - "from": "0x025276ec2de8ee570cfd4c1010319f14a6d9f0dd", - "gas": "0x7530", - "gasPrice": "0x1919617600", - "maxFeePerGas": "0x1919617600", - "maxPriorityFeePerGas": "0x5c7261c0", - "hash": "0xa58e54464b2ca62a5e2d976604ed9a53b13f8823a170ee4c3ae0cd91cde2a6c5", - "input": "0x", - "nonce": "0x4", - "to": "0x025276ec2de8ee570cfd4c1010319f14a6d9f0dd", - "transactionIndex": null, - "value": "0x0", - "type": "0x2", - "accessList": [], - "chainId": "0x1", - "v": "0x1", - "r": "0xb6a571191c4b5b667876295571c42c9411bbb4569eea1a6ad149572e4efc55a9", - "s": "0x248a72dab9b24568dd9cbe289c205eaba1a6b58b32b5a96c48554945d3fd0d86" - } - }, - "0x02666081cfb787de3562efbbca5f0fe890e927f1": { - "44": { - "blockHash": null, - "blockNumber": null, - "from": "0x02666081cfb787de3562efbbca5f0fe890e927f1", - "gas": "0x16404", - "gasPrice": "0x4bad00695", - "maxFeePerGas": "0x4bad00695", - "maxPriorityFeePerGas": "0xa3e9ab80", - "hash": "0xf627e59d7a59eb650f4c9df222858572601a566263809fdacbb755ac2277a4a7", - "input": "0x095ea7b300000000000000000000000029fbd00940df70cfc5dad3f2370686991e2bbf5cffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", - "nonce": "0x2c", - "to": "0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48", - "transactionIndex": null, - "value": "0x0", - "type": "0x2", - "accessList": [], - "chainId": "0x1", - "v": "0x1", - "r": "0xcfc88f55fc0779d12705acba58719cd7d0ed5b0c1a7c3c3682b56397ca493dd5", - "s": "0x7e7dc008058c543ebfdae67154c797639447db5e8006f8fc0585352d857c1b6c" - } - } - } -}"#; - let deserialized: TxpoolContent = serde_json::from_str(txpool_content_json).unwrap(); - let serialized: String = serde_json::to_string_pretty(&deserialized).unwrap(); - - let origin: serde_json::Value = serde_json::from_str(txpool_content_json).unwrap(); - let serialized_value = serde_json::to_value(deserialized.clone()).unwrap(); - assert_eq!(origin, serialized_value); - assert_eq!(deserialized, serde_json::from_str::(&serialized).unwrap()); - } - - #[test] - fn serde_txpool_inspect() { - let txpool_inspect_json = r#" -{ - "pending": { - "0x0512261a7486b1e29704ac49a5eb355b6fd86872": { - "124930": "0x000000000000000000000000000000000000007E: 0 wei + 100187 gas × 20000000000 wei" - }, - "0x201354729f8d0f8b64e9a0c353c672c6a66b3857": { - "252350": "0xd10e3Be2bc8f959Bc8C41CF65F60dE721cF89ADF: 0 wei + 65792 gas × 2000000000 wei", - "252351": "0xd10e3Be2bc8f959Bc8C41CF65F60dE721cF89ADF: 0 wei + 65792 gas × 2000000000 wei", - "252352": "0xd10e3Be2bc8f959Bc8C41CF65F60dE721cF89ADF: 0 wei + 65780 gas × 2000000000 wei", - "252353": "0xd10e3Be2bc8f959Bc8C41CF65F60dE721cF89ADF: 0 wei + 65780 gas × 2000000000 wei" - }, - "0x00000000863B56a3C1f0F1be8BC4F8b7BD78F57a": { - "40": "contract creation: 0 wei + 612412 gas × 6000000000 wei" - } - }, - "queued": { - "0x0f87ffcd71859233eb259f42b236c8e9873444e3": { - "7": "0x3479BE69e07E838D9738a301Bb0c89e8EA2Bef4a: 1000000000000000 wei + 21000 gas × 10000000000 wei", - "8": "0x73Aaf691bc33fe38f86260338EF88f9897eCaa4F: 1000000000000000 wei + 21000 gas × 10000000000 wei" - }, - "0x307e8f249bcccfa5b245449256c5d7e6e079943e": { - "3": "0x73Aaf691bc33fe38f86260338EF88f9897eCaa4F: 10000000000000000 wei + 21000 gas × 10000000000 wei" - } - } -}"#; - let deserialized: TxpoolInspect = serde_json::from_str(txpool_inspect_json).unwrap(); - assert_eq!(deserialized, expected_txpool_inspect()); - - let serialized = serde_json::to_string(&deserialized).unwrap(); - let deserialized2: TxpoolInspect = serde_json::from_str(&serialized).unwrap(); - assert_eq!(deserialized2, deserialized); - } - - #[test] - fn serde_txpool_status() { - let txpool_status_json = r#" -{ - "pending": "0x23", - "queued": "0x20" -}"#; - let deserialized: TxpoolStatus = serde_json::from_str(txpool_status_json).unwrap(); - let serialized: String = serde_json::to_string_pretty(&deserialized).unwrap(); - assert_eq!(txpool_status_json.trim(), serialized); - } - - fn expected_txpool_inspect() -> TxpoolInspect { - let mut pending_map = BTreeMap::new(); - let mut pending_map_inner = BTreeMap::new(); - pending_map_inner.insert( - "124930".to_string(), - TxpoolInspectSummary { - to: Some(Address::from_str("000000000000000000000000000000000000007E").unwrap()), - value: U256::from(0u128), - gas: U256::from(100187u128), - gas_price: U256::from(20000000000u128), - }, - ); - pending_map.insert( - Address::from_str("0512261a7486b1e29704ac49a5eb355b6fd86872").unwrap(), - pending_map_inner.clone(), - ); - pending_map_inner.clear(); - pending_map_inner.insert( - "252350".to_string(), - TxpoolInspectSummary { - to: Some(Address::from_str("d10e3Be2bc8f959Bc8C41CF65F60dE721cF89ADF").unwrap()), - value: U256::from(0u128), - gas: U256::from(65792u128), - gas_price: U256::from(2000000000u128), - }, - ); - pending_map_inner.insert( - "252351".to_string(), - TxpoolInspectSummary { - to: Some(Address::from_str("d10e3Be2bc8f959Bc8C41CF65F60dE721cF89ADF").unwrap()), - value: U256::from(0u128), - gas: U256::from(65792u128), - gas_price: U256::from(2000000000u128), - }, - ); - pending_map_inner.insert( - "252352".to_string(), - TxpoolInspectSummary { - to: Some(Address::from_str("d10e3Be2bc8f959Bc8C41CF65F60dE721cF89ADF").unwrap()), - value: U256::from(0u128), - gas: U256::from(65780u128), - gas_price: U256::from(2000000000u128), - }, - ); - pending_map_inner.insert( - "252353".to_string(), - TxpoolInspectSummary { - to: Some(Address::from_str("d10e3Be2bc8f959Bc8C41CF65F60dE721cF89ADF").unwrap()), - value: U256::from(0u128), - gas: U256::from(65780u128), - gas_price: U256::from(2000000000u128), - }, - ); - pending_map.insert( - Address::from_str("201354729f8d0f8b64e9a0c353c672c6a66b3857").unwrap(), - pending_map_inner.clone(), - ); - pending_map_inner.clear(); - pending_map_inner.insert( - "40".to_string(), - TxpoolInspectSummary { - to: None, - value: U256::from(0u128), - gas: U256::from(612412u128), - gas_price: U256::from(6000000000u128), - }, - ); - pending_map.insert( - Address::from_str("00000000863B56a3C1f0F1be8BC4F8b7BD78F57a").unwrap(), - pending_map_inner, - ); - let mut queued_map = BTreeMap::new(); - let mut queued_map_inner = BTreeMap::new(); - queued_map_inner.insert( - "7".to_string(), - TxpoolInspectSummary { - to: Some(Address::from_str("3479BE69e07E838D9738a301Bb0c89e8EA2Bef4a").unwrap()), - value: U256::from(1000000000000000u128), - gas: U256::from(21000u128), - gas_price: U256::from(10000000000u128), - }, - ); - queued_map_inner.insert( - "8".to_string(), - TxpoolInspectSummary { - to: Some(Address::from_str("73Aaf691bc33fe38f86260338EF88f9897eCaa4F").unwrap()), - value: U256::from(1000000000000000u128), - gas: U256::from(21000u128), - gas_price: U256::from(10000000000u128), - }, - ); - queued_map.insert( - Address::from_str("0f87ffcd71859233eb259f42b236c8e9873444e3").unwrap(), - queued_map_inner.clone(), - ); - queued_map_inner.clear(); - queued_map_inner.insert( - "3".to_string(), - TxpoolInspectSummary { - to: Some(Address::from_str("73Aaf691bc33fe38f86260338EF88f9897eCaa4F").unwrap()), - value: U256::from(10000000000000000u128), - gas: U256::from(21000u128), - gas_price: U256::from(10000000000u128), - }, - ); - queued_map.insert( - Address::from_str("307e8f249bcccfa5b245449256c5d7e6e079943e").unwrap(), - queued_map_inner, - ); - - TxpoolInspect { pending: pending_map, queued: queued_map } - } -} diff --git a/crates/rpc/rpc-types/src/eth/withdrawal.rs b/crates/rpc/rpc-types/src/eth/withdrawal.rs deleted file mode 100644 index c8f54d4ec..000000000 --- a/crates/rpc/rpc-types/src/eth/withdrawal.rs +++ /dev/null @@ -1,57 +0,0 @@ -//! Withdrawal type and serde helpers. - -use crate::serde_helpers::u64_hex; -use alloy_primitives::{Address, U256}; -use alloy_rlp::{RlpDecodable, RlpEncodable}; -use serde::{Deserialize, Serialize}; -use std::mem; - -/// Multiplier for converting gwei to wei. -pub const GWEI_TO_WEI: u64 = 1_000_000_000; - -/// Withdrawal represents a validator withdrawal from the consensus layer. -#[derive( - Debug, Clone, PartialEq, Eq, Default, Hash, RlpEncodable, RlpDecodable, Serialize, Deserialize, -)] -#[cfg_attr(feature = "ssz", derive(ssz_derive::Encode, ssz_derive::Decode))] -pub struct Withdrawal { - /// Monotonically increasing identifier issued by consensus layer. - #[serde(with = "u64_hex")] - pub index: u64, - /// Index of validator associated with withdrawal. - #[serde(with = "u64_hex", rename = "validatorIndex")] - pub validator_index: u64, - /// Target address for withdrawn ether. - pub address: Address, - /// Value of the withdrawal in gwei. - #[serde(with = "u64_hex")] - pub amount: u64, -} - -impl Withdrawal { - /// Return the withdrawal amount in wei. - pub fn amount_wei(&self) -> U256 { - U256::from(self.amount) * U256::from(GWEI_TO_WEI) - } - - /// Calculate a heuristic for the in-memory size of the [Withdrawal]. - #[inline] - pub fn size(&self) -> usize { - mem::size_of::() - } -} - -#[cfg(test)] -mod tests { - use super::*; - - // - #[test] - fn test_withdrawal_serde_roundtrip() { - let input = r#"[{"index":"0x0","validatorIndex":"0x0","address":"0x0000000000000000000000000000000000001000","amount":"0x1"},{"index":"0x1","validatorIndex":"0x1","address":"0x0000000000000000000000000000000000001001","amount":"0x1"},{"index":"0x2","validatorIndex":"0x2","address":"0x0000000000000000000000000000000000001002","amount":"0x1"},{"index":"0x3","validatorIndex":"0x3","address":"0x0000000000000000000000000000000000001003","amount":"0x1"},{"index":"0x4","validatorIndex":"0x4","address":"0x0000000000000000000000000000000000001004","amount":"0x1"},{"index":"0x5","validatorIndex":"0x5","address":"0x0000000000000000000000000000000000001005","amount":"0x1"},{"index":"0x6","validatorIndex":"0x6","address":"0x0000000000000000000000000000000000001006","amount":"0x1"},{"index":"0x7","validatorIndex":"0x7","address":"0x0000000000000000000000000000000000001007","amount":"0x1"},{"index":"0x8","validatorIndex":"0x8","address":"0x0000000000000000000000000000000000001008","amount":"0x1"},{"index":"0x9","validatorIndex":"0x9","address":"0x0000000000000000000000000000000000001009","amount":"0x1"},{"index":"0xa","validatorIndex":"0xa","address":"0x000000000000000000000000000000000000100a","amount":"0x1"},{"index":"0xb","validatorIndex":"0xb","address":"0x000000000000000000000000000000000000100b","amount":"0x1"},{"index":"0xc","validatorIndex":"0xc","address":"0x000000000000000000000000000000000000100c","amount":"0x1"},{"index":"0xd","validatorIndex":"0xd","address":"0x000000000000000000000000000000000000100d","amount":"0x1"},{"index":"0xe","validatorIndex":"0xe","address":"0x000000000000000000000000000000000000100e","amount":"0x1"},{"index":"0xf","validatorIndex":"0xf","address":"0x000000000000000000000000000000000000100f","amount":"0x1"}]"#; - - let withdrawals: Vec = serde_json::from_str(input).unwrap(); - let s = serde_json::to_string(&withdrawals).unwrap(); - assert_eq!(input, s); - } -} diff --git a/crates/rpc/rpc-types/src/eth/work.rs b/crates/rpc/rpc-types/src/eth/work.rs deleted file mode 100644 index a7fe3a99a..000000000 --- a/crates/rpc/rpc-types/src/eth/work.rs +++ /dev/null @@ -1,69 +0,0 @@ -use alloy_primitives::{B256, U256}; -use serde::{ - de::{Error, SeqAccess, Visitor}, - Deserialize, Deserializer, Serialize, Serializer, -}; -use std::fmt; - -/// The result of an `eth_getWork` request -#[derive(Clone, Debug, PartialEq, Eq, Default)] -pub struct Work { - /// The proof-of-work hash. - pub pow_hash: B256, - /// The seed hash. - pub seed_hash: B256, - /// The target. - pub target: B256, - /// The block number: this isn't always stored. - pub number: Option, -} - -impl Serialize for Work { - fn serialize(&self, s: S) -> Result - where - S: Serializer, - { - match self.number.as_ref() { - Some(num) => { - (&self.pow_hash, &self.seed_hash, &self.target, U256::from(*num)).serialize(s) - } - None => (&self.pow_hash, &self.seed_hash, &self.target).serialize(s), - } - } -} - -impl<'a> Deserialize<'a> for Work { - fn deserialize(deserializer: D) -> Result - where - D: Deserializer<'a>, - { - struct WorkVisitor; - - impl<'a> Visitor<'a> for WorkVisitor { - type Value = Work; - - fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(formatter, "Work object") - } - - fn visit_seq(self, mut seq: A) -> Result - where - A: SeqAccess<'a>, - { - let pow_hash = seq - .next_element::()? - .ok_or_else(|| A::Error::custom("missing pow hash"))?; - let seed_hash = seq - .next_element::()? - .ok_or_else(|| A::Error::custom("missing seed hash"))?; - let target = seq - .next_element::()? - .ok_or_else(|| A::Error::custom("missing target"))?; - let number = seq.next_element::()?; - Ok(Work { pow_hash, seed_hash, target, number }) - } - } - - deserializer.deserialize_any(WorkVisitor) - } -} diff --git a/crates/rpc/rpc-types/src/lib.rs b/crates/rpc/rpc-types/src/lib.rs index 84cd0992b..f1673b45b 100644 --- a/crates/rpc/rpc-types/src/lib.rs +++ b/crates/rpc/rpc-types/src/lib.rs @@ -20,11 +20,22 @@ pub mod relay; mod rpc; pub mod serde_helpers; +// Ethereum specific rpc types coming from alloy. +pub use alloy_rpc_types::*; +// Ethereum specific rpc types related to typed transaction requests and the engine API. +pub use eth::{ + engine, + engine::{ + ExecutionPayload, ExecutionPayloadV1, ExecutionPayloadV2, ExecutionPayloadV3, PayloadError, + }, + transaction::{ + self, BlobTransactionSidecar, TransactionKind, TransactionRequest, TypedTransactionRequest, + }, +}; + pub use admin::*; -pub use eth::*; pub use mev::*; pub use net::*; pub use otterscan::*; pub use peer::*; pub use rpc::*; -pub use serde_helpers::*; diff --git a/crates/rpc/rpc-types/src/relay/mod.rs b/crates/rpc/rpc-types/src/relay/mod.rs index c6dcfa20d..ecd6c8968 100644 --- a/crates/rpc/rpc-types/src/relay/mod.rs +++ b/crates/rpc/rpc-types/src/relay/mod.rs @@ -4,8 +4,9 @@ use crate::{ beacon::{BlsPublicKey, BlsSignature}, - engine::{BlobsBundleV1, ExecutionPayloadV1, ExecutionPayloadV2, ExecutionPayloadV3}, - ExecutionPayload, + engine::{ + BlobsBundleV1, ExecutionPayload, ExecutionPayloadV1, ExecutionPayloadV2, ExecutionPayloadV3, + }, }; use alloy_primitives::{Address, B256, U256}; use serde::{Deserialize, Serialize}; diff --git a/crates/rpc/rpc/src/eth/api/transactions.rs b/crates/rpc/rpc/src/eth/api/transactions.rs index 560272c22..db91ad806 100644 --- a/crates/rpc/rpc/src/eth/api/transactions.rs +++ b/crates/rpc/rpc/src/eth/api/transactions.rs @@ -49,6 +49,8 @@ use crate::eth::error::OptimismEthApiError; #[cfg(feature = "optimism")] use reth_revm::optimism::RethL1BlockInfo; #[cfg(feature = "optimism")] +use reth_rpc_types::OptimismTransactionReceiptFields; +#[cfg(feature = "optimism")] use revm::L1BlockInfo; #[cfg(feature = "optimism")] use std::ops::Div; @@ -1201,22 +1203,28 @@ pub(crate) fn build_transaction_receipt_with_block_receipts( // EIP-4844 fields blob_gas_price: meta.excess_blob_gas.map(calc_blob_gasprice).map(U128::from), blob_gas_used: transaction.transaction.blob_gas_used().map(U128::from), - // Optimism fields - #[cfg(feature = "optimism")] - deposit_nonce: receipt.deposit_nonce.map(U64::from), ..Default::default() }; #[cfg(feature = "optimism")] - if let Some(l1_block_info) = optimism_tx_meta.l1_block_info { - if !transaction.is_deposit() { - res_receipt.l1_fee = optimism_tx_meta.l1_fee; - res_receipt.l1_gas_used = - optimism_tx_meta.l1_data_gas.map(|dg| dg + l1_block_info.l1_fee_overhead); - res_receipt.l1_fee_scalar = - Some(l1_block_info.l1_fee_scalar.div(U256::from(1_000_000))); - res_receipt.l1_gas_price = Some(l1_block_info.l1_base_fee); + { + let mut op_fields = OptimismTransactionReceiptFields { + deposit_nonce: receipt.deposit_nonce.map(U64::from), + ..Default::default() + }; + + if let Some(l1_block_info) = optimism_tx_meta.l1_block_info { + if !transaction.is_deposit() { + op_fields.l1_fee = optimism_tx_meta.l1_fee; + op_fields.l1_gas_used = + optimism_tx_meta.l1_data_gas.map(|dg| dg + l1_block_info.l1_fee_overhead); + op_fields.l1_fee_scalar = + Some(l1_block_info.l1_fee_scalar.div(U256::from(1_000_000))); + op_fields.l1_gas_price = Some(l1_block_info.l1_base_fee); + } } + + res_receipt.other = op_fields.into(); } match transaction.transaction.kind() { diff --git a/crates/transaction-pool/benches/reorder.rs b/crates/transaction-pool/benches/reorder.rs index b50598e29..d2fb5b0b1 100644 --- a/crates/transaction-pool/benches/reorder.rs +++ b/crates/transaction-pool/benches/reorder.rs @@ -124,7 +124,7 @@ mod implementations { /// This implementation appends the transactions and uses [Vec::sort_by] function for sorting. #[derive(Default)] - pub struct VecTxPoolSortStable { + pub(crate) struct VecTxPoolSortStable { inner: Vec, } @@ -145,7 +145,7 @@ mod implementations { /// This implementation appends the transactions and uses [Vec::sort_unstable_by] function for /// sorting. #[derive(Default)] - pub struct VecTxPoolSortUnstable { + pub(crate) struct VecTxPoolSortUnstable { inner: Vec, } @@ -190,7 +190,7 @@ mod implementations { /// This implementation uses BinaryHeap which is drained and reconstructed on each reordering. #[derive(Default)] - pub struct BinaryHeapTxPool { + pub(crate) struct BinaryHeapTxPool { inner: BinaryHeap, base_fee: Option, } diff --git a/deny.toml b/deny.toml index dd8e50780..ac039d148 100644 --- a/deny.toml +++ b/deny.toml @@ -96,7 +96,7 @@ unknown-git = "deny" allow-git = [ # TODO: remove, see ./Cargo.toml "https://github.com/bluealloy/revm", - + "https://github.com/alloy-rs/alloy", "https://github.com/ethereum/c-kzg-4844", "https://github.com/sigp/discv5", "https://github.com/stevefan1999-personal/rust-igd",