From 0afc44106908b5bdf40ae82f524f2d8f7dc793a3 Mon Sep 17 00:00:00 2001 From: Matthias Seitz Date: Tue, 9 Jul 2024 12:38:16 +0200 Subject: [PATCH] fix: support additional eth call bundle args (#9383) --- Cargo.lock | 3 + Cargo.toml | 2 + crates/cli/commands/Cargo.toml | 9 +++ .../cli/commands/src/{p2p.rs => p2p/mod.rs} | 9 ++- crates/cli/commands/src/p2p/nodeset.rs | 76 +++++++++++++++++++ crates/rpc/rpc/src/eth/bundle.rs | 39 +++++++++- 6 files changed, 135 insertions(+), 3 deletions(-) rename crates/cli/commands/src/{p2p.rs => p2p/mod.rs} (96%) create mode 100644 crates/cli/commands/src/p2p/nodeset.rs diff --git a/Cargo.lock b/Cargo.lock index 0085e71a7a236..8c0be559d56b9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -6612,10 +6612,12 @@ dependencies = [ "ahash", "arbitrary", "backon", + "chrono", "clap", "comfy-table", "confy", "crossterm", + "enr", "eyre", "fdlimit", "human_bytes", @@ -6647,6 +6649,7 @@ dependencies = [ "reth-static-file", "reth-static-file-types", "reth-trie", + "secp256k1", "serde", "serde_json", "tokio", diff --git a/Cargo.toml b/Cargo.toml index 96f8370548fd9..2ebbb4e7e09a3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -432,6 +432,7 @@ auto_impl = "1" aquamarine = "0.5" bytes = "1.5" bitflags = "2.4" +chrono = { version = "0.4.38", features = ["serde"] } clap = "4" const_format = { version = "0.2.32", features = ["rust_1_64"] } dashmap = "5.5" @@ -515,6 +516,7 @@ proptest-arbitrary-interop = "0.1.0" secp256k1 = { version = "0.29", default-features = false, features = [ "global-context", "recovery", + "serde", ] } enr = { version = "0.12.1", default-features = false } diff --git a/crates/cli/commands/Cargo.toml b/crates/cli/commands/Cargo.toml index 1bb1a4e00e2f6..4f4289ed212a6 100644 --- a/crates/cli/commands/Cargo.toml +++ b/crates/cli/commands/Cargo.toml @@ -36,12 +36,21 @@ reth-trie = { workspace = true, features = ["metrics"] } tokio.workspace = true itertools.workspace = true +enr.workspace = true + +# crypto +secp256k1 = { workspace = true, default-features = false, features = [ + "global-context", + "recovery", + "serde", +] } # misc ahash = "0.8" human_bytes = "0.4.1" eyre.workspace = true clap = { workspace = true, features = ["derive", "env"] } +chrono = { workspace = true, features = ["serde"] } serde.workspace = true serde_json.workspace = true tracing.workspace = true diff --git a/crates/cli/commands/src/p2p.rs b/crates/cli/commands/src/p2p/mod.rs similarity index 96% rename from crates/cli/commands/src/p2p.rs rename to crates/cli/commands/src/p2p/mod.rs index 0fdefac8bd886..9290db1f66619 100644 --- a/crates/cli/commands/src/p2p.rs +++ b/crates/cli/commands/src/p2p/mod.rs @@ -17,6 +17,8 @@ use reth_node_core::{ use reth_primitives::BlockHashOrNumber; use std::{path::PathBuf, sync::Arc}; +mod nodeset; + /// `reth p2p` command #[derive(Debug, Parser)] pub struct Command { @@ -68,10 +70,12 @@ pub enum Subcommands { #[arg(value_parser = hash_or_num_value_parser)] id: BlockHashOrNumber, }, + /// Node Set Utilities + Nodeset(nodeset::Command), } impl Command { /// Execute `p2p` command - pub async fn execute(&self) -> eyre::Result<()> { + pub async fn execute(self) -> eyre::Result<()> { let data_dir = self.datadir.clone().resolve_datadir(self.chain.chain); let config_path = self.config.clone().unwrap_or_else(|| data_dir.config()); @@ -151,6 +155,9 @@ impl Command { let body = result.into_iter().next().unwrap(); println!("Successfully downloaded body: {body:?}") } + Subcommands::Nodeset(command) => { + let _ = command.execute(); + } } Ok(()) diff --git a/crates/cli/commands/src/p2p/nodeset.rs b/crates/cli/commands/src/p2p/nodeset.rs new file mode 100644 index 0000000000000..3f5e0770ebbc4 --- /dev/null +++ b/crates/cli/commands/src/p2p/nodeset.rs @@ -0,0 +1,76 @@ +//! Node set related subcommand of P2P Debugging tool, + +use chrono::{DateTime, Utc}; +use clap::{Parser, Subcommand}; +use enr::Enr; +use reth_fs_util as fs; +use secp256k1::SecretKey; +use serde::{Deserialize, Serialize}; +use std::{collections::HashMap, path::PathBuf}; + +/// The arguments for the `reth p2p nodeset` command +#[derive(Parser, Debug)] +pub struct Command { + #[clap(subcommand)] + subcommand: Subcommands, +} + +#[derive(Serialize, Deserialize, Debug)] +struct Record { + seq: u64, + record: Enr, + score: i64, + firstResponse: DateTime, + lastResponse: DateTime, + lastCheck: DateTime, +} + +impl Command { + /// Execute `p2p nodeset` command + pub fn execute(self) -> eyre::Result<()> { + match self.subcommand { + Subcommands::Info { file } => { + let content = fs::read_to_string(file)?; + + let nodes: HashMap = + serde_json::from_str(&content).expect("failed to deserialize json"); + + println!("Set contains {} nodes", nodes.len()); + + let mut keys = HashMap::new(); + + for (_, node) in nodes.iter() { + for (key, _) in node.record.iter() { + *keys.entry(String::from_utf8_lossy(&key).to_string()).or_insert(0) += 1; + } + } + + let max_key_len = keys.keys().map(|k| k.len()).max().unwrap_or(0); + + let mut result: Vec<_> = keys.iter().collect(); + + result.sort_by(|a, b| a.0.cmp(b.0)); + + for (key, count) in result { + let key_len = key.len(); + let padding = " ".repeat(max_key_len - key_len); + println!("{}{}: {}", padding, key, count); + } + } + } + + Ok(()) + } +} + +#[derive(Subcommand, Debug)] +enum Subcommands { + /// Show statistics about a node set + Info { + /// The path of the JSON file used to store node set. + #[arg(long, value_name = "FILE", default_value = "known-peers.json", verbatim_doc_comment)] + file: PathBuf, + }, + // TODO: implement `filter` subcommand + // ref: https://github.com/ethereum/go-ethereum/blob/master/cmd/devp2p/nodesetcmd.go#L51 +} diff --git a/crates/rpc/rpc/src/eth/bundle.rs b/crates/rpc/rpc/src/eth/bundle.rs index aab706b28b232..d28013822ee1e 100644 --- a/crates/rpc/rpc/src/eth/bundle.rs +++ b/crates/rpc/rpc/src/eth/bundle.rs @@ -16,8 +16,9 @@ use revm::{ db::CacheDB, primitives::{ResultAndState, TxEnv}, }; -use revm_primitives::{EnvKzgSettings, EnvWithHandlerCfg, MAX_BLOB_GAS_PER_BLOCK}; +use revm_primitives::{EnvKzgSettings, EnvWithHandlerCfg, SpecId, MAX_BLOB_GAS_PER_BLOCK}; +use reth_provider::{ChainSpecProvider, HeaderProvider}; use reth_rpc_eth_api::{ helpers::{Call, EthTransactions, LoadPendingBlock}, EthCallBundleApiServer, @@ -48,7 +49,15 @@ where /// state, or it can be used to simulate a past block. The sender is responsible for signing the /// transactions and using the correct nonce and ensuring validity pub async fn call_bundle(&self, bundle: EthCallBundle) -> EthResult { - let EthCallBundle { txs, block_number, state_block_number, timestamp, .. } = bundle; + let EthCallBundle { + txs, + block_number, + state_block_number, + timestamp, + gas_limit, + difficulty, + base_fee, + } = bundle; if txs.is_empty() { return Err(EthApiError::InvalidParams( EthBundleError::EmptyBundleTransactions.to_string(), @@ -88,6 +97,7 @@ where } let block_id: reth_rpc_types::BlockId = state_block_number.into(); + // Note: the block number is considered the `parent` block: let (cfg, mut block_env, at) = self.inner.eth_api.evm_env_at(block_id).await?; // need to adjust the timestamp for the next block @@ -97,6 +107,31 @@ where block_env.timestamp += U256::from(12); } + if let Some(difficulty) = difficulty { + block_env.difficulty = U256::from(difficulty); + } + + if let Some(gas_limit) = gas_limit { + block_env.gas_limit = U256::from(gas_limit); + } + + if let Some(base_fee) = base_fee { + block_env.basefee = U256::from(base_fee); + } else if cfg.handler_cfg.spec_id.is_enabled_in(SpecId::LONDON) { + let parent_block = block_env.number.saturating_to::(); + // here we need to fetch the _next_ block's basefee based on the parent block + let parent = LoadPendingBlock::provider(&self.inner.eth_api) + .header_by_number(parent_block)? + .ok_or_else(|| EthApiError::UnknownBlockNumber)?; + if let Some(base_fee) = parent.next_block_base_fee( + LoadPendingBlock::provider(&self.inner.eth_api) + .chain_spec() + .base_fee_params_at_block(parent_block), + ) { + block_env.basefee = U256::from(base_fee); + } + } + let state_block_number = block_env.number; // use the block number of the request block_env.number = U256::from(block_number);