From 346821684269a61be37ec7f62bfb7151aaa7bef0 Mon Sep 17 00:00:00 2001 From: Luca Provini Date: Fri, 24 Nov 2023 10:46:09 +0100 Subject: [PATCH 001/277] Results to MockEthProvider (#5556) --- crates/storage/provider/src/test_utils/mock.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/crates/storage/provider/src/test_utils/mock.rs b/crates/storage/provider/src/test_utils/mock.rs index 8a1cb6ca379e..e7f8726664a1 100644 --- a/crates/storage/provider/src/test_utils/mock.rs +++ b/crates/storage/provider/src/test_utils/mock.rs @@ -502,14 +502,14 @@ impl AccountReader for MockEthProvider { impl StateRootProvider for MockEthProvider { fn state_root(&self, _bundle_state: &BundleStateWithReceipts) -> ProviderResult { - todo!() + Ok(B256::default()) } fn state_root_with_updates( &self, _bundle_state: &BundleStateWithReceipts, ) -> ProviderResult<(B256, TrieUpdates)> { - todo!() + Ok((B256::default(), Default::default())) } } @@ -536,7 +536,7 @@ impl StateProvider for MockEthProvider { } fn proof(&self, _address: Address, _keys: &[B256]) -> ProviderResult { - todo!() + Ok(AccountProof::default()) } } From c37f187eed9ea88b5ec250f93a687b52b66117e3 Mon Sep 17 00:00:00 2001 From: Matthias Seitz Date: Fri, 24 Nov 2023 12:48:21 +0100 Subject: [PATCH 002/277] chore(deps): bump alloy 0.5 (#5557) --- Cargo.lock | 56 ++++++++++++++++++++++------------- Cargo.toml | 6 ++-- crates/net/ecies/src/codec.rs | 2 +- crates/primitives/src/lib.rs | 2 +- 4 files changed, 41 insertions(+), 25 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index b14f1c39973a..2fc9c302f0cf 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -140,9 +140,9 @@ checksum = "0942ffc6dcaadf03badf6e6a2d0228460359d5e34b57ccdc720b7382dfbd5ec5" [[package]] name = "alloy-dyn-abi" -version = "0.4.2" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e4e0daba57ddaba12dc9b21f608b843251f3de017f94a431dca4e7f4f72e5ba9" +checksum = "c9fe14fb6dded4be3f44d053e59402a405bb231561e36a88bc2283a9829d81fe" dependencies = [ "alloy-json-abi", "alloy-primitives", @@ -153,13 +153,14 @@ dependencies = [ "itoa", "serde", "serde_json", + "winnow", ] [[package]] name = "alloy-json-abi" -version = "0.4.2" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "63c9319ad8b2b623c6a3ac15899f8ffb71479224762dbaedc385c16efbb6cfe3" +checksum = "85cbbc8fcae58f39dbfbdc94ead48de0779910528889aebc074aed75959bffe7" dependencies = [ "alloy-primitives", "alloy-sol-type-parser", @@ -169,9 +170,9 @@ dependencies = [ [[package]] name = "alloy-primitives" -version = "0.4.2" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a0628ec0ba5b98b3370bb6be17b12f23bfce8ee4ad83823325a20546d9b03b78" +checksum = "f5def4b5e1bb8fe7ea37eeac1063246d4ef26f56cbdccf864a5a6bdcb80e91f4" dependencies = [ "alloy-rlp", "arbitrary", @@ -180,6 +181,7 @@ dependencies = [ "const-hex", "derive_arbitrary", "derive_more", + "ethereum_ssz", "getrandom 0.2.11", "hex-literal", "itoa", @@ -216,13 +218,14 @@ dependencies = [ [[package]] name = "alloy-sol-macro" -version = "0.4.2" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a98ad1696a2e17f010ae8e43e9f2a1e930ed176a8e3ff77acfeff6dfb07b42c" +checksum = "f0acd5b8d2699b095a57a0ecea6a6a2045b8e5ed6f2607bfa3382961d2889e82" dependencies = [ "const-hex", "dunce", "heck", + "indexmap 2.1.0", "proc-macro-error", "proc-macro2", "quote", @@ -233,18 +236,18 @@ dependencies = [ [[package]] name = "alloy-sol-type-parser" -version = "0.4.2" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81c61ccc29e7c58bf16a2f780898852348183f58b127bde03ced6d07ad544787" +checksum = "cc77699fb68c8a2cf18efb2c51df43e09b42b53886c6eb552951b19c41ebfc84" dependencies = [ "winnow", ] [[package]] name = "alloy-sol-types" -version = "0.4.2" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "98d7107bed88e8f09f0ddcc3335622d87bfb6821f3e0c7473329fb1cfad5e015" +checksum = "97d483e9c6db659cdb24fc736684ef68b743705fbdb0677c6404815361871b92" dependencies = [ "alloy-primitives", "alloy-sol-macro", @@ -2368,6 +2371,17 @@ dependencies = [ "uint", ] +[[package]] +name = "ethereum_ssz" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e61ffea29f26e8249d35128a82ec8d3bd4fbc80179ea5f5e5e3daafef6a80fcb" +dependencies = [ + "ethereum-types", + "itertools 0.10.5", + "smallvec", +] + [[package]] name = "ethers-contract" version = "2.0.11" @@ -2652,6 +2666,7 @@ version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "835c052cb0c08c1acf6ffd71c022172e18723949c8282f2b9f27efbc51e64534" dependencies = [ + "arbitrary", "byteorder", "rand 0.8.5", "rustc-hex", @@ -6607,7 +6622,7 @@ dependencies = [ [[package]] name = "revm" version = "3.5.0" -source = "git+https://github.com/bluealloy/revm?branch=reth_freeze#74643d37fc6231d558868ccc8b97400506e10906" +source = "git+https://github.com/bluealloy/revm?branch=reth_freeze#b00ebab8b3477f87e3d876a11b8f18d00a8f4103" dependencies = [ "auto_impl", "revm-interpreter", @@ -6617,7 +6632,7 @@ dependencies = [ [[package]] name = "revm-interpreter" version = "1.3.0" -source = "git+https://github.com/bluealloy/revm?branch=reth_freeze#74643d37fc6231d558868ccc8b97400506e10906" +source = "git+https://github.com/bluealloy/revm?branch=reth_freeze#b00ebab8b3477f87e3d876a11b8f18d00a8f4103" dependencies = [ "revm-primitives", ] @@ -6625,7 +6640,7 @@ dependencies = [ [[package]] name = "revm-precompile" version = "2.2.0" -source = "git+https://github.com/bluealloy/revm?branch=reth_freeze#74643d37fc6231d558868ccc8b97400506e10906" +source = "git+https://github.com/bluealloy/revm?branch=reth_freeze#b00ebab8b3477f87e3d876a11b8f18d00a8f4103" dependencies = [ "aurora-engine-modexp", "c-kzg", @@ -6641,7 +6656,7 @@ dependencies = [ [[package]] name = "revm-primitives" version = "1.3.0" -source = "git+https://github.com/bluealloy/revm?branch=reth_freeze#74643d37fc6231d558868ccc8b97400506e10906" +source = "git+https://github.com/bluealloy/revm?branch=reth_freeze#b00ebab8b3477f87e3d876a11b8f18d00a8f4103" dependencies = [ "alloy-primitives", "alloy-rlp", @@ -6773,9 +6788,9 @@ dependencies = [ [[package]] name = "ruint" -version = "1.11.0" +version = "1.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "724fd11728a3804e9944b14cab63825024c40bf42f8af87c8b5d97c4bbacf426" +checksum = "608a5726529f2f0ef81b8fde9873c4bb829d6b5b5ca6be4d97345ddf0749c825" dependencies = [ "alloy-rlp", "arbitrary", @@ -7662,9 +7677,9 @@ dependencies = [ [[package]] name = "syn-solidity" -version = "0.4.2" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "86b837ef12ab88835251726eb12237655e61ec8dc8a280085d1961cdc3dfd047" +checksum = "e349ed2ca56eff703d7c3ea013771bcbab9ad2ad39dddf863fc51d820329dc41" dependencies = [ "paste", "proc-macro2", @@ -8425,6 +8440,7 @@ version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "76f64bba2c53b04fcab63c01a7d7427eadc821e3bc48c34dc9ba29c501164b52" dependencies = [ + "arbitrary", "byteorder", "crunchy", "hex", diff --git a/Cargo.toml b/Cargo.toml index 009997b8478a..d5a275a3fe69 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -139,9 +139,9 @@ revm = { git = "https://github.com/bluealloy/revm", branch = "reth_freeze", fea revm-primitives = { git = "https://github.com/bluealloy/revm", branch = "reth_freeze", features = ["std"], default-features = false } # eth -alloy-primitives = "0.4" -alloy-dyn-abi = "0.4" -alloy-sol-types = "0.4" +alloy-primitives = "0.5" +alloy-dyn-abi = "0.5" +alloy-sol-types = "0.5" alloy-rlp = "0.3" ethers-core = { version = "2.0", default-features = false } ethers-providers = { version = "2.0", default-features = false } diff --git a/crates/net/ecies/src/codec.rs b/crates/net/ecies/src/codec.rs index d7335c5bf9d9..890113fd69f8 100644 --- a/crates/net/ecies/src/codec.rs +++ b/crates/net/ecies/src/codec.rs @@ -1,5 +1,5 @@ use crate::{algorithm::ECIES, ECIESError, EgressECIESValue, IngressECIESValue}; -use reth_primitives::{bytes::BytesMut, B512 as PeerId}; +use reth_primitives::{BytesMut, B512 as PeerId}; use secp256k1::SecretKey; use std::{fmt::Debug, io}; use tokio_util::codec::{Decoder, Encoder}; diff --git a/crates/primitives/src/lib.rs b/crates/primitives/src/lib.rs index a5b3b73444f9..63ad9c14a062 100644 --- a/crates/primitives/src/lib.rs +++ b/crates/primitives/src/lib.rs @@ -53,7 +53,7 @@ pub use block::{ Block, BlockBody, BlockBodyRoots, BlockHashOrNumber, BlockId, BlockNumHash, BlockNumberOrTag, BlockWithSenders, ForkBlock, RpcBlockHash, SealedBlock, SealedBlockWithSenders, }; -pub use bytes::{Buf, BufMut, BytesMut}; +pub use bytes::{self, Buf, BufMut, BytesMut}; pub use chain::{ AllGenesisFormats, BaseFeeParams, Chain, ChainInfo, ChainSpec, ChainSpecBuilder, DisplayHardforks, ForkCondition, ForkTimestamps, NamedChain, DEV, GOERLI, HOLESKY, MAINNET, From 2624f4667587e69258e88b2b28975d0ec11eafb5 Mon Sep 17 00:00:00 2001 From: DoTheBestToGetTheBest <146037313+DoTheBestToGetTheBest@users.noreply.github.com> Date: Fri, 24 Nov 2023 04:04:45 -0800 Subject: [PATCH 003/277] feat(rpc-testing-utils) : implement debug_traceCall JSON-RPC method (#5418) --- crates/rpc/rpc-testing-util/src/debug.rs | 42 ++++++++++++++++++++++-- 1 file changed, 40 insertions(+), 2 deletions(-) diff --git a/crates/rpc/rpc-testing-util/src/debug.rs b/crates/rpc/rpc-testing-util/src/debug.rs index cc9111821657..14a6b86908b9 100644 --- a/crates/rpc/rpc-testing-util/src/debug.rs +++ b/crates/rpc/rpc-testing-util/src/debug.rs @@ -4,12 +4,14 @@ use futures::{Stream, StreamExt}; use jsonrpsee::core::Error as RpcError; use reth_primitives::{BlockId, TxHash, B256}; use reth_rpc_api::{clients::DebugApiClient, EthApiClient}; -use reth_rpc_types::trace::geth::{GethDebugTracerType, GethDebugTracingOptions}; +use reth_rpc_types::{ + trace::geth::{GethDebugTracerType, GethDebugTracingOptions}, + CallRequest, +}; use std::{ pin::Pin, task::{Context, Poll}, }; - const NOOP_TRACER: &str = include_str!("../assets/noop-tracer.js"); const JS_TRACER_TEMPLATE: &str = include_str!("../assets/tracer-template.js"); @@ -37,6 +39,19 @@ pub trait DebugApiExt { ) -> Result, jsonrpsee::core::Error> where B: Into + Send; + /// method for debug_traceCall + async fn debug_trace_call_json( + &self, + request: CallRequest, + opts: GethDebugTracingOptions, + ) -> Result; + + /// method for debug_traceCall using raw JSON strings for the request and options. + async fn debug_trace_call_raw_json( + &self, + request_json: String, + opts_json: String, + ) -> Result; } #[async_trait::async_trait] @@ -81,6 +96,29 @@ where Ok(DebugTraceTransactionsStream { stream: Box::pin(stream) }) } + async fn debug_trace_call_json( + &self, + request: CallRequest, + opts: GethDebugTracingOptions, + ) -> Result { + let mut params = jsonrpsee::core::params::ArrayParams::new(); + params.insert(request).unwrap(); + params.insert(opts).unwrap(); + self.request("debug_traceCall", params).await + } + + async fn debug_trace_call_raw_json( + &self, + request_json: String, + opts_json: String, + ) -> Result { + let request = serde_json::from_str::(&request_json) + .map_err(|e| RpcError::Custom(e.to_string()))?; + let opts = serde_json::from_str::(&opts_json) + .map_err(|e| RpcError::Custom(e.to_string()))?; + + self.debug_trace_call_json(request, opts).await + } } /// A helper type that can be used to build a javascript tracer. From 8100a88f9a3deecb94af47e0f7a716a09f32ba9b Mon Sep 17 00:00:00 2001 From: DoTheBestToGetTheBest <146037313+DoTheBestToGetTheBest@users.noreply.github.com> Date: Fri, 24 Nov 2023 04:07:23 -0800 Subject: [PATCH 004/277] feat(rpc-testing-utils) : make replay transactions reponses comparaison in RpcComparer. (#5407) --- crates/rpc/rpc-testing-util/Cargo.toml | 5 +++ crates/rpc/rpc-testing-util/src/trace.rs | 42 ++++++++++++++++++++++-- 2 files changed, 45 insertions(+), 2 deletions(-) diff --git a/crates/rpc/rpc-testing-util/Cargo.toml b/crates/rpc/rpc-testing-util/Cargo.toml index d2488984bfd0..53b04c7a0e99 100644 --- a/crates/rpc/rpc-testing-util/Cargo.toml +++ b/crates/rpc/rpc-testing-util/Cargo.toml @@ -22,5 +22,10 @@ futures.workspace = true jsonrpsee = { workspace = true, features = ["client", "async-client"] } serde_json.workspace = true + +# assertions +similar-asserts = "1.5.0" + + [dev-dependencies] tokio = { workspace = true, features = ["rt-multi-thread", "macros", "rt"] } diff --git a/crates/rpc/rpc-testing-util/src/trace.rs b/crates/rpc/rpc-testing-util/src/trace.rs index 3b4e28c2c525..c0e594bb2b47 100644 --- a/crates/rpc/rpc-testing-util/src/trace.rs +++ b/crates/rpc/rpc-testing-util/src/trace.rs @@ -447,8 +447,9 @@ where while let Some((result1, result2)) = zipped_streams.next().await { match (result1, result2) { (Ok((ref traces1_data, ref block1)), Ok((ref traces2_data, ref block2))) => { - assert_eq!( - traces1_data, traces2_data, + similar_asserts::assert_eq!( + traces1_data, + traces2_data, "Mismatch in traces for block: {:?}", block1 ); @@ -467,6 +468,43 @@ where } } } + + /// Compares the `replay_transactions` responses from the two RPC clients. + pub async fn compare_replay_transaction_responses( + &self, + transaction_hashes: Vec, + trace_types: HashSet, + ) { + let stream1 = + self.client1.replay_transactions(transaction_hashes.clone(), trace_types.clone()); + let stream2 = self.client2.replay_transactions(transaction_hashes, trace_types); + + let mut zipped_streams = stream1.zip(stream2); + + while let Some((result1, result2)) = zipped_streams.next().await { + match (result1, result2) { + (Ok((ref trace1_data, ref tx_hash1)), Ok((ref trace2_data, ref tx_hash2))) => { + similar_asserts::assert_eq!( + trace1_data, + trace2_data, + "Mismatch in trace results for transaction: {:?}", + tx_hash1 + ); + assert_eq!(tx_hash1, tx_hash2, "Mismatch in transaction hashes."); + } + (Err((ref err1, ref tx_hash1)), Err((ref err2, ref tx_hash2))) => { + assert_eq!( + format!("{:?}", err1), + format!("{:?}", err2), + "Different errors for transaction: {:?}", + tx_hash1 + ); + assert_eq!(tx_hash1, tx_hash2, "Mismatch in transaction hashes."); + } + _ => panic!("One client returned Ok while the other returned Err."), + } + } + } } #[cfg(test)] mod tests { From 1e1cdceb0c2246fc448fec74e2a4b1ef2ff34a6d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A5vard=20Anda=20Estensen?= Date: Fri, 24 Nov 2023 13:13:09 +0100 Subject: [PATCH 005/277] feat(rpc-types): implement serialization for LocalizedTransactionTrace (#5446) Co-authored-by: Matthias Seitz --- crates/rpc/rpc-types/src/eth/trace/parity.rs | 273 ++++++++++++++++++- 1 file changed, 258 insertions(+), 15 deletions(-) diff --git a/crates/rpc/rpc-types/src/eth/trace/parity.rs b/crates/rpc/rpc-types/src/eth/trace/parity.rs index 0cf5bef5cb16..5a24a151d8db 100644 --- a/crates/rpc/rpc-types/src/eth/trace/parity.rs +++ b/crates/rpc/rpc-types/src/eth/trace/parity.rs @@ -4,7 +4,7 @@ //! See use alloy_primitives::{Address, Bytes, B256, U256, U64}; -use serde::{Deserialize, Serialize}; +use serde::{ser::SerializeStruct, Deserialize, Serialize, Serializer}; use std::{ collections::BTreeMap, ops::{Deref, DerefMut}, @@ -171,6 +171,16 @@ impl 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. @@ -231,12 +241,12 @@ pub struct CallAction { pub struct CreateAction { /// The address of the creator. pub from: Address, - /// The value with which the new account is endowed. - pub value: U256, /// 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, } #[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)] @@ -251,10 +261,10 @@ pub enum RewardType { pub struct RewardAction { /// Author's address. pub author: Address, - /// Reward amount. - pub value: U256, /// Reward type. pub reward_type: RewardType, + /// Reward amount. + pub value: U256, } /// Represents a _selfdestruct_ action fka `suicide`. @@ -263,10 +273,10 @@ pub struct RewardAction { pub struct SelfdestructAction { /// destroyed/suicided address. pub address: Address, - /// destroyed contract heir. - pub refund_address: Address, /// Balance of the contract just before it was destroyed. pub balance: U256, + /// destroyed contract heir. + pub refund_address: Address, } #[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)] @@ -279,9 +289,9 @@ pub struct CallOutput { #[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct CreateOutput { - pub gas_used: U64, - pub code: Bytes, pub address: Address, + pub code: Bytes, + pub gas_used: U64, } /// Represents the output of a trace. @@ -327,29 +337,88 @@ pub struct TransactionTrace { pub trace_address: Vec, } -#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)] +#[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 + /// Hash of the block, if not pending. /// /// Note: this deviates from which always returns a block number - #[serde(skip_serializing_if = "Option::is_none")] pub block_hash: Option, /// Block number the transaction is included in, None if pending. /// /// Note: this deviates from which always returns a block number - #[serde(skip_serializing_if = "Option::is_none")] pub block_number: Option, /// Hash of the transaction - #[serde(skip_serializing_if = "Option::is_none")] pub transaction_hash: Option, /// Transaction index within the block, None if pending. - #[serde(skip_serializing_if = "Option::is_none")] 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")] @@ -413,6 +482,8 @@ pub struct StorageDelta { #[cfg(test)] mod tests { use super::*; + use serde_json::{json, Value}; + use std::str::FromStr; #[test] fn test_transaction_trace() { @@ -469,4 +540,176 @@ mod tests { 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); + } } From 62cebf06436b35e31d1d6f65d8dc9bd203506ad4 Mon Sep 17 00:00:00 2001 From: Matthias Seitz Date: Fri, 24 Nov 2023 13:35:50 +0100 Subject: [PATCH 006/277] chore: some multiplex followup (#5553) --- crates/net/eth-wire/src/capability.rs | 26 +++-- crates/net/eth-wire/src/multiplex.rs | 134 +++++++++++++++++--------- crates/net/eth-wire/src/p2pstream.rs | 14 ++- 3 files changed, 115 insertions(+), 59 deletions(-) diff --git a/crates/net/eth-wire/src/capability.rs b/crates/net/eth-wire/src/capability.rs index 3a72504e93b5..9bd4afa82e0c 100644 --- a/crates/net/eth-wire/src/capability.rs +++ b/crates/net/eth-wire/src/capability.rs @@ -376,17 +376,24 @@ impl SharedCapabilities { /// Returns the matching shared capability for the given capability offset. /// - /// `offset` is the multiplexed message id offset of the capability relative to - /// [`MAX_RESERVED_MESSAGE_ID`]. + /// `offset` is the multiplexed message id offset of the capability relative to the reserved + /// message id space. In other words, counting starts at [`MAX_RESERVED_MESSAGE_ID`] + 1, which + /// corresponds to the first non-reserved message id. + /// + /// For example: `offset == 0` corresponds to the first shared message across the shared + /// capabilities and will return the first shared capability that supports messages. #[inline] pub fn find_by_relative_offset(&self, offset: u8) -> Option<&SharedCapability> { - self.find_by_offset(offset.saturating_add(MAX_RESERVED_MESSAGE_ID)) + self.find_by_offset(offset.saturating_add(MAX_RESERVED_MESSAGE_ID + 1)) } /// Returns the matching shared capability for the given capability offset. /// /// `offset` is the multiplexed message id offset of the capability that includes the reserved /// message id space. + /// + /// This will always return None if `offset` is less than or equal to + /// [`MAX_RESERVED_MESSAGE_ID`] because the reserved message id space is not shared. #[inline] pub fn find_by_offset(&self, offset: u8) -> Option<&SharedCapability> { let mut iter = self.0.iter(); @@ -637,12 +644,14 @@ mod tests { let shared = SharedCapabilities::try_new(local_capabilities, peer_capabilities).unwrap(); - assert!(shared.find_by_relative_offset(0).is_none()); - let shared_eth = shared.find_by_relative_offset(1).unwrap(); + let shared_eth = shared.find_by_relative_offset(0).unwrap(); assert_eq!(shared_eth.name(), "eth"); let shared_eth = shared.find_by_offset(MAX_RESERVED_MESSAGE_ID + 1).unwrap(); assert_eq!(shared_eth.name(), "eth"); + + // reserved message id space + assert!(shared.find_by_offset(MAX_RESERVED_MESSAGE_ID).is_none()); } #[test] @@ -654,15 +663,14 @@ mod tests { let shared = SharedCapabilities::try_new(local_capabilities, peer_capabilities).unwrap(); - assert!(shared.find_by_relative_offset(0).is_none()); - let shared_eth = shared.find_by_relative_offset(1).unwrap(); + let shared_eth = shared.find_by_relative_offset(0).unwrap(); assert_eq!(shared_eth.name(), proto.cap.name); let shared_eth = shared.find_by_offset(MAX_RESERVED_MESSAGE_ID + 1).unwrap(); assert_eq!(shared_eth.name(), proto.cap.name); - // the 5th shared message is the last message of the aaa capability - let shared_eth = shared.find_by_relative_offset(5).unwrap(); + // the 5th shared message (0,1,2,3,4) is the last message of the aaa capability + let shared_eth = shared.find_by_relative_offset(4).unwrap(); assert_eq!(shared_eth.name(), proto.cap.name); let shared_eth = shared.find_by_offset(MAX_RESERVED_MESSAGE_ID + 5).unwrap(); assert_eq!(shared_eth.name(), proto.cap.name); diff --git a/crates/net/eth-wire/src/multiplex.rs b/crates/net/eth-wire/src/multiplex.rs index d0dcf467e59c..e3bb92ecad3a 100644 --- a/crates/net/eth-wire/src/multiplex.rs +++ b/crates/net/eth-wire/src/multiplex.rs @@ -65,15 +65,16 @@ impl RlpxProtocolMultiplexer { mut self, cap: &Capability, handshake: F, - ) -> Result, Self> + ) -> Result, Err> where F: FnOnce(ProtocolProxy) -> Fut, Fut: Future>, St: Stream> + Sink + Unpin, + P2PStreamError: Into, { let Ok(shared_cap) = self.shared_capabilities().ensure_matching_capability(cap).cloned() else { - return Err(self) + return Err(P2PStreamError::CapabilityNotShared.into()) }; let (to_primary, from_wire) = mpsc::unbounded_channel(); @@ -87,20 +88,36 @@ impl RlpxProtocolMultiplexer { let f = handshake(proxy); pin_mut!(f); - // handle messages until the handshake is complete + // this polls the connection and the primary stream concurrently until the handshake is + // complete loop { - // TODO error handling tokio::select! { Some(Ok(msg)) = self.conn.next() => { - // TODO handle multiplex - let _ = to_primary.send(msg); + // Ensure the message belongs to the primary protocol + let offset = msg[0]; + if let Some(cap) = self.conn.shared_capabilities().find_by_relative_offset(offset) { + if cap == &shared_cap { + // delegate to primary + let _ = to_primary.send(msg); + } else { + // delegate to satellite + for proto in &self.protocols { + if proto.cap == *cap { + // TODO: need some form of backpressure here so buffering can't be abused + proto.send_raw(msg); + break + } + } + } + } else { + return Err(P2PStreamError::UnknownReservedMessageId(offset).into()) + } } Some(msg) = from_primary.recv() => { - // TODO error handling - self.conn.send(msg).await.unwrap(); + self.conn.send(msg).await.map_err(Into::into)?; } res = &mut f => { - let Ok(primary) = res else { return Err(self) }; + let primary = res?; return Ok(RlpxSatelliteStream { conn: self.conn, to_primary, @@ -117,24 +134,47 @@ impl RlpxProtocolMultiplexer { } /// A Stream and Sink type that acts as a wrapper around a primary RLPx subprotocol (e.g. "eth") +/// +/// Only emits and sends _non-empty_ messages #[derive(Debug)] pub struct ProtocolProxy { cap: SharedCapability, + /// Receives _non-empty_ messages from the wire from_wire: UnboundedReceiverStream, + /// Sends _non-empty_ messages from the wire to_wire: UnboundedSender, } impl ProtocolProxy { + /// Sends a _non-empty_ message on the wire. + fn try_send(&self, msg: Bytes) -> Result<(), io::Error> { + if msg.is_empty() { + // message must not be empty + return Err(io::ErrorKind::InvalidInput.into()) + } + self.to_wire.send(self.mask_msg_id(msg)).map_err(|_| io::ErrorKind::BrokenPipe.into()) + } + + /// Masks the message ID of a message to be sent on the wire. + /// + /// # Panics + /// + /// If the message is empty. + #[inline] fn mask_msg_id(&self, msg: Bytes) -> Bytes { - // TODO handle empty messages let mut masked_bytes = BytesMut::zeroed(msg.len()); masked_bytes[0] = msg[0] + self.cap.relative_message_id_offset(); masked_bytes[1..].copy_from_slice(&msg[1..]); masked_bytes.freeze() } + /// Unmasks the message ID of a message received from the wire. + /// + /// # Panics + /// + /// If the message is empty. + #[inline] fn unmask_id(&self, mut msg: BytesMut) -> BytesMut { - // TODO handle empty messages msg[0] -= self.cap.relative_message_id_offset(); msg } @@ -157,8 +197,7 @@ impl Sink for ProtocolProxy { } fn start_send(self: Pin<&mut Self>, item: Bytes) -> Result<(), Self::Error> { - let msg = self.mask_msg_id(item); - self.to_wire.send(msg).map_err(|_| io::ErrorKind::BrokenPipe.into()) + self.get_mut().try_send(item) } fn poll_flush(self: Pin<&mut Self>, _: &mut Context<'_>) -> Poll> { @@ -181,7 +220,7 @@ impl CanDisconnect for ProtocolProxy { } } -/// A connection channel to receive messages for the negotiated protocol. +/// A connection channel to receive _non_empty_ messages for the negotiated protocol. /// /// This is a [Stream] that returns raw bytes of the received messages for this protocol. #[derive(Debug)] @@ -287,34 +326,28 @@ where Poll::Ready(Some(Ok(msg))) => { delegated = true; let offset = msg[0]; - // find the protocol that matches the offset - // TODO optimize this by keeping a better index - let mut lowest_satellite = None; - // find the protocol with the lowest offset that is greater than the message - // offset - for (i, proto) in this.satellites.iter().enumerate() { - let proto_offset = proto.cap.relative_message_id_offset(); - if proto_offset >= offset { - if let Some((_, lowest_offset)) = lowest_satellite { - if proto_offset < lowest_offset { - lowest_satellite = Some((i, proto_offset)); + // delegate the multiplexed message to the correct protocol + if let Some(cap) = + this.conn.shared_capabilities().find_by_relative_offset(offset) + { + if cap == &this.primary_capability { + // delegate to primary + let _ = this.to_primary.send(msg); + } else { + // delegate to satellite + for proto in &this.satellites { + if proto.cap == *cap { + proto.send_raw(msg); + break } - } else { - lowest_satellite = Some((i, proto_offset)); } } + } else { + return Poll::Ready(Some(Err(P2PStreamError::UnknownReservedMessageId( + offset, + ) + .into()))) } - - if let Some((idx, lowest_offset)) = lowest_satellite { - if lowest_offset < this.primary_capability.relative_message_id_offset() - { - // delegate to satellite - this.satellites[idx].send_raw(msg); - continue - } - } - // delegate to primary - let _ = this.to_primary.send(msg); } Poll::Ready(Some(Err(err))) => return Poll::Ready(Some(Err(err.into()))), Poll::Ready(None) => { @@ -373,18 +406,29 @@ struct ProtocolStream { } impl ProtocolStream { + /// Masks the message ID of a message to be sent on the wire. + /// + /// # Panics + /// + /// If the message is empty. + #[inline] fn mask_msg_id(&self, mut msg: BytesMut) -> Bytes { - // TODO handle empty messages msg[0] += self.cap.relative_message_id_offset(); msg.freeze() } + /// Unmasks the message ID of a message received from the wire. + /// + /// # Panics + /// + /// If the message is empty. + #[inline] fn unmask_id(&self, mut msg: BytesMut) -> BytesMut { - // TODO handle empty messages msg[0] -= self.cap.relative_message_id_offset(); msg } + /// Sends the message to the satellite stream. fn send_raw(&self, msg: BytesMut) { let _ = self.to_satellite.send(self.unmask_id(msg)); } @@ -396,7 +440,7 @@ impl Stream for ProtocolStream { fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { let this = self.get_mut(); let msg = ready!(this.satellite_st.as_mut().poll_next(cx)); - Poll::Ready(msg.map(|msg| this.mask_msg_id(msg))) + Poll::Ready(msg.filter(|msg| !msg.is_empty()).map(|msg| this.mask_msg_id(msg))) } } @@ -408,15 +452,13 @@ impl fmt::Debug for ProtocolStream { #[cfg(test)] mod tests { - use tokio::net::TcpListener; - use tokio_util::codec::Decoder; - + use super::*; use crate::{ test_utils::{connect_passthrough, eth_handshake, eth_hello}, UnauthedEthStream, UnauthedP2PStream, }; - - use super::*; + use tokio::net::TcpListener; + use tokio_util::codec::Decoder; #[tokio::test] async fn eth_satellite() { diff --git a/crates/net/eth-wire/src/p2pstream.rs b/crates/net/eth-wire/src/p2pstream.rs index ed6001fb934a..8d407c4872e2 100644 --- a/crates/net/eth-wire/src/p2pstream.rs +++ b/crates/net/eth-wire/src/p2pstream.rs @@ -228,9 +228,10 @@ where /// /// See also /// -/// This stream emits Bytes that start with the normalized message id, so that the first byte of -/// each message starts from 0. If this stream only supports a single capability, for example `eth` -/// then the first byte of each message will match [EthMessageID](crate::types::EthMessageID). +/// This stream emits _non-empty_ Bytes that start with the normalized message id, so that the first +/// byte of each message starts from 0. If this stream only supports a single capability, for +/// example `eth` then the first byte of each message will match +/// [EthMessageID](crate::types::EthMessageID). #[pin_project] #[derive(Debug)] pub struct P2PStream { @@ -405,6 +406,11 @@ where None => return Poll::Ready(None), }; + if bytes.is_empty() { + // empty messages are not allowed + return Poll::Ready(Some(Err(P2PStreamError::EmptyProtocolMessage))) + } + // first check that the compressed message length does not exceed the max // payload size let decompressed_len = snap::raw::decompress_len(&bytes[1..])?; @@ -430,7 +436,7 @@ where err })?; - let id = *bytes.first().ok_or(P2PStreamError::EmptyProtocolMessage)?; + let id = bytes[0]; match id { _ if id == P2PMessageID::Ping as u8 => { trace!("Received Ping, Sending Pong"); From 269073b2d4f544e37878ef02b70642d9a6c5ed38 Mon Sep 17 00:00:00 2001 From: Dan Cline <6798349+Rjected@users.noreply.github.com> Date: Fri, 24 Nov 2023 12:43:48 -0500 Subject: [PATCH 007/277] feat: add metrics for best and resolved payloads (#5469) --- crates/payload/builder/src/metrics.rs | 20 +++++++++++++++++++- crates/payload/builder/src/service.rs | 25 ++++++++++++++++++++++++- 2 files changed, 43 insertions(+), 2 deletions(-) diff --git a/crates/payload/builder/src/metrics.rs b/crates/payload/builder/src/metrics.rs index a400b74fbf02..57e751c1fdfd 100644 --- a/crates/payload/builder/src/metrics.rs +++ b/crates/payload/builder/src/metrics.rs @@ -6,7 +6,7 @@ use reth_metrics::{ }; /// Payload builder service metrics -#[derive(Metrics)] +#[derive(Metrics, Clone)] #[metrics(scope = "payloads")] pub(crate) struct PayloadBuilderServiceMetrics { /// Number of active jobs @@ -15,6 +15,14 @@ pub(crate) struct PayloadBuilderServiceMetrics { pub(crate) initiated_jobs: Counter, /// Total number of failed jobs pub(crate) failed_jobs: Counter, + /// Coinbase revenue for best payloads + pub(crate) best_revenue: Gauge, + /// Current block returned as the best payload + pub(crate) best_block: Gauge, + /// Coinbase revenue for resolved payloads + pub(crate) resolved_revenue: Gauge, + /// Current block returned as the resolved payload + pub(crate) resolved_block: Gauge, } impl PayloadBuilderServiceMetrics { @@ -29,4 +37,14 @@ impl PayloadBuilderServiceMetrics { pub(crate) fn set_active_jobs(&self, value: usize) { self.active_jobs.set(value as f64) } + + pub(crate) fn set_best_revenue(&self, block: u64, value: f64) { + self.best_block.set(block as f64); + self.best_revenue.set(value) + } + + pub(crate) fn set_resolved_revenue(&self, block: u64, value: f64) { + self.resolved_block.set(block as f64); + self.resolved_revenue.set(value) + } } diff --git a/crates/payload/builder/src/service.rs b/crates/payload/builder/src/service.rs index 0750fc009779..79abbde03318 100644 --- a/crates/payload/builder/src/service.rs +++ b/crates/payload/builder/src/service.rs @@ -208,7 +208,17 @@ where &self, id: PayloadId, ) -> Option, PayloadBuilderError>> { - self.payload_jobs.iter().find(|(_, job_id)| *job_id == id).map(|(j, _)| j.best_payload()) + let res = self + .payload_jobs + .iter() + .find(|(_, job_id)| *job_id == id) + .map(|(j, _)| j.best_payload()); + if let Some(Ok(ref best)) = res { + // TODO: remove `to` + self.metrics.set_best_revenue(best.block.number, best.fees().to::() as f64); + } + + res } /// Returns the payload attributes for the given payload. @@ -233,6 +243,19 @@ where trace!(%id, "terminated resolved job"); } + // Since the fees will not be known until the payload future is resolved / awaited, we wrap + // the future in a new future that will update the metrics. + let resolved_metrics = self.metrics.clone(); + let fut = async move { + let res = fut.await; + if let Ok(ref payload) = res { + // TODO: remove `to` + resolved_metrics + .set_resolved_revenue(payload.block.number, payload.fees().to::() as f64); + } + res + }; + Some(Box::pin(fut)) } } From af8885172313aca66816dfc72537728d0c3c4e36 Mon Sep 17 00:00:00 2001 From: joshieDo <93316087+joshieDo@users.noreply.github.com> Date: Fri, 24 Nov 2023 18:02:14 +0000 Subject: [PATCH 008/277] feat: refactor generation of snapshots from the cli (#5464) --- Cargo.lock | 2 + bin/reth/Cargo.toml | 1 + bin/reth/src/db/snapshots/headers.rs | 48 +---- bin/reth/src/db/snapshots/mod.rs | 177 +++++++++++++++--- bin/reth/src/db/snapshots/receipts.rs | 48 +---- bin/reth/src/db/snapshots/transactions.rs | 51 +---- crates/primitives/src/snapshot/segment.rs | 5 + crates/snapshot/src/segments/headers.rs | 4 +- crates/snapshot/src/segments/mod.rs | 2 +- crates/snapshot/src/segments/receipts.rs | 4 +- crates/snapshot/src/segments/transactions.rs | 4 +- crates/snapshot/src/snapshotter.rs | 5 +- crates/storage/nippy-jar/src/filter/cuckoo.rs | 4 + crates/storage/nippy-jar/src/filter/mod.rs | 9 + crates/storage/nippy-jar/src/lib.rs | 19 ++ 15 files changed, 225 insertions(+), 158 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 2fc9c302f0cf..6059240ad981 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5581,6 +5581,7 @@ dependencies = [ "pretty_assertions", "proptest", "rand 0.8.5", + "rayon", "reth-auto-seal-consensus", "reth-basic-payload-builder", "reth-beacon-consensus", @@ -6378,6 +6379,7 @@ dependencies = [ "reth-rpc-api", "reth-rpc-types", "serde_json", + "similar-asserts", "tokio", ] diff --git a/bin/reth/Cargo.toml b/bin/reth/Cargo.toml index 6b04ae2996c9..273466aa145c 100644 --- a/bin/reth/Cargo.toml +++ b/bin/reth/Cargo.toml @@ -103,6 +103,7 @@ humantime = "2.1.0" const-str = "0.5.6" boyer-moore-magiclen = "0.2.16" itertools.workspace = true +rayon.workspace = true [target.'cfg(not(windows))'.dependencies] jemallocator = { version = "0.5.0", optional = true } diff --git a/bin/reth/src/db/snapshots/headers.rs b/bin/reth/src/db/snapshots/headers.rs index d05ff80c8c9d..5253601e3aeb 100644 --- a/bin/reth/src/db/snapshots/headers.rs +++ b/bin/reth/src/db/snapshots/headers.rs @@ -3,56 +3,22 @@ use super::{ Command, }; use rand::{seq::SliceRandom, Rng}; -use reth_db::{database::Database, open_db_read_only, snapshot::HeaderMask}; +use reth_db::{open_db_read_only, snapshot::HeaderMask}; use reth_interfaces::db::LogLevel; use reth_primitives::{ snapshot::{Compression, Filters, InclusionFilter, PerfectHashingFunction}, BlockHash, ChainSpec, Header, SnapshotSegment, }; use reth_provider::{ - providers::SnapshotProvider, DatabaseProviderRO, HeaderProvider, ProviderError, - ProviderFactory, TransactionsProviderExt, + providers::SnapshotProvider, BlockNumReader, HeaderProvider, ProviderError, ProviderFactory, + TransactionsProviderExt, }; -use reth_snapshot::{segments, segments::Segment}; use std::{ path::{Path, PathBuf}, sync::Arc, }; impl Command { - pub(crate) fn generate_headers_snapshot( - &self, - provider: &DatabaseProviderRO, - compression: Compression, - inclusion_filter: InclusionFilter, - phf: PerfectHashingFunction, - ) -> eyre::Result<()> { - let range = self.block_range(); - let filters = if self.with_filters { - Filters::WithFilters(inclusion_filter, phf) - } else { - Filters::WithoutFilters - }; - - let segment = segments::Headers::new(compression, filters); - - segment.snapshot::(provider, PathBuf::default(), range.clone())?; - - // Default name doesn't have any configuration - let tx_range = provider.transaction_range_by_block_range(range.clone())?; - reth_primitives::fs::rename( - SnapshotSegment::Headers.filename(&range, &tx_range), - SnapshotSegment::Headers.filename_with_configuration( - filters, - compression, - &range, - &tx_range, - ), - )?; - - Ok(()) - } - pub(crate) fn bench_headers_snapshot( &self, db_path: &Path, @@ -62,14 +28,18 @@ impl Command { inclusion_filter: InclusionFilter, phf: PerfectHashingFunction, ) -> eyre::Result<()> { + let factory = ProviderFactory::new(open_db_read_only(db_path, log_level)?, chain.clone()); + let provider = factory.provider()?; + let tip = provider.last_block_number()?; + let block_range = + self.block_ranges(tip).first().expect("has been generated before").clone(); + let filters = if self.with_filters { Filters::WithFilters(inclusion_filter, phf) } else { Filters::WithoutFilters }; - let block_range = self.block_range(); - let mut row_indexes = block_range.clone().collect::>(); let mut rng = rand::thread_rng(); diff --git a/bin/reth/src/db/snapshots/mod.rs b/bin/reth/src/db/snapshots/mod.rs index 1113d7086830..b3909c29d194 100644 --- a/bin/reth/src/db/snapshots/mod.rs +++ b/bin/reth/src/db/snapshots/mod.rs @@ -1,13 +1,21 @@ -use clap::Parser; +use clap::{builder::RangedU64ValueParser, Parser}; use itertools::Itertools; -use reth_db::{open_db_read_only, DatabaseEnv}; +use rayon::iter::{IntoParallelIterator, ParallelIterator}; +use reth_db::{database::Database, open_db_read_only, DatabaseEnv}; use reth_interfaces::db::LogLevel; +use reth_nippy_jar::NippyJar; use reth_primitives::{ - snapshot::{Compression, InclusionFilter, PerfectHashingFunction}, + snapshot::{Compression, Filters, InclusionFilter, PerfectHashingFunction, SegmentHeader}, BlockNumber, ChainSpec, SnapshotSegment, }; -use reth_provider::ProviderFactory; -use std::{ops::RangeInclusive, path::Path, sync::Arc}; +use reth_provider::{BlockNumReader, ProviderFactory, TransactionsProviderExt}; +use reth_snapshot::{segments as snap_segments, segments::Segment}; +use std::{ + ops::RangeInclusive, + path::{Path, PathBuf}, + sync::Arc, + time::{Duration, Instant}, +}; mod bench; mod headers; @@ -28,6 +36,19 @@ pub struct Command { #[arg(long, short, default_value = "500000")] block_interval: u64, + /// Sets the number of snapshots built in parallel. Note: Each parallel build is + /// memory-intensive. + #[arg( + long, short, + default_value = "1", + value_parser = RangedU64ValueParser::::new().range(1..) + )] + parallel: u64, + + /// Flag to skip snapshot creation and print snapshot files stats. + #[arg(long, default_value = "false")] + only_stats: bool, + /// Flag to enable database-to-snapshot benchmarking. #[arg(long, default_value = "false")] bench: bool, @@ -41,7 +62,7 @@ pub struct Command { compression: Vec, /// Flag to enable inclusion list filters and PHFs. - #[arg(long, default_value = "true")] + #[arg(long, default_value = "false")] with_filters: bool, /// Specifies the perfect hashing function to use. @@ -65,39 +86,36 @@ impl Command { { let db = open_db_read_only(db_path, None)?; - let factory = ProviderFactory::new(db, chain.clone()); - let provider = factory.provider()?; + let factory = Arc::new(ProviderFactory::new(db, chain.clone())); if !self.only_bench { for ((mode, compression), phf) in all_combinations.clone() { + let filters = if self.with_filters { + Filters::WithFilters(InclusionFilter::Cuckoo, *phf) + } else { + Filters::WithoutFilters + }; + match mode { - SnapshotSegment::Headers => self.generate_headers_snapshot::( - &provider, - *compression, - InclusionFilter::Cuckoo, - *phf, + SnapshotSegment::Headers => self.generate_snapshot::( + factory.clone(), + snap_segments::Headers::new(*compression, filters), + )?, + SnapshotSegment::Transactions => self.generate_snapshot::( + factory.clone(), + snap_segments::Transactions::new(*compression, filters), + )?, + SnapshotSegment::Receipts => self.generate_snapshot::( + factory.clone(), + snap_segments::Receipts::new(*compression, filters), )?, - SnapshotSegment::Transactions => self - .generate_transactions_snapshot::( - &provider, - *compression, - InclusionFilter::Cuckoo, - *phf, - )?, - SnapshotSegment::Receipts => self - .generate_receipts_snapshot::( - &provider, - *compression, - InclusionFilter::Cuckoo, - *phf, - )?, } } } } if self.only_bench || self.bench { - for ((mode, compression), phf) in all_combinations { + for ((mode, compression), phf) in all_combinations.clone() { match mode { SnapshotSegment::Headers => self.bench_headers_snapshot( db_path, @@ -130,8 +148,105 @@ impl Command { Ok(()) } - /// Gives out the inclusive block range for the snapshot requested by the user. - fn block_range(&self) -> RangeInclusive { - self.from..=(self.from + self.block_interval - 1) + /// Generates successive inclusive block ranges up to the tip starting at `self.from`. + fn block_ranges(&self, tip: BlockNumber) -> Vec> { + let mut from = self.from; + let mut ranges = Vec::new(); + + while from <= tip { + let end_range = std::cmp::min(from + self.block_interval - 1, tip); + ranges.push(from..=end_range); + from = end_range + 1; + } + + ranges + } + + /// Generates snapshots from `self.from` with a `self.block_interval`. Generates them in + /// parallel if specified. + fn generate_snapshot( + &self, + factory: Arc>, + segment: impl Segment + Send + Sync, + ) -> eyre::Result<()> { + let dir = PathBuf::default(); + let ranges = self.block_ranges(factory.last_block_number()?); + + let mut created_snapshots = vec![]; + + // Filter/PHF is memory intensive, so we have to limit the parallelism. + for block_ranges in ranges.chunks(self.parallel as usize) { + let created_files = block_ranges + .into_par_iter() + .map(|block_range| { + let provider = factory.provider()?; + + if !self.only_stats { + segment.snapshot::(&provider, &dir, block_range.clone())?; + } + + let tx_range = + provider.transaction_range_by_block_range(block_range.clone())?; + + Ok(segment.segment().filename(block_range, &tx_range)) + }) + .collect::, eyre::Report>>()?; + + created_snapshots.extend(created_files); + } + + self.stats(created_snapshots) + } + + /// Prints detailed statistics for each snapshot, including loading time. + /// + /// This function loads each snapshot from the provided paths and prints + /// statistics about various aspects of each snapshot, such as filters size, + /// offset index size, offset list size, and loading time. + fn stats(&self, snapshots: Vec>) -> eyre::Result<()> { + let mb = 1024.0 * 1024.0; + let mut total_filters_size = 0; + let mut total_index_size = 0; + let mut total_offsets_size = 0; + let mut total_duration = Duration::new(0, 0); + let mut total_file_size = 0; + + for snap in &snapshots { + let start_time = Instant::now(); + let jar = NippyJar::::load(snap.as_ref())?; + let duration = start_time.elapsed(); + let file_size = snap.as_ref().metadata()?.len(); + + total_filters_size += jar.filter_size(); + total_index_size += jar.offsets_index_size(); + total_offsets_size += jar.offsets_size(); + total_duration += duration; + total_file_size += file_size; + + println!("Snapshot: {:?}", snap.as_ref().file_name()); + println!(" File Size: {:>7.2} MB", file_size as f64 / mb); + println!(" Filters Size: {:>7.2} MB", jar.filter_size() as f64 / mb); + println!(" Offset Index Size: {:>7.2} MB", jar.offsets_index_size() as f64 / mb); + println!(" Offset List Size: {:>7.2} MB", jar.offsets_size() as f64 / mb); + println!( + " Loading Time: {:>7.2} ms | {:>7.2} µs", + duration.as_millis() as f64, + duration.as_micros() as f64 + ); + } + + let avg_duration = total_duration / snapshots.len() as u32; + + println!("Total Filters Size: {:>7.2} MB", total_filters_size as f64 / mb); + println!("Total Offset Index Size: {:>7.2} MB", total_index_size as f64 / mb); + println!("Total Offset List Size: {:>7.2} MB", total_offsets_size as f64 / mb); + println!("Total File Size: {:>7.2} GB", total_file_size as f64 / (mb * 1024.0)); + println!( + "Average Loading Time: {:>7.2} ms | {:>7.2} µs", + avg_duration.as_millis() as f64, + avg_duration.as_micros() as f64 + ); + + Ok(()) } } diff --git a/bin/reth/src/db/snapshots/receipts.rs b/bin/reth/src/db/snapshots/receipts.rs index b24eccda51d8..ce028f79b84f 100644 --- a/bin/reth/src/db/snapshots/receipts.rs +++ b/bin/reth/src/db/snapshots/receipts.rs @@ -3,55 +3,23 @@ use super::{ Command, Compression, PerfectHashingFunction, }; use rand::{seq::SliceRandom, Rng}; -use reth_db::{database::Database, open_db_read_only, snapshot::ReceiptMask}; +use reth_db::{open_db_read_only, snapshot::ReceiptMask}; use reth_interfaces::db::LogLevel; use reth_primitives::{ snapshot::{Filters, InclusionFilter}, ChainSpec, Receipt, SnapshotSegment, }; use reth_provider::{ - providers::SnapshotProvider, DatabaseProviderRO, ProviderError, ProviderFactory, - ReceiptProvider, TransactionsProvider, TransactionsProviderExt, + providers::SnapshotProvider, BlockNumReader, ProviderError, ProviderFactory, ReceiptProvider, + TransactionsProvider, TransactionsProviderExt, }; -use reth_snapshot::{segments, segments::Segment}; + use std::{ path::{Path, PathBuf}, sync::Arc, }; impl Command { - pub(crate) fn generate_receipts_snapshot( - &self, - provider: &DatabaseProviderRO, - compression: Compression, - inclusion_filter: InclusionFilter, - phf: PerfectHashingFunction, - ) -> eyre::Result<()> { - let block_range = self.block_range(); - let filters = if self.with_filters { - Filters::WithFilters(inclusion_filter, phf) - } else { - Filters::WithoutFilters - }; - - let segment: segments::Receipts = segments::Receipts::new(compression, filters); - segment.snapshot::(provider, PathBuf::default(), block_range.clone())?; - - // Default name doesn't have any configuration - let tx_range = provider.transaction_range_by_block_range(block_range.clone())?; - reth_primitives::fs::rename( - SnapshotSegment::Receipts.filename(&block_range, &tx_range), - SnapshotSegment::Receipts.filename_with_configuration( - filters, - compression, - &block_range, - &tx_range, - ), - )?; - - Ok(()) - } - pub(crate) fn bench_receipts_snapshot( &self, db_path: &Path, @@ -61,14 +29,18 @@ impl Command { inclusion_filter: InclusionFilter, phf: PerfectHashingFunction, ) -> eyre::Result<()> { + let factory = ProviderFactory::new(open_db_read_only(db_path, log_level)?, chain.clone()); + let provider = factory.provider()?; + let tip = provider.last_block_number()?; + let block_range = + self.block_ranges(tip).first().expect("has been generated before").clone(); + let filters = if self.with_filters { Filters::WithFilters(inclusion_filter, phf) } else { Filters::WithoutFilters }; - let block_range = self.block_range(); - let mut rng = rand::thread_rng(); let tx_range = ProviderFactory::new(open_db_read_only(db_path, log_level)?, chain.clone()) diff --git a/bin/reth/src/db/snapshots/transactions.rs b/bin/reth/src/db/snapshots/transactions.rs index 94a61d262a8e..690ca45b2427 100644 --- a/bin/reth/src/db/snapshots/transactions.rs +++ b/bin/reth/src/db/snapshots/transactions.rs @@ -3,56 +3,23 @@ use super::{ Command, Compression, PerfectHashingFunction, }; use rand::{seq::SliceRandom, Rng}; -use reth_db::{database::Database, open_db_read_only, snapshot::TransactionMask}; +use reth_db::{open_db_read_only, snapshot::TransactionMask}; use reth_interfaces::db::LogLevel; use reth_primitives::{ snapshot::{Filters, InclusionFilter}, ChainSpec, SnapshotSegment, TransactionSignedNoHash, }; use reth_provider::{ - providers::SnapshotProvider, DatabaseProviderRO, ProviderError, ProviderFactory, + providers::SnapshotProvider, BlockNumReader, ProviderError, ProviderFactory, TransactionsProvider, TransactionsProviderExt, }; -use reth_snapshot::{segments, segments::Segment}; + use std::{ path::{Path, PathBuf}, sync::Arc, }; impl Command { - pub(crate) fn generate_transactions_snapshot( - &self, - provider: &DatabaseProviderRO, - compression: Compression, - inclusion_filter: InclusionFilter, - phf: PerfectHashingFunction, - ) -> eyre::Result<()> { - let block_range = self.block_range(); - let filters = if self.with_filters { - Filters::WithFilters(inclusion_filter, phf) - } else { - Filters::WithoutFilters - }; - - let segment = segments::Transactions::new(compression, filters); - - segment.snapshot::(provider, PathBuf::default(), block_range.clone())?; - - // Default name doesn't have any configuration - let tx_range = provider.transaction_range_by_block_range(block_range.clone())?; - reth_primitives::fs::rename( - SnapshotSegment::Transactions.filename(&block_range, &tx_range), - SnapshotSegment::Transactions.filename_with_configuration( - filters, - compression, - &block_range, - &tx_range, - ), - )?; - - Ok(()) - } - pub(crate) fn bench_transactions_snapshot( &self, db_path: &Path, @@ -62,19 +29,21 @@ impl Command { inclusion_filter: InclusionFilter, phf: PerfectHashingFunction, ) -> eyre::Result<()> { + let factory = ProviderFactory::new(open_db_read_only(db_path, log_level)?, chain.clone()); + let provider = factory.provider()?; + let tip = provider.last_block_number()?; + let block_range = + self.block_ranges(tip).first().expect("has been generated before").clone(); + let filters = if self.with_filters { Filters::WithFilters(inclusion_filter, phf) } else { Filters::WithoutFilters }; - let block_range = self.block_range(); - let mut rng = rand::thread_rng(); - let tx_range = ProviderFactory::new(open_db_read_only(db_path, log_level)?, chain.clone()) - .provider()? - .transaction_range_by_block_range(block_range.clone())?; + let tx_range = provider.transaction_range_by_block_range(block_range.clone())?; let mut row_indexes = tx_range.clone().collect::>(); diff --git a/crates/primitives/src/snapshot/segment.rs b/crates/primitives/src/snapshot/segment.rs index d8357fc169cb..90145f14fef6 100644 --- a/crates/primitives/src/snapshot/segment.rs +++ b/crates/primitives/src/snapshot/segment.rs @@ -158,6 +158,11 @@ impl SegmentHeader { *self.block_range.start() } + /// Returns the last block number of the segment. + pub fn block_end(&self) -> BlockNumber { + *self.block_range.end() + } + /// Returns the first transaction number of the segment. pub fn tx_start(&self) -> TxNumber { *self.tx_range.start() diff --git a/crates/snapshot/src/segments/headers.rs b/crates/snapshot/src/segments/headers.rs index 2bf73b2f7cf6..0a524e86c3b9 100644 --- a/crates/snapshot/src/segments/headers.rs +++ b/crates/snapshot/src/segments/headers.rs @@ -31,7 +31,7 @@ impl Default for Headers { } impl Segment for Headers { - fn segment() -> SnapshotSegment { + fn segment(&self) -> SnapshotSegment { SnapshotSegment::Headers } @@ -45,7 +45,7 @@ impl Segment for Headers { let mut jar = prepare_jar::( provider, directory, - Self::segment(), + self.segment(), self.config, range.clone(), range_len, diff --git a/crates/snapshot/src/segments/mod.rs b/crates/snapshot/src/segments/mod.rs index 88cdc52ef685..68b1b81b0999 100644 --- a/crates/snapshot/src/segments/mod.rs +++ b/crates/snapshot/src/segments/mod.rs @@ -37,7 +37,7 @@ pub trait Segment: Default { ) -> ProviderResult<()>; /// Returns this struct's [`SnapshotSegment`]. - fn segment() -> SnapshotSegment; + fn segment(&self) -> SnapshotSegment; /// Generates the dataset to train a zstd dictionary with the most recent rows (at most 1000). fn dataset_for_compression>( diff --git a/crates/snapshot/src/segments/receipts.rs b/crates/snapshot/src/segments/receipts.rs index 4b82a7133a4e..5c5a48112976 100644 --- a/crates/snapshot/src/segments/receipts.rs +++ b/crates/snapshot/src/segments/receipts.rs @@ -28,7 +28,7 @@ impl Default for Receipts { } impl Segment for Receipts { - fn segment() -> SnapshotSegment { + fn segment(&self) -> SnapshotSegment { SnapshotSegment::Receipts } @@ -44,7 +44,7 @@ impl Segment for Receipts { let mut jar = prepare_jar::( provider, directory, - Self::segment(), + self.segment(), self.config, block_range, tx_range_len, diff --git a/crates/snapshot/src/segments/transactions.rs b/crates/snapshot/src/segments/transactions.rs index 585bc9625e42..ea936bd95bff 100644 --- a/crates/snapshot/src/segments/transactions.rs +++ b/crates/snapshot/src/segments/transactions.rs @@ -28,7 +28,7 @@ impl Default for Transactions { } impl Segment for Transactions { - fn segment() -> SnapshotSegment { + fn segment(&self) -> SnapshotSegment { SnapshotSegment::Transactions } @@ -44,7 +44,7 @@ impl Segment for Transactions { let mut jar = prepare_jar::( provider, directory, - Self::segment(), + self.segment(), self.config, block_range, tx_range_len, diff --git a/crates/snapshot/src/snapshotter.rs b/crates/snapshot/src/snapshotter.rs index 729b0c1b974f..21a072ab372d 100644 --- a/crates/snapshot/src/snapshotter.rs +++ b/crates/snapshot/src/snapshotter.rs @@ -210,9 +210,10 @@ impl Snapshotter { let temp = self.snapshots_path.join(TEMPORARY_SUBDIRECTORY); let provider = self.provider_factory.provider()?; let tx_range = provider.transaction_range_by_block_range(block_range.clone())?; - let filename = S::segment().filename(&block_range, &tx_range); + let segment = S::default(); + let filename = segment.segment().filename(&block_range, &tx_range); - S::default().snapshot::(&provider, temp.clone(), block_range)?; + segment.snapshot::(&provider, temp.clone(), block_range)?; reth_primitives::fs::rename(temp.join(&filename), self.snapshots_path.join(filename))?; } diff --git a/crates/storage/nippy-jar/src/filter/cuckoo.rs b/crates/storage/nippy-jar/src/filter/cuckoo.rs index 2e4110e58cc7..70c3be24a408 100644 --- a/crates/storage/nippy-jar/src/filter/cuckoo.rs +++ b/crates/storage/nippy-jar/src/filter/cuckoo.rs @@ -39,6 +39,10 @@ impl InclusionFilter for Cuckoo { fn contains(&self, element: &[u8]) -> Result { Ok(self.filter.contains(element)) } + + fn size(&self) -> usize { + self.filter.memory_usage() + } } impl std::fmt::Debug for Cuckoo { diff --git a/crates/storage/nippy-jar/src/filter/mod.rs b/crates/storage/nippy-jar/src/filter/mod.rs index e8e6294ebfad..dd3e7804905a 100644 --- a/crates/storage/nippy-jar/src/filter/mod.rs +++ b/crates/storage/nippy-jar/src/filter/mod.rs @@ -11,6 +11,8 @@ pub trait InclusionFilter { /// Checks if the element belongs to the inclusion list. **There might be false positives.** fn contains(&self, element: &[u8]) -> Result; + + fn size(&self) -> usize; } /// Enum with different [`InclusionFilter`] types. @@ -36,4 +38,11 @@ impl InclusionFilter for InclusionFilters { InclusionFilters::Unused => todo!(), } } + + fn size(&self) -> usize { + match self { + InclusionFilters::Cuckoo(c) => c.size(), + InclusionFilters::Unused => 0, + } + } } diff --git a/crates/storage/nippy-jar/src/lib.rs b/crates/storage/nippy-jar/src/lib.rs index c7515305d76c..435359e87030 100644 --- a/crates/storage/nippy-jar/src/lib.rs +++ b/crates/storage/nippy-jar/src/lib.rs @@ -201,6 +201,21 @@ where &self.user_header } + /// Gets a reference to `self.offsets`. + pub fn offsets_size(&self) -> usize { + self.offsets.size_in_bytes() + } + + /// Gets a reference to `self.offsets`. + pub fn filter_size(&self) -> usize { + self.size() + } + + /// Gets a reference to `self.offsets_index`. + pub fn offsets_index_size(&self) -> usize { + self.offsets_index.size_in_bytes() + } + /// Gets a reference to the compressor. pub fn compressor(&self) -> Option<&Compressors> { self.compressor.as_ref() @@ -480,6 +495,10 @@ where fn contains(&self, element: &[u8]) -> Result { self.filter.as_ref().ok_or(NippyJarError::FilterMissing)?.contains(element) } + + fn size(&self) -> usize { + self.filter.as_ref().map(|f| f.size()).unwrap_or(0) + } } impl PerfectHashingFunction for NippyJar From 5f38a5fbb80e15c5cfb2d8b64b12b318b6fb28ab Mon Sep 17 00:00:00 2001 From: Dan Cline <6798349+Rjected@users.noreply.github.com> Date: Sat, 25 Nov 2023 00:32:38 -0500 Subject: [PATCH 009/277] fix: use direct conversion for payload fee metrics (#5564) --- crates/payload/builder/src/service.rs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/crates/payload/builder/src/service.rs b/crates/payload/builder/src/service.rs index 79abbde03318..eb17bf2f1b83 100644 --- a/crates/payload/builder/src/service.rs +++ b/crates/payload/builder/src/service.rs @@ -214,8 +214,7 @@ where .find(|(_, job_id)| *job_id == id) .map(|(j, _)| j.best_payload()); if let Some(Ok(ref best)) = res { - // TODO: remove `to` - self.metrics.set_best_revenue(best.block.number, best.fees().to::() as f64); + self.metrics.set_best_revenue(best.block.number, f64::from(best.fees)); } res @@ -249,9 +248,8 @@ where let fut = async move { let res = fut.await; if let Ok(ref payload) = res { - // TODO: remove `to` resolved_metrics - .set_resolved_revenue(payload.block.number, payload.fees().to::() as f64); + .set_resolved_revenue(payload.block.number, f64::from(payload.fees)); } res }; From 4679bce41ac83534482186080d27a3db5633dc71 Mon Sep 17 00:00:00 2001 From: gbrew <127057440+gbrew@users.noreply.github.com> Date: Fri, 24 Nov 2023 22:40:59 -0700 Subject: [PATCH 010/277] fix(tracing): fix the parity vmTrace stack pushes #5561 (#5563) Co-authored-by: fake Co-authored-by: Matthias Seitz --- .../src/tracing/builder/parity.rs | 153 +++++++++--------- .../revm-inspectors/src/tracing/config.rs | 85 ++++++---- .../revm/revm-inspectors/src/tracing/mod.rs | 17 +- 3 files changed, 141 insertions(+), 114 deletions(-) diff --git a/crates/revm/revm-inspectors/src/tracing/builder/parity.rs b/crates/revm/revm-inspectors/src/tracing/builder/parity.rs index 943a85ee2b1c..d091a019b268 100644 --- a/crates/revm/revm-inspectors/src/tracing/builder/parity.rs +++ b/crates/revm/revm-inspectors/src/tracing/builder/parity.rs @@ -8,7 +8,10 @@ use alloy_primitives::{Address, U64}; use reth_rpc_types::{trace::parity::*, TransactionInfo}; use revm::{ db::DatabaseRef, - interpreter::opcode::{self, spec_opcode_gas}, + interpreter::{ + opcode::{self, spec_opcode_gas}, + OpCode, + }, primitives::{Account, ExecutionResult, ResultAndState, SpecId, KECCAK_EMPTY}, }; use std::collections::{HashSet, VecDeque}; @@ -385,87 +388,9 @@ impl ParityTraceBuilder { }) }; - // Calculate the stack items at this step - let push_stack = { - let step_op = step.op.get(); - let show_stack: usize; - if (opcode::PUSH0..=opcode::PUSH32).contains(&step_op) { - show_stack = 1; - } else if (opcode::SWAP1..=opcode::SWAP16).contains(&step_op) { - show_stack = (step_op - opcode::SWAP1) as usize + 2; - } else if (opcode::DUP1..=opcode::DUP16).contains(&step_op) { - show_stack = (step_op - opcode::DUP1) as usize + 2; - } else { - show_stack = match step_op { - opcode::CALLDATALOAD | - opcode::SLOAD | - opcode::MLOAD | - opcode::CALLDATASIZE | - opcode::LT | - opcode::GT | - opcode::DIV | - opcode::SDIV | - opcode::SAR | - opcode::AND | - opcode::EQ | - opcode::CALLVALUE | - opcode::ISZERO | - opcode::ADD | - opcode::EXP | - opcode::CALLER | - opcode::KECCAK256 | - opcode::SUB | - opcode::ADDRESS | - opcode::GAS | - opcode::MUL | - opcode::RETURNDATASIZE | - opcode::NOT | - opcode::SHR | - opcode::SHL | - opcode::EXTCODESIZE | - opcode::SLT | - opcode::OR | - opcode::NUMBER | - opcode::PC | - opcode::TIMESTAMP | - opcode::BALANCE | - opcode::SELFBALANCE | - opcode::MULMOD | - opcode::ADDMOD | - opcode::BASEFEE | - opcode::BLOCKHASH | - opcode::BYTE | - opcode::XOR | - opcode::ORIGIN | - opcode::CODESIZE | - opcode::MOD | - opcode::SIGNEXTEND | - opcode::GASLIMIT | - opcode::DIFFICULTY | - opcode::SGT | - opcode::GASPRICE | - opcode::MSIZE | - opcode::EXTCODEHASH | - opcode::SMOD | - opcode::CHAINID | - opcode::COINBASE => 1, - _ => 0, - } - }; - let mut push_stack = step.push_stack.clone().unwrap_or_default(); - if let Some(stack) = step.stack.as_ref() { - for idx in (0..show_stack).rev() { - if stack.len() > idx { - push_stack.push(stack[stack.len() - idx - 1]) - } - } - } - push_stack - }; - let maybe_execution = Some(VmExecutedOperation { used: step.gas_remaining, - push: push_stack, + push: step.push_stack.clone().unwrap_or_default(), mem: maybe_memory, store: maybe_storage, }); @@ -637,3 +562,71 @@ where Ok(()) } + +/// Returns the number of items pushed on the stack by a given opcode. +/// This used to determine how many stack etries to put in the `push` element +/// in a parity vmTrace. +/// The value is obvious for most opcodes, but SWAP* and DUP* are a bit weird, +/// and we handle those as they are handled in parity vmtraces. +/// For reference: +pub(crate) fn stack_push_count(step_op: OpCode) -> usize { + let step_op = step_op.get(); + match step_op { + opcode::PUSH0..=opcode::PUSH32 => 1, + opcode::SWAP1..=opcode::SWAP16 => (step_op - opcode::SWAP1) as usize + 2, + opcode::DUP1..=opcode::DUP16 => (step_op - opcode::DUP1) as usize + 2, + opcode::CALLDATALOAD | + opcode::SLOAD | + opcode::MLOAD | + opcode::CALLDATASIZE | + opcode::LT | + opcode::GT | + opcode::DIV | + opcode::SDIV | + opcode::SAR | + opcode::AND | + opcode::EQ | + opcode::CALLVALUE | + opcode::ISZERO | + opcode::ADD | + opcode::EXP | + opcode::CALLER | + opcode::KECCAK256 | + opcode::SUB | + opcode::ADDRESS | + opcode::GAS | + opcode::MUL | + opcode::RETURNDATASIZE | + opcode::NOT | + opcode::SHR | + opcode::SHL | + opcode::EXTCODESIZE | + opcode::SLT | + opcode::OR | + opcode::NUMBER | + opcode::PC | + opcode::TIMESTAMP | + opcode::BALANCE | + opcode::SELFBALANCE | + opcode::MULMOD | + opcode::ADDMOD | + opcode::BASEFEE | + opcode::BLOCKHASH | + opcode::BYTE | + opcode::XOR | + opcode::ORIGIN | + opcode::CODESIZE | + opcode::MOD | + opcode::SIGNEXTEND | + opcode::GASLIMIT | + opcode::DIFFICULTY | + opcode::SGT | + opcode::GASPRICE | + opcode::MSIZE | + opcode::EXTCODEHASH | + opcode::SMOD | + opcode::CHAINID | + opcode::COINBASE => 1, + _ => 0, + } +} diff --git a/crates/revm/revm-inspectors/src/tracing/config.rs b/crates/revm/revm-inspectors/src/tracing/config.rs index 689fa16de949..6bc85f76c9c2 100644 --- a/crates/revm/revm-inspectors/src/tracing/config.rs +++ b/crates/revm/revm-inspectors/src/tracing/config.rs @@ -1,25 +1,6 @@ use reth_rpc_types::trace::{geth::GethDefaultTracingOptions, parity::TraceType}; use std::collections::HashSet; -/// What kind of tracing style this is. -/// -/// This affects things like error messages. -#[derive(Debug, Clone, Copy, Eq, PartialEq)] -pub(crate) enum TraceStyle { - /// Parity style tracer - Parity, - /// Geth style tracer - #[allow(unused)] - Geth, -} - -impl TraceStyle { - /// Returns true if this is a parity style tracer. - pub(crate) const fn is_parity(self) -> bool { - matches!(self, Self::Parity) - } -} - /// Gives guidance to the [TracingInspector](crate::tracing::TracingInspector). /// /// Use [TracingInspectorConfig::default_parity] or [TracingInspectorConfig::default_geth] to get @@ -31,7 +12,7 @@ pub struct TracingInspectorConfig { /// Whether to record individual memory snapshots. pub record_memory_snapshots: bool, /// Whether to record individual stack snapshots. - pub record_stack_snapshots: bool, + pub record_stack_snapshots: StackSnapshotType, /// Whether to record state diffs. pub record_state_diff: bool, /// Whether to ignore precompile calls. @@ -48,7 +29,7 @@ impl TracingInspectorConfig { Self { record_steps: true, record_memory_snapshots: true, - record_stack_snapshots: true, + record_stack_snapshots: StackSnapshotType::Full, record_state_diff: false, exclude_precompile_calls: false, record_call_return_data: false, @@ -63,7 +44,7 @@ impl TracingInspectorConfig { Self { record_steps: false, record_memory_snapshots: false, - record_stack_snapshots: false, + record_stack_snapshots: StackSnapshotType::None, record_state_diff: false, exclude_precompile_calls: true, record_call_return_data: false, @@ -78,7 +59,7 @@ impl TracingInspectorConfig { Self { record_steps: true, record_memory_snapshots: true, - record_stack_snapshots: true, + record_stack_snapshots: StackSnapshotType::Full, record_state_diff: true, exclude_precompile_calls: false, record_call_return_data: false, @@ -93,9 +74,11 @@ impl TracingInspectorConfig { #[inline] pub fn from_parity_config(trace_types: &HashSet) -> Self { let needs_vm_trace = trace_types.contains(&TraceType::VmTrace); + let snap_type = + if needs_vm_trace { StackSnapshotType::Pushes } else { StackSnapshotType::None }; TracingInspectorConfig::default_parity() .set_steps(needs_vm_trace) - .set_stack_snapshots(needs_vm_trace) + .set_stack_snapshots(snap_type) .set_memory_snapshots(needs_vm_trace) } @@ -104,7 +87,11 @@ impl TracingInspectorConfig { pub fn from_geth_config(config: &GethDefaultTracingOptions) -> Self { Self { record_memory_snapshots: config.enable_memory.unwrap_or_default(), - record_stack_snapshots: !config.disable_stack.unwrap_or_default(), + record_stack_snapshots: if config.disable_stack.unwrap_or_default() { + StackSnapshotType::None + } else { + StackSnapshotType::Full + }, record_state_diff: !config.disable_storage.unwrap_or_default(), ..Self::default_geth() } @@ -130,8 +117,8 @@ impl TracingInspectorConfig { self } - /// Configure whether the tracer should record stack snapshots - pub fn set_stack_snapshots(mut self, record_stack_snapshots: bool) -> Self { + /// Configure how the tracer should record stack snapshots + pub fn set_stack_snapshots(mut self, record_stack_snapshots: StackSnapshotType) -> Self { self.record_stack_snapshots = record_stack_snapshots; self } @@ -164,6 +151,50 @@ impl TracingInspectorConfig { } } +/// How much of the stack to record. Nothing, just the items pushed, or the full stack +#[derive(Debug, Clone, Copy, Eq, PartialEq)] +pub enum StackSnapshotType { + /// Don't record stack snapshots + None, + /// Record only the items pushed to the stack + Pushes, + /// Record the full stack + Full, +} + +impl StackSnapshotType { + /// Returns true if this is the [StackSnapshotType::Full] variant + #[inline] + pub fn is_full(self) -> bool { + matches!(self, Self::Full) + } + + /// Returns true if this is the [StackSnapshotType::Pushes] variant + #[inline] + pub fn is_pushes(self) -> bool { + matches!(self, Self::Pushes) + } +} + +/// What kind of tracing style this is. +/// +/// This affects things like error messages. +#[derive(Debug, Clone, Copy, Eq, PartialEq)] +pub(crate) enum TraceStyle { + /// Parity style tracer + Parity, + /// Geth style tracer + #[allow(unused)] + Geth, +} + +impl TraceStyle { + /// Returns true if this is a parity style tracer. + pub(crate) const fn is_parity(self) -> bool { + matches!(self, Self::Parity) + } +} + #[cfg(test)] mod tests { use super::*; diff --git a/crates/revm/revm-inspectors/src/tracing/mod.rs b/crates/revm/revm-inspectors/src/tracing/mod.rs index 31d7c29c2e8f..0f1ba19147a5 100644 --- a/crates/revm/revm-inspectors/src/tracing/mod.rs +++ b/crates/revm/revm-inspectors/src/tracing/mod.rs @@ -22,6 +22,7 @@ mod fourbyte; mod opcount; pub mod types; mod utils; +use self::parity::stack_push_count; use crate::tracing::{ arena::PushTraceKind, types::{CallTraceNode, RecordedMemory, StorageChange, StorageChangeReason}, @@ -282,7 +283,11 @@ impl TracingInspector { .record_memory_snapshots .then(|| RecordedMemory::new(interp.shared_memory.context_memory().to_vec())) .unwrap_or_default(); - let stack = self.config.record_stack_snapshots.then(|| interp.stack.data().clone()); + let stack = if self.config.record_stack_snapshots.is_full() { + Some(interp.stack.data().clone()) + } else { + None + }; let op = OpCode::new(interp.current_opcode()) .or_else(|| { @@ -325,12 +330,10 @@ impl TracingInspector { self.step_stack.pop().expect("can't fill step without starting a step first"); let step = &mut self.traces.arena[trace_idx].trace.steps[step_idx]; - if let Some(stack) = step.stack.as_ref() { - // only check stack changes if record stack snapshots is enabled: if stack is Some - if interp.stack.len() > stack.len() { - // if the stack grew, we need to record the new values - step.push_stack = Some(interp.stack.data()[stack.len()..].to_vec()); - } + if self.config.record_stack_snapshots.is_pushes() { + let num_pushed = stack_push_count(step.op); + let start = interp.stack.len() - num_pushed; + step.push_stack = Some(interp.stack.data()[start..].to_vec()); } if self.config.record_memory_snapshots { From 7ef2d49a1f4388add6ff172eb3a94d5f0c166069 Mon Sep 17 00:00:00 2001 From: Matthias Seitz Date: Sat, 25 Nov 2023 08:27:12 +0100 Subject: [PATCH 011/277] feat: add into_registry function (#5565) --- crates/rpc/rpc-builder/src/lib.rs | 94 +++++++++++++++++++++++++++++++ 1 file changed, 94 insertions(+) diff --git a/crates/rpc/rpc-builder/src/lib.rs b/crates/rpc/rpc-builder/src/lib.rs index bd2a8db3b711..9da2a4395952 100644 --- a/crates/rpc/rpc-builder/src/lib.rs +++ b/crates/rpc/rpc-builder/src/lib.rs @@ -417,6 +417,37 @@ where (modules, auth_module, registry) } + /// Converts the builder into a [RethModuleRegistry] which can be used to create all components. + /// + /// This is useful for getting access to API handlers directly: + /// + /// # Example + /// + /// ```no_run + /// use reth_network_api::noop::NoopNetwork; + /// use reth_provider::test_utils::{NoopProvider, TestCanonStateSubscriptions}; + /// use reth_rpc_builder::RpcModuleBuilder; + /// use reth_tasks::TokioTaskExecutor; + /// use reth_transaction_pool::noop::NoopTransactionPool; + /// + /// let mut registry = RpcModuleBuilder::default() + /// .with_provider(NoopProvider::default()) + /// .with_pool(NoopTransactionPool::default()) + /// .with_network(NoopNetwork::default()) + /// .with_executor(TokioTaskExecutor::default()) + /// .with_events(TestCanonStateSubscriptions::default()) + /// .into_registry(Default::default()); + /// + /// let eth_api = registry.eth_api(); + /// ``` + pub fn into_registry( + self, + config: RpcModuleConfig, + ) -> RethModuleRegistry { + let Self { provider, pool, network, executor, events } = self; + RethModuleRegistry::new(provider, pool, network, executor, events, config) + } + /// Configures all [RpcModule]s specific to the given [TransportRpcModuleConfig] which can be /// used to start the transport server(s). /// @@ -918,6 +949,10 @@ where Events: CanonStateSubscriptions + Clone + 'static, { /// Register Eth Namespace + /// + /// # Panics + /// + /// If called outside of the tokio runtime. See also [Self::eth_api] pub fn register_eth(&mut self) -> &mut Self { let eth_api = self.eth_api(); self.modules.insert(RethRpcModule::Eth, eth_api.into_rpc().into()); @@ -925,6 +960,10 @@ where } /// Register Otterscan Namespace + /// + /// # Panics + /// + /// If called outside of the tokio runtime. See also [Self::eth_api] pub fn register_ots(&mut self) -> &mut Self { let otterscan_api = self.otterscan_api(); self.modules.insert(RethRpcModule::Ots, otterscan_api.into_rpc().into()); @@ -932,6 +971,10 @@ where } /// Register Debug Namespace + /// + /// # Panics + /// + /// If called outside of the tokio runtime. See also [Self::eth_api] pub fn register_debug(&mut self) -> &mut Self { let debug_api = self.debug_api(); self.modules.insert(RethRpcModule::Debug, debug_api.into_rpc().into()); @@ -939,6 +982,10 @@ where } /// Register Trace Namespace + /// + /// # Panics + /// + /// If called outside of the tokio runtime. See also [Self::eth_api] pub fn register_trace(&mut self) -> &mut Self { let trace_api = self.trace_api(); self.modules.insert(RethRpcModule::Trace, trace_api.into_rpc().into()); @@ -967,6 +1014,12 @@ where } /// Register Net Namespace + /// + /// See also [Self::eth_api] + /// + /// # Panics + /// + /// If called outside of the tokio runtime. pub fn register_net(&mut self) -> &mut Self { let netapi = self.net_api(); self.modules.insert(RethRpcModule::Net, netapi.into_rpc().into()); @@ -974,6 +1027,12 @@ where } /// Register Reth namespace + /// + /// See also [Self::eth_api] + /// + /// # Panics + /// + /// If called outside of the tokio runtime. pub fn register_reth(&mut self) -> &mut Self { let rethapi = self.reth_api(); self.modules.insert(RethRpcModule::Reth, rethapi.into_rpc().into()); @@ -1002,6 +1061,10 @@ where /// /// If this is the first time the namespace is requested, a new instance of API implementation /// will be created. + /// + /// # Panics + /// + /// If called outside of the tokio runtime. See also [Self::eth_api] pub fn reth_methods( &mut self, namespaces: impl Iterator, @@ -1150,33 +1213,60 @@ where } /// Returns the configured [EthHandlers] or creates it if it does not exist yet + /// + /// # Panics + /// + /// If called outside of the tokio runtime. See also [Self::eth_api] pub fn eth_handlers(&mut self) -> EthHandlers { self.with_eth(|handlers| handlers.clone()) } /// Returns the configured [EthApi] or creates it if it does not exist yet + /// + /// Caution: This will spawn the necessary tasks required by the [EthApi]: [EthStateCache]. + /// + /// # Panics + /// + /// If called outside of the tokio runtime. pub fn eth_api(&mut self) -> EthApi { self.with_eth(|handlers| handlers.api.clone()) } + /// Instantiates TraceApi + /// + /// # Panics + /// + /// If called outside of the tokio runtime. See also [Self::eth_api] pub fn trace_api(&mut self) -> TraceApi> { let eth = self.eth_handlers(); TraceApi::new(self.provider.clone(), eth.api, self.blocking_pool_guard.clone()) } /// Instantiates [EthBundle] Api + /// + /// # Panics + /// + /// If called outside of the tokio runtime. See also [Self::eth_api] pub fn bundle_api(&mut self) -> EthBundle> { let eth_api = self.eth_api(); EthBundle::new(eth_api, self.blocking_pool_guard.clone()) } /// Instantiates OtterscanApi + /// + /// # Panics + /// + /// If called outside of the tokio runtime. See also [Self::eth_api] pub fn otterscan_api(&mut self) -> OtterscanApi> { let eth_api = self.eth_api(); OtterscanApi::new(eth_api) } /// Instantiates DebugApi + /// + /// # Panics + /// + /// If called outside of the tokio runtime. See also [Self::eth_api] pub fn debug_api(&mut self) -> DebugApi> { let eth_api = self.eth_api(); DebugApi::new( @@ -1188,6 +1278,10 @@ where } /// Instantiates NetApi + /// + /// # Panics + /// + /// If called outside of the tokio runtime. See also [Self::eth_api] pub fn net_api(&mut self) -> NetApi> { let eth_api = self.eth_api(); NetApi::new(self.network.clone(), eth_api) From 973ca0082016691a8a397200fde81f5a978534f2 Mon Sep 17 00:00:00 2001 From: Matthias Seitz Date: Sat, 25 Nov 2023 08:42:08 +0100 Subject: [PATCH 012/277] feat: add block+receipts iter (#5560) --- crates/rpc/rpc/src/eth/cache/mod.rs | 22 ++++++++-------------- crates/storage/provider/src/chain.rs | 17 +++++++++++++++++ 2 files changed, 25 insertions(+), 14 deletions(-) diff --git a/crates/rpc/rpc/src/eth/cache/mod.rs b/crates/rpc/rpc/src/eth/cache/mod.rs index 2ca9406cb10a..80cf92d1b76d 100644 --- a/crates/rpc/rpc/src/eth/cache/mod.rs +++ b/crates/rpc/rpc/src/eth/cache/mod.rs @@ -535,20 +535,14 @@ where { while let Some(event) = events.next().await { if let Some(committed) = event.committed() { - // we're only interested in new committed blocks - let (blocks, state) = committed.inner(); - - let blocks = blocks.iter().map(|(_, block)| block.clone()).collect::>(); - - // also cache all receipts of the blocks - let mut receipts = Vec::with_capacity(blocks.len()); - for block in &blocks { - let block_receipts = BlockReceipts { - block_hash: block.block.hash, - receipts: state.receipts_by_block(block.number).to_vec(), - }; - receipts.push(block_receipts); - } + let (blocks, receipts): (Vec<_>, Vec<_>) = committed + .blocks_and_receipts() + .map(|(block, receipts)| { + let block_receipts = + BlockReceipts { block_hash: block.block.hash, receipts: receipts.clone() }; + (block.clone(), block_receipts) + }) + .unzip(); let _ = eth_state_cache .to_service diff --git a/crates/storage/provider/src/chain.rs b/crates/storage/provider/src/chain.rs index 3a6ae3fffc9f..c970f8f35fcf 100644 --- a/crates/storage/provider/src/chain.rs +++ b/crates/storage/provider/src/chain.rs @@ -105,6 +105,23 @@ impl Chain { (ChainBlocks { blocks: Cow::Borrowed(&self.blocks) }, &self.state) } + /// Returns an iterator over all the receipts of the blocks in the chain. + pub fn block_receipts_iter(&self) -> impl Iterator>> + '_ { + self.state.receipts().iter() + } + + /// Returns an iterator over all blocks in the chain with increasing block number. + pub fn blocks_iter(&self) -> impl Iterator + '_ { + self.blocks().iter().map(|block| block.1) + } + + /// Returns an iterator over all blocks and their receipts in the chain. + pub fn blocks_and_receipts( + &self, + ) -> impl Iterator>)> + '_ { + self.blocks_iter().zip(self.block_receipts_iter()) + } + /// Get the block at which this chain forked. #[track_caller] pub fn fork_block(&self) -> ForkBlock { From 7de2582000c3ff051dacaefd7720595e5905ed69 Mon Sep 17 00:00:00 2001 From: Bjerg Date: Sat, 25 Nov 2023 10:21:01 +0100 Subject: [PATCH 013/277] chore: export `StackSnapshotType` (#5566) --- crates/revm/revm-inspectors/src/tracing/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/revm/revm-inspectors/src/tracing/mod.rs b/crates/revm/revm-inspectors/src/tracing/mod.rs index 0f1ba19147a5..5bba7eddde0f 100644 --- a/crates/revm/revm-inspectors/src/tracing/mod.rs +++ b/crates/revm/revm-inspectors/src/tracing/mod.rs @@ -32,7 +32,7 @@ pub use builder::{ geth::{self, GethTraceBuilder}, parity::{self, ParityTraceBuilder}, }; -pub use config::TracingInspectorConfig; +pub use config::{StackSnapshotType, TracingInspectorConfig}; pub use fourbyte::FourByteInspector; pub use opcount::OpcodeCountInspector; From 1308b6729ecfb636e4d54a40dd1e3309887838cf Mon Sep 17 00:00:00 2001 From: Matthias Seitz Date: Sat, 25 Nov 2023 15:06:14 +0100 Subject: [PATCH 014/277] feat: initial relay ssz support (#5568) --- Cargo.lock | 50 +++++++++++++++++++++++++++++++ crates/rpc/rpc-types/Cargo.toml | 9 ++++-- crates/rpc/rpc-types/src/relay.rs | 2 ++ 3 files changed, 59 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 6059240ad981..a300176471d3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1740,6 +1740,16 @@ dependencies = [ "darling_macro 0.10.2", ] +[[package]] +name = "darling" +version = "0.13.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a01d95850c592940db9b8194bc39f4bc0e89dee5c4265e4b1807c34a9aba453c" +dependencies = [ + "darling_core 0.13.4", + "darling_macro 0.13.4", +] + [[package]] name = "darling" version = "0.20.3" @@ -1764,6 +1774,20 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "darling_core" +version = "0.13.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "859d65a907b6852c9361e3185c862aae7fafd2887876799fa55f5f99dc40d610" +dependencies = [ + "fnv", + "ident_case", + "proc-macro2", + "quote", + "strsim 0.10.0", + "syn 1.0.109", +] + [[package]] name = "darling_core" version = "0.20.3" @@ -1789,6 +1813,17 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "darling_macro" +version = "0.13.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c972679f83bdf9c42bd905396b6c3588a843a17f0f16dfcfa3e2c5d57441835" +dependencies = [ + "darling_core 0.13.4", + "quote", + "syn 1.0.109", +] + [[package]] name = "darling_macro" version = "0.20.3" @@ -2382,6 +2417,18 @@ dependencies = [ "smallvec", ] +[[package]] +name = "ethereum_ssz_derive" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6085d7fd3cf84bd2b8fec150d54c8467fb491d8db9c460607c5534f653a0ee38" +dependencies = [ + "darling 0.13.4", + "proc-macro2", + "quote", + "syn 1.0.109", +] + [[package]] name = "ethers-contract" version = "2.0.11" @@ -6450,6 +6497,8 @@ dependencies = [ "alloy-rlp", "arbitrary", "bytes", + "ethereum_ssz", + "ethereum_ssz_derive", "itertools 0.11.0", "jsonrpsee-types", "proptest", @@ -6799,6 +6848,7 @@ dependencies = [ "ark-ff 0.3.0", "ark-ff 0.4.2", "bytes", + "ethereum_ssz", "fastrlp", "num-bigint", "num-traits", diff --git a/crates/rpc/rpc-types/Cargo.toml b/crates/rpc/rpc-types/Cargo.toml index e867d13479c6..7074686e39ec 100644 --- a/crates/rpc/rpc-types/Cargo.toml +++ b/crates/rpc/rpc-types/Cargo.toml @@ -9,8 +9,11 @@ repository.workspace = true description = "Reth RPC types" [dependencies] -# # ethereum +# ethereum alloy-rlp = { workspace = true, features = ["arrayvec", "derive"] } +alloy-primitives = { workspace = true, features = ["rand", "rlp", "serde"] } +ethereum_ssz_derive = { version = "0.5", optional = true } +ethereum_ssz = { version = "0.5", optional = true } # misc thiserror.workspace = true @@ -19,13 +22,13 @@ serde = { workspace = true, features = ["derive"] } serde_with = "3.3" serde_json.workspace = true jsonrpsee-types = { workspace = true, optional = true } -alloy-primitives = { workspace = true, features = ["rand", "rlp", "serde"] } url = "2.3" # necessary so we don't hit a "undeclared 'std'": # https://github.com/paradigmxyz/reth/pull/177#discussion_r1021172198 secp256k1.workspace = true bytes.workspace = true +# arbitrary arbitrary = { workspace = true, features = ["derive"], optional = true } proptest = { workspace = true, optional = true } proptest-derive = { workspace = true, optional = true } @@ -33,8 +36,10 @@ 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"] optimism = [] + [dev-dependencies] # misc alloy-primitives = { workspace = true, features = ["rand", "rlp", "serde", "arbitrary"] } diff --git a/crates/rpc/rpc-types/src/relay.rs b/crates/rpc/rpc-types/src/relay.rs index c00a33db5c02..51ac83d21649 100644 --- a/crates/rpc/rpc-types/src/relay.rs +++ b/crates/rpc/rpc-types/src/relay.rs @@ -44,6 +44,7 @@ pub struct ValidatorRegistrationMessage { /// (not necessarily a value confirmed by the relay). #[serde_as] #[derive(Default, Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +#[cfg_attr(feature = "ssz", derive(ssz_derive::Encode, ssz_derive::Decode))] pub struct BidTrace { #[serde_as(as = "DisplayFromStr")] pub slot: u64, @@ -64,6 +65,7 @@ pub struct BidTrace { /// SignedBidTrace is a BidTrace with a signature #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +#[cfg_attr(feature = "ssz", derive(ssz_derive::Encode, ssz_derive::Decode))] pub struct SignedBidTrace { pub message: BidTrace, pub signature: BlsSignature, From b0944fb6137aaf1dfcd25f223d49cb3ebd65f0dd Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Sun, 26 Nov 2023 08:37:52 +0100 Subject: [PATCH 015/277] chore(deps): weekly `cargo update` (#5573) Co-authored-by: github-merge-queue --- Cargo.lock | 80 ++++++++++++++++++++++++++++++------------------------ 1 file changed, 45 insertions(+), 35 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index a300176471d3..9b14a7496eb9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1850,9 +1850,9 @@ dependencies = [ [[package]] name = "data-encoding" -version = "2.4.0" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c2e66c9d817f1720209181c316d28635c050fa304f9c79e47a520882661b7308" +checksum = "7e962a19be5cfc3f3bf6dd8f61eb50107f356ad6270fbb3ed41476571db78be5" [[package]] name = "debug-helper" @@ -1866,7 +1866,7 @@ version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bef552e6f588e446098f6ba40d89ac146c8c7b64aade83c051ee00bb5d2bc18d" dependencies = [ - "uuid 1.5.0", + "uuid 1.6.1", ] [[package]] @@ -2070,7 +2070,7 @@ dependencies = [ "hex", "hkdf", "lazy_static", - "lru 0.12.0", + "lru 0.12.1", "more-asserts", "parking_lot 0.11.2", "rand 0.8.5", @@ -2208,9 +2208,9 @@ checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07" [[package]] name = "elliptic-curve" -version = "0.13.7" +version = "0.13.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e9775b22bc152ad86a0cf23f0f348b884b26add12bf741e7ffc4d4ab2ab4d205" +checksum = "b5e6043086bf7973472e0c7dff2142ea0b680d30e18d9cc40f267efbf222bd47" dependencies = [ "base16ct", "crypto-bigint", @@ -2762,9 +2762,9 @@ checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" [[package]] name = "form_urlencoded" -version = "1.2.0" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a62bc1cf6f830c2ec14a513a9fb124d0a213a629668a4186f329db21fe045652" +checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456" dependencies = [ "percent-encoding", ] @@ -2959,9 +2959,9 @@ dependencies = [ [[package]] name = "gimli" -version = "0.28.0" +version = "0.28.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6fb8d784f27acf97159b40fc4db5ecd8aa23b9ad5ef69cdd136d3bc80665f0c0" +checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253" [[package]] name = "glob" @@ -3520,6 +3520,16 @@ dependencies = [ "unicode-normalization", ] +[[package]] +name = "idna" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "634d9b1461af396cad843f47fdba5597a4f9e6ddd4bfb6ff5d85028c25cb12f6" +dependencies = [ + "unicode-bidi", + "unicode-normalization", +] + [[package]] name = "if_chain" version = "1.0.2" @@ -3636,9 +3646,9 @@ checksum = "64e9829a50b42bb782c1df523f78d332fe371b10c661e78b7a3c34b0198e9fac" [[package]] name = "inferno" -version = "0.11.18" +version = "0.11.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "abfb2e51b23c338595ae0b6bdaaa7a4a8b860b8d788a4331cb07b50fe5dea71b" +checksum = "321f0f839cd44a4686e9504b0a62b4d69a50b62072144c71c68f5873c167b8d9" dependencies = [ "ahash", "indexmap 2.1.0", @@ -4094,9 +4104,9 @@ dependencies = [ [[package]] name = "lru" -version = "0.12.0" +version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1efa59af2ddfad1854ae27d75009d538d0998b4b2fd47083e743ac1a10e46c60" +checksum = "2994eeba8ed550fd9b47a0b38f0242bc3344e496483c6180b69139cc2fa5d1d7" dependencies = [ "hashbrown 0.14.2", ] @@ -4657,9 +4667,9 @@ dependencies = [ [[package]] name = "openssl" -version = "0.10.59" +version = "0.10.60" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a257ad03cd8fb16ad4172fedf8094451e1af1c4b70097636ef2eac9a5f0cc33" +checksum = "79a4c6c3a2b158f7f8f2a2fc5a969fa3a068df6fc9dbb4a43845436e3af7c800" dependencies = [ "bitflags 2.4.1", "cfg-if", @@ -4689,9 +4699,9 @@ checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" [[package]] name = "openssl-sys" -version = "0.9.95" +version = "0.9.96" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "40a4130519a360279579c2053038317e40eff64d13fd3f004f9e1b72b8a6aaf9" +checksum = "3812c071ba60da8b5677cc12bcb1d42989a65553772897a7e0355545a819838f" dependencies = [ "cc", "libc", @@ -4867,9 +4877,9 @@ dependencies = [ [[package]] name = "percent-encoding" -version = "2.3.0" +version = "2.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b2a4787296e9989611394c33f193f676704af1686e70b8f8033ab5ba9a35a94" +checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" [[package]] name = "pest" @@ -6907,9 +6917,9 @@ dependencies = [ [[package]] name = "rustix" -version = "0.38.24" +version = "0.38.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ad981d6c340a49cdc40a1028d9c6084ec7e9fa33fcb839cab656a267071e234" +checksum = "dc99bc2d4f1fed22595588a013687477aedf3cdcfb26558c559edb67b4d9b22e" dependencies = [ "bitflags 2.4.1", "errno", @@ -7197,9 +7207,9 @@ checksum = "cd0b0ec5f1c1ca621c432a25813d8d60c88abe6d3e08a3eb9cf37d97a0fe3d73" [[package]] name = "serde" -version = "1.0.192" +version = "1.0.193" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bca2a08484b285dcb282d0f67b26cadc0df8b19f8c12502c13d966bf9482f001" +checksum = "25dd9975e68d0cb5aa1120c288333fc98731bd1dd12f561e468ea4728c042b89" dependencies = [ "serde_derive", ] @@ -7215,9 +7225,9 @@ dependencies = [ [[package]] name = "serde_derive" -version = "1.0.192" +version = "1.0.193" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d6c7207fbec9faa48073f3e3074cbe553af6ea512d7c21ba46e434e70ea9fbc1" +checksum = "43576ca501357b9b071ac53cdc7da8ef0cbd9493d8df094cd821777ea6e894d3" dependencies = [ "proc-macro2", "quote", @@ -7691,7 +7701,7 @@ dependencies = [ "debugid", "memmap2 0.8.0", "stable_deref_trait", - "uuid 1.5.0", + "uuid 1.6.1", ] [[package]] @@ -8196,7 +8206,7 @@ dependencies = [ "tower-layer", "tower-service", "tracing", - "uuid 1.5.0", + "uuid 1.6.1", ] [[package]] @@ -8577,12 +8587,12 @@ checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" [[package]] name = "url" -version = "2.4.1" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "143b538f18257fac9cad154828a57c6bf5157e1aa604d4816b5995bf6de87ae5" +checksum = "31e6302e3bb753d46e83516cae55ae196fc0c309407cf11ab35cc51a4c2a4633" dependencies = [ "form_urlencoded", - "idna 0.4.0", + "idna 0.5.0", "percent-encoding", "serde", ] @@ -8623,9 +8633,9 @@ dependencies = [ [[package]] name = "uuid" -version = "1.5.0" +version = "1.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "88ad59a7560b41a70d191093a945f0b87bc1deeda46fb237479708a1d6b6cdfc" +checksum = "5e395fcf16a7a3d8127ec99782007af141946b4795001f876d54fb0d55978560" dependencies = [ "getrandom 0.2.11", ] @@ -8796,9 +8806,9 @@ dependencies = [ [[package]] name = "webpki-roots" -version = "0.25.2" +version = "0.25.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "14247bb57be4f377dfb94c72830b8ce8fc6beac03cf4bf7b9732eadd414123fc" +checksum = "1778a42e8b3b90bff8d0f5032bf22250792889a5cdc752aa0020c84abe3aaf10" [[package]] name = "which" From 8e6fb6b5d180e8b59ff0c92e3e43ae969099b32d Mon Sep 17 00:00:00 2001 From: Matthias Seitz Date: Sun, 26 Nov 2023 08:48:29 +0100 Subject: [PATCH 016/277] docs: add panics section (#5574) --- crates/primitives/src/block.rs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/crates/primitives/src/block.rs b/crates/primitives/src/block.rs index 54e0085f531b..9e703faaab0d 100644 --- a/crates/primitives/src/block.rs +++ b/crates/primitives/src/block.rs @@ -54,6 +54,11 @@ impl Block { } /// Transform into a [`BlockWithSenders`]. + /// + /// # Panics + /// + /// If the number of senders does not match the number of transactions in the block. + #[track_caller] pub fn with_senders(self, senders: Vec
) -> BlockWithSenders { assert_eq!(self.body.len(), senders.len(), "Unequal number of senders"); From 8667c336a129b41cf576f2dab4bae66bdaddf5a6 Mon Sep 17 00:00:00 2001 From: DoTheBestToGetTheBest <146037313+DoTheBestToGetTheBest@users.noreply.github.com> Date: Sun, 26 Nov 2023 00:35:19 -0800 Subject: [PATCH 017/277] feat(bin/args): enhance transactionpool with --txpool.nolocals Flag (#5538) Co-authored-by: Matthias Seitz --- bin/reth/src/args/txpool_args.rs | 10 ++++++--- crates/transaction-pool/src/config.rs | 25 +++++++++++++++++++++ crates/transaction-pool/src/lib.rs | 6 ++--- crates/transaction-pool/src/pool/txpool.rs | 8 +++++-- crates/transaction-pool/src/validate/eth.rs | 22 +++++++++++++++--- 5 files changed, 60 insertions(+), 11 deletions(-) diff --git a/bin/reth/src/args/txpool_args.rs b/bin/reth/src/args/txpool_args.rs index f6c3a37bbf75..9a4843d2c9ef 100644 --- a/bin/reth/src/args/txpool_args.rs +++ b/bin/reth/src/args/txpool_args.rs @@ -2,9 +2,9 @@ use clap::Args; use reth_transaction_pool::{ - PoolConfig, PriceBumpConfig, SubPoolLimit, DEFAULT_PRICE_BUMP, REPLACE_BLOB_PRICE_BUMP, - TXPOOL_MAX_ACCOUNT_SLOTS_PER_SENDER, TXPOOL_SUBPOOL_MAX_SIZE_MB_DEFAULT, - TXPOOL_SUBPOOL_MAX_TXS_DEFAULT, + LocalTransactionConfig, PoolConfig, PriceBumpConfig, SubPoolLimit, DEFAULT_PRICE_BUMP, + REPLACE_BLOB_PRICE_BUMP, TXPOOL_MAX_ACCOUNT_SLOTS_PER_SENDER, + TXPOOL_SUBPOOL_MAX_SIZE_MB_DEFAULT, TXPOOL_SUBPOOL_MAX_TXS_DEFAULT, }; /// Parameters for debugging purposes @@ -43,12 +43,16 @@ pub struct TxPoolArgs { /// Price bump percentage to replace an already existing blob transaction #[arg(long = "blobpool.pricebump", default_value_t = REPLACE_BLOB_PRICE_BUMP)] pub blob_transaction_price_bump: u128, + /// Flag to disable local transaction exemptions. + #[arg(long = "txpool.nolocals")] + pub no_locals: bool, } impl TxPoolArgs { /// Returns transaction pool configuration. pub fn pool_config(&self) -> PoolConfig { PoolConfig { + local_transactions_config: LocalTransactionConfig { no_exemptions: self.no_locals }, pending_limit: SubPoolLimit { max_txs: self.pending_max_count, max_size: self.pending_max_size * 1024 * 1024, diff --git a/crates/transaction-pool/src/config.rs b/crates/transaction-pool/src/config.rs index 2e561e35fbe3..fae191136109 100644 --- a/crates/transaction-pool/src/config.rs +++ b/crates/transaction-pool/src/config.rs @@ -30,6 +30,9 @@ pub struct PoolConfig { pub max_account_slots: usize, /// Price bump (in %) for the transaction pool underpriced check. pub price_bumps: PriceBumpConfig, + /// How to handle locally received transactions: + /// [TransactionOrigin::Local](crate::TransactionOrigin). + pub local_transactions_config: LocalTransactionConfig, } impl Default for PoolConfig { @@ -40,6 +43,7 @@ impl Default for PoolConfig { queued_limit: Default::default(), max_account_slots: TXPOOL_MAX_ACCOUNT_SLOTS_PER_SENDER, price_bumps: Default::default(), + local_transactions_config: Default::default(), } } } @@ -99,3 +103,24 @@ impl Default for PriceBumpConfig { } } } + +/// Configuration options for the locally received transactions: +/// [TransactionOrigin::Local](crate::TransactionOrigin) +#[derive(Debug, Clone, Eq, PartialEq, Default)] +pub struct LocalTransactionConfig { + /// Apply no exemptions to the locally received transactions. + /// + /// This includes: + /// - available slots are limited to the configured `max_account_slots` of [PoolConfig] + /// - no price exemptions + /// - no eviction exemptions + pub no_exemptions: bool, +} + +impl LocalTransactionConfig { + /// Returns whether local transactions are not exempt from the configured limits. + #[inline] + pub fn no_local_exemptions(&self) -> bool { + self.no_exemptions + } +} diff --git a/crates/transaction-pool/src/lib.rs b/crates/transaction-pool/src/lib.rs index 84c9e15ecf9d..1621c6ffe179 100644 --- a/crates/transaction-pool/src/lib.rs +++ b/crates/transaction-pool/src/lib.rs @@ -155,9 +155,9 @@ use tracing::{instrument, trace}; pub use crate::{ blobstore::{BlobStore, BlobStoreError}, config::{ - PoolConfig, PriceBumpConfig, SubPoolLimit, DEFAULT_PRICE_BUMP, REPLACE_BLOB_PRICE_BUMP, - TXPOOL_MAX_ACCOUNT_SLOTS_PER_SENDER, TXPOOL_SUBPOOL_MAX_SIZE_MB_DEFAULT, - TXPOOL_SUBPOOL_MAX_TXS_DEFAULT, + LocalTransactionConfig, PoolConfig, PriceBumpConfig, SubPoolLimit, DEFAULT_PRICE_BUMP, + REPLACE_BLOB_PRICE_BUMP, TXPOOL_MAX_ACCOUNT_SLOTS_PER_SENDER, + TXPOOL_SUBPOOL_MAX_SIZE_MB_DEFAULT, TXPOOL_SUBPOOL_MAX_TXS_DEFAULT, }, error::PoolResult, ordering::{CoinbaseTipOrdering, Priority, TransactionOrdering}, diff --git a/crates/transaction-pool/src/pool/txpool.rs b/crates/transaction-pool/src/pool/txpool.rs index 046af476117f..dfc2bf6c7c0d 100644 --- a/crates/transaction-pool/src/pool/txpool.rs +++ b/crates/transaction-pool/src/pool/txpool.rs @@ -1,6 +1,6 @@ //! The internal transaction pool implementation. use crate::{ - config::TXPOOL_MAX_ACCOUNT_SLOTS_PER_SENDER, + config::{LocalTransactionConfig, TXPOOL_MAX_ACCOUNT_SLOTS_PER_SENDER}, error::{Eip4844PoolTransactionError, InvalidPoolTransactionError, PoolError, PoolErrorKind}, identifier::{SenderId, TransactionId}, metrics::TxPoolMetrics, @@ -918,6 +918,8 @@ pub(crate) struct AllTransactions { pending_fees: PendingFees, /// Configured price bump settings for replacements price_bumps: PriceBumpConfig, + /// How to handle [TransactionOrigin::Local](crate::TransactionOrigin) transactions. + local_transactions_config: LocalTransactionConfig, } impl AllTransactions { @@ -926,6 +928,7 @@ impl AllTransactions { Self { max_account_slots: config.max_account_slots, price_bumps: config.price_bumps, + local_transactions_config: config.local_transactions_config.clone(), ..Default::default() } } @@ -1274,7 +1277,7 @@ impl AllTransactions { &self, transaction: ValidPoolTransaction, ) -> Result, InsertErr> { - if !transaction.origin.is_local() { + if !transaction.origin.is_local() || self.local_transactions_config.no_local_exemptions() { let current_txs = self.tx_counter.get(&transaction.sender_id()).copied().unwrap_or_default(); if current_txs >= self.max_account_slots { @@ -1664,6 +1667,7 @@ impl Default for AllTransactions { last_seen_block_hash: Default::default(), pending_fees: Default::default(), price_bumps: Default::default(), + local_transactions_config: Default::default(), } } } diff --git a/crates/transaction-pool/src/validate/eth.rs b/crates/transaction-pool/src/validate/eth.rs index 169f17263e4a..340258c2f7c2 100644 --- a/crates/transaction-pool/src/validate/eth.rs +++ b/crates/transaction-pool/src/validate/eth.rs @@ -5,8 +5,8 @@ use crate::{ error::{Eip4844PoolTransactionError, InvalidPoolTransactionError}, traits::TransactionOrigin, validate::{ValidTransaction, ValidationTask, MAX_INIT_CODE_SIZE, TX_MAX_SIZE}, - EthBlobTransactionSidecar, EthPoolTransaction, PoolTransaction, TransactionValidationOutcome, - TransactionValidationTaskExecutor, TransactionValidator, + EthBlobTransactionSidecar, EthPoolTransaction, LocalTransactionConfig, PoolTransaction, + TransactionValidationOutcome, TransactionValidationTaskExecutor, TransactionValidator, }; use reth_primitives::{ constants::{ @@ -124,6 +124,8 @@ where propagate_local_transactions: bool, /// Stores the setup and parameters needed for validating KZG proofs. kzg_settings: Arc, + /// How to handle [TransactionOrigin::Local](TransactionOrigin) transactions. + local_transactions_config: LocalTransactionConfig, /// Marker for the transaction type _marker: PhantomData, } @@ -235,7 +237,7 @@ where // Drop non-local transactions with a fee lower than the configured fee for acceptance into // the pool. - if !origin.is_local() && + if (!origin.is_local() || self.local_transactions_config.no_local_exemptions()) && transaction.is_eip1559() && transaction.max_priority_fee_per_gas() < self.minimum_priority_fee { @@ -484,6 +486,8 @@ pub struct EthTransactionValidatorBuilder { /// Stores the setup and parameters needed for validating KZG proofs. kzg_settings: Arc, + /// How to handle [TransactionOrigin::Local](TransactionOrigin) transactions. + local_transactions_config: LocalTransactionConfig, } impl EthTransactionValidatorBuilder { @@ -500,6 +504,7 @@ impl EthTransactionValidatorBuilder { // default to true, can potentially take this as a param in the future propagate_local_transactions: true, kzg_settings: Arc::clone(&MAINNET_KZG_TRUSTED_SETUP), + local_transactions_config: Default::default(), // by default all transaction types are allowed eip2718: true, @@ -519,6 +524,15 @@ impl EthTransactionValidatorBuilder { self.set_cancun(false) } + /// Whether to allow exemptions for local transaction exemptions. + pub fn set_local_transactions_config( + mut self, + local_transactions_config: LocalTransactionConfig, + ) -> Self { + self.local_transactions_config = local_transactions_config; + self + } + /// Set the Cancun fork. pub fn set_cancun(mut self, cancun: bool) -> Self { self.cancun = cancun; @@ -624,6 +638,7 @@ impl EthTransactionValidatorBuilder { minimum_priority_fee, propagate_local_transactions, kzg_settings, + local_transactions_config, .. } = self; @@ -642,6 +657,7 @@ impl EthTransactionValidatorBuilder { propagate_local_transactions, blob_store: Box::new(blob_store), kzg_settings, + local_transactions_config, _marker: Default::default(), }; From 13af07c1fbc25ecfffe3293bf7ec0c197ad8e5d9 Mon Sep 17 00:00:00 2001 From: Matthias Seitz Date: Mon, 27 Nov 2023 10:28:59 +0100 Subject: [PATCH 018/277] fix: return net version as number (#5570) --- crates/rpc/rpc/src/net.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/crates/rpc/rpc/src/net.rs b/crates/rpc/rpc/src/net.rs index 9c3582a5847b..2def768e57df 100644 --- a/crates/rpc/rpc/src/net.rs +++ b/crates/rpc/rpc/src/net.rs @@ -32,7 +32,8 @@ where { /// Handler for `net_version` fn version(&self) -> Result { - Ok(self.eth.chain_id().to_string()) + // Note: net_version is numeric: ().to_string()) } /// Handler for `net_peerCount` From ca9f236947cc5e07db9f0ae0bf5f4a02e727f2c5 Mon Sep 17 00:00:00 2001 From: Stefan Batalka Date: Mon, 27 Nov 2023 09:25:24 +0000 Subject: [PATCH 019/277] feat: make PayloadBuilderHandle pub (#5578) --- crates/payload/builder/src/service.rs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/crates/payload/builder/src/service.rs b/crates/payload/builder/src/service.rs index eb17bf2f1b83..8e02a0779731 100644 --- a/crates/payload/builder/src/service.rs +++ b/crates/payload/builder/src/service.rs @@ -78,7 +78,11 @@ pub struct PayloadBuilderHandle { // === impl PayloadBuilderHandle === impl PayloadBuilderHandle { - pub(crate) fn new(to_service: mpsc::UnboundedSender) -> Self { + /// Creates a new payload builder handle for the given channel. + /// + /// Note: this is only used internally by the [PayloadBuilderService] to manage the payload + /// building flow See [PayloadBuilderService::poll] for implementation details. + pub fn new(to_service: mpsc::UnboundedSender) -> Self { Self { to_service } } @@ -349,7 +353,7 @@ type PayloadFuture = Pin, PayloadBuilderError>> + Send + Sync>>; /// Message type for the [PayloadBuilderService]. -pub(crate) enum PayloadServiceCommand { +pub enum PayloadServiceCommand { /// Start building a new payload. BuildNewPayload( PayloadBuilderAttributes, From 57653b1a5805458f758de486c7ed6ffc0e6dd022 Mon Sep 17 00:00:00 2001 From: Matthias Seitz Date: Mon, 27 Nov 2023 11:41:19 +0100 Subject: [PATCH 020/277] chore: limited node command cleanup (#5539) --- bin/reth/src/cli/ext.rs | 6 ++-- bin/reth/src/node/mod.rs | 60 +++++++++++++++++++++++----------------- 2 files changed, 36 insertions(+), 30 deletions(-) diff --git a/bin/reth/src/cli/ext.rs b/bin/reth/src/cli/ext.rs index 352997527396..4bfbeb64d105 100644 --- a/bin/reth/src/cli/ext.rs +++ b/bin/reth/src/cli/ext.rs @@ -1,8 +1,8 @@ //! Support for integrating customizations into the CLI. use crate::cli::{ - components::{RethNodeComponents, RethRpcComponents}, - config::{PayloadBuilderConfig, RethRpcConfig}, + components::{RethNodeComponents, RethRpcComponents, RethRpcServerHandles}, + config::{PayloadBuilderConfig, RethNetworkConfig, RethRpcConfig}, }; use clap::Args; use reth_basic_payload_builder::{BasicPayloadJobGenerator, BasicPayloadJobGeneratorConfig}; @@ -10,8 +10,6 @@ use reth_payload_builder::{PayloadBuilderHandle, PayloadBuilderService}; use reth_tasks::TaskSpawner; use std::{fmt, marker::PhantomData}; -use crate::cli::{components::RethRpcServerHandles, config::RethNetworkConfig}; - /// A trait that allows for extending parts of the CLI with additional functionality. /// /// This is intended as a way to allow to _extend_ the node command. For example, to register diff --git a/bin/reth/src/node/mod.rs b/bin/reth/src/node/mod.rs index 42d3e0136f59..aa0ee115de97 100644 --- a/bin/reth/src/node/mod.rs +++ b/bin/reth/src/node/mod.rs @@ -13,7 +13,7 @@ use crate::{ config::RethRpcConfig, ext::{RethCliExt, RethNodeCommandConfig}, }, - dirs::{DataDirPath, MaybePlatformPath}, + dirs::{ChainPath, DataDirPath, MaybePlatformPath}, init::init_genesis, node::cl_events::ConsensusLayerHealthEvents, prometheus_exporter, @@ -241,18 +241,14 @@ impl NodeCommand { // Does not do anything on windows. raise_fd_limit(); - // add network name to data dir - let data_dir = self.datadir.unwrap_or_chain_default(self.chain.chain); - let config_path = self.config.clone().unwrap_or(data_dir.config_path()); - - let mut config: Config = self.load_config(config_path.clone())?; - - // always store reth.toml in the data dir, not the chain specific data dir - info!(target: "reth::cli", path = ?config_path, "Configuration loaded"); + // get config + let config = self.load_config()?; let prometheus_handle = self.install_prometheus_recorder()?; + let data_dir = self.data_dir(); let db_path = data_dir.db_path(); + info!(target: "reth::cli", path = ?db_path, "Opening database"); let db = Arc::new(init_db(&db_path, self.db.log_level)?.with_metrics()); info!(target: "reth::cli", "Database opened"); @@ -273,14 +269,12 @@ impl NodeCommand { debug!(target: "reth::cli", chain=%self.chain.chain, genesis=?self.chain.genesis_hash(), "Initializing genesis"); - let genesis_hash = init_genesis(db.clone(), self.chain.clone())?; + let genesis_hash = init_genesis(Arc::clone(&db), self.chain.clone())?; info!(target: "reth::cli", "{}", DisplayHardforks::new(self.chain.hardforks())); let consensus = self.consensus(); - self.init_trusted_nodes(&mut config); - debug!(target: "reth::cli", "Spawning stages metrics listener task"); let (sync_metrics_tx, sync_metrics_rx) = unbounded_channel(); let sync_metrics_listener = reth_stages::MetricsListener::new(sync_metrics_rx); @@ -647,10 +641,35 @@ impl NodeCommand { Ok(pipeline) } + /// Returns the chain specific path to the data dir. + fn data_dir(&self) -> ChainPath { + self.datadir.unwrap_or_chain_default(self.chain.chain) + } + + /// Returns the path to the config file. + fn config_path(&self) -> PathBuf { + self.config.clone().unwrap_or_else(|| self.data_dir().config_path()) + } + /// Loads the reth config with the given datadir root - fn load_config(&self, config_path: PathBuf) -> eyre::Result { - confy::load_path::(config_path.clone()) - .wrap_err_with(|| format!("Could not load config file {:?}", config_path)) + fn load_config(&self) -> eyre::Result { + let config_path = self.config_path(); + let mut config = confy::load_path::(&config_path) + .wrap_err_with(|| format!("Could not load config file {:?}", config_path))?; + + info!(target: "reth::cli", path = ?config_path, "Configuration loaded"); + + // Update the config with the command line arguments + config.peers.connect_trusted_nodes_only = self.network.trusted_only; + + if !self.network.trusted_peers.is_empty() { + info!(target: "reth::cli", "Adding trusted nodes"); + self.network.trusted_peers.iter().for_each(|peer| { + config.peers.trusted_nodes.insert(*peer); + }); + } + + Ok(config) } /// Loads the trusted setup params from a given file path or falls back to @@ -665,17 +684,6 @@ impl NodeCommand { } } - fn init_trusted_nodes(&self, config: &mut Config) { - config.peers.connect_trusted_nodes_only = self.network.trusted_only; - - if !self.network.trusted_peers.is_empty() { - info!(target: "reth::cli", "Adding trusted nodes"); - self.network.trusted_peers.iter().for_each(|peer| { - config.peers.trusted_nodes.insert(*peer); - }); - } - } - fn install_prometheus_recorder(&self) -> eyre::Result { prometheus_exporter::install_recorder() } From 563a683a62a0e6052ea972539f42b8ea781534ef Mon Sep 17 00:00:00 2001 From: Nil Medvedev Date: Mon, 27 Nov 2023 10:59:27 +0000 Subject: [PATCH 021/277] Feat/improve fee history performance (#5182) Co-authored-by: Matthias Seitz --- .../src/test_utils/bodies_client.rs | 2 +- crates/net/eth-wire/src/capability.rs | 2 +- crates/primitives/src/snapshot/segment.rs | 4 +- crates/rpc/rpc-builder/src/auth.rs | 10 +- crates/rpc/rpc-builder/src/eth.rs | 5 +- crates/rpc/rpc-builder/src/lib.rs | 22 +- .../rpc-types/src/serde_helpers/json_u256.rs | 4 +- crates/rpc/rpc/src/eth/api/fee_history.rs | 280 ++++++++++++++++++ crates/rpc/rpc/src/eth/api/fees.rs | 198 +++++++------ crates/rpc/rpc/src/eth/api/mod.rs | 25 +- crates/rpc/rpc/src/eth/api/server.rs | 15 +- crates/rpc/rpc/src/eth/api/state.rs | 11 +- crates/rpc/rpc/src/eth/api/transactions.rs | 10 +- crates/rpc/rpc/src/eth/gas_oracle.rs | 11 +- crates/rpc/rpc/src/eth/mod.rs | 6 +- .../src/providers/database/provider.rs | 1 - .../storage/provider/src/test_utils/mock.rs | 10 +- crates/transaction-pool/src/pool/blob.rs | 2 +- 18 files changed, 492 insertions(+), 126 deletions(-) create mode 100644 crates/rpc/rpc/src/eth/api/fee_history.rs diff --git a/crates/net/downloaders/src/test_utils/bodies_client.rs b/crates/net/downloaders/src/test_utils/bodies_client.rs index ac791742d009..2f3cf2f293fb 100644 --- a/crates/net/downloaders/src/test_utils/bodies_client.rs +++ b/crates/net/downloaders/src/test_utils/bodies_client.rs @@ -92,7 +92,7 @@ impl BodiesClient for TestBodiesClient { Box::pin(async move { if should_respond_empty { - return Ok((PeerId::default(), vec![]).into()); + return Ok((PeerId::default(), vec![]).into()) } if should_delay { diff --git a/crates/net/eth-wire/src/capability.rs b/crates/net/eth-wire/src/capability.rs index 9bd4afa82e0c..8f090c72a2e4 100644 --- a/crates/net/eth-wire/src/capability.rs +++ b/crates/net/eth-wire/src/capability.rs @@ -252,7 +252,7 @@ impl SharedCapability { /// Returns an error if the offset is equal or less than [`MAX_RESERVED_MESSAGE_ID`]. pub(crate) fn new(name: &str, version: u8, offset: u8) -> Result { if offset <= MAX_RESERVED_MESSAGE_ID { - return Err(SharedCapabilityError::ReservedMessageIdOffset(offset)); + return Err(SharedCapabilityError::ReservedMessageIdOffset(offset)) } match name { diff --git a/crates/primitives/src/snapshot/segment.rs b/crates/primitives/src/snapshot/segment.rs index 90145f14fef6..879255fa6acf 100644 --- a/crates/primitives/src/snapshot/segment.rs +++ b/crates/primitives/src/snapshot/segment.rs @@ -117,7 +117,7 @@ impl SnapshotSegment { ) -> Option<(Self, RangeInclusive, RangeInclusive)> { let mut parts = name.to_str()?.split('_'); if parts.next() != Some("snapshot") { - return None; + return None } let segment = Self::from_str(parts.next()?).ok()?; @@ -125,7 +125,7 @@ impl SnapshotSegment { let (tx_start, tx_end) = (parts.next()?.parse().ok()?, parts.next()?.parse().ok()?); if block_start >= block_end || tx_start > tx_end { - return None; + return None } Some((segment, block_start..=block_end, tx_start..=tx_end)) diff --git a/crates/rpc/rpc-builder/src/auth.rs b/crates/rpc/rpc-builder/src/auth.rs index c95f3bc00880..7f1158e1cf5c 100644 --- a/crates/rpc/rpc-builder/src/auth.rs +++ b/crates/rpc/rpc-builder/src/auth.rs @@ -16,7 +16,10 @@ use reth_provider::{ StateProviderFactory, }; use reth_rpc::{ - eth::{cache::EthStateCache, gas_oracle::GasPriceOracle, EthFilterConfig}, + eth::{ + cache::EthStateCache, gas_oracle::GasPriceOracle, EthFilterConfig, FeeHistoryCache, + FeeHistoryCacheConfig, + }, AuthLayer, BlockingTaskPool, Claims, EngineEthApi, EthApi, EthFilter, EthSubscriptionIdProvider, JwtAuthValidator, JwtSecret, }; @@ -57,7 +60,11 @@ where // spawn a new cache task let eth_cache = EthStateCache::spawn_with(provider.clone(), Default::default(), executor.clone()); + let gas_oracle = GasPriceOracle::new(provider.clone(), Default::default(), eth_cache.clone()); + + let fee_history_cache = + FeeHistoryCache::new(eth_cache.clone(), FeeHistoryCacheConfig::default()); let eth_api = EthApi::with_spawner( provider.clone(), pool.clone(), @@ -67,6 +74,7 @@ where EthConfig::default().rpc_gas_cap, Box::new(executor.clone()), BlockingTaskPool::build().expect("failed to build tracing pool"), + fee_history_cache, ); let config = EthFilterConfig::default() .max_logs_per_response(DEFAULT_MAX_LOGS_PER_RESPONSE) diff --git a/crates/rpc/rpc-builder/src/eth.rs b/crates/rpc/rpc-builder/src/eth.rs index c40226d6adab..8da3405368fd 100644 --- a/crates/rpc/rpc-builder/src/eth.rs +++ b/crates/rpc/rpc-builder/src/eth.rs @@ -5,7 +5,7 @@ use reth_rpc::{ eth::{ cache::{EthStateCache, EthStateCacheConfig}, gas_oracle::GasPriceOracleConfig, - EthFilterConfig, RPC_DEFAULT_GAS_CAP, + EthFilterConfig, FeeHistoryCacheConfig, RPC_DEFAULT_GAS_CAP, }, BlockingTaskPool, EthApi, EthFilter, EthPubSub, }; @@ -46,6 +46,8 @@ pub struct EthConfig { /// /// Sets TTL for stale filters pub stale_filter_ttl: std::time::Duration, + /// Settings for the fee history cache + pub fee_history_cache: FeeHistoryCacheConfig, } impl EthConfig { @@ -71,6 +73,7 @@ impl Default for EthConfig { max_logs_per_response: DEFAULT_MAX_LOGS_PER_RESPONSE, rpc_gas_cap: RPC_DEFAULT_GAS_CAP.into(), stale_filter_ttl: DEFAULT_STALE_FILTER_TTL, + fee_history_cache: FeeHistoryCacheConfig::default(), } } } diff --git a/crates/rpc/rpc-builder/src/lib.rs b/crates/rpc/rpc-builder/src/lib.rs index 9da2a4395952..b2c7ff3af966 100644 --- a/crates/rpc/rpc-builder/src/lib.rs +++ b/crates/rpc/rpc-builder/src/lib.rs @@ -168,8 +168,9 @@ use reth_provider::{ use reth_rpc::{ eth::{ cache::{cache_new_blocks_task, EthStateCache}, + fee_history_cache_new_blocks_task, gas_oracle::GasPriceOracle, - EthBundle, + EthBundle, FeeHistoryCache, }, AdminApi, AuthLayer, BlockingTaskGuard, BlockingTaskPool, Claims, DebugApi, EngineEthApi, EthApi, EthFilter, EthPubSub, EthSubscriptionIdProvider, JwtAuthValidator, JwtSecret, NetApi, @@ -1153,6 +1154,10 @@ where } /// Creates the [EthHandlers] type the first time this is called. + /// + /// This will spawn the required service tasks for [EthApi] for: + /// - [EthStateCache] + /// - [FeeHistoryCache] fn with_eth(&mut self, f: F) -> R where F: FnOnce(&EthHandlers) -> R, @@ -1170,6 +1175,7 @@ where ); let new_canonical_blocks = self.events.canonical_state_stream(); let c = cache.clone(); + self.executor.spawn_critical( "cache canonical blocks task", Box::pin(async move { @@ -1177,6 +1183,19 @@ where }), ); + let fee_history_cache = + FeeHistoryCache::new(cache.clone(), self.config.eth.fee_history_cache.clone()); + let new_canonical_blocks = self.events.canonical_state_stream(); + let fhc = fee_history_cache.clone(); + let provider_clone = self.provider.clone(); + self.executor.spawn_critical( + "cache canonical blocks for fee history task", + Box::pin(async move { + fee_history_cache_new_blocks_task(fhc, new_canonical_blocks, provider_clone) + .await; + }), + ); + let executor = Box::new(self.executor.clone()); let blocking_task_pool = BlockingTaskPool::build().expect("failed to build tracing pool"); @@ -1189,6 +1208,7 @@ where self.config.eth.rpc_gas_cap, executor.clone(), blocking_task_pool.clone(), + fee_history_cache, ); let filter = EthFilter::new( self.provider.clone(), diff --git a/crates/rpc/rpc-types/src/serde_helpers/json_u256.rs b/crates/rpc/rpc-types/src/serde_helpers/json_u256.rs index 3ed3859a2c84..a22280e8a884 100644 --- a/crates/rpc/rpc-types/src/serde_helpers/json_u256.rs +++ b/crates/rpc/rpc-types/src/serde_helpers/json_u256.rs @@ -162,11 +162,11 @@ where } else { // We could try to convert to a u128 here but there would probably be loss of // precision, so we just return an error. - return Err(Error::custom("Deserializing a large non-mainnet TTD is not supported")); + return Err(Error::custom("Deserializing a large non-mainnet TTD is not supported")) } } else { // must be i64 - negative numbers are not supported - return Err(Error::custom("Negative TTD values are invalid and will not be deserialized")); + return Err(Error::custom("Negative TTD values are invalid and will not be deserialized")) }; Ok(num) diff --git a/crates/rpc/rpc/src/eth/api/fee_history.rs b/crates/rpc/rpc/src/eth/api/fee_history.rs new file mode 100644 index 000000000000..f25ecc6e00e4 --- /dev/null +++ b/crates/rpc/rpc/src/eth/api/fee_history.rs @@ -0,0 +1,280 @@ +//! Consist of types adjacent to the fee history cache and its configs + +use crate::eth::{cache::EthStateCache, error::EthApiError, gas_oracle::MAX_HEADER_HISTORY}; +use futures::{Stream, StreamExt}; +use metrics::atomics::AtomicU64; +use reth_primitives::{Receipt, SealedBlock, TransactionSigned, B256, U256}; +use reth_provider::{BlockReaderIdExt, CanonStateNotification, ChainSpecProvider}; +use reth_rpc_types::TxGasAndReward; +use serde::{Deserialize, Serialize}; +use std::{ + collections::BTreeMap, + fmt::Debug, + sync::{atomic::Ordering::SeqCst, Arc}, +}; + +/// Settings for the [FeeHistoryCache]. +#[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct FeeHistoryCacheConfig { + /// Max number of blocks in cache. + /// + /// Default is [MAX_HEADER_HISTORY] plus some change to also serve slightly older blocks from + /// cache, since fee_history supports the entire range + pub max_blocks: u64, + /// Percentile approximation resolution + /// + /// Default is 4 which means 0.25 + pub resolution: u64, +} + +impl Default for FeeHistoryCacheConfig { + fn default() -> Self { + FeeHistoryCacheConfig { max_blocks: MAX_HEADER_HISTORY + 100, resolution: 4 } + } +} + +/// Wrapper struct for BTreeMap +#[derive(Debug, Clone)] +pub struct FeeHistoryCache { + /// Stores the lower bound of the cache + lower_bound: Arc, + upper_bound: Arc, + /// Config for FeeHistoryCache, consists of resolution for percentile approximation + /// and max number of blocks + config: FeeHistoryCacheConfig, + /// Stores the entries of the cache + entries: Arc>>, + #[allow(unused)] + eth_cache: EthStateCache, +} + +impl FeeHistoryCache { + /// Creates new FeeHistoryCache instance, initialize it with the mose recent data, set bounds + pub fn new(eth_cache: EthStateCache, config: FeeHistoryCacheConfig) -> Self { + let init_tree_map = BTreeMap::new(); + + let entries = Arc::new(tokio::sync::RwLock::new(init_tree_map)); + + let upper_bound = Arc::new(AtomicU64::new(0)); + let lower_bound = Arc::new(AtomicU64::new(0)); + + FeeHistoryCache { config, entries, upper_bound, lower_bound, eth_cache } + } + + /// How the cache is configured. + pub fn config(&self) -> &FeeHistoryCacheConfig { + &self.config + } + + /// Returns the configured resolution for percentile approximation. + #[inline] + pub fn resolution(&self) -> u64 { + self.config.resolution + } + + /// Processing of the arriving blocks + async fn insert_blocks(&self, blocks: I) + where + I: Iterator)>, + { + let mut entries = self.entries.write().await; + + let percentiles = self.predefined_percentiles(); + // Insert all new blocks and calculate approximated rewards + for (block, receipts) in blocks { + let mut fee_history_entry = FeeHistoryEntry::new(&block); + fee_history_entry.rewards = calculate_reward_percentiles_for_block( + &percentiles, + fee_history_entry.gas_used, + fee_history_entry.base_fee_per_gas, + &block.body, + &receipts, + ) + .unwrap_or_default(); + entries.insert(block.number, fee_history_entry); + } + + // enforce bounds by popping the oldest entries + while entries.len() > self.config.max_blocks as usize { + entries.pop_first(); + } + + if entries.len() == 0 { + self.upper_bound.store(0, SeqCst); + self.lower_bound.store(0, SeqCst); + return + } + + let upper_bound = *entries.last_entry().expect("Contains at least one entry").key(); + let lower_bound = *entries.first_entry().expect("Contains at least one entry").key(); + self.upper_bound.store(upper_bound, SeqCst); + self.lower_bound.store(lower_bound, SeqCst); + } + + /// Get UpperBound value for FeeHistoryCache + pub fn upper_bound(&self) -> u64 { + self.upper_bound.load(SeqCst) + } + + /// Get LowerBound value for FeeHistoryCache + pub fn lower_bound(&self) -> u64 { + self.lower_bound.load(SeqCst) + } + + /// Collect fee history for given range. + /// + /// This function retrieves fee history entries from the cache for the specified range. + /// If the requested range (start_block to end_block) is within the cache bounds, + /// it returns the corresponding entries. + /// Otherwise it returns None. + pub async fn get_history( + &self, + start_block: u64, + end_block: u64, + ) -> Option> { + let lower_bound = self.lower_bound(); + let upper_bound = self.upper_bound(); + if start_block >= lower_bound && end_block <= upper_bound { + let entries = self.entries.read().await; + let result = entries + .range(start_block..=end_block + 1) + .map(|(_, fee_entry)| fee_entry.clone()) + .collect::>(); + + if result.is_empty() { + return None + } + + Some(result) + } else { + None + } + } + + /// Generates predefined set of percentiles + /// + /// This returns 100 * resolution points + pub fn predefined_percentiles(&self) -> Vec { + let res = self.resolution() as f64; + (0..=100 * self.resolution()).map(|p| p as f64 / res).collect() + } +} + +/// Awaits for new chain events and directly inserts them into the cache so they're available +/// immediately before they need to be fetched from disk. +pub async fn fee_history_cache_new_blocks_task( + fee_history_cache: FeeHistoryCache, + mut events: St, + _provider: Provider, +) where + St: Stream + Unpin + 'static, + Provider: BlockReaderIdExt + ChainSpecProvider + 'static, +{ + // TODO: keep track of gaps in the chain and fill them from disk + + while let Some(event) = events.next().await { + if let Some(committed) = event.committed() { + let (blocks, receipts): (Vec<_>, Vec<_>) = committed + .blocks_and_receipts() + .map(|(block, receipts)| { + (block.block.clone(), receipts.iter().flatten().cloned().collect::>()) + }) + .unzip(); + fee_history_cache.insert_blocks(blocks.into_iter().zip(receipts)).await; + } + } +} + +/// Calculates reward percentiles for transactions in a block header. +/// Given a list of percentiles and a sealed block header, this function computes +/// the corresponding rewards for the transactions at each percentile. +/// +/// The results are returned as a vector of U256 values. +pub(crate) fn calculate_reward_percentiles_for_block( + percentiles: &[f64], + gas_used: u64, + base_fee_per_gas: u64, + transactions: &[TransactionSigned], + receipts: &[Receipt], +) -> Result, EthApiError> { + let mut transactions = transactions + .iter() + .zip(receipts) + .scan(0, |previous_gas, (tx, receipt)| { + // Convert the cumulative gas used in the receipts + // to the gas usage by the transaction + // + // While we will sum up the gas again later, it is worth + // noting that the order of the transactions will be different, + // so the sum will also be different for each receipt. + let gas_used = receipt.cumulative_gas_used - *previous_gas; + *previous_gas = receipt.cumulative_gas_used; + + Some(TxGasAndReward { + gas_used, + reward: tx.effective_tip_per_gas(Some(base_fee_per_gas)).unwrap_or_default(), + }) + }) + .collect::>(); + + // Sort the transactions by their rewards in ascending order + transactions.sort_by_key(|tx| tx.reward); + + // Find the transaction that corresponds to the given percentile + // + // We use a `tx_index` here that is shared across all percentiles, since we know + // the percentiles are monotonically increasing. + let mut tx_index = 0; + let mut cumulative_gas_used = transactions.first().map(|tx| tx.gas_used).unwrap_or_default(); + let mut rewards_in_block = Vec::new(); + for percentile in percentiles { + // Empty blocks should return in a zero row + if transactions.is_empty() { + rewards_in_block.push(U256::ZERO); + continue + } + + let threshold = (gas_used as f64 * percentile / 100.) as u64; + while cumulative_gas_used < threshold && tx_index < transactions.len() - 1 { + tx_index += 1; + cumulative_gas_used += transactions[tx_index].gas_used; + } + rewards_in_block.push(U256::from(transactions[tx_index].reward)); + } + + Ok(rewards_in_block) +} + +/// A cached entry for a block's fee history. +#[derive(Debug, Clone)] +pub struct FeeHistoryEntry { + /// The base fee per gas for this block. + pub base_fee_per_gas: u64, + /// Gas used ratio this block. + pub gas_used_ratio: f64, + /// Gas used by this block. + pub gas_used: u64, + /// Gas limit by this block. + pub gas_limit: u64, + /// Hash of the block. + pub header_hash: B256, + /// Approximated rewards for the configured percentiles. + pub rewards: Vec, +} + +impl FeeHistoryEntry { + /// Creates a new entry from a sealed block. + /// + /// Note: This does not calculate the rewards for the block. + pub fn new(block: &SealedBlock) -> Self { + FeeHistoryEntry { + base_fee_per_gas: block.base_fee_per_gas.unwrap_or_default(), + gas_used_ratio: block.gas_used as f64 / block.gas_limit as f64, + gas_used: block.gas_used, + header_hash: block.hash, + gas_limit: block.gas_limit, + rewards: Vec::new(), + } + } +} diff --git a/crates/rpc/rpc/src/eth/api/fees.rs b/crates/rpc/rpc/src/eth/api/fees.rs index a16ca5de9316..481a8d01a389 100644 --- a/crates/rpc/rpc/src/eth/api/fees.rs +++ b/crates/rpc/rpc/src/eth/api/fees.rs @@ -1,17 +1,17 @@ //! Contains RPC handler implementations for fee history. use crate::{ - eth::error::{EthApiError, EthResult}, + eth::{ + api::fee_history::{calculate_reward_percentiles_for_block, FeeHistoryEntry}, + error::{EthApiError, EthResult}, + }, EthApi, }; use reth_network_api::NetworkInfo; -use reth_primitives::{ - basefee::calculate_next_block_base_fee, BlockNumberOrTag, SealedHeader, U256, -}; +use reth_primitives::{basefee::calculate_next_block_base_fee, BlockNumberOrTag, U256}; use reth_provider::{BlockReaderIdExt, ChainSpecProvider, EvmEnvProvider, StateProviderFactory}; -use reth_rpc_types::{FeeHistory, TxGasAndReward}; +use reth_rpc_types::FeeHistory; use reth_transaction_pool::TransactionPool; - use tracing::debug; impl EthApi @@ -46,8 +46,10 @@ where self.gas_oracle().suggest_tip_cap().await } - /// Reports the fee history, for the given amount of blocks, up until the newest block - /// provided. + /// Reports the fee history, for the given amount of blocks, up until the given newest block. + /// + /// If `reward_percentiles` are provided the [FeeHistory] will include the _approximated_ + /// rewards for the requested range. pub(crate) async fn fee_history( &self, mut block_count: u64, @@ -85,9 +87,9 @@ where block_count = end_block_plus; } - // If reward percentiles were specified, we need to validate that they are monotonically + // If reward percentiles were specified, we + // need to validate that they are monotonically // increasing and 0 <= p <= 100 - // // Note: The types used ensure that the percentiles are never < 0 if let Some(percentiles) = &reward_percentiles { if percentiles.windows(2).any(|w| w[0] > w[1] || w[0] > 100.) { @@ -101,38 +103,89 @@ where // otherwise `newest_block - 2 // SAFETY: We ensured that block count is capped let start_block = end_block_plus - block_count; - let headers = self.provider().sealed_headers_range(start_block..=end_block)?; - if headers.len() != block_count as usize { - return Err(EthApiError::InvalidBlockRange) - } // Collect base fees, gas usage ratios and (optionally) reward percentile data let mut base_fee_per_gas: Vec = Vec::new(); let mut gas_used_ratio: Vec = Vec::new(); let mut rewards: Vec> = Vec::new(); - for header in &headers { - base_fee_per_gas - .push(U256::try_from(header.base_fee_per_gas.unwrap_or_default()).unwrap()); - gas_used_ratio.push(header.gas_used as f64 / header.gas_limit as f64); - - // Percentiles were specified, so we need to collect reward percentile ino - if let Some(percentiles) = &reward_percentiles { - rewards.push(self.calculate_reward_percentiles(percentiles, header).await?); + + // Check if the requested range is within the cache bounds + let fee_entries = self.fee_history_cache().get_history(start_block, end_block).await; + + if let Some(fee_entries) = fee_entries { + if fee_entries.len() != block_count as usize { + return Err(EthApiError::InvalidBlockRange) } - } - // The spec states that `base_fee_per_gas` "[..] includes the next block after the newest of - // the returned range, because this value can be derived from the newest block" - // - // The unwrap is safe since we checked earlier that we got at least 1 header. - let last_header = headers.last().unwrap(); - let chain_spec = self.provider().chain_spec(); - base_fee_per_gas.push(U256::from(calculate_next_block_base_fee( - last_header.gas_used, - last_header.gas_limit, - last_header.base_fee_per_gas.unwrap_or_default(), - chain_spec.base_fee_params, - ))); + for entry in &fee_entries { + base_fee_per_gas.push(U256::from(entry.base_fee_per_gas)); + gas_used_ratio.push(entry.gas_used_ratio); + + if let Some(percentiles) = &reward_percentiles { + let mut block_rewards = Vec::with_capacity(percentiles.len()); + for &percentile in percentiles.iter() { + block_rewards.push(self.approximate_percentile(entry, percentile)); + } + rewards.push(block_rewards); + } + } + let last_entry = fee_entries.last().expect("is not empty"); + base_fee_per_gas.push(U256::from(calculate_next_block_base_fee( + last_entry.gas_used, + last_entry.gas_limit, + last_entry.base_fee_per_gas, + self.provider().chain_spec().base_fee_params, + ))); + } else { + // read the requested header range + let headers = self.provider().sealed_headers_range(start_block..=end_block)?; + if headers.len() != block_count as usize { + return Err(EthApiError::InvalidBlockRange) + } + + for header in &headers { + base_fee_per_gas.push(U256::from(header.base_fee_per_gas.unwrap_or_default())); + gas_used_ratio.push(header.gas_used as f64 / header.gas_limit as f64); + + // Percentiles were specified, so we need to collect reward percentile ino + if let Some(percentiles) = &reward_percentiles { + let (transactions, receipts) = self + .cache() + .get_transactions_and_receipts(header.hash) + .await? + .ok_or(EthApiError::InvalidBlockRange)?; + rewards.push( + calculate_reward_percentiles_for_block( + percentiles, + header.gas_used, + header.base_fee_per_gas.unwrap_or_default(), + &transactions, + &receipts, + ) + .unwrap_or_default(), + ); + } + } + + // The spec states that `base_fee_per_gas` "[..] includes the next block after the + // newest of the returned range, because this value can be derived from the + // newest block" + // + // The unwrap is safe since we checked earlier that we got at least 1 header. + + // The spec states that `base_fee_per_gas` "[..] includes the next block after the + // newest of the returned range, because this value can be derived from the + // newest block" + // + // The unwrap is safe since we checked earlier that we got at least 1 header. + let last_header = headers.last().expect("is present"); + base_fee_per_gas.push(U256::from(calculate_next_block_base_fee( + last_header.gas_used, + last_header.gas_limit, + last_header.base_fee_per_gas.unwrap_or_default(), + self.provider().chain_spec().base_fee_params, + ))); + }; Ok(FeeHistory { base_fee_per_gas, @@ -142,68 +195,17 @@ where }) } - /// Calculates reward percentiles for transactions in a block header. - /// Given a list of percentiles and a sealed block header, this function computes - /// the corresponding rewards for the transactions at each percentile. - /// - /// The results are returned as a vector of U256 values. - async fn calculate_reward_percentiles( - &self, - percentiles: &[f64], - header: &SealedHeader, - ) -> Result, EthApiError> { - let (transactions, receipts) = self - .cache() - .get_transactions_and_receipts(header.hash) - .await? - .ok_or(EthApiError::InvalidBlockRange)?; - - let mut transactions = transactions - .into_iter() - .zip(receipts) - .scan(0, |previous_gas, (tx, receipt)| { - // Convert the cumulative gas used in the receipts - // to the gas usage by the transaction - // - // While we will sum up the gas again later, it is worth - // noting that the order of the transactions will be different, - // so the sum will also be different for each receipt. - let gas_used = receipt.cumulative_gas_used - *previous_gas; - *previous_gas = receipt.cumulative_gas_used; - - Some(TxGasAndReward { - gas_used, - reward: tx.effective_tip_per_gas(header.base_fee_per_gas).unwrap_or_default(), - }) - }) - .collect::>(); - - // Sort the transactions by their rewards in ascending order - transactions.sort_by_key(|tx| tx.reward); - - // Find the transaction that corresponds to the given percentile - // - // We use a `tx_index` here that is shared across all percentiles, since we know - // the percentiles are monotonically increasing. - let mut tx_index = 0; - let mut cumulative_gas_used = - transactions.first().map(|tx| tx.gas_used).unwrap_or_default(); - let mut rewards_in_block = Vec::new(); - for percentile in percentiles { - // Empty blocks should return in a zero row - if transactions.is_empty() { - rewards_in_block.push(U256::ZERO); - continue - } - - let threshold = (header.gas_used as f64 * percentile / 100.) as u64; - while cumulative_gas_used < threshold && tx_index < transactions.len() - 1 { - tx_index += 1; - cumulative_gas_used += transactions[tx_index].gas_used; - } - rewards_in_block.push(U256::from(transactions[tx_index].reward)); - } - - Ok(rewards_in_block) + /// Approximates reward at a given percentile for a specific block + /// Based on the configured resolution + fn approximate_percentile(&self, entry: &FeeHistoryEntry, requested_percentile: f64) -> U256 { + let resolution = self.fee_history_cache().resolution(); + let rounded_percentile = + (requested_percentile * resolution as f64).round() / resolution as f64; + let clamped_percentile = rounded_percentile.clamp(0.0, 100.0); + + // Calculate the index in the precomputed rewards array + let index = (clamped_percentile / (1.0 / resolution as f64)).round() as usize; + // Fetch the reward from the FeeHistoryEntry + entry.rewards.get(index).cloned().unwrap_or(U256::ZERO) } } diff --git a/crates/rpc/rpc/src/eth/api/mod.rs b/crates/rpc/rpc/src/eth/api/mod.rs index 4ff22571dec9..96830b9f3009 100644 --- a/crates/rpc/rpc/src/eth/api/mod.rs +++ b/crates/rpc/rpc/src/eth/api/mod.rs @@ -1,15 +1,17 @@ -//! Provides everything related to `eth_` namespace -//! //! The entire implementation of the namespace is quite large, hence it is divided across several //! files. use crate::eth::{ - api::pending_block::{PendingBlock, PendingBlockEnv, PendingBlockEnvOrigin}, + api::{ + fee_history::FeeHistoryCache, + pending_block::{PendingBlock, PendingBlockEnv, PendingBlockEnvOrigin}, + }, cache::EthStateCache, error::{EthApiError, EthResult}, gas_oracle::GasPriceOracle, signer::EthSigner, }; + use async_trait::async_trait; use reth_interfaces::RethResult; use reth_network_api::NetworkInfo; @@ -17,6 +19,7 @@ use reth_primitives::{ revm_primitives::{BlockEnv, CfgEnv}, Address, BlockId, BlockNumberOrTag, ChainInfo, SealedBlockWithSenders, B256, U256, U64, }; + use reth_provider::{ BlockReaderIdExt, ChainSpecProvider, EvmEnvProvider, StateProviderBox, StateProviderFactory, }; @@ -24,14 +27,17 @@ use reth_rpc_types::{SyncInfo, SyncStatus}; use reth_tasks::{TaskSpawner, TokioTaskExecutor}; use reth_transaction_pool::TransactionPool; use std::{ + fmt::Debug, future::Future, sync::Arc, time::{Duration, Instant}, }; + use tokio::sync::{oneshot, Mutex}; mod block; mod call; +pub(crate) mod fee_history; mod fees; #[cfg(feature = "optimism")] mod optimism; @@ -86,6 +92,7 @@ where Provider: BlockReaderIdExt + ChainSpecProvider, { /// Creates a new, shareable instance using the default tokio task spawner. + #[allow(clippy::too_many_arguments)] pub fn new( provider: Provider, pool: Pool, @@ -94,6 +101,7 @@ where gas_oracle: GasPriceOracle, gas_cap: impl Into, blocking_task_pool: BlockingTaskPool, + fee_history_cache: FeeHistoryCache, ) -> Self { Self::with_spawner( provider, @@ -104,6 +112,7 @@ where gas_cap.into().into(), Box::::default(), blocking_task_pool, + fee_history_cache, ) } @@ -118,6 +127,7 @@ where gas_cap: u64, task_spawner: Box, blocking_task_pool: BlockingTaskPool, + fee_history_cache: FeeHistoryCache, ) -> Self { // get the block number of the latest block let latest_block = provider @@ -139,9 +149,11 @@ where task_spawner, pending_block: Default::default(), blocking_task_pool, + fee_history_cache, #[cfg(feature = "optimism")] http_client: reqwest::Client::new(), }; + Self { inner: Arc::new(inner) } } @@ -194,6 +206,11 @@ where pub fn pool(&self) -> &Pool { &self.inner.pool } + + /// Returns fee history cache + pub fn fee_history_cache(&self) -> &FeeHistoryCache { + &self.inner.fee_history_cache + } } // === State access helpers === @@ -448,6 +465,8 @@ struct EthApiInner { pending_block: Mutex>, /// A pool dedicated to blocking tasks. blocking_task_pool: BlockingTaskPool, + /// Cache for block fees history + fee_history_cache: FeeHistoryCache, /// An http client for communicating with sequencers. #[cfg(feature = "optimism")] http_client: reqwest::Client, diff --git a/crates/rpc/rpc/src/eth/api/server.rs b/crates/rpc/rpc/src/eth/api/server.rs index 08bfa54db031..c63de87e710b 100644 --- a/crates/rpc/rpc/src/eth/api/server.rs +++ b/crates/rpc/rpc/src/eth/api/server.rs @@ -391,7 +391,10 @@ where #[cfg(test)] mod tests { use crate::{ - eth::{cache::EthStateCache, gas_oracle::GasPriceOracle}, + eth::{ + cache::EthStateCache, gas_oracle::GasPriceOracle, FeeHistoryCache, + FeeHistoryCacheConfig, + }, BlockingTaskPool, EthApi, }; use jsonrpsee::types::error::INVALID_PARAMS_CODE; @@ -422,14 +425,19 @@ mod tests { provider: P, ) -> EthApi { let cache = EthStateCache::spawn(provider.clone(), Default::default()); + + let fee_history_cache = + FeeHistoryCache::new(cache.clone(), FeeHistoryCacheConfig::default()); + EthApi::new( provider.clone(), testing_pool(), NoopNetwork::default(), cache.clone(), - GasPriceOracle::new(provider, Default::default(), cache), + GasPriceOracle::new(provider.clone(), Default::default(), cache.clone()), ETHEREUM_BLOCK_GAS_LIMIT, BlockingTaskPool::build().expect("failed to build tracing pool"), + fee_history_cache, ) } @@ -446,6 +454,7 @@ mod tests { let mut gas_used_ratios = Vec::new(); let mut base_fees_per_gas = Vec::new(); let mut last_header = None; + let mut parent_hash = B256::default(); for i in (0..block_count).rev() { let hash = rng.gen(); @@ -459,9 +468,11 @@ mod tests { gas_limit, gas_used, base_fee_per_gas, + parent_hash, ..Default::default() }; last_header = Some(header.clone()); + parent_hash = hash; let mut transactions = vec![]; for _ in 0..100 { diff --git a/crates/rpc/rpc/src/eth/api/state.rs b/crates/rpc/rpc/src/eth/api/state.rs index 4cf4a69a6aa8..a63e08126d52 100644 --- a/crates/rpc/rpc/src/eth/api/state.rs +++ b/crates/rpc/rpc/src/eth/api/state.rs @@ -125,7 +125,10 @@ where mod tests { use super::*; use crate::{ - eth::{cache::EthStateCache, gas_oracle::GasPriceOracle}, + eth::{ + cache::EthStateCache, gas_oracle::GasPriceOracle, FeeHistoryCache, + FeeHistoryCacheConfig, + }, BlockingTaskPool, }; use reth_primitives::{constants::ETHEREUM_BLOCK_GAS_LIMIT, StorageKey, StorageValue}; @@ -144,9 +147,10 @@ mod tests { pool.clone(), (), cache.clone(), - GasPriceOracle::new(NoopProvider::default(), Default::default(), cache), + GasPriceOracle::new(NoopProvider::default(), Default::default(), cache.clone()), ETHEREUM_BLOCK_GAS_LIMIT, BlockingTaskPool::build().expect("failed to build tracing pool"), + FeeHistoryCache::new(cache.clone(), FeeHistoryCacheConfig::default()), ); let address = Address::random(); let storage = eth_api.storage_at(address, U256::ZERO.into(), None).unwrap(); @@ -166,9 +170,10 @@ mod tests { pool, (), cache.clone(), - GasPriceOracle::new(mock_provider, Default::default(), cache), + GasPriceOracle::new(mock_provider.clone(), Default::default(), cache.clone()), ETHEREUM_BLOCK_GAS_LIMIT, BlockingTaskPool::build().expect("failed to build tracing pool"), + FeeHistoryCache::new(cache.clone(), FeeHistoryCacheConfig::default()), ); let storage_key: U256 = storage_key.into(); diff --git a/crates/rpc/rpc/src/eth/api/transactions.rs b/crates/rpc/rpc/src/eth/api/transactions.rs index 58af7c7697a9..0da8886cc954 100644 --- a/crates/rpc/rpc/src/eth/api/transactions.rs +++ b/crates/rpc/rpc/src/eth/api/transactions.rs @@ -1254,7 +1254,10 @@ pub(crate) fn build_transaction_receipt_with_block_receipts( mod tests { use super::*; use crate::{ - eth::{cache::EthStateCache, gas_oracle::GasPriceOracle}, + eth::{ + cache::EthStateCache, gas_oracle::GasPriceOracle, FeeHistoryCache, + FeeHistoryCacheConfig, + }, BlockingTaskPool, EthApi, }; use reth_network_api::noop::NoopNetwork; @@ -1270,14 +1273,17 @@ mod tests { let pool = testing_pool(); let cache = EthStateCache::spawn(noop_provider, Default::default()); + let fee_history_cache = + FeeHistoryCache::new(cache.clone(), FeeHistoryCacheConfig::default()); let eth_api = EthApi::new( noop_provider, pool.clone(), noop_network_provider, cache.clone(), - GasPriceOracle::new(noop_provider, Default::default(), cache), + GasPriceOracle::new(noop_provider, Default::default(), cache.clone()), ETHEREUM_BLOCK_GAS_LIMIT, BlockingTaskPool::build().expect("failed to build tracing pool"), + fee_history_cache, ); // https://etherscan.io/tx/0xa694b71e6c128a2ed8e2e0f6770bddbe52e3bb8f10e8472f9a79ab81497a8b5d diff --git a/crates/rpc/rpc/src/eth/gas_oracle.rs b/crates/rpc/rpc/src/eth/gas_oracle.rs index f3afc3ff7be3..4eaaa5cdcfc5 100644 --- a/crates/rpc/rpc/src/eth/gas_oracle.rs +++ b/crates/rpc/rpc/src/eth/gas_oracle.rs @@ -16,6 +16,9 @@ use tracing::warn; /// The number of transactions sampled in a block pub const SAMPLE_NUMBER: usize = 3_usize; +/// The default maximum number of blocks to use for the gas price oracle. +pub const MAX_HEADER_HISTORY: u64 = 1024; + /// The default maximum gas price to use for the estimate pub const DEFAULT_MAX_PRICE: U256 = U256::from_limbs([500_000_000_000u64, 0, 0, 0]); @@ -53,8 +56,8 @@ impl Default for GasPriceOracleConfig { GasPriceOracleConfig { blocks: 20, percentile: 60, - max_header_history: 1024, - max_block_history: 1024, + max_header_history: MAX_HEADER_HISTORY, + max_block_history: MAX_HEADER_HISTORY, default: None, max_price: Some(DEFAULT_MAX_PRICE), ignore_price: Some(DEFAULT_IGNORE_PRICE), @@ -73,8 +76,8 @@ impl GasPriceOracleConfig { Self { blocks: blocks.unwrap_or(20), percentile: percentile.unwrap_or(60), - max_header_history: 1024, - max_block_history: 1024, + max_header_history: MAX_HEADER_HISTORY, + max_block_history: MAX_HEADER_HISTORY, default: None, max_price: max_price.map(U256::from).or(Some(DEFAULT_MAX_PRICE)), ignore_price: ignore_price.map(U256::from).or(Some(DEFAULT_IGNORE_PRICE)), diff --git a/crates/rpc/rpc/src/eth/mod.rs b/crates/rpc/rpc/src/eth/mod.rs index 90ee0e1042e8..97f57af68855 100644 --- a/crates/rpc/rpc/src/eth/mod.rs +++ b/crates/rpc/rpc/src/eth/mod.rs @@ -13,7 +13,11 @@ pub mod revm_utils; mod signer; pub(crate) mod utils; -pub use api::{EthApi, EthApiSpec, EthTransactions, TransactionSource, RPC_DEFAULT_GAS_CAP}; +pub use api::{ + fee_history::{fee_history_cache_new_blocks_task, FeeHistoryCache, FeeHistoryCacheConfig}, + EthApi, EthApiSpec, EthTransactions, TransactionSource, RPC_DEFAULT_GAS_CAP, +}; + pub use bundle::EthBundle; pub use filter::{EthFilter, EthFilterConfig}; pub use id_provider::EthSubscriptionIdProvider; diff --git a/crates/storage/provider/src/providers/database/provider.rs b/crates/storage/provider/src/providers/database/provider.rs index 244011c7b619..585f73e16be7 100644 --- a/crates/storage/provider/src/providers/database/provider.rs +++ b/crates/storage/provider/src/providers/database/provider.rs @@ -1114,7 +1114,6 @@ impl BlockReader for DatabaseProvider { transaction_kind: TransactionVariant, ) -> ProviderResult> { let Some(block_number) = self.convert_hash_or_number(id)? else { return Ok(None) }; - let Some(header) = self.header_by_number(block_number)? else { return Ok(None) }; let ommers = self.ommers(block_number.into())?.unwrap_or_default(); diff --git a/crates/storage/provider/src/test_utils/mock.rs b/crates/storage/provider/src/test_utils/mock.rs index e7f8726664a1..ddc8718aa434 100644 --- a/crates/storage/provider/src/test_utils/mock.rs +++ b/crates/storage/provider/src/test_utils/mock.rs @@ -462,8 +462,14 @@ impl BlockReader for MockEthProvider { Ok(None) } - fn block_range(&self, _range: RangeInclusive) -> ProviderResult> { - Ok(vec![]) + fn block_range(&self, range: RangeInclusive) -> ProviderResult> { + let lock = self.blocks.lock(); + + let mut blocks: Vec<_> = + lock.values().filter(|block| range.contains(&block.number)).cloned().collect(); + blocks.sort_by_key(|block| block.number); + + Ok(blocks) } } diff --git a/crates/transaction-pool/src/pool/blob.rs b/crates/transaction-pool/src/pool/blob.rs index 24c1fa6cade4..e7f6fa8f2448 100644 --- a/crates/transaction-pool/src/pool/blob.rs +++ b/crates/transaction-pool/src/pool/blob.rs @@ -290,7 +290,7 @@ const LOG_2_1_125: f64 = 0.16992500144231237; pub fn fee_delta(max_tx_fee: u128, current_fee: u128) -> i64 { if max_tx_fee == current_fee { // if these are equal, then there's no fee jump - return 0; + return 0 } let max_tx_fee_jumps = if max_tx_fee == 0 { From f1e38cdb0852d0b5e94514be68d27e9019e5d072 Mon Sep 17 00:00:00 2001 From: Roman Krasiuk Date: Mon, 27 Nov 2023 03:19:10 -0800 Subject: [PATCH 022/277] fix: always recover senders on mismatch (#5575) --- crates/primitives/src/block.rs | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/crates/primitives/src/block.rs b/crates/primitives/src/block.rs index 9e703faaab0d..61f2468107ff 100644 --- a/crates/primitives/src/block.rs +++ b/crates/primitives/src/block.rs @@ -57,10 +57,16 @@ impl Block { /// /// # Panics /// - /// If the number of senders does not match the number of transactions in the block. + /// If the number of senders does not match the number of transactions in the block + /// and the signer recovery for one of the transactions fails. #[track_caller] pub fn with_senders(self, senders: Vec
) -> BlockWithSenders { - assert_eq!(self.body.len(), senders.len(), "Unequal number of senders"); + let senders = if self.body.len() == senders.len() { + senders + } else { + TransactionSigned::recover_signers(&self.body, self.body.len()) + .expect("stored block is valid") + }; BlockWithSenders { block: self, senders } } From 8892d04a88365ba507f28c3314d99a6b54735d3f Mon Sep 17 00:00:00 2001 From: DoTheBestToGetTheBest <146037313+DoTheBestToGetTheBest@users.noreply.github.com> Date: Mon, 27 Nov 2023 03:44:27 -0800 Subject: [PATCH 023/277] feat(storage) implement cursor walking functions (#5554) --- crates/storage/db/src/abstraction/mock.rs | 31 +++++++++++++++++++---- 1 file changed, 26 insertions(+), 5 deletions(-) diff --git a/crates/storage/db/src/abstraction/mock.rs b/crates/storage/db/src/abstraction/mock.rs index d5427b49fbe7..d25286c49c71 100644 --- a/crates/storage/db/src/abstraction/mock.rs +++ b/crates/storage/db/src/abstraction/mock.rs @@ -1,6 +1,6 @@ //! Mock database use crate::{ - common::{PairResult, ValueOnlyResult}, + common::{IterPairResult, PairResult, ValueOnlyResult}, cursor::{ DbCursorRO, DbCursorRW, DbDupCursorRO, DbDupCursorRW, DupWalker, RangeWalker, ReverseWalker, Walker, @@ -10,6 +10,7 @@ use crate::{ transaction::{DbTx, DbTxMut}, DatabaseError, }; +use core::ops::Bound; use std::{collections::BTreeMap, ops::RangeBounds}; /// Mock database used for testing with inner BTreeMap structure @@ -132,15 +133,35 @@ impl DbCursorRO for CursorMock { Ok(None) } - fn walk(&mut self, _start_key: Option) -> Result, DatabaseError> { - todo!() + fn walk(&mut self, start_key: Option) -> Result, DatabaseError> { + let start: IterPairResult = match start_key { + Some(key) => >::seek(self, key).transpose(), + None => >::first(self).transpose(), + }; + + Ok(Walker::new(self, start)) } fn walk_range( &mut self, - _range: impl RangeBounds, + range: impl RangeBounds, ) -> Result, DatabaseError> { - todo!() + let start_key = match range.start_bound() { + Bound::Included(key) | Bound::Excluded(key) => Some((*key).clone()), + Bound::Unbounded => None, + }; + + let end_key = match range.end_bound() { + Bound::Included(key) | Bound::Excluded(key) => Bound::Included((*key).clone()), + Bound::Unbounded => Bound::Unbounded, + }; + + let start: IterPairResult = match start_key { + Some(key) => >::seek(self, key).transpose(), + None => >::first(self).transpose(), + }; + + Ok(RangeWalker::new(self, start, end_key)) } fn walk_back( From 02e4135bb41c5fc58a600714ecb04934ec88e707 Mon Sep 17 00:00:00 2001 From: Matthias Seitz Date: Mon, 27 Nov 2023 13:12:12 +0100 Subject: [PATCH 024/277] chore: move fee history cache fields to inner type (#5587) --- crates/rpc/rpc/src/eth/api/fee_history.rs | 65 +++++++++++++---------- 1 file changed, 36 insertions(+), 29 deletions(-) diff --git a/crates/rpc/rpc/src/eth/api/fee_history.rs b/crates/rpc/rpc/src/eth/api/fee_history.rs index f25ecc6e00e4..b78c7f04ec09 100644 --- a/crates/rpc/rpc/src/eth/api/fee_history.rs +++ b/crates/rpc/rpc/src/eth/api/fee_history.rs @@ -37,40 +37,32 @@ impl Default for FeeHistoryCacheConfig { /// Wrapper struct for BTreeMap #[derive(Debug, Clone)] pub struct FeeHistoryCache { - /// Stores the lower bound of the cache - lower_bound: Arc, - upper_bound: Arc, - /// Config for FeeHistoryCache, consists of resolution for percentile approximation - /// and max number of blocks - config: FeeHistoryCacheConfig, - /// Stores the entries of the cache - entries: Arc>>, - #[allow(unused)] - eth_cache: EthStateCache, + inner: Arc, } impl FeeHistoryCache { /// Creates new FeeHistoryCache instance, initialize it with the mose recent data, set bounds pub fn new(eth_cache: EthStateCache, config: FeeHistoryCacheConfig) -> Self { - let init_tree_map = BTreeMap::new(); - - let entries = Arc::new(tokio::sync::RwLock::new(init_tree_map)); - - let upper_bound = Arc::new(AtomicU64::new(0)); - let lower_bound = Arc::new(AtomicU64::new(0)); - - FeeHistoryCache { config, entries, upper_bound, lower_bound, eth_cache } + let inner = FeeHistoryCacheInner { + lower_bound: Default::default(), + upper_bound: Default::default(), + config, + entries: Default::default(), + eth_cache, + }; + Self { inner: Arc::new(inner) } } /// How the cache is configured. + #[inline] pub fn config(&self) -> &FeeHistoryCacheConfig { - &self.config + &self.inner.config } /// Returns the configured resolution for percentile approximation. #[inline] pub fn resolution(&self) -> u64 { - self.config.resolution + self.config().resolution } /// Processing of the arriving blocks @@ -78,7 +70,7 @@ impl FeeHistoryCache { where I: Iterator)>, { - let mut entries = self.entries.write().await; + let mut entries = self.inner.entries.write().await; let percentiles = self.predefined_percentiles(); // Insert all new blocks and calculate approximated rewards @@ -96,30 +88,30 @@ impl FeeHistoryCache { } // enforce bounds by popping the oldest entries - while entries.len() > self.config.max_blocks as usize { + while entries.len() > self.inner.config.max_blocks as usize { entries.pop_first(); } if entries.len() == 0 { - self.upper_bound.store(0, SeqCst); - self.lower_bound.store(0, SeqCst); + self.inner.upper_bound.store(0, SeqCst); + self.inner.lower_bound.store(0, SeqCst); return } let upper_bound = *entries.last_entry().expect("Contains at least one entry").key(); let lower_bound = *entries.first_entry().expect("Contains at least one entry").key(); - self.upper_bound.store(upper_bound, SeqCst); - self.lower_bound.store(lower_bound, SeqCst); + self.inner.upper_bound.store(upper_bound, SeqCst); + self.inner.lower_bound.store(lower_bound, SeqCst); } /// Get UpperBound value for FeeHistoryCache pub fn upper_bound(&self) -> u64 { - self.upper_bound.load(SeqCst) + self.inner.upper_bound.load(SeqCst) } /// Get LowerBound value for FeeHistoryCache pub fn lower_bound(&self) -> u64 { - self.lower_bound.load(SeqCst) + self.inner.lower_bound.load(SeqCst) } /// Collect fee history for given range. @@ -136,7 +128,7 @@ impl FeeHistoryCache { let lower_bound = self.lower_bound(); let upper_bound = self.upper_bound(); if start_block >= lower_bound && end_block <= upper_bound { - let entries = self.entries.read().await; + let entries = self.inner.entries.read().await; let result = entries .range(start_block..=end_block + 1) .map(|(_, fee_entry)| fee_entry.clone()) @@ -161,6 +153,21 @@ impl FeeHistoryCache { } } +/// Container type for shared state in [FeeHistoryCache] +#[derive(Debug)] +struct FeeHistoryCacheInner { + /// Stores the lower bound of the cache + lower_bound: AtomicU64, + upper_bound: AtomicU64, + /// Config for FeeHistoryCache, consists of resolution for percentile approximation + /// and max number of blocks + config: FeeHistoryCacheConfig, + /// Stores the entries of the cache + entries: tokio::sync::RwLock>, + #[allow(unused)] + eth_cache: EthStateCache, +} + /// Awaits for new chain events and directly inserts them into the cache so they're available /// immediately before they need to be fetched from disk. pub async fn fee_history_cache_new_blocks_task( From 846a377f8e7e659ea05a0edde73faced495f407c Mon Sep 17 00:00:00 2001 From: Matthias Seitz Date: Mon, 27 Nov 2023 13:32:00 +0100 Subject: [PATCH 025/277] chore: bump ef-tests stack size (#5585) --- testing/ef-tests/src/cases/blockchain_test.rs | 9 +-------- testing/ef-tests/tests/tests.rs | 13 ++++++++++++- 2 files changed, 13 insertions(+), 9 deletions(-) diff --git a/testing/ef-tests/src/cases/blockchain_test.rs b/testing/ef-tests/src/cases/blockchain_test.rs index 3706f13ffd35..a162efdd577e 100644 --- a/testing/ef-tests/src/cases/blockchain_test.rs +++ b/testing/ef-tests/src/cases/blockchain_test.rs @@ -104,15 +104,8 @@ impl Case for BlockchainTestCase { let mut stage = ExecutionStage::new_with_factory( reth_revm::EvmProcessorFactory::new(Arc::new(case.network.clone().into())), ); - let target = last_block.as_ref().map(|b| b.number); - tokio::runtime::Builder::new_current_thread() - .build() - .expect("Could not build tokio RT") - .block_on(async { - // ignore error - let _ = stage.execute(&provider, ExecInput { target, checkpoint: None }); - }); + let _ = stage.execute(&provider, ExecInput { target, checkpoint: None }); } // Validate post state diff --git a/testing/ef-tests/tests/tests.rs b/testing/ef-tests/tests/tests.rs index afbaa2e796e7..75e6e2a8cd27 100644 --- a/testing/ef-tests/tests/tests.rs +++ b/testing/ef-tests/tests/tests.rs @@ -6,7 +6,18 @@ macro_rules! general_state_test { ($test_name:ident, $dir:ident) => { #[test] fn $test_name() { - BlockchainTests::new(format!("GeneralStateTests/{}", stringify!($dir))).run(); + // TODO: can be removed with revm call-loop support + // + std::thread::Builder::new() + .stack_size( + 1024 * 1024 * 4, // 4MB + ) + .spawn(move || { + BlockchainTests::new(format!("GeneralStateTests/{}", stringify!($dir))).run(); + }) + .unwrap() + .join() + .unwrap(); } }; } From c49cda62b0cdd5ca3e6163f794873c3245b039e3 Mon Sep 17 00:00:00 2001 From: Bjerg Date: Mon, 27 Nov 2023 15:35:40 +0100 Subject: [PATCH 026/277] chore: make `traces` public (#5567) Co-authored-by: Enrique Ortiz --- crates/revm/revm-inspectors/src/tracing/mod.rs | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/crates/revm/revm-inspectors/src/tracing/mod.rs b/crates/revm/revm-inspectors/src/tracing/mod.rs index 5bba7eddde0f..5fbc9bb2ccb2 100644 --- a/crates/revm/revm-inspectors/src/tracing/mod.rs +++ b/crates/revm/revm-inspectors/src/tracing/mod.rs @@ -83,6 +83,16 @@ impl TracingInspector { } } + /// Gets a reference to the recorded call traces. + pub fn get_traces(&self) -> &CallTraceArena { + &self.traces + } + + /// Gets a mutable reference to the recorded call traces. + pub fn get_traces_mut(&mut self) -> &mut CallTraceArena { + &mut self.traces + } + /// Manually the gas used of the root trace. /// /// This is useful if the root trace's gasUsed should mirror the actual gas used by the From 462ea82f911af5666fb6b6ebe23b9b9083597436 Mon Sep 17 00:00:00 2001 From: clabby Date: Mon, 27 Nov 2023 10:53:25 -0500 Subject: [PATCH 027/277] feat(op-reth): Canyon hardfork spec (#5503) --- bin/reth/Cargo.toml | 1 + crates/net/eth-wire/src/multiplex.rs | 2 +- crates/primitives/src/chain/spec.rs | 64 +++++++++++++++++++++++++--- crates/primitives/src/hardfork.rs | 17 +++++--- crates/primitives/src/revm/config.rs | 12 +++++- 5 files changed, 82 insertions(+), 14 deletions(-) diff --git a/bin/reth/Cargo.toml b/bin/reth/Cargo.toml index 273466aa145c..aad259ed067e 100644 --- a/bin/reth/Cargo.toml +++ b/bin/reth/Cargo.toml @@ -127,6 +127,7 @@ optimism = [ "reth-transaction-pool/optimism", "reth-provider/optimism", "reth-beacon-consensus/optimism", + "reth-auto-seal-consensus/optimism", "reth-basic-payload-builder/optimism", "reth-network/optimism", "reth-network-api/optimism", diff --git a/crates/net/eth-wire/src/multiplex.rs b/crates/net/eth-wire/src/multiplex.rs index e3bb92ecad3a..f76be4fbe826 100644 --- a/crates/net/eth-wire/src/multiplex.rs +++ b/crates/net/eth-wire/src/multiplex.rs @@ -278,7 +278,7 @@ where return Poll::Ready(Some(Err(err.into()))) } } else { - break; + break } } Poll::Pending => { diff --git a/crates/primitives/src/chain/spec.rs b/crates/primitives/src/chain/spec.rs index 4f5e4f6374c4..1e3917fef5f7 100644 --- a/crates/primitives/src/chain/spec.rs +++ b/crates/primitives/src/chain/spec.rs @@ -274,6 +274,8 @@ pub static OP_GOERLI: Lazy> = Lazy::new(|| { ), (Hardfork::Bedrock, ForkCondition::Block(4061224)), (Hardfork::Regolith, ForkCondition::Timestamp(1679079600)), + (Hardfork::Shanghai, ForkCondition::Timestamp(1699981200)), + (Hardfork::Canyon, ForkCondition::Timestamp(1699981200)), ]), base_fee_params: BaseFeeParams::optimism(), prune_delete_limit: 1700, @@ -315,6 +317,8 @@ pub static BASE_GOERLI: Lazy> = Lazy::new(|| { ), (Hardfork::Bedrock, ForkCondition::Block(0)), (Hardfork::Regolith, ForkCondition::Timestamp(1683219600)), + (Hardfork::Shanghai, ForkCondition::Timestamp(1699981200)), + (Hardfork::Canyon, ForkCondition::Timestamp(1699981200)), ]), base_fee_params: BaseFeeParams::optimism_goerli(), prune_delete_limit: 1700, @@ -1032,9 +1036,7 @@ impl ChainSpecBuilder { self } - /// Enable Regolith at the timestamp of activation. - /// For post-bedrock op-stack chains, this will be at genesis. - /// For pre-bedrock op-stack chains, this will be at the timestamp of regolith activation. + /// Enable Regolith at genesis #[cfg(feature = "optimism")] pub fn regolith_activated(mut self) -> Self { self = self.bedrock_activated(); @@ -1042,6 +1044,16 @@ impl ChainSpecBuilder { self } + /// Enable Canyon at genesis + #[cfg(feature = "optimism")] + pub fn canyon_activated(mut self) -> Self { + self = self.regolith_activated(); + // Canyon also activates changes from L1's Shanghai hardfork + self.hardforks.insert(Hardfork::Shanghai, ForkCondition::Timestamp(0)); + self.hardforks.insert(Hardfork::Canyon, ForkCondition::Timestamp(0)); + self + } + /// Build the resulting [`ChainSpec`]. /// /// # Panics @@ -1984,13 +1996,55 @@ Post-merge hard forks (timestamp based): Head { number: 0, ..Default::default() }, ForkId { hash: ForkHash([0x6d, 0x63, 0x76, 0xbe]), next: 4061224 }, ), + ( + Head { number: 4061223, ..Default::default() }, + ForkId { hash: ForkHash([0x6d, 0x63, 0x76, 0xbe]), next: 4061224 }, + ), ( Head { number: 4061224, timestamp: 1679079599, ..Default::default() }, ForkId { hash: ForkHash([0x03, 0x47, 0x85, 0x69]), next: 1679079600 }, ), ( - Head { number: 4061224, timestamp: 1679079600, ..Default::default() }, - ForkId { hash: ForkHash([0x6d, 0x43, 0x1d, 0x6c]), next: 0 }, + Head { number: 4061225, timestamp: 1679079600, ..Default::default() }, + ForkId { hash: ForkHash([0x6d, 0x43, 0x1d, 0x6c]), next: 1699981200 }, + ), + ( + Head { number: 4061226, timestamp: 1699981199, ..Default::default() }, + ForkId { hash: ForkHash([0x6d, 0x43, 0x1d, 0x6c]), next: 1699981200 }, + ), + ( + Head { number: 4061227, timestamp: 1699981200, ..Default::default() }, + ForkId { hash: ForkHash([0x7f, 0x4a, 0x72, 0x1f]), next: 0 }, + ), + ], + ); + } + + #[cfg(feature = "optimism")] + #[test] + fn base_goerli_forkids() { + test_fork_ids( + &BASE_GOERLI, + &[ + ( + Head { number: 0, ..Default::default() }, + ForkId { hash: ForkHash([0xd4, 0x0c, 0x23, 0x50]), next: 1683219600 }, + ), + ( + Head { number: 1, timestamp: 1683219599, ..Default::default() }, + ForkId { hash: ForkHash([0xd4, 0x0c, 0x23, 0x50]), next: 1683219600 }, + ), + ( + Head { number: 2, timestamp: 1683219600, ..Default::default() }, + ForkId { hash: ForkHash([0xd5, 0x45, 0x43, 0x5d]), next: 1699981200 }, + ), + ( + Head { number: 3, timestamp: 1699981199, ..Default::default() }, + ForkId { hash: ForkHash([0xd5, 0x45, 0x43, 0x5d]), next: 1699981200 }, + ), + ( + Head { number: 4, timestamp: 1699981200, ..Default::default() }, + ForkId { hash: ForkHash([0xb3, 0x29, 0x13, 0xde]), next: 0 }, ), ], ); diff --git a/crates/primitives/src/hardfork.rs b/crates/primitives/src/hardfork.rs index e1e29fc3673c..793499610187 100644 --- a/crates/primitives/src/hardfork.rs +++ b/crates/primitives/src/hardfork.rs @@ -37,16 +37,19 @@ pub enum Hardfork { GrayGlacier, /// Paris. Paris, - /// Shanghai. - Shanghai, - /// Cancun. - Cancun, /// Bedrock. #[cfg(feature = "optimism")] Bedrock, /// Regolith #[cfg(feature = "optimism")] Regolith, + /// Shanghai. + Shanghai, + /// Canyon + #[cfg(feature = "optimism")] + Canyon, + /// Cancun. + Cancun, } impl Hardfork { @@ -95,6 +98,8 @@ impl FromStr for Hardfork { "bedrock" => Hardfork::Bedrock, #[cfg(feature = "optimism")] "regolith" => Hardfork::Regolith, + #[cfg(feature = "optimism")] + "canyon" => Hardfork::Canyon, _ => return Err(format!("Unknown hardfork: {s}")), }; Ok(hardfork) @@ -163,8 +168,8 @@ mod tests { #[test] #[cfg(feature = "optimism")] fn check_op_hardfork_from_str() { - let hardfork_str = ["beDrOck", "rEgOlITH"]; - let expected_hardforks = [Hardfork::Bedrock, Hardfork::Regolith]; + let hardfork_str = ["beDrOck", "rEgOlITH", "cAnYoN"]; + let expected_hardforks = [Hardfork::Bedrock, Hardfork::Regolith, Hardfork::Canyon]; let hardforks: Vec = hardfork_str.iter().map(|h| Hardfork::from_str(h).unwrap()).collect(); diff --git a/crates/primitives/src/revm/config.rs b/crates/primitives/src/revm/config.rs index 33848a844298..8e9619be40b7 100644 --- a/crates/primitives/src/revm/config.rs +++ b/crates/primitives/src/revm/config.rs @@ -10,7 +10,9 @@ pub fn revm_spec_by_timestamp_after_merge( ) -> revm_primitives::SpecId { #[cfg(feature = "optimism")] if chain_spec.is_optimism() { - if chain_spec.fork(Hardfork::Regolith).active_at_timestamp(timestamp) { + if chain_spec.fork(Hardfork::Canyon).active_at_timestamp(timestamp) { + return revm_primitives::CANYON + } else if chain_spec.fork(Hardfork::Regolith).active_at_timestamp(timestamp) { return revm_primitives::REGOLITH } else { return revm_primitives::BEDROCK @@ -30,7 +32,9 @@ pub fn revm_spec_by_timestamp_after_merge( pub fn revm_spec(chain_spec: &ChainSpec, block: Head) -> revm_primitives::SpecId { #[cfg(feature = "optimism")] if chain_spec.is_optimism() { - if chain_spec.fork(Hardfork::Regolith).active_at_head(&block) { + if chain_spec.fork(Hardfork::Canyon).active_at_head(&block) { + return revm_primitives::CANYON + } else if chain_spec.fork(Hardfork::Regolith).active_at_head(&block) { return revm_primitives::REGOLITH } else if chain_spec.fork(Hardfork::Bedrock).active_at_head(&block) { return revm_primitives::BEDROCK @@ -138,6 +142,10 @@ mod tests { f(cs).build() } + assert_eq!( + revm_spec(&op_cs(|cs| cs.canyon_activated()), Head::default()), + revm_primitives::CANYON + ); assert_eq!( revm_spec(&op_cs(|cs| cs.bedrock_activated()), Head::default()), revm_primitives::BEDROCK From 7debf93f36ac1299537b85d7e4d3b5e62c7c1111 Mon Sep 17 00:00:00 2001 From: clabby Date: Mon, 27 Nov 2023 11:05:41 -0500 Subject: [PATCH 028/277] feat(op-reth): Canyon receipts root RLP fix (#5504) --- crates/consensus/auto-seal/src/lib.rs | 20 +- crates/payload/basic/src/lib.rs | 10 +- crates/payload/basic/src/optimism.rs | 4 +- crates/primitives/src/proofs.rs | 514 ++++++++++-------- crates/primitives/src/receipt.rs | 16 + crates/revm/src/optimism/processor.rs | 10 +- crates/revm/src/processor.rs | 10 +- crates/rpc/rpc/src/eth/api/pending_block.rs | 10 +- .../bundle_state_with_receipts.rs | 14 + 9 files changed, 372 insertions(+), 236 deletions(-) diff --git a/crates/consensus/auto-seal/src/lib.rs b/crates/consensus/auto-seal/src/lib.rs index 2065f2e188f9..e2dfc9c91111 100644 --- a/crates/consensus/auto-seal/src/lib.rs +++ b/crates/consensus/auto-seal/src/lib.rs @@ -333,6 +333,7 @@ impl StorageInner { bundle_state: &BundleStateWithReceipts, client: &S, gas_used: u64, + #[cfg(feature = "optimism")] chain_spec: &ChainSpec, ) -> Result { let receipts = bundle_state.receipts_by_block(header.number); header.receipts_root = if receipts.is_empty() { @@ -344,7 +345,13 @@ impl StorageInner { .collect::>(); header.logs_bloom = receipts_with_bloom.iter().fold(Bloom::ZERO, |bloom, r| bloom | r.bloom); - proofs::calculate_receipt_root(&receipts_with_bloom) + proofs::calculate_receipt_root( + &receipts_with_bloom, + #[cfg(feature = "optimism")] + chain_spec, + #[cfg(feature = "optimism")] + header.timestamp, + ) }; header.gas_used = gas_used; @@ -382,7 +389,7 @@ impl StorageInner { .with_database_boxed(Box::new(StateProviderDatabase::new(client.latest().unwrap()))) .with_bundle_update() .build(); - let mut executor = EVMProcessor::new_with_state(chain_spec, db); + let mut executor = EVMProcessor::new_with_state(chain_spec.clone(), db); let (bundle_state, gas_used) = self.execute(&block, &mut executor, senders)?; @@ -392,7 +399,14 @@ impl StorageInner { trace!(target: "consensus::auto", ?bundle_state, ?header, ?body, "executed block, calculating state root and completing header"); // fill in the rest of the fields - let header = self.complete_header(header, &bundle_state, client, gas_used)?; + let header = self.complete_header( + header, + &bundle_state, + client, + gas_used, + #[cfg(feature = "optimism")] + chain_spec.as_ref(), + )?; trace!(target: "consensus::auto", root=?header.state_root, ?body, "calculated root"); diff --git a/crates/payload/basic/src/lib.rs b/crates/payload/basic/src/lib.rs index 0bc30049f5ab..3f806d5b50dd 100644 --- a/crates/payload/basic/src/lib.rs +++ b/crates/payload/basic/src/lib.rs @@ -918,7 +918,15 @@ where Receipts::from_vec(vec![receipts]), block_number, ); - let receipts_root = bundle.receipts_root_slow(block_number).expect("Number is in range"); + let receipts_root = bundle + .receipts_root_slow( + block_number, + #[cfg(feature = "optimism")] + chain_spec.as_ref(), + #[cfg(feature = "optimism")] + attributes.timestamp, + ) + .expect("Number is in range"); let logs_bloom = bundle.block_logs_bloom(block_number).expect("Number is in range"); // calculate the state root diff --git a/crates/payload/basic/src/optimism.rs b/crates/payload/basic/src/optimism.rs index 19f62db1dedd..8fade69b7023 100644 --- a/crates/payload/basic/src/optimism.rs +++ b/crates/payload/basic/src/optimism.rs @@ -235,7 +235,9 @@ where Receipts::from_vec(vec![receipts]), block_number, ); - let receipts_root = bundle.receipts_root_slow(block_number).expect("Number is in range"); + let receipts_root = bundle + .receipts_root_slow(block_number, chain_spec.as_ref(), attributes.timestamp) + .expect("Number is in range"); let logs_bloom = bundle.block_logs_bloom(block_number).expect("Number is in range"); // calculate the state root diff --git a/crates/primitives/src/proofs.rs b/crates/primitives/src/proofs.rs index 6bf4ab8f0eac..0efc906397f4 100644 --- a/crates/primitives/src/proofs.rs +++ b/crates/primitives/src/proofs.rs @@ -69,13 +69,26 @@ pub fn calculate_withdrawals_root(withdrawals: &[Withdrawal]) -> B256 { } /// Calculates the receipt root for a header. +#[cfg(not(feature = "optimism"))] pub fn calculate_receipt_root(receipts: &[ReceiptWithBloom]) -> B256 { - #[cfg(feature = "optimism")] + ordered_trie_root_with_encoder(receipts, |r, buf| r.encode_inner(buf, false)) +} + +/// Calculates the receipt root for a header. +#[cfg(feature = "optimism")] +pub fn calculate_receipt_root( + receipts: &[ReceiptWithBloom], + chain_spec: &crate::ChainSpec, + timestamp: u64, +) -> B256 { + // There is a minor bug in op-geth and op-erigon where in the Regolith hardfork, + // the receipt root calculation does not include the deposit nonce in the receipt + // encoding. In the Regolith Hardfork, we must strip the deposit nonce from the + // receipts before calculating the receipt root. This was corrected in the Canyon + // hardfork. + if chain_spec.is_fork_active_at_timestamp(crate::Hardfork::Regolith, timestamp) && + !chain_spec.is_fork_active_at_timestamp(crate::Hardfork::Canyon, timestamp) { - // There is a minor bug in op-geth and op-erigon where in the Regolith hardfork, - // the receipt root calculation does not include the deposit nonce in the receipt - // encoding. This will be fixd in the next hardfork, however for now, we must strip - // the deposit nonce from the receipts before calculating the receipt root. let receipts = receipts .iter() .cloned() @@ -85,23 +98,41 @@ pub fn calculate_receipt_root(receipts: &[ReceiptWithBloom]) -> B256 { }) .collect::>(); - ordered_trie_root_with_encoder(receipts.as_slice(), |r, buf| r.encode_inner(buf, false)) + return ordered_trie_root_with_encoder(receipts.as_slice(), |r, buf| { + r.encode_inner(buf, false) + }) } - #[cfg(not(feature = "optimism"))] ordered_trie_root_with_encoder(receipts, |r, buf| r.encode_inner(buf, false)) } /// Calculates the receipt root for a header for the reference type of [Receipt]. /// /// NOTE: Prefer [calculate_receipt_root] if you have log blooms memoized. +#[cfg(not(feature = "optimism"))] pub fn calculate_receipt_root_ref(receipts: &[&Receipt]) -> B256 { - #[cfg(feature = "optimism")] + ordered_trie_root_with_encoder(receipts, |r, buf| { + ReceiptWithBloomRef::from(*r).encode_inner(buf, false) + }) +} + +/// Calculates the receipt root for a header for the reference type of [Receipt]. +/// +/// NOTE: Prefer [calculate_receipt_root] if you have log blooms memoized. +#[cfg(feature = "optimism")] +pub fn calculate_receipt_root_ref( + receipts: &[&Receipt], + chain_spec: &crate::ChainSpec, + timestamp: u64, +) -> B256 { + // There is a minor bug in op-geth and op-erigon where in the Regolith hardfork, + // the receipt root calculation does not include the deposit nonce in the receipt + // encoding. In the Regolith Hardfork, we must strip the deposit nonce from the + // receipts before calculating the receipt root. This was corrected in the Canyon + // hardfork. + if chain_spec.is_fork_active_at_timestamp(crate::Hardfork::Regolith, timestamp) && + !chain_spec.is_fork_active_at_timestamp(crate::Hardfork::Canyon, timestamp) { - // There is a minor bug in op-geth and op-erigon where in the Regolith hardfork, - // the receipt root calculation does not include the deposit nonce in the receipt - // encoding. This will be fixd in the next hardfork, however for now, we must strip - // the deposit nonce from the receipts before calculating the receipt root. let receipts = receipts .iter() .map(|r| { @@ -111,12 +142,11 @@ pub fn calculate_receipt_root_ref(receipts: &[&Receipt]) -> B256 { }) .collect::>(); - ordered_trie_root_with_encoder(&receipts, |r, buf| { + return ordered_trie_root_with_encoder(&receipts, |r, buf| { ReceiptWithBloomRef::from(r).encode_inner(buf, false) }) } - #[cfg(not(feature = "optimism"))] ordered_trie_root_with_encoder(receipts, |r, buf| { ReceiptWithBloomRef::from(*r).encode_inner(buf, false) }) @@ -212,222 +242,248 @@ mod tests { #[cfg(feature = "optimism")] #[test] fn check_optimism_receipt_root() { - use crate::{Bloom, Bytes}; - - let receipts = vec![ - // 0xb0d6ee650637911394396d81172bd1c637d568ed1fbddab0daddfca399c58b53 - ReceiptWithBloom { - receipt: Receipt { - tx_type: TxType::DEPOSIT, - success: true, - cumulative_gas_used: 46913, - logs: vec![], - #[cfg(feature = "optimism")] - deposit_nonce: Some(4012991u64), + use crate::{Bloom, Bytes, OP_GOERLI}; + + let cases = [ + // Deposit nonces didn't exist in Bedrock; No need to strip. For the purposes of this + // test, we do have them, so we should get the same root as Canyon. + ( + "bedrock", + 1679079599, + b256!("6eefbb5efb95235476654a8bfbf8cb64a4f5f0b0c80b700b0c5964550beee6d7"), + ), + // Deposit nonces introduced in Regolith. They weren't included in the receipt RLP, + // so we need to strip them - the receipt root will differ. + ( + "regolith", + 1679079600, + b256!("e255fed45eae7ede0556fe4fabc77b0d294d18781a5a581cab09127bc4cd9ffb"), + ), + // Receipt root hashing bug fixed in Canyon. Back to including the deposit nonce + // in the receipt RLP when computing the receipt root. + ( + "canyon", + 1699981200, + b256!("6eefbb5efb95235476654a8bfbf8cb64a4f5f0b0c80b700b0c5964550beee6d7"), + ), + ]; + + for case in cases { + let receipts = vec![ + // 0xb0d6ee650637911394396d81172bd1c637d568ed1fbddab0daddfca399c58b53 + ReceiptWithBloom { + receipt: Receipt { + tx_type: TxType::DEPOSIT, + success: true, + cumulative_gas_used: 46913, + logs: vec![], + #[cfg(feature = "optimism")] + deposit_nonce: Some(4012991u64), + }, + bloom: Bloom(hex!("00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000").into()), }, - bloom: Bloom(hex!("00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000").into()), - }, - // 0x2f433586bae30573c393adfa02bc81d2a1888a3d6c9869f473fb57245166bd9a - ReceiptWithBloom { - receipt: Receipt { - tx_type: TxType::EIP1559, - success: true, - cumulative_gas_used: 118083, - logs: vec![ - Log { - address: hex!("ddb6dcce6b794415145eb5caa6cd335aeda9c272").into(), - topics: vec![ -b256!("c3d58168c5ae7397731d063d5bbf3d657854427343f4c083240f7aacaa2d0f62"), -b256!("000000000000000000000000c498902843af527e674846bb7edefa8ad62b8fb9"), -b256!("000000000000000000000000c498902843af527e674846bb7edefa8ad62b8fb9"), -b256!("0000000000000000000000000000000000000000000000000000000000000000"), - ], - data: Bytes::from_static(&hex!("00000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000001")), - }, - Log { - address: hex!("ddb6dcce6b794415145eb5caa6cd335aeda9c272").into(), - topics: vec![ -b256!("c3d58168c5ae7397731d063d5bbf3d657854427343f4c083240f7aacaa2d0f62"), -b256!("000000000000000000000000c498902843af527e674846bb7edefa8ad62b8fb9"), -b256!("0000000000000000000000000000000000000000000000000000000000000000"), -b256!("000000000000000000000000c498902843af527e674846bb7edefa8ad62b8fb9"), - ], - data: Bytes::from_static(&hex!("00000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000001")), - }, - Log { - address: hex!("ddb6dcce6b794415145eb5caa6cd335aeda9c272").into(), - topics: vec![ -b256!("0eb774bb9698a73583fe07b6972cf2dcc08d1d97581a22861f45feb86b395820"), -b256!("000000000000000000000000c498902843af527e674846bb7edefa8ad62b8fb9"), -b256!("000000000000000000000000c498902843af527e674846bb7edefa8ad62b8fb9"), - ], -data: Bytes::from_static(&hex!("0000000000000000000000000000000000000000000000000000000000000003")), - }, - ], - #[cfg(feature = "optimism")] - deposit_nonce: None, + // 0x2f433586bae30573c393adfa02bc81d2a1888a3d6c9869f473fb57245166bd9a + ReceiptWithBloom { + receipt: Receipt { + tx_type: TxType::EIP1559, + success: true, + cumulative_gas_used: 118083, + logs: vec![ + Log { + address: hex!("ddb6dcce6b794415145eb5caa6cd335aeda9c272").into(), + topics: vec![ + b256!("c3d58168c5ae7397731d063d5bbf3d657854427343f4c083240f7aacaa2d0f62"), + b256!("000000000000000000000000c498902843af527e674846bb7edefa8ad62b8fb9"), + b256!("000000000000000000000000c498902843af527e674846bb7edefa8ad62b8fb9"), + b256!("0000000000000000000000000000000000000000000000000000000000000000"), + ], + data: Bytes::from_static(&hex!("00000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000001")), + }, + Log { + address: hex!("ddb6dcce6b794415145eb5caa6cd335aeda9c272").into(), + topics: vec![ + b256!("c3d58168c5ae7397731d063d5bbf3d657854427343f4c083240f7aacaa2d0f62"), + b256!("000000000000000000000000c498902843af527e674846bb7edefa8ad62b8fb9"), + b256!("0000000000000000000000000000000000000000000000000000000000000000"), + b256!("000000000000000000000000c498902843af527e674846bb7edefa8ad62b8fb9"), + ], + data: Bytes::from_static(&hex!("00000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000001")), + }, + Log { + address: hex!("ddb6dcce6b794415145eb5caa6cd335aeda9c272").into(), + topics: vec![ + b256!("0eb774bb9698a73583fe07b6972cf2dcc08d1d97581a22861f45feb86b395820"), + b256!("000000000000000000000000c498902843af527e674846bb7edefa8ad62b8fb9"), + b256!("000000000000000000000000c498902843af527e674846bb7edefa8ad62b8fb9"), + ], + data: Bytes::from_static(&hex!("0000000000000000000000000000000000000000000000000000000000000003")), + }, + ], + #[cfg(feature = "optimism")] + deposit_nonce: None, + }, + bloom: Bloom(hex!("00001000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000000000000000020000000000000000000800000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000040000000000004000000000080000000000000000000000000000000000000000000000000000008000000000000080020000000000000000000000000002000000000000000000000000000080000010000").into()), }, - bloom: Bloom(hex!("00001000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000000000000000020000000000000000000800000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000040000000000004000000000080000000000000000000000000000000000000000000000000000008000000000000080020000000000000000000000000002000000000000000000000000000080000010000").into()), - }, - // 0x6c33676e8f6077f46a62eabab70bc6d1b1b18a624b0739086d77093a1ecf8266 - ReceiptWithBloom { - receipt: Receipt { - tx_type: TxType::EIP1559, - success: true, - cumulative_gas_used: 189253, - logs: vec![ - Log { - address: hex!("ddb6dcce6b794415145eb5caa6cd335aeda9c272").into(), - topics: vec![ -b256!("c3d58168c5ae7397731d063d5bbf3d657854427343f4c083240f7aacaa2d0f62"), -b256!("0000000000000000000000009d521a04bee134ff8136d2ec957e5bc8c50394ec"), -b256!("0000000000000000000000009d521a04bee134ff8136d2ec957e5bc8c50394ec"), -b256!("0000000000000000000000000000000000000000000000000000000000000000"), - ], - data: Bytes::from_static(&hex!("00000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000001")), - }, - Log { - address: hex!("ddb6dcce6b794415145eb5caa6cd335aeda9c272").into(), - topics: vec![ -b256!("c3d58168c5ae7397731d063d5bbf3d657854427343f4c083240f7aacaa2d0f62"), -b256!("0000000000000000000000009d521a04bee134ff8136d2ec957e5bc8c50394ec"), -b256!("0000000000000000000000000000000000000000000000000000000000000000"), -b256!("0000000000000000000000009d521a04bee134ff8136d2ec957e5bc8c50394ec"), - ], - data: Bytes::from_static(&hex!("00000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000001")), - }, - Log { - address: hex!("ddb6dcce6b794415145eb5caa6cd335aeda9c272").into(), - topics: vec![ -b256!("0eb774bb9698a73583fe07b6972cf2dcc08d1d97581a22861f45feb86b395820"), -b256!("0000000000000000000000009d521a04bee134ff8136d2ec957e5bc8c50394ec"), -b256!("0000000000000000000000009d521a04bee134ff8136d2ec957e5bc8c50394ec"), - ], -data: Bytes::from_static(&hex!("0000000000000000000000000000000000000000000000000000000000000003")), - }, - ], - #[cfg(feature = "optimism")] - deposit_nonce: None, + // 0x6c33676e8f6077f46a62eabab70bc6d1b1b18a624b0739086d77093a1ecf8266 + ReceiptWithBloom { + receipt: Receipt { + tx_type: TxType::EIP1559, + success: true, + cumulative_gas_used: 189253, + logs: vec![ + Log { + address: hex!("ddb6dcce6b794415145eb5caa6cd335aeda9c272").into(), + topics: vec![ + b256!("c3d58168c5ae7397731d063d5bbf3d657854427343f4c083240f7aacaa2d0f62"), + b256!("0000000000000000000000009d521a04bee134ff8136d2ec957e5bc8c50394ec"), + b256!("0000000000000000000000009d521a04bee134ff8136d2ec957e5bc8c50394ec"), + b256!("0000000000000000000000000000000000000000000000000000000000000000"), + ], + data: Bytes::from_static(&hex!("00000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000001")), + }, + Log { + address: hex!("ddb6dcce6b794415145eb5caa6cd335aeda9c272").into(), + topics: vec![ + b256!("c3d58168c5ae7397731d063d5bbf3d657854427343f4c083240f7aacaa2d0f62"), + b256!("0000000000000000000000009d521a04bee134ff8136d2ec957e5bc8c50394ec"), + b256!("0000000000000000000000000000000000000000000000000000000000000000"), + b256!("0000000000000000000000009d521a04bee134ff8136d2ec957e5bc8c50394ec"), + ], + data: Bytes::from_static(&hex!("00000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000001")), + }, + Log { + address: hex!("ddb6dcce6b794415145eb5caa6cd335aeda9c272").into(), + topics: vec![ + b256!("0eb774bb9698a73583fe07b6972cf2dcc08d1d97581a22861f45feb86b395820"), + b256!("0000000000000000000000009d521a04bee134ff8136d2ec957e5bc8c50394ec"), + b256!("0000000000000000000000009d521a04bee134ff8136d2ec957e5bc8c50394ec"), + ], + data: Bytes::from_static(&hex!("0000000000000000000000000000000000000000000000000000000000000003")), + }, + ], + #[cfg(feature = "optimism")] + deposit_nonce: None, + }, + bloom: Bloom(hex!("00000000000000000000200000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000002000000000020000000000000000000000000000000000000000000000000000000000000000020000000000000000000800000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000040000000000004000000000080000000000000000000000000000000000000000000000000000008000000000000080020000000000000000000000000002000000000000000000000000000080000000000").into()), }, - bloom: Bloom(hex!("00000000000000000000200000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000002000000000020000000000000000000000000000000000000000000000000000000000000000020000000000000000000800000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000040000000000004000000000080000000000000000000000000000000000000000000000000000008000000000000080020000000000000000000000000002000000000000000000000000000080000000000").into()), - }, - // 0x4d3ecbef04ba7ce7f5ab55be0c61978ca97c117d7da448ed9771d4ff0c720a3f - ReceiptWithBloom { - receipt: Receipt { - tx_type: TxType::EIP1559, - success: true, - cumulative_gas_used: 346969, - logs: vec![ - Log { - address: hex!("4200000000000000000000000000000000000006").into(), - topics: vec![ -b256!("ddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef"), -b256!("000000000000000000000000c3feb4ef4c2a5af77add15c95bd98f6b43640cc8"), -b256!("0000000000000000000000002992607c1614484fe6d865088e5c048f0650afd4"), - ], -data: Bytes::from_static(&hex!("0000000000000000000000000000000000000000000000000018de76816d8000")), - }, - Log { - address: hex!("cf8e7e6b26f407dee615fc4db18bf829e7aa8c09").into(), - topics: vec![ -b256!("ddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef"), -b256!("0000000000000000000000002992607c1614484fe6d865088e5c048f0650afd4"), -b256!("0000000000000000000000008dbffe4c8bf3caf5deae3a99b50cfcf3648cbc09"), - ], -data: Bytes::from_static(&hex!("000000000000000000000000000000000000000000000002d24d8e9ac1aa79e2")), - }, - Log { - address: hex!("2992607c1614484fe6d865088e5c048f0650afd4").into(), - topics: vec![ -b256!("1c411e9a96e071241c2f21f7726b17ae89e3cab4c78be50e062b03a9fffbbad1"), - ], - data: Bytes::from_static(&hex!("000000000000000000000000000000000000000000000009bd50642785c15736000000000000000000000000000000000000000000011bb7ac324f724a29bbbf")), - }, - Log { - address: hex!("2992607c1614484fe6d865088e5c048f0650afd4").into(), - topics: vec![ -b256!("d78ad95fa46c994b6551d0da85fc275fe613ce37657fb8d5e3d130840159d822"), -b256!("00000000000000000000000029843613c7211d014f5dd5718cf32bcd314914cb"), -b256!("0000000000000000000000008dbffe4c8bf3caf5deae3a99b50cfcf3648cbc09"), - ], - data: Bytes::from_static(&hex!("0000000000000000000000000000000000000000000000000018de76816d800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002d24d8e9ac1aa79e2")), - }, - Log { - address: hex!("6d0f8d488b669aa9ba2d0f0b7b75a88bf5051cd3").into(), - topics: vec![ -b256!("ddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef"), -b256!("0000000000000000000000008dbffe4c8bf3caf5deae3a99b50cfcf3648cbc09"), -b256!("000000000000000000000000c3feb4ef4c2a5af77add15c95bd98f6b43640cc8"), - ], - data: Bytes::from_static(&hex!("00000000000000000000000000000000000000000000000014bc73062aea8093")), - }, - Log { - address: hex!("8dbffe4c8bf3caf5deae3a99b50cfcf3648cbc09").into(), - topics: vec![ -b256!("1c411e9a96e071241c2f21f7726b17ae89e3cab4c78be50e062b03a9fffbbad1"), - ], - data: Bytes::from_static(&hex!("00000000000000000000000000000000000000000000002f122cfadc1ca82a35000000000000000000000000000000000000000000000665879dc0609945d6d1")), - }, - Log { - address: hex!("8dbffe4c8bf3caf5deae3a99b50cfcf3648cbc09").into(), - topics: vec![ -b256!("d78ad95fa46c994b6551d0da85fc275fe613ce37657fb8d5e3d130840159d822"), -b256!("00000000000000000000000029843613c7211d014f5dd5718cf32bcd314914cb"), -b256!("000000000000000000000000c3feb4ef4c2a5af77add15c95bd98f6b43640cc8"), - ], - data: Bytes::from_static(&hex!("0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002d24d8e9ac1aa79e200000000000000000000000000000000000000000000000014bc73062aea80930000000000000000000000000000000000000000000000000000000000000000")), - }, - ], - #[cfg(feature = "optimism")] - deposit_nonce: None, + // 0x4d3ecbef04ba7ce7f5ab55be0c61978ca97c117d7da448ed9771d4ff0c720a3f + ReceiptWithBloom { + receipt: Receipt { + tx_type: TxType::EIP1559, + success: true, + cumulative_gas_used: 346969, + logs: vec![ + Log { + address: hex!("4200000000000000000000000000000000000006").into(), + topics: vec![ + b256!("ddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef"), + b256!("000000000000000000000000c3feb4ef4c2a5af77add15c95bd98f6b43640cc8"), + b256!("0000000000000000000000002992607c1614484fe6d865088e5c048f0650afd4"), + ], + data: Bytes::from_static(&hex!("0000000000000000000000000000000000000000000000000018de76816d8000")), + }, + Log { + address: hex!("cf8e7e6b26f407dee615fc4db18bf829e7aa8c09").into(), + topics: vec![ + b256!("ddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef"), + b256!("0000000000000000000000002992607c1614484fe6d865088e5c048f0650afd4"), + b256!("0000000000000000000000008dbffe4c8bf3caf5deae3a99b50cfcf3648cbc09"), + ], + data: Bytes::from_static(&hex!("000000000000000000000000000000000000000000000002d24d8e9ac1aa79e2")), + }, + Log { + address: hex!("2992607c1614484fe6d865088e5c048f0650afd4").into(), + topics: vec![ + b256!("1c411e9a96e071241c2f21f7726b17ae89e3cab4c78be50e062b03a9fffbbad1"), + ], + data: Bytes::from_static(&hex!("000000000000000000000000000000000000000000000009bd50642785c15736000000000000000000000000000000000000000000011bb7ac324f724a29bbbf")), + }, + Log { + address: hex!("2992607c1614484fe6d865088e5c048f0650afd4").into(), + topics: vec![ + b256!("d78ad95fa46c994b6551d0da85fc275fe613ce37657fb8d5e3d130840159d822"), + b256!("00000000000000000000000029843613c7211d014f5dd5718cf32bcd314914cb"), + b256!("0000000000000000000000008dbffe4c8bf3caf5deae3a99b50cfcf3648cbc09"), + ], + data: Bytes::from_static(&hex!("0000000000000000000000000000000000000000000000000018de76816d800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002d24d8e9ac1aa79e2")), + }, + Log { + address: hex!("6d0f8d488b669aa9ba2d0f0b7b75a88bf5051cd3").into(), + topics: vec![ + b256!("ddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef"), + b256!("0000000000000000000000008dbffe4c8bf3caf5deae3a99b50cfcf3648cbc09"), + b256!("000000000000000000000000c3feb4ef4c2a5af77add15c95bd98f6b43640cc8"), + ], + data: Bytes::from_static(&hex!("00000000000000000000000000000000000000000000000014bc73062aea8093")), + }, + Log { + address: hex!("8dbffe4c8bf3caf5deae3a99b50cfcf3648cbc09").into(), + topics: vec![ + b256!("1c411e9a96e071241c2f21f7726b17ae89e3cab4c78be50e062b03a9fffbbad1"), + ], + data: Bytes::from_static(&hex!("00000000000000000000000000000000000000000000002f122cfadc1ca82a35000000000000000000000000000000000000000000000665879dc0609945d6d1")), + }, + Log { + address: hex!("8dbffe4c8bf3caf5deae3a99b50cfcf3648cbc09").into(), + topics: vec![ + b256!("d78ad95fa46c994b6551d0da85fc275fe613ce37657fb8d5e3d130840159d822"), + b256!("00000000000000000000000029843613c7211d014f5dd5718cf32bcd314914cb"), + b256!("000000000000000000000000c3feb4ef4c2a5af77add15c95bd98f6b43640cc8"), + ], + data: Bytes::from_static(&hex!("0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002d24d8e9ac1aa79e200000000000000000000000000000000000000000000000014bc73062aea80930000000000000000000000000000000000000000000000000000000000000000")), + }, + ], + #[cfg(feature = "optimism")] + deposit_nonce: None, + }, + bloom: Bloom(hex!("00200000000000000000000080000000000000000000000000040000100004000000000000000000000000100000000000000000000000000000100000000000000000000000000002000008000000200000000200000000020000000000000040000000000000000400000200000000000000000000000000000010000000000400000000010400000000000000000000000000002000c80000004080002000000000000000400200000000800000000000000000000000000000000000000000000002000000000000000000000000000000000100001000000000000000000000002000000000000000000000010000000000000000000000800000800000").into()), }, - bloom: Bloom(hex!("00200000000000000000000080000000000000000000000000040000100004000000000000000000000000100000000000000000000000000000100000000000000000000000000002000008000000200000000200000000020000000000000040000000000000000400000200000000000000000000000000000010000000000400000000010400000000000000000000000000002000c80000004080002000000000000000400200000000800000000000000000000000000000000000000000000002000000000000000000000000000000000100001000000000000000000000002000000000000000000000010000000000000000000000800000800000").into()), - }, - // 0xf738af5eb00ba23dbc1be2dbce41dbc0180f0085b7fb46646e90bf737af90351 - ReceiptWithBloom { - receipt: Receipt { - tx_type: TxType::EIP1559, - success: true, - cumulative_gas_used: 623249, - logs: vec![ - Log { - address: hex!("ac6564f3718837caadd42eed742d75c12b90a052").into(), - topics: vec![ -b256!("ddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef"), -b256!("0000000000000000000000000000000000000000000000000000000000000000"), -b256!("000000000000000000000000a4fa7f3fbf0677f254ebdb1646146864c305b76e"), -b256!("000000000000000000000000000000000000000000000000000000000011a1d3"), - ], - data: Default::default(), - }, - Log { - address: hex!("ac6564f3718837caadd42eed742d75c12b90a052").into(), - topics: vec![ -b256!("9d89e36eadf856db0ad9ffb5a569e07f95634dddd9501141ecf04820484ad0dc"), -b256!("000000000000000000000000a4fa7f3fbf0677f254ebdb1646146864c305b76e"), -b256!("000000000000000000000000000000000000000000000000000000000011a1d3"), - ], - data: Bytes::from_static(&hex!("00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000037697066733a2f2f516d515141646b33736538396b47716577395256567a316b68643548375562476d4d4a485a62566f386a6d346f4a2f30000000000000000000")), - }, - Log { - address: hex!("ac6564f3718837caadd42eed742d75c12b90a052").into(), - topics: vec![ -b256!("110d160a1bedeea919a88fbc4b2a9fb61b7e664084391b6ca2740db66fef80fe"), -b256!("00000000000000000000000084d47f6eea8f8d87910448325519d1bb45c2972a"), -b256!("000000000000000000000000a4fa7f3fbf0677f254ebdb1646146864c305b76e"), -b256!("000000000000000000000000000000000000000000000000000000000011a1d3"), - ], - data: Bytes::from_static(&hex!("0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000a4fa7f3fbf0677f254ebdb1646146864c305b76e00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007717500762343034303661353035646234633961386163316433306335633332303265370000000000000000000000000000000000000000000000000000000000000037697066733a2f2f516d515141646b33736538396b47716577395256567a316b68643548375562476d4d4a485a62566f386a6d346f4a2f30000000000000000000")), - }, - ], - #[cfg(feature = "optimism")] - deposit_nonce: None, + // 0xf738af5eb00ba23dbc1be2dbce41dbc0180f0085b7fb46646e90bf737af90351 + ReceiptWithBloom { + receipt: Receipt { + tx_type: TxType::EIP1559, + success: true, + cumulative_gas_used: 623249, + logs: vec![ + Log { + address: hex!("ac6564f3718837caadd42eed742d75c12b90a052").into(), + topics: vec![ + b256!("ddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef"), + b256!("0000000000000000000000000000000000000000000000000000000000000000"), + b256!("000000000000000000000000a4fa7f3fbf0677f254ebdb1646146864c305b76e"), + b256!("000000000000000000000000000000000000000000000000000000000011a1d3"), + ], + data: Default::default(), + }, + Log { + address: hex!("ac6564f3718837caadd42eed742d75c12b90a052").into(), + topics: vec![ + b256!("9d89e36eadf856db0ad9ffb5a569e07f95634dddd9501141ecf04820484ad0dc"), + b256!("000000000000000000000000a4fa7f3fbf0677f254ebdb1646146864c305b76e"), + b256!("000000000000000000000000000000000000000000000000000000000011a1d3"), + ], + data: Bytes::from_static(&hex!("00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000037697066733a2f2f516d515141646b33736538396b47716577395256567a316b68643548375562476d4d4a485a62566f386a6d346f4a2f30000000000000000000")), + }, + Log { + address: hex!("ac6564f3718837caadd42eed742d75c12b90a052").into(), + topics: vec![ + b256!("110d160a1bedeea919a88fbc4b2a9fb61b7e664084391b6ca2740db66fef80fe"), + b256!("00000000000000000000000084d47f6eea8f8d87910448325519d1bb45c2972a"), + b256!("000000000000000000000000a4fa7f3fbf0677f254ebdb1646146864c305b76e"), + b256!("000000000000000000000000000000000000000000000000000000000011a1d3"), + ], + data: Bytes::from_static(&hex!("0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000a4fa7f3fbf0677f254ebdb1646146864c305b76e00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007717500762343034303661353035646234633961386163316433306335633332303265370000000000000000000000000000000000000000000000000000000000000037697066733a2f2f516d515141646b33736538396b47716577395256567a316b68643548375562476d4d4a485a62566f386a6d346f4a2f30000000000000000000")), + }, + ], + #[cfg(feature = "optimism")] + deposit_nonce: None, + }, + bloom: Bloom(hex!("00000000000000000000000000000000400000000000000000000000000000000000004000000000000001000000000000000002000000000100000000000000000000000000000000000008000000000000000000000000000000000000000004000000020000000000000000000800000000000000000000000010200100200008000002000000000000000000800000000000000000000002000000000000000000000000000000080000000000000000000000004000000000000000000000000002000000000000000000000000000000000000200000000000000020002000000000000000002000000000000000000000000000000000000000000000").into()), }, - bloom: Bloom(hex!("00000000000000000000000000000000400000000000000000000000000000000000004000000000000001000000000000000002000000000100000000000000000000000000000000000008000000000000000000000000000000000000000004000000020000000000000000000800000000000000000000000010200100200008000002000000000000000000800000000000000000000002000000000000000000000000000000080000000000000000000000004000000000000000000000000002000000000000000000000000000000000000200000000000000020002000000000000000002000000000000000000000000000000000000000000000").into()), - }, - ]; - let root = calculate_receipt_root(&receipts); - assert_eq!(root, b256!("e255fed45eae7ede0556fe4fabc77b0d294d18781a5a581cab09127bc4cd9ffb")) + ]; + let root = calculate_receipt_root(&receipts, OP_GOERLI.as_ref(), case.1); + assert_eq!(root, case.2); + } } #[test] @@ -446,7 +502,13 @@ b256!("000000000000000000000000000000000000000000000000000000000011a1d3"), bloom, }; let receipt = vec![receipt]; - let root = calculate_receipt_root(&receipt); + let root = calculate_receipt_root( + &receipt, + #[cfg(feature = "optimism")] + crate::OP_GOERLI.as_ref(), + #[cfg(feature = "optimism")] + 0, + ); assert_eq!(root, b256!("fe70ae4a136d98944951b2123859698d59ad251a381abc9960fa81cae3d0d4a0")); } diff --git a/crates/primitives/src/receipt.rs b/crates/primitives/src/receipt.rs index 4b2fdd182f68..b92b698f05d7 100644 --- a/crates/primitives/src/receipt.rs +++ b/crates/primitives/src/receipt.rs @@ -88,12 +88,28 @@ impl Receipts { } /// Retrieves the receipt root for all recorded receipts from index. + #[cfg(not(feature = "optimism"))] pub fn root_slow(&self, index: usize) -> Option { Some(calculate_receipt_root_ref( &self.receipt_vec[index].iter().map(Option::as_ref).collect::>>()?, )) } + /// Retrieves the receipt root for all recorded receipts from index. + #[cfg(feature = "optimism")] + pub fn root_slow( + &self, + index: usize, + chain_spec: &crate::ChainSpec, + timestamp: u64, + ) -> Option { + Some(calculate_receipt_root_ref( + &self.receipt_vec[index].iter().map(Option::as_ref).collect::>>()?, + chain_spec, + timestamp, + )) + } + /// Retrieves gas spent by transactions as a vector of tuples (transaction index, gas used). pub fn gas_spent_by_tx(&self) -> Result, PruneSegmentError> { self.last() diff --git a/crates/revm/src/optimism/processor.rs b/crates/revm/src/optimism/processor.rs index cfdc0660d141..b238fd6452b1 100644 --- a/crates/revm/src/optimism/processor.rs +++ b/crates/revm/src/optimism/processor.rs @@ -36,9 +36,13 @@ impl<'a> BlockExecutor for EVMProcessor<'a> { // See more about EIP here: https://eips.ethereum.org/EIPS/eip-658 if self.chain_spec.fork(Hardfork::Byzantium).active_at_block(block.header.number) { let time = Instant::now(); - if let Err(error) = - verify_receipt(block.header.receipts_root, block.header.logs_bloom, receipts.iter()) - { + if let Err(error) = verify_receipt( + block.header.receipts_root, + block.header.logs_bloom, + receipts.iter(), + self.chain_spec.as_ref(), + block.timestamp, + ) { debug!(target: "evm", ?error, ?receipts, "receipts verification failed"); return Err(error) }; diff --git a/crates/revm/src/processor.rs b/crates/revm/src/processor.rs index 1f30ef4dd985..0a4806e7d461 100644 --- a/crates/revm/src/processor.rs +++ b/crates/revm/src/processor.rs @@ -531,10 +531,18 @@ pub fn verify_receipt<'a>( expected_receipts_root: B256, expected_logs_bloom: Bloom, receipts: impl Iterator + Clone, + #[cfg(feature = "optimism")] chain_spec: &ChainSpec, + #[cfg(feature = "optimism")] timestamp: u64, ) -> Result<(), BlockExecutionError> { // Check receipts root. let receipts_with_bloom = receipts.map(|r| r.clone().into()).collect::>(); - let receipts_root = reth_primitives::proofs::calculate_receipt_root(&receipts_with_bloom); + let receipts_root = reth_primitives::proofs::calculate_receipt_root( + &receipts_with_bloom, + #[cfg(feature = "optimism")] + chain_spec, + #[cfg(feature = "optimism")] + timestamp, + ); if receipts_root != expected_receipts_root { return Err(BlockValidationError::ReceiptRootDiff( GotExpected { got: receipts_root, expected: expected_receipts_root }.into(), diff --git a/crates/rpc/rpc/src/eth/api/pending_block.rs b/crates/rpc/rpc/src/eth/api/pending_block.rs index 3c6f6b58793c..7bd2053c9feb 100644 --- a/crates/rpc/rpc/src/eth/api/pending_block.rs +++ b/crates/rpc/rpc/src/eth/api/pending_block.rs @@ -201,7 +201,15 @@ impl PendingBlockEnv { block_number, ); - let receipts_root = bundle.receipts_root_slow(block_number).expect("Block is present"); + let receipts_root = bundle + .receipts_root_slow( + block_number, + #[cfg(feature = "optimism")] + chain_spec.as_ref(), + #[cfg(feature = "optimism")] + block_env.timestamp.to::(), + ) + .expect("Block is present"); let logs_bloom = bundle.block_logs_bloom(block_number).expect("Block is present"); // calculate the state root diff --git a/crates/storage/provider/src/bundle_state/bundle_state_with_receipts.rs b/crates/storage/provider/src/bundle_state/bundle_state_with_receipts.rs index 2a65909fdb65..6077a2f6560f 100644 --- a/crates/storage/provider/src/bundle_state/bundle_state_with_receipts.rs +++ b/crates/storage/provider/src/bundle_state/bundle_state_with_receipts.rs @@ -250,10 +250,24 @@ impl BundleStateWithReceipts { /// Returns the receipt root for all recorded receipts. /// Note: this function calculated Bloom filters for every receipt and created merkle trees /// of receipt. This is a expensive operation. + #[cfg(not(feature = "optimism"))] pub fn receipts_root_slow(&self, block_number: BlockNumber) -> Option { self.receipts.root_slow(self.block_number_to_index(block_number)?) } + /// Returns the receipt root for all recorded receipts. + /// Note: this function calculated Bloom filters for every receipt and created merkle trees + /// of receipt. This is a expensive operation. + #[cfg(feature = "optimism")] + pub fn receipts_root_slow( + &self, + block_number: BlockNumber, + chain_spec: &reth_primitives::ChainSpec, + timestamp: u64, + ) -> Option { + self.receipts.root_slow(self.block_number_to_index(block_number)?, chain_spec, timestamp) + } + /// Return reference to receipts. pub fn receipts(&self) -> &Receipts { &self.receipts From dbe8996579c6f0807836a2c70e3e8f44cdef7406 Mon Sep 17 00:00:00 2001 From: Matthias Seitz Date: Mon, 27 Nov 2023 17:08:57 +0100 Subject: [PATCH 029/277] chore: use task manager graceful shutdown (#5591) --- bin/reth/src/node/mod.rs | 16 +++++++++++----- bin/reth/src/runner.rs | 8 +++++--- 2 files changed, 16 insertions(+), 8 deletions(-) diff --git a/bin/reth/src/node/mod.rs b/bin/reth/src/node/mod.rs index aa0ee115de97..de078146f2a3 100644 --- a/bin/reth/src/node/mod.rs +++ b/bin/reth/src/node/mod.rs @@ -728,9 +728,10 @@ impl NodeCommand { task_executor.spawn_critical("p2p eth request handler", eth); let known_peers_file = self.network.persistent_peers_file(default_peers_path); - task_executor.spawn_critical_with_shutdown_signal("p2p network task", |shutdown| { - run_network_until_shutdown(shutdown, network, known_peers_file) - }); + task_executor + .spawn_critical_with_graceful_shutdown_signal("p2p network task", |shutdown| { + run_network_until_shutdown(shutdown, network, known_peers_file) + }); handle } @@ -1029,7 +1030,7 @@ impl NodeCommand { /// Drives the [NetworkManager] future until a [Shutdown](reth_tasks::shutdown::Shutdown) signal is /// received. If configured, this writes known peers to `persistent_peers_file` afterwards. async fn run_network_until_shutdown( - shutdown: reth_tasks::shutdown::Shutdown, + shutdown: reth_tasks::shutdown::GracefulShutdown, network: NetworkManager, persistent_peers_file: Option, ) where @@ -1037,9 +1038,12 @@ async fn run_network_until_shutdown( { pin_mut!(network, shutdown); + let mut graceful_guard = None; tokio::select! { _ = &mut network => {}, - _ = shutdown => {}, + guard = shutdown => { + graceful_guard = Some(guard); + }, } if let Some(file_path) = persistent_peers_file { @@ -1057,6 +1061,8 @@ async fn run_network_until_shutdown( } } } + + drop(graceful_guard) } #[cfg(test)] diff --git a/bin/reth/src/runner.rs b/bin/reth/src/runner.rs index 8ca0b9940199..19b459165f41 100644 --- a/bin/reth/src/runner.rs +++ b/bin/reth/src/runner.rs @@ -33,9 +33,11 @@ impl CliRunner { task_manager, run_until_ctrl_c(command(context)), ))?; - // after the command has finished or exit signal was received we drop the task manager which - // fires the shutdown signal to all tasks spawned via the task executor - drop(task_manager); + + // after the command has finished or exit signal was received we shutdown the task manager + // which fires the shutdown signal to all tasks spawned via the task executor and + // awaiting on tasks spawned with graceful shutdown + task_manager.graceful_shutdown_with_timeout(std::time::Duration::from_secs(10)); // drop the tokio runtime on a separate thread because drop blocks until its pools // (including blocking pool) are shutdown. In other words `drop(tokio_runtime)` would block From 92fecc1dafedb9749f1172fe8b0171036bd45ce1 Mon Sep 17 00:00:00 2001 From: clabby Date: Mon, 27 Nov 2023 11:59:00 -0500 Subject: [PATCH 030/277] feat(op-reth): Canyon receipts version (#5526) --- .../interfaces/src/test_utils/generators.rs | 2 + crates/net/eth-wire/src/types/receipts.rs | 6 ++ crates/payload/basic/src/lib.rs | 2 + crates/payload/basic/src/optimism.rs | 9 ++ crates/primitives/src/proofs.rs | 12 +++ crates/primitives/src/receipt.rs | 97 +++++++++++++++++-- crates/revm/src/optimism/processor.rs | 8 ++ crates/rpc/rpc/src/eth/api/pending_block.rs | 2 + .../storage/provider/src/test_utils/blocks.rs | 4 + 9 files changed, 133 insertions(+), 9 deletions(-) diff --git a/crates/interfaces/src/test_utils/generators.rs b/crates/interfaces/src/test_utils/generators.rs index a9328a4e695d..eed6e5c4a4a5 100644 --- a/crates/interfaces/src/test_utils/generators.rs +++ b/crates/interfaces/src/test_utils/generators.rs @@ -368,6 +368,8 @@ pub fn random_receipt( }, #[cfg(feature = "optimism")] deposit_nonce: None, + #[cfg(feature = "optimism")] + deposit_receipt_version: None, } } diff --git a/crates/net/eth-wire/src/types/receipts.rs b/crates/net/eth-wire/src/types/receipts.rs index 215c9ea6cbbf..88a0a6e12616 100644 --- a/crates/net/eth-wire/src/types/receipts.rs +++ b/crates/net/eth-wire/src/types/receipts.rs @@ -50,6 +50,8 @@ mod test { logs: vec![], #[cfg(feature = "optimism")] deposit_nonce: None, + #[cfg(feature = "optimism")] + deposit_receipt_version: None, }, bloom: Default::default(), }]]); @@ -121,6 +123,8 @@ mod test { success: false, #[cfg(feature = "optimism")] deposit_nonce: None, + #[cfg(feature = "optimism")] + deposit_receipt_version: None, }, bloom: hex!("00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000").into(), }, @@ -158,6 +162,8 @@ mod test { success: false, #[cfg(feature = "optimism")] deposit_nonce: None, + #[cfg(feature = "optimism")] + deposit_receipt_version: None, }, bloom: hex!("00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000").into(), }, diff --git a/crates/payload/basic/src/lib.rs b/crates/payload/basic/src/lib.rs index 3f806d5b50dd..533651b7c55e 100644 --- a/crates/payload/basic/src/lib.rs +++ b/crates/payload/basic/src/lib.rs @@ -888,6 +888,8 @@ where logs: result.logs().into_iter().map(into_reth_log).collect(), #[cfg(feature = "optimism")] deposit_nonce: None, + #[cfg(feature = "optimism")] + deposit_receipt_version: None, })); // update add to total fees diff --git a/crates/payload/basic/src/optimism.rs b/crates/payload/basic/src/optimism.rs index 8fade69b7023..19a73b07d2b5 100644 --- a/crates/payload/basic/src/optimism.rs +++ b/crates/payload/basic/src/optimism.rs @@ -128,6 +128,13 @@ where logs: result.logs().into_iter().map(into_reth_log).collect(), #[cfg(feature = "optimism")] deposit_nonce: depositor.map(|account| account.nonce), + // The deposit receipt version was introduced in Canyon to indicate an update to how + // receipt hashes should be computed when set. The state transition process + // ensures this is only set for post-Canyon deposit transactions. + #[cfg(feature = "optimism")] + deposit_receipt_version: chain_spec + .is_fork_active_at_timestamp(Hardfork::Canyon, attributes.timestamp) + .then_some(1), })); // append transaction to the list of executed transactions @@ -204,6 +211,8 @@ where logs: result.logs().into_iter().map(into_reth_log).collect(), #[cfg(feature = "optimism")] deposit_nonce: None, + #[cfg(feature = "optimism")] + deposit_receipt_version: None, })); // update add to total fees diff --git a/crates/primitives/src/proofs.rs b/crates/primitives/src/proofs.rs index 0efc906397f4..f4851744fbf1 100644 --- a/crates/primitives/src/proofs.rs +++ b/crates/primitives/src/proofs.rs @@ -279,6 +279,8 @@ mod tests { logs: vec![], #[cfg(feature = "optimism")] deposit_nonce: Some(4012991u64), + #[cfg(feature = "optimism")] + deposit_receipt_version: None, }, bloom: Bloom(hex!("00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000").into()), }, @@ -321,6 +323,8 @@ mod tests { ], #[cfg(feature = "optimism")] deposit_nonce: None, + #[cfg(feature = "optimism")] + deposit_receipt_version: None, }, bloom: Bloom(hex!("00001000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000000000000000020000000000000000000800000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000040000000000004000000000080000000000000000000000000000000000000000000000000000008000000000000080020000000000000000000000000002000000000000000000000000000080000010000").into()), }, @@ -363,6 +367,8 @@ mod tests { ], #[cfg(feature = "optimism")] deposit_nonce: None, + #[cfg(feature = "optimism")] + deposit_receipt_version: None, }, bloom: Bloom(hex!("00000000000000000000200000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000002000000000020000000000000000000000000000000000000000000000000000000000000000020000000000000000000800000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000040000000000004000000000080000000000000000000000000000000000000000000000000000008000000000000080020000000000000000000000000002000000000000000000000000000080000000000").into()), }, @@ -435,6 +441,8 @@ mod tests { ], #[cfg(feature = "optimism")] deposit_nonce: None, + #[cfg(feature = "optimism")] + deposit_receipt_version: None, }, bloom: Bloom(hex!("00200000000000000000000080000000000000000000000000040000100004000000000000000000000000100000000000000000000000000000100000000000000000000000000002000008000000200000000200000000020000000000000040000000000000000400000200000000000000000000000000000010000000000400000000010400000000000000000000000000002000c80000004080002000000000000000400200000000800000000000000000000000000000000000000000000002000000000000000000000000000000000100001000000000000000000000002000000000000000000000010000000000000000000000800000800000").into()), }, @@ -477,6 +485,8 @@ mod tests { ], #[cfg(feature = "optimism")] deposit_nonce: None, + #[cfg(feature = "optimism")] + deposit_receipt_version: None, }, bloom: Bloom(hex!("00000000000000000000000000000000400000000000000000000000000000000000004000000000000001000000000000000002000000000100000000000000000000000000000000000008000000000000000000000000000000000000000004000000020000000000000000000800000000000000000000000010200100200008000002000000000000000000800000000000000000000002000000000000000000000000000000080000000000000000000000004000000000000000000000000002000000000000000000000000000000000000200000000000000020002000000000000000002000000000000000000000000000000000000000000000").into()), }, @@ -498,6 +508,8 @@ mod tests { logs, #[cfg(feature = "optimism")] deposit_nonce: None, + #[cfg(feature = "optimism")] + deposit_receipt_version: None, }, bloom, }; diff --git a/crates/primitives/src/receipt.rs b/crates/primitives/src/receipt.rs index b92b698f05d7..37c6fd37c13a 100644 --- a/crates/primitives/src/receipt.rs +++ b/crates/primitives/src/receipt.rs @@ -30,9 +30,17 @@ pub struct Receipt { pub cumulative_gas_used: u64, /// Log send from contracts. pub logs: Vec, - /// Deposit nonce for Optimism deposited transactions + /// Deposit nonce for Optimism deposit transactions #[cfg(feature = "optimism")] pub deposit_nonce: Option, + /// Deposit receipt version for Optimism deposit transactions + /// + /// + /// The deposit receipt version was introduced in Canyon to indicate an update to how + /// receipt hashes should be computed when set. The state transition process + /// ensures this is only set for post-Canyon deposit transactions. + #[cfg(feature = "optimism")] + pub deposit_receipt_version: Option, } impl Receipt { @@ -210,15 +218,29 @@ impl proptest::arbitrary::Arbitrary for Receipt { success in any::(), cumulative_gas_used in any::(), logs in proptest::collection::vec(proptest::arbitrary::any::(), 0..=20), - _deposit_nonce in any::>()) -> Receipt + _deposit_nonce in any::>(), + _deposit_receipt_version in any::>()) -> Receipt { + // Only receipts for deposit transactions may contain a deposit nonce + #[cfg(feature = "optimism")] + let (deposit_nonce, deposit_receipt_version) = if tx_type == TxType::DEPOSIT { + // The deposit receipt version is only present if the deposit nonce is present + let deposit_receipt_version = _deposit_nonce.and(_deposit_receipt_version); + (_deposit_nonce, deposit_receipt_version) + } else { + (None, None) + }; + Receipt { tx_type, success, cumulative_gas_used, logs, // Only receipts for deposit transactions may contain a deposit nonce #[cfg(feature = "optimism")] - deposit_nonce: (tx_type == TxType::DEPOSIT).then_some(_deposit_nonce).flatten() + deposit_nonce, + // Only receipts for deposit transactions may contain a deposit nonce + #[cfg(feature = "optimism")] + deposit_receipt_version } } }; @@ -238,8 +260,14 @@ impl<'a> arbitrary::Arbitrary<'a> for Receipt { // Only receipts for deposit transactions may contain a deposit nonce #[cfg(feature = "optimism")] - let deposit_nonce = - if tx_type == TxType::DEPOSIT { Option::::arbitrary(u)? } else { None }; + let (deposit_nonce, deposit_receipt_version) = if tx_type == TxType::DEPOSIT { + let deposit_nonce = Option::::arbitrary(u)?; + let deposit_nonce_version = + deposit_nonce.map(|_| Option::::arbitrary(u)).transpose()?.flatten(); + (deposit_nonce, deposit_nonce_version) + } else { + (None, None) + }; Ok(Self { tx_type, @@ -248,6 +276,8 @@ impl<'a> arbitrary::Arbitrary<'a> for Receipt { logs, #[cfg(feature = "optimism")] deposit_nonce, + #[cfg(feature = "optimism")] + deposit_receipt_version, }) } } @@ -275,12 +305,20 @@ impl ReceiptWithBloom { let receipt = match tx_type { #[cfg(feature = "optimism")] TxType::DEPOSIT => { - let consumed = started_len - b.len(); - let has_nonce = rlp_head.payload_length - consumed > 0; + let remaining = |b: &[u8]| rlp_head.payload_length - (started_len - b.len()) > 0; let deposit_nonce = - if has_nonce { Some(alloy_rlp::Decodable::decode(b)?) } else { None }; + remaining(b).then(|| alloy_rlp::Decodable::decode(b)).transpose()?; + let deposit_receipt_version = + remaining(b).then(|| alloy_rlp::Decodable::decode(b)).transpose()?; - Receipt { tx_type, success, cumulative_gas_used, logs, deposit_nonce } + Receipt { + tx_type, + success, + cumulative_gas_used, + logs, + deposit_nonce, + deposit_receipt_version, + } } _ => Receipt { tx_type, @@ -289,6 +327,8 @@ impl ReceiptWithBloom { logs, #[cfg(feature = "optimism")] deposit_nonce: None, + #[cfg(feature = "optimism")] + deposit_receipt_version: None, }, }; @@ -421,6 +461,9 @@ impl<'a> ReceiptWithBloomEncoder<'a> { if let Some(deposit_nonce) = self.receipt.deposit_nonce { rlp_head.payload_length += deposit_nonce.length(); } + if let Some(deposit_receipt_version) = self.receipt.deposit_receipt_version { + rlp_head.payload_length += deposit_receipt_version.length(); + } } rlp_head @@ -438,6 +481,9 @@ impl<'a> ReceiptWithBloomEncoder<'a> { if let Some(deposit_nonce) = self.receipt.deposit_nonce { deposit_nonce.encode(out) } + if let Some(deposit_receipt_version) = self.receipt.deposit_receipt_version { + deposit_receipt_version.encode(out) + } } } @@ -528,6 +574,8 @@ mod tests { success: false, #[cfg(feature = "optimism")] deposit_nonce: None, + #[cfg(feature = "optimism")] + deposit_receipt_version: None, }, bloom: [0; 256].into(), }; @@ -560,6 +608,8 @@ mod tests { success: false, #[cfg(feature = "optimism")] deposit_nonce: None, + #[cfg(feature = "optimism")] + deposit_receipt_version: None, }, bloom: [0; 256].into(), }; @@ -581,6 +631,7 @@ mod tests { logs: vec![], success: true, deposit_nonce: Some(4012991), + deposit_receipt_version: None, }, bloom: [0; 256].into(), }; @@ -593,6 +644,32 @@ mod tests { assert_eq!(buf.freeze(), &data[..]); } + #[cfg(feature = "optimism")] + #[test] + fn decode_deposit_receipt_canyon_roundtrip() { + let data = hex!("7ef9010d0182b741b9010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c0833d3bbf01"); + + // Deposit Receipt (post-regolith) + let expected = ReceiptWithBloom { + receipt: Receipt { + tx_type: TxType::DEPOSIT, + cumulative_gas_used: 46913, + logs: vec![], + success: true, + deposit_nonce: Some(4012991), + deposit_receipt_version: Some(1), + }, + bloom: [0; 256].into(), + }; + + let receipt = ReceiptWithBloom::decode(&mut &data[..]).unwrap(); + assert_eq!(receipt, expected); + + let mut buf = BytesMut::default(); + expected.encode_inner(&mut buf, false); + assert_eq!(buf.freeze(), &data[..]); + } + #[test] fn gigantic_receipt() { let receipt = Receipt { @@ -617,6 +694,8 @@ mod tests { ], #[cfg(feature = "optimism")] deposit_nonce: None, + #[cfg(feature = "optimism")] + deposit_receipt_version: None, }; let mut data = vec![]; diff --git a/crates/revm/src/optimism/processor.rs b/crates/revm/src/optimism/processor.rs index b238fd6452b1..891a0d63f470 100644 --- a/crates/revm/src/optimism/processor.rs +++ b/crates/revm/src/optimism/processor.rs @@ -129,6 +129,14 @@ impl<'a> BlockExecutor for EVMProcessor<'a> { logs: result.into_logs().into_iter().map(into_reth_log).collect(), #[cfg(feature = "optimism")] deposit_nonce: depositor.map(|account| account.nonce), + // The deposit receipt version was introduced in Canyon to indicate an update to how + // receipt hashes should be computed when set. The state transition process ensures + // this is only set for post-Canyon deposit transactions. + #[cfg(feature = "optimism")] + deposit_receipt_version: self + .chain_spec() + .is_fork_active_at_timestamp(Hardfork::Canyon, block.timestamp) + .then_some(1), }); } diff --git a/crates/rpc/rpc/src/eth/api/pending_block.rs b/crates/rpc/rpc/src/eth/api/pending_block.rs index 7bd2053c9feb..32c2fdc252ad 100644 --- a/crates/rpc/rpc/src/eth/api/pending_block.rs +++ b/crates/rpc/rpc/src/eth/api/pending_block.rs @@ -174,6 +174,8 @@ impl PendingBlockEnv { logs: result.logs().into_iter().map(into_reth_log).collect(), #[cfg(feature = "optimism")] deposit_nonce: None, + #[cfg(feature = "optimism")] + deposit_receipt_version: None, })); // append transaction to the list of executed transactions diff --git a/crates/storage/provider/src/test_utils/blocks.rs b/crates/storage/provider/src/test_utils/blocks.rs index 3162266bc781..562483b5e1d8 100644 --- a/crates/storage/provider/src/test_utils/blocks.rs +++ b/crates/storage/provider/src/test_utils/blocks.rs @@ -139,6 +139,8 @@ fn block1(number: BlockNumber) -> (SealedBlockWithSenders, BundleStateWithReceip }], #[cfg(feature = "optimism")] deposit_nonce: None, + #[cfg(feature = "optimism")] + deposit_receipt_version: None, })]]), number, ); @@ -196,6 +198,8 @@ fn block2( }], #[cfg(feature = "optimism")] deposit_nonce: None, + #[cfg(feature = "optimism")] + deposit_receipt_version: None, })]]), number, ); From cda471ceb4da1c23fa6577277306e055b4f0e551 Mon Sep 17 00:00:00 2001 From: clabby Date: Mon, 27 Nov 2023 12:25:26 -0500 Subject: [PATCH 031/277] feat(op-reth): Canyon transition `create2Deployer` force deployment (#5527) --- crates/payload/basic/src/optimism.rs | 11 +++++ crates/primitives/src/chain/spec.rs | 33 +++++++++++++- crates/revm/src/optimism/mod.rs | 65 +++++++++++++++++++++++++-- crates/revm/src/optimism/processor.rs | 10 ++++- 4 files changed, 111 insertions(+), 8 deletions(-) diff --git a/crates/payload/basic/src/optimism.rs b/crates/payload/basic/src/optimism.rs index 19a73b07d2b5..d7f1eb0292de 100644 --- a/crates/payload/basic/src/optimism.rs +++ b/crates/payload/basic/src/optimism.rs @@ -54,6 +54,17 @@ where let is_regolith = chain_spec.is_fork_active_at_timestamp(Hardfork::Regolith, attributes.timestamp); + // Ensure that the create2deployer is force-deployed at the canyon transition. Optimism + // blocks will always have at least a single transaction in them (the L1 info transaction), + // so we can safely assume that this will always be triggered upon the transition and that + // the above check for empty blocks will never be hit on OP chains. + reth_revm::optimism::ensure_create2_deployer(chain_spec.clone(), attributes.timestamp, &mut db) + .map_err(|_| { + PayloadBuilderError::Internal(RethError::Custom( + "Failed to force create2deployer account code".to_string(), + )) + })?; + let mut receipts = Vec::new(); for sequencer_tx in attributes.optimism_payload_attributes.transactions { // Check if the job was cancelled, if so we can exit early. diff --git a/crates/primitives/src/chain/spec.rs b/crates/primitives/src/chain/spec.rs index 1e3917fef5f7..746b043cdf14 100644 --- a/crates/primitives/src/chain/spec.rs +++ b/crates/primitives/src/chain/spec.rs @@ -252,7 +252,7 @@ pub static OP_GOERLI: Lazy> = Lazy::new(|| { genesis_hash: Some(b256!( "c1fc15cd51159b1f1e5cbc4b82e85c1447ddfa33c52cf1d98d14fba0d6354be1" )), - fork_timestamps: ForkTimestamps::default(), + fork_timestamps: ForkTimestamps::default().shanghai(1699981200).canyon(1699981200), paris_block_and_final_difficulty: Some((0, U256::from(0))), hardforks: BTreeMap::from([ (Hardfork::Frontier, ForkCondition::Block(0)), @@ -295,7 +295,7 @@ pub static BASE_GOERLI: Lazy> = Lazy::new(|| { genesis_hash: Some(b256!( "a3ab140f15ea7f7443a4702da64c10314eb04d488e72974e02e2d728096b4f76" )), - fork_timestamps: ForkTimestamps::default(), + fork_timestamps: ForkTimestamps::default().shanghai(1699981200).canyon(1699981200), paris_block_and_final_difficulty: Some((0, U256::from(0))), hardforks: BTreeMap::from([ (Hardfork::Frontier, ForkCondition::Block(0)), @@ -831,6 +831,12 @@ pub struct ForkTimestamps { pub shanghai: Option, /// The timestamp of the cancun fork pub cancun: Option, + /// The timestamp of the Regolith fork + #[cfg(feature = "optimism")] + pub regolith: Option, + /// The timestamp of the Canyon fork + #[cfg(feature = "optimism")] + pub canyon: Option, } impl ForkTimestamps { @@ -843,6 +849,15 @@ impl ForkTimestamps { if let Some(cancun) = forks.get(&Hardfork::Cancun).and_then(|f| f.as_timestamp()) { timestamps = timestamps.cancun(cancun); } + #[cfg(feature = "optimism")] + { + if let Some(regolith) = forks.get(&Hardfork::Regolith).and_then(|f| f.as_timestamp()) { + timestamps = timestamps.regolith(regolith); + } + if let Some(canyon) = forks.get(&Hardfork::Canyon).and_then(|f| f.as_timestamp()) { + timestamps = timestamps.canyon(canyon); + } + } timestamps } @@ -857,6 +872,20 @@ impl ForkTimestamps { self.cancun = Some(cancun); self } + + /// Sets the given regolith timestamp + #[cfg(feature = "optimism")] + pub fn regolith(mut self, regolith: u64) -> Self { + self.regolith = Some(regolith); + self + } + + /// Sets the given canyon timestamp + #[cfg(feature = "optimism")] + pub fn canyon(mut self, canyon: u64) -> Self { + self.canyon = Some(canyon); + self + } } /// A helper type for compatibility with geth's config diff --git a/crates/revm/src/optimism/mod.rs b/crates/revm/src/optimism/mod.rs index ced44bd91ca9..3ecb632a6c33 100644 --- a/crates/revm/src/optimism/mod.rs +++ b/crates/revm/src/optimism/mod.rs @@ -1,13 +1,28 @@ -use reth_interfaces::executor::{self as reth_executor, BlockExecutionError}; -use reth_primitives::{Block, Bytes, ChainSpec, Hardfork, TransactionKind, U256}; +use reth_interfaces::{ + executor::{self as reth_executor, BlockExecutionError}, + RethError, +}; +use reth_primitives::{ + address, b256, hex, Address, Block, Bytes, ChainSpec, Hardfork, TransactionKind, B256, U256, +}; use revm::{ - primitives::{BedrockSpec, RegolithSpec}, - L1BlockInfo, + primitives::{BedrockSpec, Bytecode, HashMap, RegolithSpec}, + DatabaseCommit, L1BlockInfo, }; +use std::sync::Arc; +use tracing::trace; /// Optimism-specific processor implementation for the `EVMProcessor` pub mod processor; +/// The address of the create2 deployer +const CREATE_2_DEPLOYER_ADDR: Address = address!("13b0D85CcB8bf860b6b79AF3029fCA081AE9beF2"); +/// The codehash of the create2 deployer contract. +const CREATE_2_DEPLOYER_CODEHASH: B256 = + b256!("b0550b5b431e30d38000efb7107aaa0ade03d48a7198a140edda9d27134468b2"); +/// The raw bytecode of the create2 deployer contract. +const CREATE_2_DEPLOYER_BYTECODE: [u8; 1584] = hex!("6080604052600436106100435760003560e01c8063076c37b21461004f578063481286e61461007157806356299481146100ba57806366cfa057146100da57600080fd5b3661004a57005b600080fd5b34801561005b57600080fd5b5061006f61006a366004610327565b6100fa565b005b34801561007d57600080fd5b5061009161008c366004610327565b61014a565b60405173ffffffffffffffffffffffffffffffffffffffff909116815260200160405180910390f35b3480156100c657600080fd5b506100916100d5366004610349565b61015d565b3480156100e657600080fd5b5061006f6100f53660046103ca565b610172565b61014582826040518060200161010f9061031a565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe082820381018352601f90910116604052610183565b505050565b600061015683836102e7565b9392505050565b600061016a8484846102f0565b949350505050565b61017d838383610183565b50505050565b6000834710156101f4576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601d60248201527f437265617465323a20696e73756666696369656e742062616c616e636500000060448201526064015b60405180910390fd5b815160000361025f576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820181905260248201527f437265617465323a2062797465636f6465206c656e677468206973207a65726f60448201526064016101eb565b8282516020840186f5905073ffffffffffffffffffffffffffffffffffffffff8116610156576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601960248201527f437265617465323a204661696c6564206f6e206465706c6f790000000000000060448201526064016101eb565b60006101568383305b6000604051836040820152846020820152828152600b8101905060ff815360559020949350505050565b61014e806104ad83390190565b6000806040838503121561033a57600080fd5b50508035926020909101359150565b60008060006060848603121561035e57600080fd5b8335925060208401359150604084013573ffffffffffffffffffffffffffffffffffffffff8116811461039057600080fd5b809150509250925092565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b6000806000606084860312156103df57600080fd5b8335925060208401359150604084013567ffffffffffffffff8082111561040557600080fd5b818601915086601f83011261041957600080fd5b81358181111561042b5761042b61039b565b604051601f82017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0908116603f011681019083821181831017156104715761047161039b565b8160405282815289602084870101111561048a57600080fd5b826020860160208301376000602084830101528095505050505050925092509256fe608060405234801561001057600080fd5b5061012e806100206000396000f3fe6080604052348015600f57600080fd5b506004361060285760003560e01c8063249cb3fa14602d575b600080fd5b603c603836600460b1565b604e565b60405190815260200160405180910390f35b60008281526020818152604080832073ffffffffffffffffffffffffffffffffffffffff8516845290915281205460ff16608857600060aa565b7fa2ef4600d742022d532d4747cb3547474667d6f13804902513b2ec01c848f4b45b9392505050565b6000806040838503121560c357600080fd5b82359150602083013573ffffffffffffffffffffffffffffffffffffffff8116811460ed57600080fd5b80915050925092905056fea26469706673582212205ffd4e6cede7d06a5daf93d48d0541fc68189eeb16608c1999a82063b666eb1164736f6c63430008130033a2646970667358221220fdc4a0fe96e3b21c108ca155438d37c9143fb01278a3c1d274948bad89c564ba64736f6c63430008130033"); + /// Extracts the [L1BlockInfo] from the L2 block. The L1 info transaction is always the first /// transaction in the L2 block. pub fn extract_l1_info(block: &Block) -> Result { @@ -151,6 +166,48 @@ impl RethL1BlockInfo for L1BlockInfo { } } +/// The Canyon hardfork issues an irregular state transition that force-deploys the create2 +/// deployer contract. This is done by directly setting the code of the create2 deployer account +/// prior to executing any transactions on the timestamp activation of the fork. +pub fn ensure_create2_deployer( + chain_spec: Arc, + timestamp: u64, + db: &mut revm::State, +) -> Result<(), RethError> +where + DB: revm::Database, +{ + // If the canyon hardfork is active at the current timestamp, and it was not active at the + // previous block timestamp (heuristically, block time is not perfectly constant at 2s), and the + // chain is an optimism chain, then we need to force-deploy the create2 deployer contract. + if chain_spec.is_fork_active_at_timestamp(Hardfork::Canyon, timestamp) && + !chain_spec.is_fork_active_at_timestamp(Hardfork::Canyon, timestamp - 2) && + chain_spec.is_optimism() + { + trace!(target: "evm", "Forcing create2 deployer contract deployment on Canyon transition"); + + // Load the create2 deployer account from the cache. + let acc = db + .load_cache_account(CREATE_2_DEPLOYER_ADDR) + .map_err(|_| RethError::Custom("Failed to load account".to_string()))?; + + // Update the account info with the create2 deployer codehash and bytecode. + let mut acc_info = acc.account_info().unwrap_or_default(); + acc_info.code_hash = CREATE_2_DEPLOYER_CODEHASH; + acc_info.code = Some(Bytecode::new_raw(Bytes::from_static(&CREATE_2_DEPLOYER_BYTECODE))); + + // Convert the cache account back into a revm account and mark it as touched. + let mut revm_acc: revm::primitives::Account = acc_info.into(); + revm_acc.mark_touch(); + + // Commit the create2 deployer account to the database. + db.commit(HashMap::from([(CREATE_2_DEPLOYER_ADDR, revm_acc)])); + return Ok(()) + } + + Ok(()) +} + #[cfg(test)] mod test_l1_fee { #[test] diff --git a/crates/revm/src/optimism/processor.rs b/crates/revm/src/optimism/processor.rs index 891a0d63f470..c0f3ba6a57ee 100644 --- a/crates/revm/src/optimism/processor.rs +++ b/crates/revm/src/optimism/processor.rs @@ -1,3 +1,4 @@ +use crate::processor::{verify_receipt, EVMProcessor}; use reth_interfaces::executor::{BlockExecutionError, BlockValidationError}; use reth_primitives::{ revm::compat::into_reth_log, revm_primitives::ResultAndState, Address, Block, Hardfork, @@ -8,8 +9,6 @@ use revm::DatabaseCommit; use std::time::Instant; use tracing::{debug, trace}; -use crate::processor::{verify_receipt, EVMProcessor}; - impl<'a> BlockExecutor for EVMProcessor<'a> { fn execute( &mut self, @@ -70,6 +69,13 @@ impl<'a> BlockExecutor for EVMProcessor<'a> { let is_regolith = self.chain_spec.fork(Hardfork::Regolith).active_at_timestamp(block.timestamp); + // Ensure that the create2deployer is force-deployed at the canyon transition. Optimism + // blocks will always have at least a single transaction in them (the L1 info transaction), + // so we can safely assume that this will always be triggered upon the transition and that + // the above check for empty blocks will never be hit on OP chains. + super::ensure_create2_deployer(self.chain_spec().clone(), block.timestamp, self.db_mut()) + .map_err(|_| BlockExecutionError::ProviderError)?; + let mut cumulative_gas_used = 0; let mut receipts = Vec::with_capacity(block.body.len()); for (transaction, sender) in block.body.iter().zip(senders) { From b2be07aa386eaece9ae5e83794c6bdd360a800df Mon Sep 17 00:00:00 2001 From: clabby Date: Mon, 27 Nov 2023 12:26:01 -0500 Subject: [PATCH 032/277] feat(op-reth): Canyon - Dynamic eip1559 params (#5542) --- crates/consensus/auto-seal/src/lib.rs | 6 +- crates/consensus/common/src/validation.rs | 5 +- crates/payload/builder/src/payload.rs | 4 +- crates/primitives/src/chain/mod.rs | 5 +- crates/primitives/src/chain/spec.rs | 114 ++++++++++++++++++++-- crates/primitives/src/constants/mod.rs | 10 ++ crates/primitives/src/lib.rs | 6 +- crates/rpc/rpc/src/eth/api/fees.rs | 11 ++- crates/rpc/rpc/src/eth/api/mod.rs | 3 +- crates/transaction-pool/src/maintain.rs | 12 ++- 10 files changed, 148 insertions(+), 28 deletions(-) diff --git a/crates/consensus/auto-seal/src/lib.rs b/crates/consensus/auto-seal/src/lib.rs index e2dfc9c91111..01035e35dc78 100644 --- a/crates/consensus/auto-seal/src/lib.rs +++ b/crates/consensus/auto-seal/src/lib.rs @@ -254,11 +254,13 @@ impl StorageInner { transactions: &Vec, chain_spec: Arc, ) -> Header { + let timestamp = SystemTime::now().duration_since(UNIX_EPOCH).unwrap_or_default().as_secs(); + // check previous block for base fee let base_fee_per_gas = self .headers .get(&self.best_block) - .and_then(|parent| parent.next_block_base_fee(chain_spec.base_fee_params)); + .and_then(|parent| parent.next_block_base_fee(chain_spec.base_fee_params(timestamp))); let mut header = Header { parent_hash: self.best_hash, @@ -273,7 +275,7 @@ impl StorageInner { number: self.best_block + 1, gas_limit: ETHEREUM_BLOCK_GAS_LIMIT, gas_used: 0, - timestamp: SystemTime::now().duration_since(UNIX_EPOCH).unwrap_or_default().as_secs(), + timestamp, mix_hash: Default::default(), nonce: 0, base_fee_per_gas, diff --git a/crates/consensus/common/src/validation.rs b/crates/consensus/common/src/validation.rs index 8ba4e535fa1e..a2fce109efc0 100644 --- a/crates/consensus/common/src/validation.rs +++ b/crates/consensus/common/src/validation.rs @@ -263,7 +263,8 @@ fn check_gas_limit( // By consensus, gas_limit is multiplied by elasticity (*2) on // on exact block that hardfork happens. if chain_spec.fork(Hardfork::London).transitions_at_block(child.number) { - parent_gas_limit = parent.gas_limit * chain_spec.base_fee_params.elasticity_multiplier; + parent_gas_limit = + parent.gas_limit * chain_spec.base_fee_params(child.timestamp).elasticity_multiplier; } if child.gas_limit > parent_gas_limit { @@ -336,7 +337,7 @@ pub fn validate_header_regarding_parent( } else { // This BaseFeeMissing will not happen as previous blocks are checked to have them. parent - .next_block_base_fee(chain_spec.base_fee_params) + .next_block_base_fee(chain_spec.base_fee_params(child.timestamp)) .ok_or(ConsensusError::BaseFeeMissing)? }; if expected_base_fee != base_fee { diff --git a/crates/payload/builder/src/payload.rs b/crates/payload/builder/src/payload.rs index 919c0705afcd..57c4c82b0c51 100644 --- a/crates/payload/builder/src/payload.rs +++ b/crates/payload/builder/src/payload.rs @@ -251,7 +251,9 @@ impl PayloadBuilderAttributes { gas_limit: U256::from(parent.gas_limit), // calculate basefee based on parent block's gas usage basefee: U256::from( - parent.next_block_base_fee(chain_spec.base_fee_params).unwrap_or_default(), + parent + .next_block_base_fee(chain_spec.base_fee_params(self.timestamp)) + .unwrap_or_default(), ), // calculate excess gas based on parent block's blob gas usage blob_excess_gas_and_price, diff --git a/crates/primitives/src/chain/mod.rs b/crates/primitives/src/chain/mod.rs index c78d365fde9b..ac016b4f1631 100644 --- a/crates/primitives/src/chain/mod.rs +++ b/crates/primitives/src/chain/mod.rs @@ -13,8 +13,9 @@ use strum::{AsRefStr, EnumCount, EnumIter, EnumString, EnumVariantNames}; // The chain spec module. mod spec; pub use spec::{ - AllGenesisFormats, BaseFeeParams, ChainSpec, ChainSpecBuilder, DisplayHardforks, ForkCondition, - ForkTimestamps, DEV, GOERLI, HOLESKY, MAINNET, SEPOLIA, + AllGenesisFormats, BaseFeeParams, BaseFeeParamsKind, ChainSpec, ChainSpecBuilder, + DisplayHardforks, ForkBaseFeeParams, ForkCondition, ForkTimestamps, DEV, GOERLI, HOLESKY, + MAINNET, SEPOLIA, }; #[cfg(feature = "optimism")] diff --git a/crates/primitives/src/chain/spec.rs b/crates/primitives/src/chain/spec.rs index 746b043cdf14..a1d5d96a8a9f 100644 --- a/crates/primitives/src/chain/spec.rs +++ b/crates/primitives/src/chain/spec.rs @@ -63,7 +63,7 @@ pub static MAINNET: Lazy> = Lazy::new(|| { 11052984, b256!("649bbc62d0e31342afea4e5cd82d4049e7e1ee912fc0889aa790803be39038c5"), )), - base_fee_params: BaseFeeParams::ethereum(), + base_fee_params: BaseFeeParamsKind::Constant(BaseFeeParams::ethereum()), prune_delete_limit: 3500, snapshot_block_interval: 500_000, } @@ -106,7 +106,7 @@ pub static GOERLI: Lazy> = Lazy::new(|| { 4367322, b256!("649bbc62d0e31342afea4e5cd82d4049e7e1ee912fc0889aa790803be39038c5"), )), - base_fee_params: BaseFeeParams::ethereum(), + base_fee_params: BaseFeeParamsKind::Constant(BaseFeeParams::ethereum()), prune_delete_limit: 1700, snapshot_block_interval: 1_000_000, } @@ -153,8 +153,7 @@ pub static SEPOLIA: Lazy> = Lazy::new(|| { 1273020, b256!("649bbc62d0e31342afea4e5cd82d4049e7e1ee912fc0889aa790803be39038c5"), )), - - base_fee_params: BaseFeeParams::ethereum(), + base_fee_params: BaseFeeParamsKind::Constant(BaseFeeParams::ethereum()), prune_delete_limit: 1700, snapshot_block_interval: 1_000_000, } @@ -196,7 +195,7 @@ pub static HOLESKY: Lazy> = Lazy::new(|| { 0, b256!("649bbc62d0e31342afea4e5cd82d4049e7e1ee912fc0889aa790803be39038c5"), )), - base_fee_params: BaseFeeParams::ethereum(), + base_fee_params: BaseFeeParamsKind::Constant(BaseFeeParams::ethereum()), prune_delete_limit: 1700, snapshot_block_interval: 1_000_000, } @@ -236,6 +235,7 @@ pub static DEV: Lazy> = Lazy::new(|| { ), (Hardfork::Shanghai, ForkCondition::Timestamp(0)), ]), + base_fee_params: BaseFeeParamsKind::Constant(BaseFeeParams::ethereum()), deposit_contract: None, // TODO: do we even have? ..Default::default() } @@ -277,7 +277,13 @@ pub static OP_GOERLI: Lazy> = Lazy::new(|| { (Hardfork::Shanghai, ForkCondition::Timestamp(1699981200)), (Hardfork::Canyon, ForkCondition::Timestamp(1699981200)), ]), - base_fee_params: BaseFeeParams::optimism(), + base_fee_params: BaseFeeParamsKind::Variable( + vec![ + (Hardfork::London, BaseFeeParams::optimism_goerli()), + (Hardfork::Canyon, BaseFeeParams::optimism_goerli_canyon()), + ] + .into(), + ), prune_delete_limit: 1700, snapshot_block_interval: 1_000_000, ..Default::default() @@ -320,7 +326,13 @@ pub static BASE_GOERLI: Lazy> = Lazy::new(|| { (Hardfork::Shanghai, ForkCondition::Timestamp(1699981200)), (Hardfork::Canyon, ForkCondition::Timestamp(1699981200)), ]), - base_fee_params: BaseFeeParams::optimism_goerli(), + base_fee_params: BaseFeeParamsKind::Variable( + vec![ + (Hardfork::London, BaseFeeParams::optimism_goerli()), + (Hardfork::Canyon, BaseFeeParams::optimism_goerli_canyon()), + ] + .into(), + ), prune_delete_limit: 1700, snapshot_block_interval: 1_000_000, ..Default::default() @@ -361,7 +373,13 @@ pub static BASE_MAINNET: Lazy> = Lazy::new(|| { (Hardfork::Bedrock, ForkCondition::Block(0)), (Hardfork::Regolith, ForkCondition::Timestamp(0)), ]), - base_fee_params: BaseFeeParams::optimism(), + base_fee_params: BaseFeeParamsKind::Variable( + vec![ + (Hardfork::London, BaseFeeParams::optimism()), + (Hardfork::Canyon, BaseFeeParams::optimism_canyon()), + ] + .into(), + ), prune_delete_limit: 1700, snapshot_block_interval: 1_000_000, ..Default::default() @@ -369,6 +387,41 @@ pub static BASE_MAINNET: Lazy> = Lazy::new(|| { .into() }); +/// A wrapper around [BaseFeeParams] that allows for specifying constant or dynamic EIP-1559 +/// parameters based on the active [Hardfork]. +#[derive(Clone, Debug, Deserialize, Serialize)] +#[serde(untagged)] +pub enum BaseFeeParamsKind { + /// Constant [BaseFeeParams]; used for chains that don't have dynamic EIP-1559 parameters + Constant(BaseFeeParams), + /// Variable [BaseFeeParams]; used for chains that have dynamic EIP-1559 parameters like + /// Optimism + Variable(ForkBaseFeeParams), +} + +impl From for BaseFeeParamsKind { + fn from(params: BaseFeeParams) -> Self { + BaseFeeParamsKind::Constant(params) + } +} + +impl From for BaseFeeParamsKind { + fn from(params: ForkBaseFeeParams) -> Self { + BaseFeeParamsKind::Variable(params) + } +} + +/// A type alias to a vector of tuples of [Hardfork] and [BaseFeeParams], sorted by [Hardfork] +/// activation order. This is used to specify dynamic EIP-1559 parameters for chains like Optimism. +#[derive(Clone, Debug, Serialize, Deserialize)] +pub struct ForkBaseFeeParams(Vec<(Hardfork, BaseFeeParams)>); + +impl From> for ForkBaseFeeParams { + fn from(params: Vec<(Hardfork, BaseFeeParams)>) -> Self { + ForkBaseFeeParams(params) + } +} + /// BaseFeeParams contains the config parameters that control block base fee computation #[derive(Serialize, Deserialize, Debug, Copy, Clone, PartialEq, Eq)] pub struct BaseFeeParams { @@ -398,6 +451,17 @@ impl BaseFeeParams { } } + /// Get the base fee parameters for optimism goerli (post Canyon) + #[cfg(feature = "optimism")] + pub const fn optimism_goerli_canyon() -> BaseFeeParams { + BaseFeeParams { + max_change_denominator: + crate::constants::OP_GOERLI_EIP1559_BASE_FEE_MAX_CHANGE_DENOMINATOR_CANYON, + elasticity_multiplier: + crate::constants::OP_GOERLI_EIP1559_DEFAULT_ELASTICITY_MULTIPLIER, + } + } + /// Get the base fee parameters for optimism mainnet #[cfg(feature = "optimism")] pub const fn optimism() -> BaseFeeParams { @@ -408,6 +472,17 @@ impl BaseFeeParams { crate::constants::OP_MAINNET_EIP1559_DEFAULT_ELASTICITY_MULTIPLIER, } } + + /// Get the base fee parameters for optimism mainnet (post Canyon) + #[cfg(feature = "optimism")] + pub const fn optimism_canyon() -> BaseFeeParams { + BaseFeeParams { + max_change_denominator: + crate::constants::OP_MAINNET_EIP1559_BASE_FEE_MAX_CHANGE_DENOMINATOR_CANYON, + elasticity_multiplier: + crate::constants::OP_MAINNET_EIP1559_DEFAULT_ELASTICITY_MULTIPLIER, + } + } } /// An Ethereum chain specification. @@ -450,7 +525,7 @@ pub struct ChainSpec { pub deposit_contract: Option, /// The parameters that configure how a block's base fee is computed - pub base_fee_params: BaseFeeParams, + pub base_fee_params: BaseFeeParamsKind, /// The delete limit for pruner, per block. In the actual pruner run it will be multiplied by /// the amount of blocks between pruner runs to account for the difference in amount of new @@ -472,7 +547,7 @@ impl Default for ChainSpec { fork_timestamps: Default::default(), hardforks: Default::default(), deposit_contract: Default::default(), - base_fee_params: BaseFeeParams::ethereum(), + base_fee_params: BaseFeeParamsKind::Constant(BaseFeeParams::ethereum()), prune_delete_limit: MAINNET.prune_delete_limit, snapshot_block_interval: Default::default(), } @@ -559,6 +634,25 @@ impl ChainSpec { (self.fork(Hardfork::London).active_at_block(0)).then_some(genesis_base_fee) } + /// Get the [BaseFeeParams] for the chain at the given timestamp. + pub fn base_fee_params(&self, timestamp: u64) -> BaseFeeParams { + match self.base_fee_params { + BaseFeeParamsKind::Constant(bf_params) => bf_params, + BaseFeeParamsKind::Variable(ForkBaseFeeParams { 0: ref bf_params }) => { + // Walk through the base fee params configuration in reverse order, and return the + // first one that corresponds to a hardfork that is active at the + // given timestamp. + for (fork, params) in bf_params.iter().rev() { + if self.is_fork_active_at_timestamp(*fork, timestamp) { + return *params + } + } + + bf_params.first().map(|(_, params)| *params).unwrap_or(BaseFeeParams::ethereum()) + } + } + } + /// Get the hash of the genesis block. pub fn genesis_hash(&self) -> B256 { if let Some(hash) = self.genesis_hash { diff --git a/crates/primitives/src/constants/mod.rs b/crates/primitives/src/constants/mod.rs index 2e3c691d9c15..9d2b3185571a 100644 --- a/crates/primitives/src/constants/mod.rs +++ b/crates/primitives/src/constants/mod.rs @@ -67,6 +67,11 @@ pub const EIP1559_DEFAULT_ELASTICITY_MULTIPLIER: u64 = 2; #[cfg(feature = "optimism")] pub const OP_MAINNET_EIP1559_DEFAULT_BASE_FEE_MAX_CHANGE_DENOMINATOR: u64 = 50; +/// Base fee max change denominator for Optimism Mainnet as defined in the Optimism Canyon +/// hardfork. +#[cfg(feature = "optimism")] +pub const OP_MAINNET_EIP1559_BASE_FEE_MAX_CHANGE_DENOMINATOR_CANYON: u64 = 250; + /// Base fee max change denominator for Optimism Mainnet as defined in the Optimism /// [transaction costs](https://community.optimism.io/docs/developers/build/differences/#transaction-costs) doc. #[cfg(feature = "optimism")] @@ -77,6 +82,11 @@ pub const OP_MAINNET_EIP1559_DEFAULT_ELASTICITY_MULTIPLIER: u64 = 6; #[cfg(feature = "optimism")] pub const OP_GOERLI_EIP1559_DEFAULT_BASE_FEE_MAX_CHANGE_DENOMINATOR: u64 = 50; +/// Base fee max change denominator for Optimism Goerli as defined in the Optimism Canyon +/// hardfork. +#[cfg(feature = "optimism")] +pub const OP_GOERLI_EIP1559_BASE_FEE_MAX_CHANGE_DENOMINATOR_CANYON: u64 = 250; + /// Base fee max change denominator for Optimism Goerli as defined in the Optimism /// [transaction costs](https://community.optimism.io/docs/developers/build/differences/#transaction-costs) doc. #[cfg(feature = "optimism")] diff --git a/crates/primitives/src/lib.rs b/crates/primitives/src/lib.rs index 63ad9c14a062..2f40f2a197b1 100644 --- a/crates/primitives/src/lib.rs +++ b/crates/primitives/src/lib.rs @@ -55,9 +55,9 @@ pub use block::{ }; pub use bytes::{self, Buf, BufMut, BytesMut}; pub use chain::{ - AllGenesisFormats, BaseFeeParams, Chain, ChainInfo, ChainSpec, ChainSpecBuilder, - DisplayHardforks, ForkCondition, ForkTimestamps, NamedChain, DEV, GOERLI, HOLESKY, MAINNET, - SEPOLIA, + AllGenesisFormats, BaseFeeParams, BaseFeeParamsKind, Chain, ChainInfo, ChainSpec, + ChainSpecBuilder, DisplayHardforks, ForkBaseFeeParams, ForkCondition, ForkTimestamps, + NamedChain, DEV, GOERLI, HOLESKY, MAINNET, SEPOLIA, }; #[cfg(feature = "optimism")] pub use chain::{BASE_GOERLI, BASE_MAINNET, OP_GOERLI}; diff --git a/crates/rpc/rpc/src/eth/api/fees.rs b/crates/rpc/rpc/src/eth/api/fees.rs index 481a8d01a389..f8d435871226 100644 --- a/crates/rpc/rpc/src/eth/api/fees.rs +++ b/crates/rpc/rpc/src/eth/api/fees.rs @@ -130,11 +130,18 @@ where } } let last_entry = fee_entries.last().expect("is not empty"); + + let last_entry_timestamp = self + .provider() + .header_by_hash_or_number(last_entry.header_hash.into())? + .map(|h| h.timestamp) + .unwrap_or_default(); + base_fee_per_gas.push(U256::from(calculate_next_block_base_fee( last_entry.gas_used, last_entry.gas_limit, last_entry.base_fee_per_gas, - self.provider().chain_spec().base_fee_params, + self.provider().chain_spec().base_fee_params(last_entry_timestamp), ))); } else { // read the requested header range @@ -183,7 +190,7 @@ where last_header.gas_used, last_header.gas_limit, last_header.base_fee_per_gas.unwrap_or_default(), - self.provider().chain_spec().base_fee_params, + self.provider().chain_spec().base_fee_params(last_header.timestamp), ))); }; diff --git a/crates/rpc/rpc/src/eth/api/mod.rs b/crates/rpc/rpc/src/eth/api/mod.rs index 96830b9f3009..7e2cd00f8549 100644 --- a/crates/rpc/rpc/src/eth/api/mod.rs +++ b/crates/rpc/rpc/src/eth/api/mod.rs @@ -277,7 +277,8 @@ where latest.timestamp += 12; // base fee of the child block let chain_spec = self.provider().chain_spec(); - latest.base_fee_per_gas = latest.next_block_base_fee(chain_spec.base_fee_params); + latest.base_fee_per_gas = + latest.next_block_base_fee(chain_spec.base_fee_params(latest.timestamp)); PendingBlockEnvOrigin::DerivedFromLatest(latest) }; diff --git a/crates/transaction-pool/src/maintain.rs b/crates/transaction-pool/src/maintain.rs index 4f834de4de3b..5bd8a985fa22 100644 --- a/crates/transaction-pool/src/maintain.rs +++ b/crates/transaction-pool/src/maintain.rs @@ -92,7 +92,7 @@ pub async fn maintain_transaction_pool( last_seen_block_hash: latest.hash, last_seen_block_number: latest.number, pending_basefee: latest - .next_block_base_fee(chain_spec.base_fee_params) + .next_block_base_fee(chain_spec.base_fee_params(latest.timestamp + 12)) .unwrap_or_default(), pending_blob_fee: latest.next_block_blob_fee(), }; @@ -238,8 +238,9 @@ pub async fn maintain_transaction_pool( let chain_spec = client.chain_spec(); // fees for the next block: `new_tip+1` - let pending_block_base_fee = - new_tip.next_block_base_fee(chain_spec.base_fee_params).unwrap_or_default(); + let pending_block_base_fee = new_tip + .next_block_base_fee(chain_spec.base_fee_params(new_tip.timestamp + 12)) + .unwrap_or_default(); let pending_block_blob_fee = new_tip.next_block_blob_fee(); // we know all changed account in the new chain @@ -342,8 +343,9 @@ pub async fn maintain_transaction_pool( let chain_spec = client.chain_spec(); // fees for the next block: `tip+1` - let pending_block_base_fee = - tip.next_block_base_fee(chain_spec.base_fee_params).unwrap_or_default(); + let pending_block_base_fee = tip + .next_block_base_fee(chain_spec.base_fee_params(tip.timestamp + 12)) + .unwrap_or_default(); let pending_block_blob_fee = tip.next_block_blob_fee(); let first_block = blocks.first(); From 8161c261d96e0c14160387713267bd9904da3ea8 Mon Sep 17 00:00:00 2001 From: clabby Date: Mon, 27 Nov 2023 13:07:48 -0500 Subject: [PATCH 033/277] feat(op-reth): Remove `Canyon` hardfork warning (#5551) --- bin/reth/src/optimism.rs | 13 ------------- book/run/optimism.md | 3 --- 2 files changed, 16 deletions(-) diff --git a/bin/reth/src/optimism.rs b/bin/reth/src/optimism.rs index 61f6bf222081..554b91a3ae28 100644 --- a/bin/reth/src/optimism.rs +++ b/bin/reth/src/optimism.rs @@ -8,21 +8,8 @@ compile_error!("Cannot build the `op-reth` binary with the `optimism` feature fl #[cfg(feature = "optimism")] fn main() { - print_canyon_warning(); if let Err(err) = reth::cli::run() { eprintln!("Error: {err:?}"); std::process::exit(1); } } - -#[inline] -fn print_canyon_warning() { - println!("---------------------- [ WARNING! ] ----------------------"); - println!("`op-reth` does not currently support the Canyon Hardfork,"); - println!("which went live on 2023-14-11 12PM EST on Sepolia and Goerli."); - println!("The node will cease to sync at that blocktime (1699981200)."); - println!("Please consult the Canyon Hardfork tracking issue to follow"); - println!("along with the progress of the hardfork implementation:"); - println!("https://github.com/paradigmxyz/reth/issues/5210"); - println!("----------------------------------------------------------\n"); -} diff --git a/book/run/optimism.md b/book/run/optimism.md index a623f4a471f0..1e6a884af8fe 100644 --- a/book/run/optimism.md +++ b/book/run/optimism.md @@ -1,8 +1,5 @@ # Running Reth on OP Stack chains -> **Note**: `op-reth` does not currently support the Canyon Hardfork, which went live on 2023-14-11 12PM EST on Sepolia and Goerli. If the network being synced has enabled Canyon, `op-reth` will cease to sync at that blocktime (`1699981200`). -> Please consult the [Canyon Hardfork tracking issue](https://github.com/paradigmxyz/reth/issues/5210) to follow along with the progress of the hardfork implementation. - `reth` ships with the `optimism` feature flag in several crates, including the binary, enabling support for OP Stack chains out of the box. Optimism has a small diff from the [L1 EELS][l1-el-spec], comprising of the following key changes: 1. A new transaction type, [`0x7E (Deposit)`][deposit-spec], which is used to deposit funds from L1 to L2. From 3d291ea292d7682b27ebaed7b0e391e221de91e3 Mon Sep 17 00:00:00 2001 From: Roman Krasiuk Date: Mon, 27 Nov 2023 10:16:05 -0800 Subject: [PATCH 034/277] release: v0.1.0-alpha.12 (#5595) --- Cargo.lock | 94 +++++++++++++++++++++++++++--------------------------- Cargo.toml | 2 +- 2 files changed, 48 insertions(+), 48 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 9b14a7496eb9..6b6cda0b5813 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1294,7 +1294,7 @@ checksum = "67ba02a97a2bd10f4b59b25c7973101c79642302776489e030cd13cdab09ed15" [[package]] name = "codecs-derive" -version = "0.1.0-alpha.11" +version = "0.1.0-alpha.12" dependencies = [ "convert_case 0.6.0", "parity-scale-codec", @@ -2184,7 +2184,7 @@ dependencies = [ [[package]] name = "ef-tests" -version = "0.1.0-alpha.11" +version = "0.1.0-alpha.12" dependencies = [ "alloy-rlp", "reth-db", @@ -5609,7 +5609,7 @@ dependencies = [ [[package]] name = "reth" -version = "0.1.0-alpha.11" +version = "0.1.0-alpha.12" dependencies = [ "alloy-rlp", "aquamarine", @@ -5687,7 +5687,7 @@ dependencies = [ [[package]] name = "reth-auto-seal-consensus" -version = "0.1.0-alpha.11" +version = "0.1.0-alpha.12" dependencies = [ "clap", "eyre", @@ -5710,7 +5710,7 @@ dependencies = [ [[package]] name = "reth-basic-payload-builder" -version = "0.1.0-alpha.11" +version = "0.1.0-alpha.12" dependencies = [ "alloy-rlp", "futures-core", @@ -5731,7 +5731,7 @@ dependencies = [ [[package]] name = "reth-beacon-consensus" -version = "0.1.0-alpha.11" +version = "0.1.0-alpha.12" dependencies = [ "assert_matches", "cfg-if", @@ -5764,7 +5764,7 @@ dependencies = [ [[package]] name = "reth-blockchain-tree" -version = "0.1.0-alpha.11" +version = "0.1.0-alpha.12" dependencies = [ "aquamarine", "assert_matches", @@ -5784,7 +5784,7 @@ dependencies = [ [[package]] name = "reth-codecs" -version = "0.1.0-alpha.11" +version = "0.1.0-alpha.12" dependencies = [ "arbitrary", "bytes", @@ -5799,7 +5799,7 @@ dependencies = [ [[package]] name = "reth-config" -version = "0.1.0-alpha.11" +version = "0.1.0-alpha.12" dependencies = [ "confy", "reth-discv4", @@ -5816,7 +5816,7 @@ dependencies = [ [[package]] name = "reth-consensus-common" -version = "0.1.0-alpha.11" +version = "0.1.0-alpha.12" dependencies = [ "assert_matches", "cfg-if", @@ -5828,7 +5828,7 @@ dependencies = [ [[package]] name = "reth-db" -version = "0.1.0-alpha.11" +version = "0.1.0-alpha.12" dependencies = [ "arbitrary", "assert_matches", @@ -5873,7 +5873,7 @@ dependencies = [ [[package]] name = "reth-discv4" -version = "0.1.0-alpha.11" +version = "0.1.0-alpha.12" dependencies = [ "alloy-rlp", "discv5", @@ -5896,7 +5896,7 @@ dependencies = [ [[package]] name = "reth-dns-discovery" -version = "0.1.0-alpha.11" +version = "0.1.0-alpha.12" dependencies = [ "alloy-rlp", "async-trait", @@ -5920,7 +5920,7 @@ dependencies = [ [[package]] name = "reth-downloaders" -version = "0.1.0-alpha.11" +version = "0.1.0-alpha.12" dependencies = [ "alloy-rlp", "assert_matches", @@ -5947,7 +5947,7 @@ dependencies = [ [[package]] name = "reth-ecies" -version = "0.1.0-alpha.11" +version = "0.1.0-alpha.12" dependencies = [ "aes 0.8.3", "alloy-rlp", @@ -5977,7 +5977,7 @@ dependencies = [ [[package]] name = "reth-eth-wire" -version = "0.1.0-alpha.11" +version = "0.1.0-alpha.12" dependencies = [ "alloy-rlp", "arbitrary", @@ -6009,7 +6009,7 @@ dependencies = [ [[package]] name = "reth-interfaces" -version = "0.1.0-alpha.11" +version = "0.1.0-alpha.12" dependencies = [ "arbitrary", "async-trait", @@ -6036,7 +6036,7 @@ dependencies = [ [[package]] name = "reth-ipc" -version = "0.1.0-alpha.11" +version = "0.1.0-alpha.12" dependencies = [ "async-trait", "bytes", @@ -6055,7 +6055,7 @@ dependencies = [ [[package]] name = "reth-libmdbx" -version = "0.1.0-alpha.11" +version = "0.1.0-alpha.12" dependencies = [ "bitflags 2.4.1", "byteorder", @@ -6074,7 +6074,7 @@ dependencies = [ [[package]] name = "reth-mdbx-sys" -version = "0.1.0-alpha.11" +version = "0.1.0-alpha.12" dependencies = [ "bindgen 0.68.1", "cc", @@ -6083,7 +6083,7 @@ dependencies = [ [[package]] name = "reth-metrics" -version = "0.1.0-alpha.11" +version = "0.1.0-alpha.12" dependencies = [ "futures", "metrics", @@ -6094,7 +6094,7 @@ dependencies = [ [[package]] name = "reth-metrics-derive" -version = "0.1.0-alpha.11" +version = "0.1.0-alpha.12" dependencies = [ "metrics", "once_cell", @@ -6108,7 +6108,7 @@ dependencies = [ [[package]] name = "reth-net-common" -version = "0.1.0-alpha.11" +version = "0.1.0-alpha.12" dependencies = [ "pin-project", "reth-primitives", @@ -6117,7 +6117,7 @@ dependencies = [ [[package]] name = "reth-net-nat" -version = "0.1.0-alpha.11" +version = "0.1.0-alpha.12" dependencies = [ "igd", "pin-project-lite", @@ -6131,7 +6131,7 @@ dependencies = [ [[package]] name = "reth-network" -version = "0.1.0-alpha.11" +version = "0.1.0-alpha.12" dependencies = [ "alloy-rlp", "aquamarine", @@ -6181,7 +6181,7 @@ dependencies = [ [[package]] name = "reth-network-api" -version = "0.1.0-alpha.11" +version = "0.1.0-alpha.12" dependencies = [ "async-trait", "reth-discv4", @@ -6195,7 +6195,7 @@ dependencies = [ [[package]] name = "reth-nippy-jar" -version = "0.1.0-alpha.11" +version = "0.1.0-alpha.12" dependencies = [ "anyhow", "bincode", @@ -6215,7 +6215,7 @@ dependencies = [ [[package]] name = "reth-payload-builder" -version = "0.1.0-alpha.11" +version = "0.1.0-alpha.12" dependencies = [ "alloy-rlp", "futures-util", @@ -6237,7 +6237,7 @@ dependencies = [ [[package]] name = "reth-primitives" -version = "0.1.0-alpha.11" +version = "0.1.0-alpha.12" dependencies = [ "alloy-primitives", "alloy-rlp", @@ -6285,7 +6285,7 @@ dependencies = [ [[package]] name = "reth-provider" -version = "0.1.0-alpha.11" +version = "0.1.0-alpha.12" dependencies = [ "alloy-rlp", "assert_matches", @@ -6312,7 +6312,7 @@ dependencies = [ [[package]] name = "reth-prune" -version = "0.1.0-alpha.11" +version = "0.1.0-alpha.12" dependencies = [ "assert_matches", "itertools 0.11.0", @@ -6334,7 +6334,7 @@ dependencies = [ [[package]] name = "reth-revm" -version = "0.1.0-alpha.11" +version = "0.1.0-alpha.12" dependencies = [ "reth-consensus-common", "reth-interfaces", @@ -6348,7 +6348,7 @@ dependencies = [ [[package]] name = "reth-revm-inspectors" -version = "0.1.0-alpha.11" +version = "0.1.0-alpha.12" dependencies = [ "alloy-primitives", "alloy-sol-types", @@ -6364,7 +6364,7 @@ dependencies = [ [[package]] name = "reth-rpc" -version = "0.1.0-alpha.11" +version = "0.1.0-alpha.12" dependencies = [ "alloy-dyn-abi", "alloy-primitives", @@ -6417,7 +6417,7 @@ dependencies = [ [[package]] name = "reth-rpc-api" -version = "0.1.0-alpha.11" +version = "0.1.0-alpha.12" dependencies = [ "jsonrpsee", "reth-primitives", @@ -6427,7 +6427,7 @@ dependencies = [ [[package]] name = "reth-rpc-api-testing-util" -version = "0.1.0-alpha.11" +version = "0.1.0-alpha.12" dependencies = [ "async-trait", "futures", @@ -6442,7 +6442,7 @@ dependencies = [ [[package]] name = "reth-rpc-builder" -version = "0.1.0-alpha.11" +version = "0.1.0-alpha.12" dependencies = [ "hyper", "jsonrpsee", @@ -6475,7 +6475,7 @@ dependencies = [ [[package]] name = "reth-rpc-engine-api" -version = "0.1.0-alpha.11" +version = "0.1.0-alpha.12" dependencies = [ "alloy-rlp", "assert_matches", @@ -6501,7 +6501,7 @@ dependencies = [ [[package]] name = "reth-rpc-types" -version = "0.1.0-alpha.11" +version = "0.1.0-alpha.12" dependencies = [ "alloy-primitives", "alloy-rlp", @@ -6525,7 +6525,7 @@ dependencies = [ [[package]] name = "reth-rpc-types-compat" -version = "0.1.0-alpha.11" +version = "0.1.0-alpha.12" dependencies = [ "alloy-rlp", "reth-primitives", @@ -6534,7 +6534,7 @@ dependencies = [ [[package]] name = "reth-snapshot" -version = "0.1.0-alpha.11" +version = "0.1.0-alpha.12" dependencies = [ "assert_matches", "clap", @@ -6552,7 +6552,7 @@ dependencies = [ [[package]] name = "reth-stages" -version = "0.1.0-alpha.11" +version = "0.1.0-alpha.12" dependencies = [ "alloy-rlp", "aquamarine", @@ -6592,7 +6592,7 @@ dependencies = [ [[package]] name = "reth-tasks" -version = "0.1.0-alpha.11" +version = "0.1.0-alpha.12" dependencies = [ "dyn-clone", "futures-util", @@ -6606,7 +6606,7 @@ dependencies = [ [[package]] name = "reth-tokio-util" -version = "0.1.0-alpha.11" +version = "0.1.0-alpha.12" dependencies = [ "tokio", "tokio-stream", @@ -6614,7 +6614,7 @@ dependencies = [ [[package]] name = "reth-tracing" -version = "0.1.0-alpha.11" +version = "0.1.0-alpha.12" dependencies = [ "rolling-file", "tracing", @@ -6625,7 +6625,7 @@ dependencies = [ [[package]] name = "reth-transaction-pool" -version = "0.1.0-alpha.11" +version = "0.1.0-alpha.12" dependencies = [ "alloy-rlp", "aquamarine", @@ -6659,7 +6659,7 @@ dependencies = [ [[package]] name = "reth-trie" -version = "0.1.0-alpha.11" +version = "0.1.0-alpha.12" dependencies = [ "alloy-rlp", "auto_impl", diff --git a/Cargo.toml b/Cargo.toml index d5a275a3fe69..6adee94d89d4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -62,7 +62,7 @@ default-members = ["bin/reth"] resolver = "2" [workspace.package] -version = "0.1.0-alpha.11" +version = "0.1.0-alpha.12" edition = "2021" rust-version = "1.70" license = "MIT OR Apache-2.0" From 025511baa654bf664c3d3e684364707bc6db5ed2 Mon Sep 17 00:00:00 2001 From: Emilia Hane Date: Mon, 27 Nov 2023 20:01:00 +0100 Subject: [PATCH 035/277] Fix sink implementation (#5594) --- crates/net/eth-wire/src/p2pstream.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/net/eth-wire/src/p2pstream.rs b/crates/net/eth-wire/src/p2pstream.rs index 8d407c4872e2..1d3f66064350 100644 --- a/crates/net/eth-wire/src/p2pstream.rs +++ b/crates/net/eth-wire/src/p2pstream.rs @@ -578,8 +578,7 @@ where Ok(()) } - /// Returns Poll::Ready(Ok(())) when no buffered items remain and the sink has been successfully - /// closed. + /// Returns `Poll::Ready(Ok(()))` when no buffered items remain. fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { let mut this = self.project(); loop { @@ -599,6 +598,7 @@ where fn poll_close(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { ready!(self.as_mut().poll_flush(cx))?; + ready!(self.project().inner.poll_close(cx))?; Poll::Ready(Ok(())) } From b58bfe6e37f8e8e017c6c5d43c5a9fca9af7e819 Mon Sep 17 00:00:00 2001 From: Matthias Seitz Date: Mon, 27 Nov 2023 21:20:30 +0100 Subject: [PATCH 036/277] chore: add more pool maintain metrics (#5590) --- crates/transaction-pool/src/maintain.rs | 6 +++++- crates/transaction-pool/src/metrics.rs | 14 ++++++++++++++ 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/crates/transaction-pool/src/maintain.rs b/crates/transaction-pool/src/maintain.rs index 5bd8a985fa22..e4b36996302b 100644 --- a/crates/transaction-pool/src/maintain.rs +++ b/crates/transaction-pool/src/maintain.rs @@ -127,8 +127,10 @@ pub async fn maintain_transaction_pool( // dirty accounts and correct if the pool drifted from current state, for example after // restart or a pipeline run if maintained_state.is_drifted() { + metrics.inc_drift(); // assuming all senders are dirty dirty_addresses = pool.unique_senders(); + // make sure we toggle the state back to in sync maintained_state = MaintainedPoolState::InSync; } @@ -170,6 +172,7 @@ pub async fn maintain_transaction_pool( match blob_store_tracker.on_finalized_block(finalized) { BlobStoreUpdates::None => {} BlobStoreUpdates::Finalized(blobs) => { + metrics.inc_deleted_tracked_blobs(blobs.len()); // remove all finalized blobs from the blob store pool.delete_blobs(blobs); } @@ -442,7 +445,7 @@ impl FinalizedBlockTracker { /// Keeps track of the pool's state, whether the accounts in the pool are in sync with the actual /// state. -#[derive(Debug, Eq, PartialEq)] +#[derive(Debug, PartialEq, Eq)] enum MaintainedPoolState { /// Pool is assumed to be in sync with the current state InSync, @@ -452,6 +455,7 @@ enum MaintainedPoolState { impl MaintainedPoolState { /// Returns `true` if the pool is assumed to be out of sync with the current state. + #[inline] fn is_drifted(&self) -> bool { matches!(self, MaintainedPoolState::Drifted) } diff --git a/crates/transaction-pool/src/metrics.rs b/crates/transaction-pool/src/metrics.rs index 966834c63b35..b5d339c5aa15 100644 --- a/crates/transaction-pool/src/metrics.rs +++ b/crates/transaction-pool/src/metrics.rs @@ -59,8 +59,12 @@ pub struct MaintainPoolMetrics { /// Number of currently dirty addresses that need to be updated in the pool by fetching account /// info pub(crate) dirty_accounts: Gauge, + /// How often the pool drifted from the canonical state. + pub(crate) drift_count: Counter, /// Number of transaction reinserted into the pool after reorg. pub(crate) reinserted_transactions: Counter, + /// Number of transactions finalized blob transactions we were tracking. + pub(crate) deleted_tracked_finalized_blobs: Counter, } impl MaintainPoolMetrics { @@ -73,4 +77,14 @@ impl MaintainPoolMetrics { pub(crate) fn inc_reinserted_transactions(&self, count: usize) { self.reinserted_transactions.increment(count as u64); } + + #[inline] + pub(crate) fn inc_deleted_tracked_blobs(&self, count: usize) { + self.deleted_tracked_finalized_blobs.increment(count as u64); + } + + #[inline] + pub(crate) fn inc_drift(&self) { + self.drift_count.increment(1); + } } From 503c4010d3c8b5df36a24e75cd4087eb4cf16640 Mon Sep 17 00:00:00 2001 From: Matthias Seitz Date: Tue, 28 Nov 2023 00:12:36 +0100 Subject: [PATCH 037/277] chore: lower max reload accounts (#5589) --- crates/transaction-pool/src/maintain.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/transaction-pool/src/maintain.rs b/crates/transaction-pool/src/maintain.rs index e4b36996302b..a4c924bef1e7 100644 --- a/crates/transaction-pool/src/maintain.rs +++ b/crates/transaction-pool/src/maintain.rs @@ -37,13 +37,13 @@ pub struct MaintainPoolConfig { pub max_update_depth: u64, /// Maximum number of accounts to reload from state at once when updating the transaction pool. /// - /// Default: 250 + /// Default: 100 pub max_reload_accounts: usize, } impl Default for MaintainPoolConfig { fn default() -> Self { - Self { max_update_depth: 64, max_reload_accounts: 250 } + Self { max_update_depth: 64, max_reload_accounts: 100 } } } From 98250f8b502a82c3c7c15bf9acf39e9bfbf6f544 Mon Sep 17 00:00:00 2001 From: Matthias Seitz Date: Tue, 28 Nov 2023 04:31:16 +0100 Subject: [PATCH 038/277] chore: bump stacksize even more (#5602) --- testing/ef-tests/tests/tests.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/testing/ef-tests/tests/tests.rs b/testing/ef-tests/tests/tests.rs index 75e6e2a8cd27..e1d18116bccd 100644 --- a/testing/ef-tests/tests/tests.rs +++ b/testing/ef-tests/tests/tests.rs @@ -10,7 +10,7 @@ macro_rules! general_state_test { // std::thread::Builder::new() .stack_size( - 1024 * 1024 * 4, // 4MB + 1024 * 1024 * 8, // 8MB ) .spawn(move || { BlockchainTests::new(format!("GeneralStateTests/{}", stringify!($dir))).run(); From 608f1006053b84f5fc2c5a1f31e0f8a321fecd89 Mon Sep 17 00:00:00 2001 From: Roman Krasiuk Date: Tue, 28 Nov 2023 00:54:09 -0800 Subject: [PATCH 039/277] perf(provider): compute hashes and trie updates before opening write tx (#5505) --- bin/reth/src/debug_cmd/build_block.rs | 17 ++- crates/blockchain-tree/src/blockchain_tree.rs | 38 ++++-- .../bundle_state_with_receipts.rs | 8 +- .../src/bundle_state/hashed_state_changes.rs | 128 ++++++++++++++++++ .../storage/provider/src/bundle_state/mod.rs | 2 + .../src/providers/database/provider.rs | 18 ++- crates/storage/provider/src/traits/block.rs | 5 +- crates/trie/src/hashed_cursor/post_state.rs | 50 +++++-- 8 files changed, 236 insertions(+), 30 deletions(-) create mode 100644 crates/storage/provider/src/bundle_state/hashed_state_changes.rs diff --git a/bin/reth/src/debug_cmd/build_block.rs b/bin/reth/src/debug_cmd/build_block.rs index e1d57e4b7d93..4677bd551f15 100644 --- a/bin/reth/src/debug_cmd/build_block.rs +++ b/bin/reth/src/debug_cmd/build_block.rs @@ -277,11 +277,26 @@ impl Command { let state = executor.take_output_state(); debug!(target: "reth::cli", ?state, "Executed block"); + let hashed_state = state.hash_state_slow(); + let (state_root, trie_updates) = state + .state_root_calculator(provider_factory.provider()?.tx_ref(), &hashed_state) + .root_with_updates()?; + + if state_root != block_with_senders.state_root { + eyre::bail!( + "state root mismatch. expected: {}. got: {}", + block_with_senders.state_root, + state_root + ); + } + // Attempt to insert new block without committing let provider_rw = provider_factory.provider_rw()?; - provider_rw.append_blocks_with_bundle_state( + provider_rw.append_blocks_with_state( Vec::from([block_with_senders]), state, + hashed_state, + trie_updates, None, )?; info!(target: "reth::cli", "Successfully appended built block"); diff --git a/crates/blockchain-tree/src/blockchain_tree.rs b/crates/blockchain-tree/src/blockchain_tree.rs index 8d399dda2599..b510dc9008da 100644 --- a/crates/blockchain-tree/src/blockchain_tree.rs +++ b/crates/blockchain-tree/src/blockchain_tree.rs @@ -6,7 +6,7 @@ use crate::{ state::{BlockChainId, TreeState}, AppendableChain, BlockIndices, BlockchainTreeConfig, BundleStateData, TreeExternals, }; -use reth_db::database::Database; +use reth_db::{database::Database, DatabaseError}; use reth_interfaces::{ blockchain_tree::{ error::{BlockchainTreeError, CanonicalError, InsertBlockError, InsertBlockErrorKind}, @@ -14,17 +14,18 @@ use reth_interfaces::{ }, consensus::{Consensus, ConsensusError}, executor::{BlockExecutionError, BlockValidationError}, - RethResult, + provider::RootMismatch, + RethError, RethResult, }; use reth_primitives::{ - BlockHash, BlockNumHash, BlockNumber, ForkBlock, Hardfork, PruneModes, Receipt, SealedBlock, - SealedBlockWithSenders, SealedHeader, U256, + BlockHash, BlockNumHash, BlockNumber, ForkBlock, GotExpected, Hardfork, PruneModes, Receipt, + SealedBlock, SealedBlockWithSenders, SealedHeader, U256, }; use reth_provider::{ chain::{ChainSplit, ChainSplitTarget}, BlockExecutionWriter, BlockNumReader, BlockWriter, BundleStateWithReceipts, CanonStateNotification, CanonStateNotificationSender, CanonStateNotifications, Chain, - ChainSpecProvider, DisplayBlocksChain, ExecutorFactory, HeaderProvider, + ChainSpecProvider, DisplayBlocksChain, ExecutorFactory, HeaderProvider, ProviderError, }; use reth_stages::{MetricEvent, MetricEventsSender}; use std::{collections::BTreeMap, sync::Arc}; @@ -1104,14 +1105,35 @@ impl BlockchainTree { /// Write the given chain to the database as canonical. fn commit_canonical_to_database(&self, chain: Chain) -> RethResult<()> { - let provider_rw = self.externals.provider_factory.provider_rw()?; + // Compute state root before opening write transaction. + let hashed_state = chain.state().hash_state_slow(); + let (state_root, trie_updates) = chain + .state() + .state_root_calculator( + self.externals.provider_factory.provider()?.tx_ref(), + &hashed_state, + ) + .root_with_updates() + .map_err(Into::::into)?; + let tip = chain.tip(); + if state_root != tip.state_root { + return Err(RethError::Provider(ProviderError::StateRootMismatch(Box::new( + RootMismatch { + root: GotExpected { got: state_root, expected: tip.state_root }, + block_number: tip.number, + block_hash: tip.hash, + }, + )))) + } let (blocks, state) = chain.into_inner(); - + let provider_rw = self.externals.provider_factory.provider_rw()?; provider_rw - .append_blocks_with_bundle_state( + .append_blocks_with_state( blocks.into_blocks().collect(), state, + hashed_state, + trie_updates, self.prune_modes.as_ref(), ) .map_err(|e| BlockExecutionError::CanonicalCommit { inner: e.to_string() })?; diff --git a/crates/storage/provider/src/bundle_state/bundle_state_with_receipts.rs b/crates/storage/provider/src/bundle_state/bundle_state_with_receipts.rs index 6077a2f6560f..683da8160fdc 100644 --- a/crates/storage/provider/src/bundle_state/bundle_state_with_receipts.rs +++ b/crates/storage/provider/src/bundle_state/bundle_state_with_receipts.rs @@ -128,7 +128,6 @@ impl BundleStateWithReceipts { /// /// The hashed post state. pub fn hash_state_slow(&self) -> HashedPostState { - //let mut storages = BTreeMap::default(); let mut hashed_state = HashedPostState::default(); for (address, account) in self.bundle.state() { @@ -136,7 +135,7 @@ impl BundleStateWithReceipts { if let Some(account) = &account.info { hashed_state.insert_account(hashed_address, into_reth_acc(account.clone())) } else { - hashed_state.insert_cleared_account(hashed_address); + hashed_state.insert_destroyed_account(hashed_address); } // insert storage. @@ -155,8 +154,8 @@ impl BundleStateWithReceipts { hashed_state.sorted() } - /// Returns [StateRoot] calculator. - fn state_root_calculator<'a, 'b, TX: DbTx>( + /// Returns [StateRoot] calculator based on database and in-memory state. + pub fn state_root_calculator<'a, 'b, TX: DbTx>( &self, tx: &'a TX, hashed_post_state: &'b HashedPostState, @@ -167,6 +166,7 @@ impl BundleStateWithReceipts { .with_hashed_cursor_factory(hashed_cursor_factory) .with_changed_account_prefixes(account_prefix_set) .with_changed_storage_prefixes(storage_prefix_set) + .with_destroyed_accounts(hashed_post_state.destroyed_accounts()) } /// Calculate the state root for this [BundleState]. diff --git a/crates/storage/provider/src/bundle_state/hashed_state_changes.rs b/crates/storage/provider/src/bundle_state/hashed_state_changes.rs new file mode 100644 index 000000000000..cfdacb973674 --- /dev/null +++ b/crates/storage/provider/src/bundle_state/hashed_state_changes.rs @@ -0,0 +1,128 @@ +use reth_db::{ + cursor::{DbCursorRO, DbCursorRW, DbDupCursorRO, DbDupCursorRW}, + tables, + transaction::{DbTx, DbTxMut}, + DatabaseError, +}; +use reth_primitives::{Account, StorageEntry, B256, U256}; +use reth_trie::hashed_cursor::HashedPostState; +use std::collections::BTreeMap; + +/// Changes to the hashed state. +#[derive(Debug, Default)] +pub struct HashedStateChanges(pub HashedPostState); + +impl HashedStateChanges { + /// Write the bundle state to the database. + pub fn write_to_db(self, tx: &TX) -> Result<(), DatabaseError> { + // Collect hashed account changes. + let mut hashed_accounts = BTreeMap::>::default(); + for (hashed_address, account) in self.0.accounts() { + hashed_accounts.insert(hashed_address, account); + } + + // Write hashed account updates. + let mut hashed_accounts_cursor = tx.cursor_write::()?; + for (hashed_address, account) in hashed_accounts { + if let Some(account) = account { + hashed_accounts_cursor.upsert(hashed_address, account)?; + } else if hashed_accounts_cursor.seek_exact(hashed_address)?.is_some() { + hashed_accounts_cursor.delete_current()?; + } + } + + // Collect hashed storage changes. + let mut hashed_storages = BTreeMap::)>::default(); + for (hashed_address, storage) in self.0.storages() { + let entry = hashed_storages.entry(*hashed_address).or_default(); + entry.0 |= storage.wiped(); + for (hashed_slot, value) in storage.storage_slots() { + entry.1.insert(hashed_slot, value); + } + } + + // Write hashed storage changes. + let mut hashed_storage_cursor = tx.cursor_dup_write::()?; + for (hashed_address, (wiped, storage)) in hashed_storages { + if wiped && hashed_storage_cursor.seek_exact(hashed_address)?.is_some() { + hashed_storage_cursor.delete_current_duplicates()?; + } + + for (hashed_slot, value) in storage { + let entry = StorageEntry { key: hashed_slot, value }; + if let Some(db_entry) = + hashed_storage_cursor.seek_by_key_subkey(hashed_address, entry.key)? + { + if db_entry.key == entry.key { + hashed_storage_cursor.delete_current()?; + } + } + + if entry.value != U256::ZERO { + hashed_storage_cursor.upsert(hashed_address, entry)?; + } + } + } + + Ok(()) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::test_utils::create_test_provider_factory; + use reth_primitives::{keccak256, Address}; + use reth_trie::hashed_cursor::HashedStorage; + + #[test] + fn wiped_entries_are_removed() { + let provider_factory = create_test_provider_factory(); + + let addresses = (0..10).map(|_| Address::random()).collect::>(); + let destroyed_address = *addresses.first().unwrap(); + let destroyed_address_hashed = keccak256(destroyed_address); + let slot = B256::with_last_byte(1); + let hashed_slot = keccak256(slot); + { + let provider_rw = provider_factory.provider_rw().unwrap(); + let mut accounts_cursor = + provider_rw.tx_ref().cursor_write::().unwrap(); + let mut storage_cursor = + provider_rw.tx_ref().cursor_write::().unwrap(); + + for address in addresses { + let hashed_address = keccak256(address); + accounts_cursor + .insert(hashed_address, Account { nonce: 1, ..Default::default() }) + .unwrap(); + storage_cursor + .insert(hashed_address, StorageEntry { key: hashed_slot, value: U256::from(1) }) + .unwrap(); + } + provider_rw.commit().unwrap(); + } + + let mut hashed_state = HashedPostState::default(); + hashed_state.insert_destroyed_account(destroyed_address_hashed); + hashed_state.insert_hashed_storage(destroyed_address_hashed, HashedStorage::new(true)); + + let provider_rw = provider_factory.provider_rw().unwrap(); + assert_eq!(HashedStateChanges(hashed_state).write_to_db(provider_rw.tx_ref()), Ok(())); + provider_rw.commit().unwrap(); + + let provider = provider_factory.provider().unwrap(); + assert_eq!( + provider.tx_ref().get::(destroyed_address_hashed), + Ok(None) + ); + assert_eq!( + provider + .tx_ref() + .cursor_read::() + .unwrap() + .seek_by_key_subkey(destroyed_address_hashed, hashed_slot), + Ok(None) + ); + } +} diff --git a/crates/storage/provider/src/bundle_state/mod.rs b/crates/storage/provider/src/bundle_state/mod.rs index 88b17ad563f0..3f5da6ec6242 100644 --- a/crates/storage/provider/src/bundle_state/mod.rs +++ b/crates/storage/provider/src/bundle_state/mod.rs @@ -1,11 +1,13 @@ //! Bundle state module. //! This module contains all the logic related to bundle state. mod bundle_state_with_receipts; +mod hashed_state_changes; mod state_changes; mod state_reverts; pub use bundle_state_with_receipts::{ AccountRevertInit, BundleStateInit, BundleStateWithReceipts, OriginalValuesKnown, RevertsInit, }; +pub use hashed_state_changes::HashedStateChanges; pub use state_changes::StateChanges; pub use state_reverts::StateReverts; diff --git a/crates/storage/provider/src/providers/database/provider.rs b/crates/storage/provider/src/providers/database/provider.rs index 585f73e16be7..da31f50d8319 100644 --- a/crates/storage/provider/src/providers/database/provider.rs +++ b/crates/storage/provider/src/providers/database/provider.rs @@ -1,5 +1,5 @@ use crate::{ - bundle_state::{BundleStateInit, BundleStateWithReceipts, RevertsInit}, + bundle_state::{BundleStateInit, BundleStateWithReceipts, HashedStateChanges, RevertsInit}, providers::{database::metrics, SnapshotProvider}, traits::{ AccountExtReader, BlockSource, ChangeSetReader, ReceiptProvider, StageCheckpointWriter, @@ -43,7 +43,9 @@ use reth_primitives::{ TransactionMeta, TransactionSigned, TransactionSignedEcRecovered, TransactionSignedNoHash, TxHash, TxNumber, Withdrawal, B256, U256, }; -use reth_trie::{prefix_set::PrefixSetMut, StateRoot}; +use reth_trie::{ + hashed_cursor::HashedPostState, prefix_set::PrefixSetMut, updates::TrieUpdates, StateRoot, +}; use revm::primitives::{BlockEnv, CfgEnv, SpecId}; use std::{ collections::{hash_map, BTreeMap, BTreeSet, HashMap, HashSet}, @@ -2287,10 +2289,12 @@ impl BlockWriter for DatabaseProvider { Ok(block_indices) } - fn append_blocks_with_bundle_state( + fn append_blocks_with_state( &self, blocks: Vec, state: BundleStateWithReceipts, + hashed_state: HashedPostState, + trie_updates: TrieUpdates, prune_modes: Option<&PruneModes>, ) -> ProviderResult<()> { if blocks.is_empty() { @@ -2303,8 +2307,6 @@ impl BlockWriter for DatabaseProvider { let last = blocks.last().unwrap(); let last_block_number = last.number; - let last_block_hash = last.hash(); - let expected_state_root = last.state_root; let mut durations_recorder = metrics::DurationsRecorder::default(); @@ -2320,7 +2322,11 @@ impl BlockWriter for DatabaseProvider { state.write_to_db(self.tx_ref(), OriginalValuesKnown::No)?; durations_recorder.record_relative(metrics::Action::InsertState); - self.insert_hashes(first_number..=last_block_number, last_block_hash, expected_state_root)?; + // insert hashes and intermediate merkle nodes + { + HashedStateChanges(hashed_state).write_to_db(&self.tx)?; + trie_updates.flush(&self.tx)?; + } durations_recorder.record_relative(metrics::Action::InsertHashes); self.update_history_indices(first_number..=last_block_number)?; diff --git a/crates/storage/provider/src/traits/block.rs b/crates/storage/provider/src/traits/block.rs index 44951a3fcac8..137d14fbbff7 100644 --- a/crates/storage/provider/src/traits/block.rs +++ b/crates/storage/provider/src/traits/block.rs @@ -10,6 +10,7 @@ use reth_primitives::{ ChainSpec, Header, PruneModes, Receipt, SealedBlock, SealedBlockWithSenders, SealedHeader, B256, }; +use reth_trie::{hashed_cursor::HashedPostState, updates::TrieUpdates}; use std::ops::RangeInclusive; /// Enum to control transaction hash inclusion. @@ -291,10 +292,12 @@ pub trait BlockWriter: Send + Sync { /// # Returns /// /// Returns `Ok(())` on success, or an error if any operation fails. - fn append_blocks_with_bundle_state( + fn append_blocks_with_state( &self, blocks: Vec, state: BundleStateWithReceipts, + hashed_state: HashedPostState, + trie_updates: TrieUpdates, prune_modes: Option<&PruneModes>, ) -> ProviderResult<()>; } diff --git a/crates/trie/src/hashed_cursor/post_state.rs b/crates/trie/src/hashed_cursor/post_state.rs index 62e028657fc0..de276f819967 100644 --- a/crates/trie/src/hashed_cursor/post_state.rs +++ b/crates/trie/src/hashed_cursor/post_state.rs @@ -32,6 +32,19 @@ impl HashedStorage { } } + /// Returns `true` if the storage was wiped. + pub fn wiped(&self) -> bool { + self.wiped + } + + /// Returns all storage slots. + pub fn storage_slots(&self) -> impl Iterator + '_ { + self.zero_valued_slots + .iter() + .map(|slot| (*slot, U256::ZERO)) + .chain(self.non_zero_valued_storage.iter().cloned()) + } + /// Sorts the non zero value storage entries. pub fn sort_storage(&mut self) { if !self.sorted { @@ -58,8 +71,8 @@ impl HashedStorage { pub struct HashedPostState { /// Map of hashed addresses to account info. accounts: Vec<(B256, Account)>, - /// Set of cleared accounts. - cleared_accounts: HashSet, + /// Set of destroyed accounts. + destroyed_accounts: HashSet, /// Map of hashed addresses to hashed storage. storages: HashMap, /// Whether the account and storage entries were sorted or not. @@ -70,7 +83,7 @@ impl Default for HashedPostState { fn default() -> Self { Self { accounts: Vec::new(), - cleared_accounts: HashSet::new(), + destroyed_accounts: HashSet::new(), storages: HashMap::new(), sorted: true, // empty is sorted } @@ -84,6 +97,18 @@ impl HashedPostState { self } + /// Returns all accounts with their state. + pub fn accounts(&self) -> impl Iterator)> + '_ { + self.destroyed_accounts.iter().map(|hashed_address| (*hashed_address, None)).chain( + self.accounts.iter().map(|(hashed_address, account)| (*hashed_address, Some(*account))), + ) + } + + /// Returns all account storages. + pub fn storages(&self) -> impl Iterator { + self.storages.iter() + } + /// Sort account and storage entries. pub fn sort(&mut self) { if !self.sorted { @@ -102,9 +127,9 @@ impl HashedPostState { self.sorted = false; } - /// Insert cleared hashed account key. - pub fn insert_cleared_account(&mut self, hashed_address: B256) { - self.cleared_accounts.insert(hashed_address); + /// Insert destroyed hashed account key. + pub fn insert_destroyed_account(&mut self, hashed_address: B256) { + self.destroyed_accounts.insert(hashed_address); } /// Insert hashed storage entry. @@ -113,6 +138,11 @@ impl HashedPostState { self.storages.insert(hashed_address, hashed_storage); } + /// Returns all destroyed accounts. + pub fn destroyed_accounts(&self) -> HashSet { + self.destroyed_accounts.clone() + } + /// Construct (PrefixSet)[PrefixSet] from hashed post state. /// The prefix sets contain the hashed account and storage keys that have been changed in the /// post state. @@ -125,7 +155,7 @@ impl HashedPostState { for (hashed_address, _) in &self.accounts { account_prefix_set.insert(Nibbles::unpack(hashed_address)); } - for hashed_address in &self.cleared_accounts { + for hashed_address in &self.destroyed_accounts { account_prefix_set.insert(Nibbles::unpack(hashed_address)); } @@ -213,7 +243,7 @@ impl<'b, C> HashedPostStateAccountCursor<'b, C> { /// This function only checks the post state, not the database, because the latter does not /// store destroyed accounts. fn is_account_cleared(&self, account: &B256) -> bool { - self.post_state.cleared_accounts.contains(account) + self.post_state.destroyed_accounts.contains(account) } /// Return the account with the lowest hashed account key. @@ -667,7 +697,7 @@ mod tests { let mut hashed_post_state = HashedPostState::default(); for (hashed_address, account) in accounts.iter().filter(|x| x.0[31] % 2 != 0) { if removed_keys.contains(hashed_address) { - hashed_post_state.insert_cleared_account(*hashed_address); + hashed_post_state.insert_destroyed_account(*hashed_address); } else { hashed_post_state.insert_account(*hashed_address, *account); } @@ -722,7 +752,7 @@ mod tests { if let Some(account) = account { hashed_post_state.insert_account(*hashed_address, *account); } else { - hashed_post_state.insert_cleared_account(*hashed_address); + hashed_post_state.insert_destroyed_account(*hashed_address); } } hashed_post_state.sort(); From b6ce3bc99999b10cd93d5226f7e5640b914de3e3 Mon Sep 17 00:00:00 2001 From: Matthias Seitz Date: Tue, 28 Nov 2023 10:46:47 +0100 Subject: [PATCH 040/277] docs: misc mdbx docs (#5599) Co-authored-by: Roman Krasiuk --- crates/storage/libmdbx-rs/src/flags.rs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/crates/storage/libmdbx-rs/src/flags.rs b/crates/storage/libmdbx-rs/src/flags.rs index e464a3b20a05..ad88c1fbedc9 100644 --- a/crates/storage/libmdbx-rs/src/flags.rs +++ b/crates/storage/libmdbx-rs/src/flags.rs @@ -128,6 +128,10 @@ impl From for EnvironmentFlags { pub struct EnvironmentFlags { pub no_sub_dir: bool, pub exclusive: bool, + /// Flag is intended to open an existing sub-database which was created with unknown flags + /// In such cases, instead of returning the `MDBX_INCOMPATIBLE` error, the sub-database will be + /// opened with flags which it was created, and then an application could determine the actual + /// flags. pub accede: bool, pub mode: Mode, pub no_rdahead: bool, @@ -137,6 +141,7 @@ pub struct EnvironmentFlags { } impl EnvironmentFlags { + /// Configures the mdbx flags to use when opening the environment. pub(crate) fn make_flags(&self) -> ffi::MDBX_env_flags_t { let mut flags = 0; From f3bc7d1c97f7186f009923e18dbad292e9cf6acb Mon Sep 17 00:00:00 2001 From: Rafael Ugolini Date: Tue, 28 Nov 2023 11:07:29 +0100 Subject: [PATCH 041/277] fix log argument (#5608) --- etc/docker-compose.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/etc/docker-compose.yml b/etc/docker-compose.yml index 84a97c1f80e6..c5f8e1334d86 100644 --- a/etc/docker-compose.yml +++ b/etc/docker-compose.yml @@ -20,7 +20,7 @@ services: node --chain mainnet --metrics 0.0.0.0:9001 - --log.directory /root/rethlogs + --log.file.directory /root/rethlogs --authrpc.addr 0.0.0.0 --authrpc.port 8551 --authrpc.jwtsecret /root/jwt/jwt.hex From 43d1f5a4996215c591fc6326d4ecd5312800252b Mon Sep 17 00:00:00 2001 From: Matthias Seitz Date: Tue, 28 Nov 2023 11:56:35 +0100 Subject: [PATCH 042/277] perf: remove redundant collect (#5600) --- .../src/providers/database/provider.rs | 26 ++++++++++++------- 1 file changed, 16 insertions(+), 10 deletions(-) diff --git a/crates/storage/provider/src/providers/database/provider.rs b/crates/storage/provider/src/providers/database/provider.rs index da31f50d8319..b6e436a97248 100644 --- a/crates/storage/provider/src/providers/database/provider.rs +++ b/crates/storage/provider/src/providers/database/provider.rs @@ -2201,21 +2201,27 @@ impl BlockWriter for DatabaseProvider { let tx_count = block.body.len() as u64; - let senders_len = senders.as_ref().map(|s| s.len()); - let tx_iter = if Some(block.body.len()) == senders_len { - block.body.into_iter().zip(senders.unwrap()).collect::>() - } else { - let senders = TransactionSigned::recover_signers(&block.body, block.body.len()) - .ok_or(ProviderError::SenderRecoveryError)?; - durations_recorder.record_relative(metrics::Action::RecoverSigners); - debug_assert_eq!(senders.len(), block.body.len(), "missing one or more senders"); - block.body.into_iter().zip(senders).collect() + // Ensures we have all the senders for the block's transactions. + let senders = match senders { + Some(senders) if block.body.len() == senders.len() => { + // senders have the correct length as transactions in the block + senders + } + _ => { + // recover senders from transactions + let senders = TransactionSigned::recover_signers(&block.body, block.body.len()) + .ok_or(ProviderError::SenderRecoveryError)?; + durations_recorder.record_relative(metrics::Action::RecoverSigners); + debug_assert_eq!(senders.len(), block.body.len(), "missing one or more senders"); + senders + } }; let mut tx_senders_elapsed = Duration::default(); let mut transactions_elapsed = Duration::default(); let mut tx_hash_numbers_elapsed = Duration::default(); - for (transaction, sender) in tx_iter { + + for (transaction, sender) in block.body.into_iter().zip(senders) { let hash = transaction.hash(); if prune_modes From ae6f1e39c0fd8ad630ec5c59ed0ead6ed3f765a1 Mon Sep 17 00:00:00 2001 From: Matthias Seitz Date: Tue, 28 Nov 2023 13:57:40 +0100 Subject: [PATCH 043/277] chore: improve clique signer recovery error (#5613) --- crates/primitives/src/revm/env.rs | 26 ++++++++++++++++++++++---- 1 file changed, 22 insertions(+), 4 deletions(-) diff --git a/crates/primitives/src/revm/env.rs b/crates/primitives/src/revm/env.rs index 26b87a159b40..634cc6db1ce7 100644 --- a/crates/primitives/src/revm/env.rs +++ b/crates/primitives/src/revm/env.rs @@ -92,26 +92,44 @@ pub fn fill_block_env_with_coinbase( /// Return the coinbase address for the given header and chain spec. pub fn block_coinbase(chain_spec: &ChainSpec, header: &Header, after_merge: bool) -> Address { if chain_spec.chain == Chain::goerli() && !after_merge { - recover_header_signer(header).expect("failed to recover signer") + recover_header_signer(header).unwrap_or_else(|err| { + panic!( + "Failed to recover goerli Clique Consensus signer from header ({}, {}) using extradata {}: {:?}", + header.number, header.hash_slow(), header.extra_data, err + ) + }) } else { header.beneficiary } } +/// Error type for recovering Clique signer from a header. +#[derive(Debug, thiserror::Error)] +pub enum CliqueSignerRecoveryError { + /// Header extradata is too short. + #[error("Invalid extra data length")] + InvalidExtraData, + /// Recovery failed. + #[error("Invalid signature: {0}")] + InvalidSignature(#[from] secp256k1::Error), +} + /// Recover the account from signed header per clique consensus rules. -pub fn recover_header_signer(header: &Header) -> Option
{ +pub fn recover_header_signer(header: &Header) -> Result { let extra_data_len = header.extra_data.len(); // Fixed number of extra-data suffix bytes reserved for signer signature. // 65 bytes fixed as signatures are based on the standard secp256k1 curve. // Filled with zeros on genesis block. let signature_start_byte = extra_data_len - 65; - let signature: [u8; 65] = header.extra_data[signature_start_byte..].try_into().ok()?; + let signature: [u8; 65] = header.extra_data[signature_start_byte..] + .try_into() + .map_err(|_| CliqueSignerRecoveryError::InvalidExtraData)?; let seal_hash = { let mut header_to_seal = header.clone(); header_to_seal.extra_data = Bytes::from(header.extra_data[..signature_start_byte].to_vec()); header_to_seal.hash_slow() }; - recover_signer(&signature, &seal_hash.0).ok() + recover_signer(&signature, &seal_hash.0).map_err(CliqueSignerRecoveryError::InvalidSignature) } /// Returns a new [TxEnv] filled with the transaction's data. From 18d563dd276f0d686872ce6f286895af9e78af49 Mon Sep 17 00:00:00 2001 From: Matthias Seitz Date: Tue, 28 Nov 2023 16:44:27 +0100 Subject: [PATCH 044/277] chore: use U256::is_zero (#5616) --- crates/primitives/src/account.rs | 2 +- crates/primitives/src/transaction/signature.rs | 2 +- crates/revm/revm-inspectors/src/tracing/mod.rs | 2 +- crates/rpc/rpc-engine-api/tests/it/payload.rs | 2 +- crates/rpc/rpc/src/eth/revm_utils.rs | 6 +++--- .../provider/src/bundle_state/bundle_state_with_receipts.rs | 2 +- crates/trie/src/hashed_cursor/post_state.rs | 4 ++-- examples/db-access.rs | 2 +- 8 files changed, 11 insertions(+), 11 deletions(-) diff --git a/crates/primitives/src/account.rs b/crates/primitives/src/account.rs index 94d245828697..c505ee5dfc1b 100644 --- a/crates/primitives/src/account.rs +++ b/crates/primitives/src/account.rs @@ -34,7 +34,7 @@ impl Account { Some(hash) => hash == KECCAK_EMPTY, }; - self.nonce == 0 && self.balance == U256::ZERO && is_bytecode_empty + self.nonce == 0 && self.balance.is_zero() && is_bytecode_empty } /// Returns an account bytecode's hash. diff --git a/crates/primitives/src/transaction/signature.rs b/crates/primitives/src/transaction/signature.rs index 76c4a893f6f3..89c30648cd7a 100644 --- a/crates/primitives/src/transaction/signature.rs +++ b/crates/primitives/src/transaction/signature.rs @@ -72,7 +72,7 @@ impl Signature { #[inline] pub fn v(&self, chain_id: Option) -> u64 { #[cfg(feature = "optimism")] - if self.r == U256::ZERO && self.s == U256::ZERO { + if self.r.is_zero() && self.s.is_zero() { return 0 } diff --git a/crates/revm/revm-inspectors/src/tracing/mod.rs b/crates/revm/revm-inspectors/src/tracing/mod.rs index 5fbc9bb2ccb2..6b4b93636364 100644 --- a/crates/revm/revm-inspectors/src/tracing/mod.rs +++ b/crates/revm/revm-inspectors/src/tracing/mod.rs @@ -144,7 +144,7 @@ impl TracingInspector { ) -> bool { if data.precompiles.contains(to) { // only if this is _not_ the root call - return self.is_deep() && value == U256::ZERO + return self.is_deep() && value.is_zero() } false } diff --git a/crates/rpc/rpc-engine-api/tests/it/payload.rs b/crates/rpc/rpc-engine-api/tests/it/payload.rs index 5b012726cee7..0b9ec850a470 100644 --- a/crates/rpc/rpc-engine-api/tests/it/payload.rs +++ b/crates/rpc/rpc-engine-api/tests/it/payload.rs @@ -90,7 +90,7 @@ fn payload_validation() { assert_matches!( try_into_sealed_block(block_with_zero_base_fee,None), - Err(PayloadError::BaseFee(val)) if val == U256::ZERO + Err(PayloadError::BaseFee(val)) if val.is_zero() ); // Invalid encoded transactions diff --git a/crates/rpc/rpc/src/eth/revm_utils.rs b/crates/rpc/rpc/src/eth/revm_utils.rs index bbfb3b2d1753..4393272fc8ce 100644 --- a/crates/rpc/rpc/src/eth/revm_utils.rs +++ b/crates/rpc/rpc/src/eth/revm_utils.rs @@ -609,7 +609,7 @@ mod tests { let CallFees { gas_price, .. } = CallFees::ensure_fees(None, None, None, U256::from(99), None, None, Some(U256::ZERO)) .unwrap(); - assert_eq!(gas_price, U256::ZERO); + assert!(gas_price.is_zero()); } #[test] @@ -617,7 +617,7 @@ mod tests { let CallFees { gas_price, max_fee_per_blob_gas, .. } = CallFees::ensure_fees(None, None, None, U256::from(99), None, None, Some(U256::ZERO)) .unwrap(); - assert_eq!(gas_price, U256::ZERO); + assert!(gas_price.is_zero()); assert_eq!(max_fee_per_blob_gas, None); let CallFees { gas_price, max_fee_per_blob_gas, .. } = CallFees::ensure_fees( @@ -630,7 +630,7 @@ mod tests { Some(U256::from(99)), ) .unwrap(); - assert_eq!(gas_price, U256::ZERO); + assert!(gas_price.is_zero()); assert_eq!(max_fee_per_blob_gas, Some(U256::from(99))); } } diff --git a/crates/storage/provider/src/bundle_state/bundle_state_with_receipts.rs b/crates/storage/provider/src/bundle_state/bundle_state_with_receipts.rs index 683da8160fdc..33dd0590e38e 100644 --- a/crates/storage/provider/src/bundle_state/bundle_state_with_receipts.rs +++ b/crates/storage/provider/src/bundle_state/bundle_state_with_receipts.rs @@ -143,7 +143,7 @@ impl BundleStateWithReceipts { for (key, value) in account.storage.iter() { let hashed_key = keccak256(B256::new(key.to_be_bytes())); - if value.present_value == U256::ZERO { + if value.present_value.is_zero() { hashed_storage.insert_zero_valued_slot(hashed_key); } else { hashed_storage.insert_non_zero_valued_storage(hashed_key, value.present_value); diff --git a/crates/trie/src/hashed_cursor/post_state.rs b/crates/trie/src/hashed_cursor/post_state.rs index de276f819967..0b739c08849a 100644 --- a/crates/trie/src/hashed_cursor/post_state.rs +++ b/crates/trie/src/hashed_cursor/post_state.rs @@ -916,7 +916,7 @@ mod tests { let wiped = false; let mut hashed_storage = HashedStorage::new(wiped); for (slot, value) in post_state_storage.iter() { - if *value == U256::ZERO { + if value.is_zero() { hashed_storage.insert_zero_valued_slot(*slot); } else { hashed_storage.insert_non_zero_valued_storage(*slot, *value); @@ -1030,7 +1030,7 @@ mod tests { for (address, (wiped, storage)) in &post_state_storages { let mut hashed_storage = HashedStorage::new(*wiped); for (slot, value) in storage { - if *value == U256::ZERO { + if value.is_zero() { hashed_storage.insert_zero_valued_slot(*slot); } else { hashed_storage.insert_non_zero_valued_storage(*slot, *value); diff --git a/examples/db-access.rs b/examples/db-access.rs index 235193a38f64..dd9c74e58016 100644 --- a/examples/db-access.rs +++ b/examples/db-access.rs @@ -67,7 +67,7 @@ fn header_provider_example(provider: T, number: u64) -> eyre: // The header's total difficulty is stored in a separate table, so we have a separate call for // it. This is not needed for post PoS transition chains. let td = provider.header_td_by_number(number)?.ok_or(eyre::eyre!("header td not found"))?; - assert_ne!(td, U256::ZERO); + assert!(!td.is_zero()); // Can query headers by range as well, already sealed! let headers = provider.sealed_headers_range(100..200)?; From 1e15241e7473eecb46843bf0f8edd6066f0c2b95 Mon Sep 17 00:00:00 2001 From: Matthias Seitz Date: Tue, 28 Nov 2023 17:10:33 +0100 Subject: [PATCH 045/277] ci: only check --lib with cargo udeps (#5615) --- .github/workflows/sanity.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/sanity.yml b/.github/workflows/sanity.yml index 9ab0b1d07973..fdcc7be4fb3d 100644 --- a/.github/workflows/sanity.yml +++ b/.github/workflows/sanity.yml @@ -22,7 +22,7 @@ jobs: - uses: dtolnay/rust-toolchain@nightly - uses: taiki-e/install-action@cargo-udeps - name: Check for unused dependencies - run: cargo udeps --features "jemalloc,${{ matrix.network }}" + run: cargo udeps --lib --features "jemalloc,${{ matrix.network }}" - uses: JasonEtco/create-an-issue@v2 if: ${{ failure() }} env: From 15c288e00b33f6701ed91d5a4b76ba385870b9ea Mon Sep 17 00:00:00 2001 From: Dan Cline <6798349+Rjected@users.noreply.github.com> Date: Tue, 28 Nov 2023 11:42:36 -0500 Subject: [PATCH 046/277] feat: add blob truncate pool method (#5597) --- crates/transaction-pool/src/pool/blob.rs | 50 ++++++++++++++++++++++-- 1 file changed, 47 insertions(+), 3 deletions(-) diff --git a/crates/transaction-pool/src/pool/blob.rs b/crates/transaction-pool/src/pool/blob.rs index e7f6fa8f2448..fc0bebbf8991 100644 --- a/crates/transaction-pool/src/pool/blob.rs +++ b/crates/transaction-pool/src/pool/blob.rs @@ -1,7 +1,7 @@ #![allow(dead_code, unused)] use crate::{ identifier::TransactionId, pool::size::SizeTracker, traits::BestTransactionsAttributes, - PoolTransaction, ValidPoolTransaction, + PoolTransaction, SubPoolLimit, ValidPoolTransaction, }; use std::{ cmp::Ordering, @@ -182,6 +182,27 @@ impl BlobTransactions { removed } + /// Removes transactions until the pool satisfies its [SubPoolLimit]. + /// + /// This is done by removing transactions according to their ordering in the pool, defined by + /// the [BlobOrd] struct. + /// + /// Removed transactions are returned in the order they were removed. + pub(crate) fn truncate_pool( + &mut self, + limit: SubPoolLimit, + ) -> Vec>> { + let mut removed = Vec::new(); + + while self.size() > limit.max_size && self.len() > limit.max_txs { + let tx = self.all.last().expect("pool is not empty"); + let id = *tx.transaction.id(); + removed.push(self.remove_transaction(&id).expect("transaction exists")); + } + + removed + } + /// Returns `true` if the transaction with the given id is already included in this pool. #[cfg(test)] #[allow(unused)] @@ -287,6 +308,13 @@ const LOG_2_1_125: f64 = 0.16992500144231237; /// /// This is supposed to get the number of fee jumps required to get from the current fee to the fee /// cap, or where the transaction would not be executable any more. +/// +/// A positive value means that the transaction will remain executable unless the current fee +/// increases. +/// +/// A negative value means that the transaction is currently not executable, and requires the +/// current fee to decrease by some number of jumps before the max fee is greater than the current +/// fee. pub fn fee_delta(max_tx_fee: u128, current_fee: u128) -> i64 { if max_tx_fee == current_fee { // if these are equal, then there's no fee jump @@ -331,16 +359,29 @@ pub fn blob_tx_priority( let delta_blob_fee = fee_delta(blob_fee_cap, blob_fee); let delta_priority_fee = fee_delta(max_priority_fee, base_fee); + // TODO: this could be u64: + // * if all are positive, zero is returned + // * if all are negative, the min negative value is returned + // * if some are positive and some are negative, the min negative value is returned + // + // the BlobOrd could then just be a u64, and higher values represent worse transactions (more + // jumps for one of the fees until the cap satisfies) + // // priority = min(delta-basefee, delta-blobfee, 0) delta_blob_fee.min(delta_priority_fee).min(0) } +/// A struct used to determine the ordering for a specific blob transaction in the pool. This uses +/// a `priority` value to determine the ordering, and uses the `submission_id` to break ties. +/// +/// The `priority` value is calculated using the [blob_tx_priority] function, and should be +/// re-calculated on each block. #[derive(Debug, Clone)] struct BlobOrd { /// Identifier that tags when transaction was submitted in the pool. pub(crate) submission_id: u64, - // The priority for this transaction, calculated using the [`blob_tx_priority`] function, - // taking into account both the blob and priority fee. + /// The priority for this transaction, calculated using the [`blob_tx_priority`] function, + /// taking into account both the blob and priority fee. pub(crate) priority: i64, } @@ -360,6 +401,9 @@ impl PartialOrd for BlobOrd { impl Ord for BlobOrd { fn cmp(&self, other: &Self) -> Ordering { + // order in reverse, so transactions with a lower ordering return Greater - this is + // important because transactions with larger negative values will take more fee jumps and + // it will take longer to become executable, so those should be evicted first let ord = other.priority.cmp(&self.priority); // use submission_id to break ties From 9fe3b02c2133ee27e79e0c9bfea92b174891bb81 Mon Sep 17 00:00:00 2001 From: Matthias Seitz Date: Tue, 28 Nov 2023 21:28:50 +0100 Subject: [PATCH 047/277] chore(deps): use rustls (#5619) --- Cargo.lock | 108 +---------------------------- crates/rpc/rpc/Cargo.toml | 4 +- deny.toml | 4 +- examples/beacon-api-sse/Cargo.toml | 2 +- 4 files changed, 8 insertions(+), 110 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 6b6cda0b5813..4947be0261de 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2745,21 +2745,6 @@ version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" -[[package]] -name = "foreign-types" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" -dependencies = [ - "foreign-types-shared", -] - -[[package]] -name = "foreign-types-shared" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" - [[package]] name = "form_urlencoded" version = "1.2.1" @@ -3335,19 +3320,6 @@ dependencies = [ "tracing", ] -[[package]] -name = "hyper-tls" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d6183ddfa99b85da61a140bea0efc93fdf56ceaa041b37d553518030827f9905" -dependencies = [ - "bytes", - "hyper", - "native-tls", - "tokio", - "tokio-native-tls", -] - [[package]] name = "iai" version = "0.1.1" @@ -4281,9 +4253,9 @@ dependencies = [ [[package]] name = "mev-share-sse" -version = "0.1.5" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e59928ecfd8f9dd3211f2eb08bdc260c78c2e2af34d1a652445a6287d3c95942" +checksum = "ba263a1c478aade75b60835fbeb6f57c0280fb0953742c3d84de45ea51139ae4" dependencies = [ "async-sse", "bytes", @@ -4396,24 +4368,6 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1fafa6961cabd9c63bcd77a45d7e3b7f3b552b70417831fb0f56db717e72407e" -[[package]] -name = "native-tls" -version = "0.2.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07226173c32f2926027b63cce4bcd8076c3552846cbe7925f3aaffeac0a3b92e" -dependencies = [ - "lazy_static", - "libc", - "log", - "openssl", - "openssl-probe", - "openssl-sys", - "schannel", - "security-framework", - "security-framework-sys", - "tempfile", -] - [[package]] name = "nibble_vec" version = "0.1.0" @@ -4665,50 +4619,12 @@ dependencies = [ "syn 1.0.109", ] -[[package]] -name = "openssl" -version = "0.10.60" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "79a4c6c3a2b158f7f8f2a2fc5a969fa3a068df6fc9dbb4a43845436e3af7c800" -dependencies = [ - "bitflags 2.4.1", - "cfg-if", - "foreign-types", - "libc", - "once_cell", - "openssl-macros", - "openssl-sys", -] - -[[package]] -name = "openssl-macros" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.39", -] - [[package]] name = "openssl-probe" version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" -[[package]] -name = "openssl-sys" -version = "0.9.96" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3812c071ba60da8b5677cc12bcb1d42989a65553772897a7e0355545a819838f" -dependencies = [ - "cc", - "libc", - "pkg-config", - "vcpkg", -] - [[package]] name = "option-ext" version = "0.2.0" @@ -5572,21 +5488,19 @@ dependencies = [ "http", "http-body", "hyper", - "hyper-tls", "ipnet", "js-sys", "log", "mime", - "native-tls", "once_cell", "percent-encoding", "pin-project-lite", + "rustls", "serde", "serde_json", "serde_urlencoded", "system-configuration", "tokio", - "tokio-native-tls", "tokio-util", "tower-service", "url", @@ -8020,16 +7934,6 @@ dependencies = [ "syn 2.0.39", ] -[[package]] -name = "tokio-native-tls" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbae76ab933c85776efabc971569dd6119c580d8f5d448769dec1764bf796ef2" -dependencies = [ - "native-tls", - "tokio", -] - [[package]] name = "tokio-rustls" version = "0.24.1" @@ -8646,12 +8550,6 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" -[[package]] -name = "vcpkg" -version = "0.2.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" - [[package]] name = "vergen" version = "8.2.6" diff --git a/crates/rpc/rpc/Cargo.toml b/crates/rpc/rpc/Cargo.toml index 44c90408830b..6e50bafbde79 100644 --- a/crates/rpc/rpc/Cargo.toml +++ b/crates/rpc/rpc/Cargo.toml @@ -40,9 +40,11 @@ jsonrpsee.workspace = true http = "0.2.8" http-body = "0.4.5" hyper = "0.14.24" -reqwest = { version = "0.11.20", optional = true } jsonwebtoken = "8" +## required for optimism sequencer delegation +reqwest = { version = "0.11", default-features = false, features = ["rustls"], optional = true } + # async async-trait.workspace = true tokio = { workspace = true, features = ["sync"] } diff --git a/deny.toml b/deny.toml index 524ab567d81c..dd8e50780b73 100644 --- a/deny.toml +++ b/deny.toml @@ -19,9 +19,7 @@ wildcards = "allow" highlight = "all" # List of crates to deny deny = [ - # Each entry the name of a crate and a version range. If version is - # not specified, all versions will be matched. - #{ name = "ansi_term", version = "=0.11.0" }, + { name = "openssl" }, ] # Certain crates/versions that will be skipped when doing duplicate detection. skip = [] diff --git a/examples/beacon-api-sse/Cargo.toml b/examples/beacon-api-sse/Cargo.toml index 892c2e229e73..60bb76c53656 100644 --- a/examples/beacon-api-sse/Cargo.toml +++ b/examples/beacon-api-sse/Cargo.toml @@ -15,4 +15,4 @@ tracing.workspace = true futures-util.workspace = true tokio = { workspace = true, features = ["time"] } -mev-share-sse = "0.1.5" \ No newline at end of file +mev-share-sse = { version = "0.1.6" , default-features = false } \ No newline at end of file From 07265d9787ba71fc6208ac01669d291b32765496 Mon Sep 17 00:00:00 2001 From: Alexey Shekhirin Date: Wed, 29 Nov 2023 08:47:47 +0000 Subject: [PATCH 048/277] perf(storage, mdbx): set rp augment limit (#5614) --- .../storage/db/src/implementation/mdbx/mod.rs | 26 +++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/crates/storage/db/src/implementation/mdbx/mod.rs b/crates/storage/db/src/implementation/mdbx/mod.rs index 3f6a5db88199..91a56ca44d7d 100644 --- a/crates/storage/db/src/implementation/mdbx/mod.rs +++ b/crates/storage/db/src/implementation/mdbx/mod.rs @@ -99,6 +99,32 @@ impl DatabaseEnv { }); // configure more readers inner_env.set_max_readers(DEFAULT_MAX_READERS); + // This parameter sets the maximum size of the "reclaimed list", and the unit of measurement + // is "pages". Reclaimed list is the list of freed pages that's populated during the + // lifetime of DB transaction, and through which MDBX searches when it needs to insert new + // record with overflow pages. The flow is roughly the following: + // 0. We need to insert a record that requires N number of overflow pages (in consecutive + // sequence inside the DB file). + // 1. Get some pages from the freelist, put them into the reclaimed list. + // 2. Search through the reclaimed list for the sequence of size N. + // 3. a. If found, return the sequence. + // 3. b. If not found, repeat steps 1-3. If the reclaimed list size is larger than + // the `rp augment limit`, stop the search and allocate new pages at the end of the file: + // https://github.com/paradigmxyz/reth/blob/2a4c78759178f66e30c8976ec5d243b53102fc9a/crates/storage/libmdbx-rs/mdbx-sys/libmdbx/mdbx.c#L11479-L11480. + // + // Basically, this parameter controls for how long do we search through the freelist before + // trying to allocate new pages. Smaller value will make MDBX to fallback to + // allocation faster, higher value will force MDBX to search through the freelist + // longer until the sequence of pages is found. + // + // The default value of this parameter is set depending on the DB size. The bigger the + // database, the larger is `rp augment limit`. + // https://github.com/paradigmxyz/reth/blob/2a4c78759178f66e30c8976ec5d243b53102fc9a/crates/storage/libmdbx-rs/mdbx-sys/libmdbx/mdbx.c#L10018-L10024. + // + // Previously, MDBX set this value as `256 * 1024` constant. Let's fallback to this, + // because we want to prioritize freelist lookup speed over database growth. + // https://github.com/paradigmxyz/reth/blob/fa2b9b685ed9787636d962f4366caf34a9186e66/crates/storage/libmdbx-rs/mdbx-sys/libmdbx/mdbx.c#L16017. + inner_env.set_rp_augment_limit(256 * 1024); if let Some(log_level) = log_level { // Levels higher than [LogLevel::Notice] require libmdbx built with `MDBX_DEBUG` option. From 320deb6a439d78efd9bc0dead6d6520e75a36f0e Mon Sep 17 00:00:00 2001 From: Matthias Seitz Date: Wed, 29 Nov 2023 16:52:48 +0100 Subject: [PATCH 049/277] chore: add more payload tracing (#5624) --- crates/payload/basic/src/lib.rs | 95 ++++++++++++++------- crates/payload/basic/src/optimism.rs | 2 +- crates/payload/builder/src/service.rs | 13 ++- crates/rpc/rpc-engine-api/src/engine_api.rs | 29 ++++--- 4 files changed, 90 insertions(+), 49 deletions(-) diff --git a/crates/payload/basic/src/lib.rs b/crates/payload/basic/src/lib.rs index 533651b7c55e..ce0ba246bc2f 100644 --- a/crates/payload/basic/src/lib.rs +++ b/crates/payload/basic/src/lib.rs @@ -16,7 +16,7 @@ use futures_util::FutureExt; use reth_interfaces::{RethError, RethResult}; use reth_payload_builder::{ database::CachedReads, error::PayloadBuilderError, BuiltPayload, KeepPayloadJobAlive, - PayloadBuilderAttributes, PayloadJob, PayloadJobGenerator, + PayloadBuilderAttributes, PayloadId, PayloadJob, PayloadJobGenerator, }; use reth_primitives::{ bytes::BytesMut, @@ -53,7 +53,7 @@ use tokio::{ sync::{oneshot, Semaphore}, time::{Interval, Sleep}, }; -use tracing::{debug, trace}; +use tracing::{debug, trace, warn}; mod metrics; @@ -387,7 +387,7 @@ where match outcome { BuildOutcome::Better { payload, cached_reads } => { this.cached_reads = Some(cached_reads); - trace!(target: "payload_builder", value = %payload.fees(), "built better payload"); + debug!(target: "payload_builder", value = %payload.fees(), "built better payload"); let payload = Arc::new(payload); this.best_payload = Some(payload); } @@ -402,7 +402,7 @@ where } Poll::Ready(Err(error)) => { // job failed, but we simply try again next interval - trace!(target: "payload_builder", ?error, "payload build attempt failed"); + debug!(target: "payload_builder", ?error, "payload build attempt failed"); this.metrics.inc_failed_payload_builds(); } Poll::Pending => { @@ -448,6 +448,8 @@ where let mut empty_payload = None; if best_payload.is_none() { + debug!(target: "payload_builder", id=%self.config.payload_id(), "no best payload yet to resolve, building empty payload"); + // if no payload has been built yet self.metrics.inc_requested_empty_payload(); // no payload built yet, so we need to return an empty payload @@ -464,31 +466,33 @@ where // upfront with the list of transactions sent in the attributes without caring about // the results of the polling job, if a best payload has not already been built. #[cfg(feature = "optimism")] - if self.config.chain_spec.is_optimism() && - self.config.attributes.optimism_payload_attributes.no_tx_pool { - let args = BuildArguments { - client: self.client.clone(), - pool: self.pool.clone(), - cached_reads: self.cached_reads.take().unwrap_or_default(), - config: self.config.clone(), - cancel: Cancelled::default(), - best_payload: None, - }; - if let Ok(BuildOutcome::Better { payload, cached_reads }) = - self.builder.try_build(args) + if self.config.chain_spec.is_optimism() && + self.config.attributes.optimism_payload_attributes.no_tx_pool { - self.cached_reads = Some(cached_reads); - trace!(target: "payload_builder", "[OPTIMISM] Forced best payload"); - let payload = Arc::new(payload); - return ( - ResolveBestPayload { - best_payload: Some(payload), - maybe_better, - empty_payload, - }, - KeepPayloadJobAlive::Yes, - ) + let args = BuildArguments { + client: self.client.clone(), + pool: self.pool.clone(), + cached_reads: self.cached_reads.take().unwrap_or_default(), + config: self.config.clone(), + cancel: Cancelled::default(), + best_payload: None, + }; + if let Ok(BuildOutcome::Better { payload, cached_reads }) = + self.builder.try_build(args) + { + self.cached_reads = Some(cached_reads); + trace!(target: "payload_builder", "[OPTIMISM] Forced best payload"); + let payload = Arc::new(payload); + return ( + ResolveBestPayload { + best_payload: Some(payload), + maybe_better, + empty_payload, + }, + KeepPayloadJobAlive::Yes, + ) + } } } @@ -531,18 +535,27 @@ impl Future for ResolveBestPayload { if let Poll::Ready(res) = fut.poll(cx) { this.maybe_better = None; if let Ok(BuildOutcome::Better { payload, .. }) = res { + debug!(target: "payload_builder", "resolving better payload"); return Poll::Ready(Ok(Arc::new(payload))) } } } if let Some(best) = this.best_payload.take() { + debug!(target: "payload_builder", "resolving best payload"); return Poll::Ready(Ok(best)) } let mut empty_payload = this.empty_payload.take().expect("polled after completion"); match empty_payload.poll_unpin(cx) { - Poll::Ready(Ok(res)) => Poll::Ready(res.map(Arc::new)), + Poll::Ready(Ok(res)) => { + if let Err(err) = &res { + warn!(target: "payload_builder", ?err, "failed to resolve empty payload"); + } else { + debug!(target: "payload_builder", "resolving empty payload"); + } + Poll::Ready(res.map(Arc::new)) + } Poll::Ready(Err(err)) => Poll::Ready(Err(err.into())), Poll::Pending => { this.empty_payload = Some(empty_payload); @@ -622,6 +635,10 @@ impl PayloadConfig { } self.extra_data.clone() } + + pub(crate) fn payload_id(&self) -> PayloadId { + self.attributes.id + } } impl PayloadConfig { @@ -770,7 +787,7 @@ where .. } = config; - debug!(target: "payload_builder", parent_hash = ?parent_block.hash, parent_number = parent_block.number, "building new payload"); + debug!(target: "payload_builder", id=%attributes.id, parent_hash = ?parent_block.hash, parent_number = parent_block.number, "building new payload"); let mut cumulative_gas_used = 0; let mut sum_blob_gas_used = 0; let block_gas_limit: u64 = initialized_block_env.gas_limit.try_into().unwrap_or(u64::MAX); @@ -1019,7 +1036,10 @@ where debug!(target: "payload_builder", parent_hash = ?parent_block.hash, parent_number = parent_block.number, "building empty payload"); - let state = client.state_by_block_hash(parent_block.hash)?; + let state = client.state_by_block_hash(parent_block.hash).map_err(|err| { + warn!(target: "payload_builder", parent_hash=%parent_block.hash, ?err, "failed to get state for empty payload"); + err + })?; let mut db = State::builder() .with_database_boxed(Box::new(StateProviderDatabase::new(&state))) .with_bundle_update() @@ -1037,10 +1057,16 @@ where &initialized_cfg, &initialized_block_env, &attributes, - )?; + ).map_err(|err| { + warn!(target: "payload_builder", parent_hash=%parent_block.hash, ?err, "failed to apply beacon root contract call for empty payload"); + err + })?; let WithdrawalsOutcome { withdrawals_root, withdrawals } = - commit_withdrawals(&mut db, &chain_spec, attributes.timestamp, attributes.withdrawals)?; + commit_withdrawals(&mut db, &chain_spec, attributes.timestamp, attributes.withdrawals).map_err(|err| { + warn!(target: "payload_builder", parent_hash=%parent_block.hash,?err, "failed to commit withdrawals for empty payload"); + err + })?; // merge all transitions into bundle state, this would apply the withdrawal balance changes and // 4788 contract call @@ -1049,7 +1075,10 @@ where // calculate the state root let bundle_state = BundleStateWithReceipts::new(db.take_bundle(), Receipts::new(), block_number); - let state_root = state.state_root(&bundle_state)?; + let state_root = state.state_root(&bundle_state).map_err(|err| { + warn!(target: "payload_builder", parent_hash=%parent_block.hash, ?err, "failed to calculate state root for empty payload"); + err + })?; let header = Header { parent_hash: parent_block.hash, diff --git a/crates/payload/basic/src/optimism.rs b/crates/payload/basic/src/optimism.rs index d7f1eb0292de..d316676d139b 100644 --- a/crates/payload/basic/src/optimism.rs +++ b/crates/payload/basic/src/optimism.rs @@ -36,7 +36,7 @@ where .. } = config; - debug!(target: "payload_builder", parent_hash = ?parent_block.hash, parent_number = parent_block.number, "building new payload"); + debug!(target: "payload_builder", id=%attributes.id, parent_hash = ?parent_block.hash, parent_number = parent_block.number, "building new payload"); let mut cumulative_gas_used = 0; let block_gas_limit: u64 = attributes .optimism_payload_attributes diff --git a/crates/payload/builder/src/service.rs b/crates/payload/builder/src/service.rs index 8e02a0779731..300e514037fe 100644 --- a/crates/payload/builder/src/service.rs +++ b/crates/payload/builder/src/service.rs @@ -229,15 +229,24 @@ where &self, id: PayloadId, ) -> Option> { - self.payload_jobs + let attributes = self + .payload_jobs .iter() .find(|(_, job_id)| *job_id == id) - .map(|(j, _)| j.payload_attributes()) + .map(|(j, _)| j.payload_attributes()); + + if attributes.is_none() { + trace!(%id, "no matching payload job found to get attributes for"); + } + + attributes } /// Returns the best payload for the given identifier that has been built so far and terminates /// the job if requested. fn resolve(&mut self, id: PayloadId) -> Option { + trace!(%id, "resolving payload job"); + let job = self.payload_jobs.iter().position(|(_, job_id)| *job_id == id)?; let (fut, keep_alive) = self.payload_jobs[job].0.resolve(); diff --git a/crates/rpc/rpc-engine-api/src/engine_api.rs b/crates/rpc/rpc-engine-api/src/engine_api.rs index a000a00ab2e5..839e53c303f3 100644 --- a/crates/rpc/rpc-engine-api/src/engine_api.rs +++ b/crates/rpc/rpc-engine-api/src/engine_api.rs @@ -6,7 +6,7 @@ use async_trait::async_trait; use jsonrpsee_core::RpcResult; use reth_beacon_consensus::BeaconConsensusEngineHandle; use reth_interfaces::consensus::ForkchoiceState; -use reth_payload_builder::PayloadStore; +use reth_payload_builder::{PayloadBuilderAttributes, PayloadStore}; use reth_primitives::{BlockHash, BlockHashOrNumber, BlockNumber, ChainSpec, Hardfork, B256, U64}; use reth_provider::{BlockReader, EvmEnvProvider, HeaderProvider, StateProviderFactory}; use reth_rpc_api::EngineApiServer; @@ -74,6 +74,19 @@ where Self { inner } } + /// Fetches the attributes for the payload with the given id. + async fn get_payload_attributes( + &self, + payload_id: PayloadId, + ) -> EngineApiResult { + Ok(self + .inner + .payload_store + .payload_attributes(payload_id) + .await + .ok_or(EngineApiError::UnknownPayload)??) + } + /// See also /// Caution: This should not accept the `withdrawals` field pub async fn new_payload_v1( @@ -189,12 +202,7 @@ where payload_id: PayloadId, ) -> EngineApiResult { // First we fetch the payload attributes to check the timestamp - let attributes = self - .inner - .payload_store - .payload_attributes(payload_id) - .await - .ok_or(EngineApiError::UnknownPayload)??; + let attributes = self.get_payload_attributes(payload_id).await?; // validate timestamp according to engine rules self.validate_payload_timestamp(EngineApiMessageVersion::V2, attributes.timestamp)?; @@ -221,12 +229,7 @@ where payload_id: PayloadId, ) -> EngineApiResult { // First we fetch the payload attributes to check the timestamp - let attributes = self - .inner - .payload_store - .payload_attributes(payload_id) - .await - .ok_or(EngineApiError::UnknownPayload)??; + let attributes = self.get_payload_attributes(payload_id).await?; // validate timestamp according to engine rules self.validate_payload_timestamp(EngineApiMessageVersion::V3, attributes.timestamp)?; From a6ad5ac421e5477b2c71352bde1080a1bec03ad1 Mon Sep 17 00:00:00 2001 From: Yash Atreya <44857776+yash-atreya@users.noreply.github.com> Date: Wed, 29 Nov 2023 11:12:59 -0500 Subject: [PATCH 050/277] use SealedBlockedWithSenders in block-level tracing (#5620) Co-authored-by: Matthias Seitz --- crates/rpc/rpc/src/eth/api/transactions.rs | 30 ++++++++-------------- 1 file changed, 11 insertions(+), 19 deletions(-) diff --git a/crates/rpc/rpc/src/eth/api/transactions.rs b/crates/rpc/rpc/src/eth/api/transactions.rs index 0da8886cc954..93577db24037 100644 --- a/crates/rpc/rpc/src/eth/api/transactions.rs +++ b/crates/rpc/rpc/src/eth/api/transactions.rs @@ -30,8 +30,8 @@ use reth_revm::{ tracing::{TracingInspector, TracingInspectorConfig}, }; use reth_rpc_types::{ - BlockError, CallRequest, Index, Log, Transaction, TransactionInfo, TransactionReceipt, - TransactionRequest, TypedTransactionRequest, + CallRequest, Index, Log, Transaction, TransactionInfo, TransactionReceipt, TransactionRequest, + TypedTransactionRequest, }; use reth_rpc_types_compat::transaction::from_recovered_with_block_context; use reth_transaction_pool::{TransactionOrigin, TransactionPool}; @@ -779,12 +779,9 @@ where R: Send + 'static, { let ((cfg, block_env, _), block) = - futures::try_join!(self.evm_env_at(block_id), self.block_by_id(block_id),)?; + futures::try_join!(self.evm_env_at(block_id), self.block_with_senders(block_id))?; - let block = match block { - Some(block) => block, - None => return Ok(None), - }; + let Some(block) = block else { return Ok(None) }; // replay all transactions of the block self.spawn_tracing_task_with(move |this| { @@ -799,13 +796,13 @@ where // prepare transactions, we do everything upfront to reduce time spent with open state let max_transactions = highest_index.map_or(block.body.len(), |highest| highest as usize); - let transactions = block - .body - .into_iter() + let mut results = Vec::with_capacity(max_transactions); + + let mut transactions = block + .into_transactions_ecrecovered() .take(max_transactions) .enumerate() - .map(|(idx, tx)| -> EthResult<_> { - let tx = tx.into_ecrecovered().ok_or(BlockError::InvalidSignature)?; + .map(|(idx, tx)| { let tx_info = TransactionInfo { hash: Some(tx.hash()), index: Some(idx as u64), @@ -814,19 +811,14 @@ where base_fee: Some(base_fee), }; let tx_env = tx_env_with_recovered(&tx); - - Ok((tx_info, tx_env)) + (tx_info, tx_env) }) - .collect::, _>>()?; - - let mut results = Vec::with_capacity(transactions.len()); + .peekable(); // now get the state let state = this.state_at(state_at.into())?; let mut db = CacheDB::new(StateProviderDatabase::new(state)); - let mut transactions = transactions.into_iter().peekable(); - while let Some((tx_info, tx)) = transactions.next() { let env = Env { cfg: cfg.clone(), block: block_env.clone(), tx }; From d9ef1a14d5da38a7e86056d27dfceb122b5d723b Mon Sep 17 00:00:00 2001 From: Matthias Seitz Date: Wed, 29 Nov 2023 19:21:46 +0100 Subject: [PATCH 051/277] fix: derive job deadline from attributes timestamp (#5626) --- crates/payload/basic/src/lib.rs | 39 +++++++++++++++++++++++++-- crates/payload/builder/src/payload.rs | 4 ++- 2 files changed, 40 insertions(+), 3 deletions(-) diff --git a/crates/payload/basic/src/lib.rs b/crates/payload/basic/src/lib.rs index ce0ba246bc2f..db3b9491c0b0 100644 --- a/crates/payload/basic/src/lib.rs +++ b/crates/payload/basic/src/lib.rs @@ -47,7 +47,7 @@ use std::{ pin::Pin, sync::{atomic::AtomicBool, Arc}, task::{Context, Poll}, - time::Duration, + time::{Duration, SystemTime, UNIX_EPOCH}, }; use tokio::{ sync::{oneshot, Semaphore}, @@ -130,6 +130,30 @@ impl BasicPayloadJobGenerator Client software SHOULD stop the updating process when either a call to engine_getPayload + // > with the build process's payloadId is made or SECONDS_PER_SLOT (12s in the Mainnet + // > configuration) have passed since the point in time identified by the timestamp parameter. + // See also + #[inline] + fn max_job_duration(&self, unix_timestamp: u64) -> Duration { + let duration_until_timestamp = duration_until(unix_timestamp); + + // safety in case clocks are bad + let duration_until_timestamp = duration_until_timestamp.min(self.config.deadline * 3); + + self.config.deadline + duration_until_timestamp + } + + /// Returns the [Instant](tokio::time::Instant) at which the job should be terminated because it + /// is considered timed out. + #[inline] + fn job_deadline(&self, unix_timestamp: u64) -> tokio::time::Instant { + tokio::time::Instant::now() + self.max_job_duration(unix_timestamp) + } } // === impl BasicPayloadJobGenerator === @@ -173,7 +197,7 @@ where self.config.compute_pending_block, ); - let until = tokio::time::Instant::now() + self.config.deadline; + let until = self.job_deadline(config.attributes.timestamp); let deadline = Box::pin(tokio::time::sleep_until(until)); Ok(BasicPayloadJob { @@ -215,6 +239,8 @@ pub struct BasicPayloadJobGeneratorConfig { /// The interval at which the job should build a new payload after the last. interval: Duration, /// The deadline for when the payload builder job should resolve. + /// + /// By default this is [SLOT_DURATION]: 12s deadline: Duration, /// Maximum number of tasks to spawn for building a payload. max_payload_tasks: usize, @@ -1215,3 +1241,12 @@ fn is_better_payload(best_payload: Option<&BuiltPayload>, new_fees: U256) -> boo true } } + +/// Returns the duration until the given unix timestamp in seconds. +/// +/// Returns `Duration::ZERO` if the given timestamp is in the past. +fn duration_until(unix_timestamp_secs: u64) -> Duration { + let unix_now = SystemTime::now().duration_since(UNIX_EPOCH).unwrap_or_default(); + let timestamp = Duration::from_secs(unix_timestamp_secs); + timestamp.saturating_sub(unix_now) +} diff --git a/crates/payload/builder/src/payload.rs b/crates/payload/builder/src/payload.rs index 57c4c82b0c51..4f1252e245a7 100644 --- a/crates/payload/builder/src/payload.rs +++ b/crates/payload/builder/src/payload.rs @@ -128,7 +128,9 @@ pub struct PayloadBuilderAttributes { pub id: PayloadId, /// Parent block to build the payload on top pub parent: B256, - /// Timestamp for the generated payload + /// Unix timestamp for the generated payload + /// + /// Number of seconds since the Unix epoch. pub timestamp: u64, /// Address of the recipient for collecting transaction fee pub suggested_fee_recipient: Address, From 5a7644709b7645e9e511d127e3f58f49933a3c31 Mon Sep 17 00:00:00 2001 From: Alexey Shekhirin Date: Wed, 29 Nov 2023 18:39:15 +0000 Subject: [PATCH 052/277] feat(storage): report backtrace only on read transactions (#5625) --- .../storage/db/src/implementation/mdbx/tx.rs | 34 +++++++++---------- crates/storage/db/src/metrics.rs | 5 +++ 2 files changed, 22 insertions(+), 17 deletions(-) diff --git a/crates/storage/db/src/implementation/mdbx/tx.rs b/crates/storage/db/src/implementation/mdbx/tx.rs index a9b4488accb3..feb25138bfea 100644 --- a/crates/storage/db/src/implementation/mdbx/tx.rs +++ b/crates/storage/db/src/implementation/mdbx/tx.rs @@ -176,28 +176,28 @@ impl MetricsHandler { } } - /// Logs the backtrace of current call if the duration that the transaction has been open is - /// more than [LONG_TRANSACTION_DURATION]. + /// Logs the backtrace of current call if the duration that the read transaction has been open + /// is more than [LONG_TRANSACTION_DURATION]. /// The backtrace is recorded and logged just once, guaranteed by `backtrace_recorded` atomic. /// /// NOTE: Backtrace is recorded using [Backtrace::force_capture], so `RUST_BACKTRACE` env var is /// not needed. fn log_backtrace_on_long_transaction(&self) { - if self.backtrace_recorded.load(Ordering::Relaxed) { - return - } - - let open_duration = self.start.elapsed(); - if open_duration > LONG_TRANSACTION_DURATION { - self.backtrace_recorded.store(true, Ordering::Relaxed); - - let backtrace = Backtrace::force_capture(); - debug!( - target: "storage::db::mdbx", - ?open_duration, - ?backtrace, - "The database transaction has been open for too long" - ); + if !self.backtrace_recorded.load(Ordering::Relaxed) && + self.transaction_mode().is_read_only() + { + let open_duration = self.start.elapsed(); + if open_duration > LONG_TRANSACTION_DURATION { + self.backtrace_recorded.store(true, Ordering::Relaxed); + + let backtrace = Backtrace::force_capture(); + debug!( + target: "storage::db::mdbx", + ?open_duration, + ?backtrace, + "The database read transaction has been open for too long" + ); + } } } } diff --git a/crates/storage/db/src/metrics.rs b/crates/storage/db/src/metrics.rs index fff6cecbd6ac..921c699bb8cc 100644 --- a/crates/storage/db/src/metrics.rs +++ b/crates/storage/db/src/metrics.rs @@ -18,6 +18,11 @@ impl TransactionMode { TransactionMode::ReadWrite => "read-write", } } + + /// Returns `true` if the transaction mode is read-only. + pub(crate) const fn is_read_only(&self) -> bool { + matches!(self, TransactionMode::ReadOnly) + } } #[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)] From 15992d9bdf432ce6860a6a728db05bfeeb80b48d Mon Sep 17 00:00:00 2001 From: chirag-bgh <76247491+chirag-bgh@users.noreply.github.com> Date: Thu, 30 Nov 2023 00:14:50 +0530 Subject: [PATCH 053/277] feat: discard txs by tx_count of sender (#4520) Co-authored-by: Dan Cline <6798349+Rjected@users.noreply.github.com> --- crates/transaction-pool/Cargo.toml | 5 + crates/transaction-pool/benches/truncate.rs | 198 ++++++++++ crates/transaction-pool/src/pool/mod.rs | 2 + crates/transaction-pool/src/pool/parked.rs | 332 +++++++++++++++-- crates/transaction-pool/src/pool/pending.rs | 337 ++++++++++++++++-- crates/transaction-pool/src/pool/txpool.rs | 15 +- .../transaction-pool/src/test_utils/mock.rs | 8 +- 7 files changed, 842 insertions(+), 55 deletions(-) create mode 100644 crates/transaction-pool/benches/truncate.rs diff --git a/crates/transaction-pool/Cargo.toml b/crates/transaction-pool/Cargo.toml index 52ceaf937447..5d00994116dc 100644 --- a/crates/transaction-pool/Cargo.toml +++ b/crates/transaction-pool/Cargo.toml @@ -74,6 +74,11 @@ optimism = [ "revm/optimism", ] +[[bench]] +name = "truncate" +required-features = ["test-utils", "arbitrary"] +harness = false + [[bench]] name = "reorder" required-features = ["test-utils", "arbitrary"] diff --git a/crates/transaction-pool/benches/truncate.rs b/crates/transaction-pool/benches/truncate.rs new file mode 100644 index 000000000000..e96ed753b3e2 --- /dev/null +++ b/crates/transaction-pool/benches/truncate.rs @@ -0,0 +1,198 @@ +use criterion::{ + criterion_group, criterion_main, measurement::WallTime, BenchmarkGroup, Criterion, +}; +use proptest::{ + prelude::*, + strategy::{Strategy, ValueTree}, + test_runner::{RngAlgorithm, TestRng, TestRunner}, +}; +use reth_primitives::{hex_literal::hex, Address}; +use reth_transaction_pool::{ + pool::{BasefeeOrd, ParkedPool, PendingPool}, + test_utils::{MockOrdering, MockTransaction, MockTransactionFactory}, + SubPoolLimit, +}; + +// constant seed to use for the rng +const SEED: [u8; 32] = hex!("1337133713371337133713371337133713371337133713371337133713371337"); + +/// Generates a set of `depth` dependent transactions, with the specified sender. Its values are +/// generated using [Arbitrary]. +fn create_transactions_for_sender( + mut runner: TestRunner, + sender: Address, + depth: usize, +) -> Vec { + // TODO: for blob truncate, this would need a flag for _only_ generating 4844 mock transactions + + // assert that depth is always greater than zero, since empty vecs do not really make sense in + // this context + assert!(depth > 0); + + // make sure these are all post-eip-1559 transactions + let mut txs = prop::collection::vec(any::(), depth) + .new_tree(&mut runner) + .unwrap() + .current(); + + let mut nonce = 0; + for tx in txs.iter_mut() { + // reject pre-eip1559 tx types, if there is a legacy tx, replace it with an eip1559 tx + if tx.is_legacy() || tx.is_eip2930() { + *tx = MockTransaction::eip1559(); + + // set fee values using arbitrary + tx.set_priority_fee(any::().new_tree(&mut runner).unwrap().current()); + tx.set_max_fee(any::().new_tree(&mut runner).unwrap().current()); + } + + tx.set_sender(sender); + tx.set_nonce(nonce); + nonce += 1; + } + + txs +} + +/// Generates many transactions, each with a different sender. The number of transactions per +/// sender is generated using [Arbitrary]. The number of senders is specified by `senders`. +/// +/// Because this uses [Arbitrary], the number of transactions per sender needs to be bounded. This +/// is done by using the `max_depth` parameter. +/// +/// This uses [create_transactions_for_sender] to generate the transactions. +fn generate_many_transactions(senders: usize, max_depth: usize) -> Vec { + let config = ProptestConfig::default(); + let rng = TestRng::from_seed(RngAlgorithm::ChaCha, &SEED); + let mut runner = TestRunner::new_with_rng(config, rng); + + let mut txs = Vec::new(); + for idx in 0..senders { + // modulo max_depth so we know it is bounded, plus one so the minimum is always 1 + let depth = any::().new_tree(&mut runner).unwrap().current() % max_depth + 1; + + // set sender to an Address determined by the sender index. This should make any necessary + // debugging easier. + let idx_slice = idx.to_be_bytes(); + + // pad with 12 bytes of zeros before rest + let addr_slice = [0u8; 12].into_iter().chain(idx_slice.into_iter()).collect::>(); + + let sender = Address::from_slice(&addr_slice); + txs.extend(create_transactions_for_sender(runner.clone(), sender, depth)); + } + + txs +} + +fn txpool_truncate(c: &mut Criterion) { + let mut group = c.benchmark_group("Transaction Pool Truncate"); + + // the first few benchmarks (5, 10, 20, 100) should cause the txpool to hit the max tx limit, + // so they are there to make sure we do not regress on best-case performance. + // + // the last few benchmarks (1000, 2000) should hit the max tx limit, at least for large enough + // depth, so these should benchmark closer to real-world performance + for senders in [5, 10, 20, 100, 1000, 2000] { + // the max we'll be benching is 20, because MAX_ACCOUNT_SLOTS so far is 16. So 20 should be + // a reasonable worst-case benchmark + for max_depth in [5, 10, 20] { + println!("Generating transactions for benchmark with {senders} unique senders and a max depth of {max_depth}..."); + let txs = generate_many_transactions(senders, max_depth); + + // benchmark parked pool + truncate_parked(&mut group, "ParkedPool", txs.clone(), senders, max_depth); + + // benchmark pending pool + truncate_pending(&mut group, "PendingPool", txs, senders, max_depth); + + // TODO: benchmark blob truncate + } + } + + let large_senders = 5000; + let max_depth = 16; + + // let's run a benchmark that includes a large number of senders and max_depth of 16 to ensure + // we hit the TXPOOL_SUBPOOL_MAX_TXS_DEFAULT limit, which is currently 10k + println!("Generating transactions for large benchmark with {large_senders} unique senders and a max depth of {max_depth}..."); + let txs = generate_many_transactions(large_senders, max_depth); + + // benchmark parked + truncate_parked(&mut group, "ParkedPool", txs.clone(), large_senders, max_depth); + + // benchmark pending + truncate_pending(&mut group, "PendingPool", txs, large_senders, max_depth); +} + +fn truncate_pending( + group: &mut BenchmarkGroup, + description: &str, + seed: Vec, + senders: usize, + max_depth: usize, +) { + let setup = || { + let mut txpool = PendingPool::new(MockOrdering::default()); + let mut f = MockTransactionFactory::default(); + + for tx in seed.iter() { + // add transactions with a basefee of zero, so they are not immediately removed + txpool.add_transaction(f.validated_arc(tx.clone()), 0); + } + txpool + }; + + let group_id = format!( + "txpool | total txs: {} | total senders: {} | max depth: {} | {}", + seed.len(), + senders, + max_depth, + description, + ); + + // for now we just use the default SubPoolLimit + group.bench_function(group_id, |b| { + b.iter_with_setup(setup, |mut txpool| { + txpool.truncate_pool(SubPoolLimit::default()); + std::hint::black_box(()); + }); + }); +} + +fn truncate_parked( + group: &mut BenchmarkGroup, + description: &str, + seed: Vec, + senders: usize, + max_depth: usize, +) { + let setup = || { + let mut txpool = ParkedPool::>::default(); + let mut f = MockTransactionFactory::default(); + + for tx in seed.iter() { + txpool.add_transaction(f.validated_arc(tx.clone())); + } + txpool + }; + + let group_id = format!( + "txpool | total txs: {} | total senders: {} | max depth: {} | {}", + seed.len(), + senders, + max_depth, + description, + ); + + // for now we just use the default SubPoolLimit + group.bench_function(group_id, |b| { + b.iter_with_setup(setup, |mut txpool| { + txpool.truncate_pool(SubPoolLimit::default()); + std::hint::black_box(()); + }); + }); +} + +criterion_group!(truncate, txpool_truncate); +criterion_main!(truncate); diff --git a/crates/transaction-pool/src/pool/mod.rs b/crates/transaction-pool/src/pool/mod.rs index cc60955470b4..1b7717a8fc5e 100644 --- a/crates/transaction-pool/src/pool/mod.rs +++ b/crates/transaction-pool/src/pool/mod.rs @@ -107,6 +107,8 @@ use crate::{ }; use alloy_rlp::Encodable; pub use listener::{AllTransactionsEvents, TransactionEvents}; +pub use parked::{BasefeeOrd, ParkedOrd, ParkedPool}; +pub use pending::PendingPool; mod best; mod blob; diff --git a/crates/transaction-pool/src/pool/parked.rs b/crates/transaction-pool/src/pool/parked.rs index fa741d32f264..da656381346c 100644 --- a/crates/transaction-pool/src/pool/parked.rs +++ b/crates/transaction-pool/src/pool/parked.rs @@ -1,8 +1,14 @@ use crate::{ - identifier::TransactionId, pool::size::SizeTracker, PoolTransaction, ValidPoolTransaction, + identifier::{SenderId, TransactionId}, + pool::size::SizeTracker, + PoolTransaction, SubPoolLimit, ValidPoolTransaction, +}; +use std::{ + cmp::Ordering, + collections::{BTreeMap, BTreeSet, BinaryHeap}, + ops::{Bound::Unbounded, Deref}, + sync::Arc, }; -use fnv::FnvHashMap; -use std::{cmp::Ordering, collections::BTreeSet, ops::Deref, sync::Arc}; /// A pool of transactions that are currently parked and are waiting for external changes (e.g. /// basefee, ancestor transactions, balance) that eventually move the transaction into the pending @@ -13,14 +19,15 @@ use std::{cmp::Ordering, collections::BTreeSet, ops::Deref, sync::Arc}; /// /// Note: This type is generic over [ParkedPool] which enforces that the underlying transaction type /// is [ValidPoolTransaction] wrapped in an [Arc]. +#[allow(missing_debug_implementations)] #[derive(Clone)] -pub(crate) struct ParkedPool { +pub struct ParkedPool { /// Keeps track of transactions inserted in the pool. /// /// This way we can determine when transactions were submitted to the pool. submission_id: u64, /// _All_ Transactions that are currently inside the pool grouped by their identifier. - by_id: FnvHashMap>, + by_id: BTreeMap>, /// All transactions sorted by their order function. /// /// The higher, the better. @@ -39,7 +46,7 @@ impl ParkedPool { /// # Panics /// /// If the transaction is already included. - pub(crate) fn add_transaction(&mut self, tx: Arc>) { + pub fn add_transaction(&mut self, tx: Arc>) { let id = *tx.id(); assert!( !self.by_id.contains_key(&id), @@ -51,6 +58,7 @@ impl ParkedPool { // keep track of size self.size_of += tx.size(); + // update or create sender entry let transaction = ParkedPoolTransaction { submission_id, transaction: tx.into() }; self.by_id.insert(id, transaction.clone()); @@ -79,10 +87,106 @@ impl ParkedPool { Some(tx.transaction.into()) } - /// Removes the worst transaction from this pool. - pub(crate) fn pop_worst(&mut self) -> Option>> { - let worst = self.best.iter().next().map(|tx| *tx.transaction.id())?; - self.remove_transaction(&worst) + /// Get transactions by sender + pub(crate) fn get_txs_by_sender(&self, sender: SenderId) -> Vec { + self.by_id + .range((sender.start_bound(), Unbounded)) + .take_while(move |(other, _)| sender == other.sender) + .map(|(_, tx)| *tx.transaction.id()) + .collect() + } + + /// Returns sender ids sorted by each sender's last submission id. Senders with older last + /// submission ids are first. Note that _last_ submission ids are the newest submission id for + /// that sender, so this sorts senders by the last time they submitted a transaction in + /// descending order. Senders that have least recently submitted a transaction are first. + /// + /// Similar to `Heartbeat` in Geth + pub fn get_senders_by_submission_id(&self) -> Vec { + // iterate through by_id, and get the last submission id for each sender + let senders = self + .by_id + .iter() + .fold(Vec::new(), |mut set: Vec, (_, tx)| { + if let Some(last) = set.last_mut() { + // sort by last + if last.sender_id == tx.transaction.sender_id() { + if last.submission_id < tx.submission_id { + // update last submission id + last.submission_id = tx.submission_id; + } + } else { + // new entry + set.push(SubmissionSenderId::new( + tx.transaction.sender_id(), + tx.submission_id, + )); + } + } else { + // first entry + set.push(SubmissionSenderId::new(tx.transaction.sender_id(), tx.submission_id)); + } + set + }) + .into_iter() + // sort by submission id + .collect::>(); + + // sort s.t. senders with older submission ids are first + senders.into_sorted_vec() + } + + /// Truncates the pool by removing transactions, until the given [SubPoolLimit] has been met. + /// + /// This is done by first ordering senders by the last time they have submitted a transaction, + /// using [get_senders_by_submission_id](ParkedPool::get_senders_by_submission_id) to determine + /// this ordering. + /// + /// Then, for each sender, all transactions for that sender are removed, until the pool limits + /// have been met. + /// + /// Any removed transactions are returned. + pub fn truncate_pool( + &mut self, + limit: SubPoolLimit, + ) -> Vec>> { + if self.len() <= limit.max_txs { + // if we are below the limits, we don't need to drop anything + return Vec::new() + } + + let mut removed = Vec::new(); + let mut sender_ids = self.get_senders_by_submission_id(); + let queued = self.len(); + let mut drop = queued - limit.max_txs; + + while drop > 0 && !sender_ids.is_empty() { + // SAFETY: This will not panic due to `!addresses.is_empty()` + let sender_id = sender_ids.pop().unwrap().sender_id; + let mut list = self.get_txs_by_sender(sender_id); + + // Drop all transactions if they are less than the overflow + if list.len() <= drop { + for txid in &list { + if let Some(tx) = self.remove_transaction(txid) { + removed.push(tx); + } + } + drop -= list.len(); + continue + } + + // Otherwise drop only last few transactions + // SAFETY: This will not panic because `list.len() > drop` + for txid in list.split_off(drop) { + if let Some(tx) = self.remove_transaction(&txid) { + removed.push(tx); + } + drop -= 1; + } + } + + removed } fn next_id(&mut self) -> u64 { @@ -226,10 +330,40 @@ impl Ord for ParkedPoolTransaction { } } +/// Includes a [SenderId] and `submission_id`. This is used to sort senders by their last +/// submission id. +#[derive(Debug, PartialEq, Eq, Clone)] +pub struct SubmissionSenderId { + /// The sender id + pub(crate) sender_id: SenderId, + /// The submission id + pub(crate) submission_id: u64, +} + +impl SubmissionSenderId { + /// Creates a new [SubmissionSenderId] based on the [SenderId] and `submission_id`. + fn new(sender_id: SenderId, submission_id: u64) -> Self { + Self { sender_id, submission_id } + } +} + +impl Ord for SubmissionSenderId { + fn cmp(&self, other: &Self) -> Ordering { + // Reverse ordering for `submission_id` + other.submission_id.cmp(&self.submission_id) + } +} + +impl PartialOrd for SubmissionSenderId { + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.cmp(other)) + } +} + /// Helper trait used for custom `Ord` wrappers around a transaction. /// /// This is effectively a wrapper for `Arc` with custom `Ord` implementation. -pub(crate) trait ParkedOrd: +pub trait ParkedOrd: Ord + Clone + From>> @@ -294,7 +428,7 @@ macro_rules! impl_ord_wrapper { /// /// Caution: This assumes all transaction in the `BaseFee` sub-pool have a fee value. #[derive(Debug)] -pub(crate) struct BasefeeOrd(Arc>); +pub struct BasefeeOrd(Arc>); impl_ord_wrapper!(BasefeeOrd); @@ -332,6 +466,8 @@ impl Ord for QueuedOrd { mod tests { use super::*; use crate::test_utils::{MockTransaction, MockTransactionFactory}; + use reth_primitives::address; + use std::collections::HashSet; #[test] fn test_enforce_parked_basefee() { @@ -359,7 +495,7 @@ mod tests { let root_tx = f.validated_arc(t.clone()); pool.add_transaction(root_tx.clone()); - let descendant_tx = f.validated_arc(t.inc_nonce().inc_price()); + let descendant_tx = f.validated_arc(t.inc_nonce().decr_price()); pool.add_transaction(descendant_tx.clone()); assert!(pool.by_id.contains_key(root_tx.id())); @@ -368,22 +504,178 @@ mod tests { let removed = pool.enforce_basefee(u64::MAX); assert!(removed.is_empty()); - + assert_eq!(pool.len(), 2); // two dependent tx in the pool with decreasing fee { + // TODO: test change might not be intended, re review let mut pool2 = pool.clone(); - let removed = pool2.enforce_basefee(descendant_tx.max_fee_per_gas() as u64); + let removed = pool2.enforce_basefee(root_tx.max_fee_per_gas() as u64); assert_eq!(removed.len(), 1); assert_eq!(pool2.len(), 1); - // descendant got popped - assert!(pool2.by_id.contains_key(root_tx.id())); - assert!(!pool2.by_id.contains_key(descendant_tx.id())); + // root got popped - descendant should be skipped + assert!(!pool2.by_id.contains_key(root_tx.id())); + assert!(pool2.by_id.contains_key(descendant_tx.id())); } - // remove root transaction via root tx fee - let removed = pool.enforce_basefee(root_tx.max_fee_per_gas() as u64); + // remove root transaction via descendant tx fee + let removed = pool.enforce_basefee(descendant_tx.max_fee_per_gas() as u64); assert_eq!(removed.len(), 2); assert!(pool.is_empty()); } + + #[test] + fn truncate_parked_by_submission_id() { + // this test ensures that we evict from the pending pool by sender + let mut f = MockTransactionFactory::default(); + let mut pool = ParkedPool::>::default(); + + let a = address!("000000000000000000000000000000000000000a"); + let b = address!("000000000000000000000000000000000000000b"); + let c = address!("000000000000000000000000000000000000000c"); + let d = address!("000000000000000000000000000000000000000d"); + + // TODO: make creating these mock tx chains easier + // create a chain of transactions by sender A, B, C + let a1 = MockTransaction::eip1559().with_sender(a); + let a2 = a1.next(); + let a3 = a2.next(); + let a4 = a3.next(); + + let b1 = MockTransaction::eip1559().with_sender(b); + let b2 = b1.next(); + let b3 = b2.next(); + + // C has the same number of txs as B + let c1 = MockTransaction::eip1559().with_sender(c); + let c2 = c1.next(); + let c3 = c2.next(); + + let d1 = MockTransaction::eip1559().with_sender(d); + + // just construct a list of all txs to add + let expected_parked = vec![c1.clone(), c2.clone(), c3.clone(), d1.clone()] + .into_iter() + .map(|tx| (tx.sender(), tx.nonce())) + .collect::>(); + + // we expect the truncate operation to go through the senders with the most txs, removing + // txs based on when they were submitted, removing the oldest txs first, until the pool is + // not over the limit + let expected_removed = vec![ + a1.clone(), + a2.clone(), + a3.clone(), + a4.clone(), + b1.clone(), + b2.clone(), + b3.clone(), + ] + .into_iter() + .map(|tx| (tx.sender(), tx.nonce())) + .collect::>(); + let all_txs = vec![a1, a2, a3, a4, b1, b2, b3, c1, c2, c3, d1]; + + // add all the transactions to the pool + for tx in all_txs { + pool.add_transaction(f.validated_arc(tx)); + } + + // we should end up with the most recently submitted transactions + let pool_limit = SubPoolLimit { max_txs: 4, max_size: usize::MAX }; + + // truncate the pool + let removed = pool.truncate_pool(pool_limit); + assert_eq!(removed.len(), expected_removed.len()); + + // get the inner txs from the removed txs + let removed = + removed.into_iter().map(|tx| (tx.sender(), tx.nonce())).collect::>(); + assert_eq!(removed, expected_removed); + + // get the parked pool + let parked = pool.all().collect::>(); + assert_eq!(parked.len(), expected_parked.len()); + + // get the inner txs from the parked txs + let parked = parked.into_iter().map(|tx| (tx.sender(), tx.nonce())).collect::>(); + assert_eq!(parked, expected_parked); + } + + #[test] + fn test_senders_by_submission_id() { + // this test ensures that we evict from the pending pool by sender + let mut f = MockTransactionFactory::default(); + let mut pool = ParkedPool::>::default(); + + let a = address!("000000000000000000000000000000000000000a"); + let b = address!("000000000000000000000000000000000000000b"); + let c = address!("000000000000000000000000000000000000000c"); + let d = address!("000000000000000000000000000000000000000d"); + + // create a chain of transactions by sender A, B, C + let a1 = MockTransaction::eip1559().with_sender(a); + let a2 = a1.next(); + let a3 = a2.next(); + let a4 = a3.next(); + + let b1 = MockTransaction::eip1559().with_sender(b); + let b2 = b1.next(); + let b3 = b2.next(); + + // C has the same number of txs as B + let c1 = MockTransaction::eip1559().with_sender(c); + let c2 = c1.next(); + let c3 = c2.next(); + + let d1 = MockTransaction::eip1559().with_sender(d); + + let all_txs = vec![ + a1.clone(), + a2.clone(), + a3.clone(), + a4.clone(), + b1.clone(), + b2.clone(), + b3.clone(), + c1.clone(), + c2.clone(), + c3.clone(), + d1.clone(), + ]; + + // add all the transactions to the pool + for tx in all_txs { + pool.add_transaction(f.validated_arc(tx)); + } + + // get senders by submission id - a4, b3, c3, d1, reversed + let senders = pool + .get_senders_by_submission_id() + .into_iter() + .map(|s| s.sender_id) + .collect::>(); + assert_eq!(senders.len(), 4); + let expected_senders = + vec![d, c, b, a].into_iter().map(|s| f.ids.sender_id(&s).unwrap()).collect::>(); + assert_eq!(senders, expected_senders); + + let mut pool = ParkedPool::>::default(); + let all_txs = vec![a1, b1, c1, d1, a2, b2, c2, a3, b3, c3, a4]; + + // add all the transactions to the pool + for tx in all_txs { + pool.add_transaction(f.validated_arc(tx)); + } + + let senders = pool + .get_senders_by_submission_id() + .into_iter() + .map(|s| s.sender_id) + .collect::>(); + assert_eq!(senders.len(), 4); + let expected_senders = + vec![a, c, b, d].into_iter().map(|s| f.ids.sender_id(&s).unwrap()).collect::>(); + assert_eq!(senders, expected_senders); + } } diff --git a/crates/transaction-pool/src/pool/pending.rs b/crates/transaction-pool/src/pool/pending.rs index 31acc91327b9..34c2d0f711db 100644 --- a/crates/transaction-pool/src/pool/pending.rs +++ b/crates/transaction-pool/src/pool/pending.rs @@ -1,7 +1,7 @@ use crate::{ identifier::TransactionId, pool::{best::BestTransactions, size::SizeTracker}, - Priority, TransactionOrdering, ValidPoolTransaction, + Priority, SubPoolLimit, TransactionOrdering, ValidPoolTransaction, }; use crate::pool::best::BestTransactionsWithBasefee; @@ -22,8 +22,9 @@ use tokio::sync::broadcast; /// /// Once an `independent` transaction was executed it *unlocks* the next nonce, if this transaction /// is also pending, then this will be moved to the `independent` queue. +#[allow(missing_debug_implementations)] #[derive(Clone)] -pub(crate) struct PendingPool { +pub struct PendingPool { /// How to order transactions. ordering: T, /// Keeps track of transactions inserted in the pool. @@ -34,6 +35,11 @@ pub(crate) struct PendingPool { by_id: BTreeMap>, /// _All_ transactions sorted by priority all: BTreeSet>, + /// The highest nonce transactions for each sender - like the `independent` set, but the + /// highest instead of lowest nonce. + /// + /// Sorted by their scoring value. + highest_nonces: BTreeSet>, /// Independent transactions that can be included directly and don't require other /// transactions. /// @@ -52,7 +58,7 @@ pub(crate) struct PendingPool { impl PendingPool { /// Create a new pool instance. - pub(crate) fn new(ordering: T) -> Self { + pub fn new(ordering: T) -> Self { let (new_transaction_notifier, _) = broadcast::channel(200); Self { ordering, @@ -60,6 +66,7 @@ impl PendingPool { by_id: Default::default(), all: Default::default(), independent_transactions: Default::default(), + highest_nonces: Default::default(), size_of: Default::default(), new_transaction_notifier, } @@ -73,6 +80,7 @@ impl PendingPool { /// Returns all transactions by id. fn clear_transactions(&mut self) -> BTreeMap> { self.independent_transactions.clear(); + self.highest_nonces.clear(); self.all.clear(); self.size_of.reset(); std::mem::take(&mut self.by_id) @@ -184,9 +192,7 @@ impl PendingPool { } } else { self.size_of += tx.transaction.size(); - if self.ancestor(&id).is_none() { - self.independent_transactions.insert(tx.clone()); - } + self.update_independents_and_highest_nonces(&tx, &id); self.all.insert(tx.clone()); self.by_id.insert(id, tx); } @@ -232,9 +238,7 @@ impl PendingPool { tx.priority = self.ordering.priority(&tx.transaction.transaction, base_fee); self.size_of += tx.transaction.size(); - if self.ancestor(&id).is_none() { - self.independent_transactions.insert(tx.clone()); - } + self.update_independents_and_highest_nonces(&tx, &id); self.all.insert(tx.clone()); self.by_id.insert(id, tx); } @@ -243,6 +247,27 @@ impl PendingPool { removed } + /// Updates the independent transaction and highest nonces set, assuming the given transaction + /// is being _added_ to the pool. + fn update_independents_and_highest_nonces( + &mut self, + tx: &PendingTransaction, + tx_id: &TransactionId, + ) { + let ancestor_id = tx_id.unchecked_ancestor(); + if let Some(ancestor) = ancestor_id.and_then(|id| self.by_id.get(&id)) { + // the transaction already has an ancestor, so we only need to ensure that the + // highest nonces set actually contains the highest nonce for that sender + self.highest_nonces.remove(ancestor); + self.highest_nonces.insert(tx.clone()); + } else { + // If there's __no__ ancestor in the pool, then this transaction is independent, this is + // guaranteed because this pool is gapless. + self.independent_transactions.insert(tx.clone()); + self.highest_nonces.insert(tx.clone()); + } + } + /// Returns the ancestor the given transaction, the transaction with `nonce - 1`. /// /// Note: for a transaction with nonce higher than the current on chain nonce this will always @@ -256,7 +281,7 @@ impl PendingPool { /// # Panics /// /// if the transaction is already included - pub(crate) fn add_transaction( + pub fn add_transaction( &mut self, tx: Arc>, base_fee: u64, @@ -276,11 +301,7 @@ impl PendingPool { let priority = self.ordering.priority(&tx.transaction, base_fee); let tx = PendingTransaction { submission_id, transaction: tx, priority }; - // If there's __no__ ancestor in the pool, then this transaction is independent, this is - // guaranteed because this pool is gapless. - if self.ancestor(&tx_id).is_none() { - self.independent_transactions.insert(tx.clone()); - } + self.update_independents_and_highest_nonces(&tx, &tx_id); self.all.insert(tx.clone()); // send the new transaction to any existing pendingpool snapshot iterators @@ -316,6 +337,12 @@ impl PendingPool { self.size_of -= tx.transaction.size(); self.all.remove(&tx); self.independent_transactions.remove(&tx); + + // switch out for the next ancestor if there is one + self.highest_nonces.remove(&tx); + if let Some(ancestor) = self.ancestor(id) { + self.highest_nonces.insert(ancestor.clone()); + } Some(tx.transaction) } @@ -325,10 +352,121 @@ impl PendingPool { id } - /// Removes the worst transaction from this pool. - pub(crate) fn pop_worst(&mut self) -> Option>> { - let worst = self.all.iter().next().map(|tx| *tx.transaction.id())?; - self.remove_transaction(&worst) + /// Traverses the pool, starting at the highest nonce set, removing the transactions which + /// would put the pool under the specified limits. + /// + /// This attempts to remove transactions by roughly the same amount for each sender. This is + /// done by removing the highest-nonce transactions for each sender. + /// + /// If the `remove_locals` flag is unset, transactions will be removed per-sender until a + /// local transaction is the highest nonce transaction for that sender. If all senders have a + /// local highest-nonce transaction, the pool will not be truncated further. + /// + /// Otherwise, if the `remove_locals` flag is set, transactions will be removed per-sender + /// until the pool is under the given limits. + /// + /// Any removed transactions will be added to the `end_removed` vector. + pub fn remove_to_limit( + &mut self, + limit: &SubPoolLimit, + remove_locals: bool, + end_removed: &mut Vec>>, + ) { + // This serves as a termination condition for the loop - it represents the number of + // _valid_ unique senders that might have descendants in the pool. + // + // If `remove_locals` is false, a value of zero means that there are no non-local txs in the + // pool that can be removed. + // + // If `remove_locals` is true, a value of zero means that there are no txs in the pool that + // can be removed. + let mut non_local_senders = self.highest_nonces.len(); + + // keep track of unique senders from previous iterations, to understand how many unique + // senders were removed in the last iteration + let mut unique_senders = self.highest_nonces.len(); + + // keep track of transactions to remove and how many have been removed so far + let original_length = self.len(); + let mut removed = Vec::new(); + let mut total_removed = 0; + + // track total `size` of transactions to remove + let original_size = self.size(); + let mut total_size = 0; + + loop { + // check how many unique senders were removed last iteration + let unique_removed = unique_senders - self.highest_nonces.len(); + + // the new number of unique senders + unique_senders = self.highest_nonces.len(); + non_local_senders -= unique_removed; + + // we can re-use the temp array + removed.clear(); + + // loop through the highest nonces set, removing transactions until we reach the limit + for tx in self.highest_nonces.iter() { + // return early if the pool is under limits + if original_size - total_size <= limit.max_size && + original_length - total_removed <= limit.max_txs || + non_local_senders == 0 + { + // need to remove remaining transactions before exiting + for id in &removed { + if let Some(tx) = self.remove_transaction(id) { + end_removed.push(tx); + } + } + + return + } + + if !remove_locals && tx.transaction.is_local() { + non_local_senders -= 1; + continue + } + + total_size += tx.transaction.size(); + total_removed += 1; + removed.push(*tx.transaction.id()); + } + + // remove the transactions from this iteration + for id in &removed { + if let Some(tx) = self.remove_transaction(id) { + end_removed.push(tx); + } + } + } + } + + /// Truncates the pool to the given [SubPoolLimit], removing transactions until the subpool + /// limits are met. + /// + /// This attempts to remove transactions by rougly the same amount for each sender. For more + /// information on this exact process see docs for + /// [remove_to_limit](PendingPool::remove_to_limit). + /// + /// This first truncates all of the non-local transactions in the pool. If the subpool is still + /// not under the limit, this truncates the entire pool, including non-local transactions. The + /// removed transactions are returned. + pub fn truncate_pool( + &mut self, + limit: SubPoolLimit, + ) -> Vec>> { + let mut removed = Vec::new(); + self.remove_to_limit(&limit, false, &mut removed); + + if self.size() <= limit.max_size && self.len() <= limit.max_txs { + return removed + } + + // now repeat for local transactions + self.remove_to_limit(&limit, true, &mut removed); + + removed } /// The reported size of all transactions in this pool. @@ -361,6 +499,14 @@ impl PendingPool { self.independent_transactions.len() <= self.all.len(), "independent.len() > all.len()" ); + assert!( + self.highest_nonces.len() <= self.all.len(), + "independent_descendants.len() > all.len()" + ); + assert!( + self.highest_nonces.len() == self.independent_transactions.len(), + "independent.len() = independent_descendants.len()" + ); } } @@ -418,6 +564,10 @@ impl Ord for PendingTransaction { #[cfg(test)] mod tests { + use std::collections::HashSet; + + use reth_primitives::address; + use super::*; use crate::{ test_utils::{MockOrdering, MockTransaction, MockTransactionFactory}, @@ -458,6 +608,7 @@ mod tests { assert_eq!(pool.len(), 2); assert_eq!(pool.independent_transactions.len(), 1); + assert_eq!(pool.highest_nonces.len(), 1); let removed = pool.update_base_fee(0); assert!(removed.is_empty()); @@ -478,6 +629,7 @@ mod tests { let removed = pool.update_base_fee((root_tx.max_fee_per_gas() + 1) as u64); assert_eq!(removed.len(), 2); assert!(pool.is_empty()); + pool.assert_invariants(); } #[test] @@ -492,6 +644,151 @@ mod tests { pool.add_transaction(f.validated_arc(t2), 0); // First transaction should be evicted. - assert_eq!(pool.pop_worst().map(|tx| *tx.hash()), Some(*t.hash())); + assert_eq!( + pool.highest_nonces.iter().next().map(|tx| *tx.transaction.hash()), + Some(*t.hash()) + ); + + // truncate pool with max size = 1, ensure it's the same transaction + let removed = pool.truncate_pool(SubPoolLimit { max_txs: 1, max_size: usize::MAX }); + assert_eq!(removed.len(), 1); + assert_eq!(removed[0].hash(), t.hash()); + } + + #[test] + fn correct_independent_descendants() { + // this test ensures that we set the right highest nonces set for each sender + let mut f = MockTransactionFactory::default(); + let mut pool = PendingPool::new(MockOrdering::default()); + + let a = address!("000000000000000000000000000000000000000a"); + let b = address!("000000000000000000000000000000000000000b"); + let c = address!("000000000000000000000000000000000000000c"); + let d = address!("000000000000000000000000000000000000000d"); + + // create a chain of transactions by sender A, B, C + let a1 = MockTransaction::eip1559().with_sender(a); + let a2 = a1.next(); + let a3 = a2.next(); + let a4 = a3.next(); + + let b1 = MockTransaction::eip1559().with_sender(b); + let b2 = b1.next(); + let b3 = b2.next(); + + // C has the same number of txs as B + let c1 = MockTransaction::eip1559().with_sender(c); + let c2 = c1.next(); + let c3 = c2.next(); + + let d1 = MockTransaction::eip1559().with_sender(d); + + // add all the transactions to the pool + let all_txs = + vec![a1, a2, a3, a4.clone(), b1, b2, b3.clone(), c1, c2, c3.clone(), d1.clone()]; + for tx in all_txs { + pool.add_transaction(f.validated_arc(tx), 0); + } + + pool.assert_invariants(); + + // the independent set is the roots of each of these tx chains, these are the highest + // nonces for each sender + let expected_highest_nonces = + vec![d1, c3, b3, a4].iter().map(|tx| (tx.sender(), tx.nonce())).collect::>(); + let actual_highest_nonces = pool + .highest_nonces + .iter() + .map(|tx| (tx.transaction.sender(), tx.transaction.nonce())) + .collect::>(); + assert_eq!(expected_highest_nonces, actual_highest_nonces); + pool.assert_invariants(); + } + + #[test] + fn truncate_by_sender() { + // this test ensures that we evict from the pending pool by sender + let mut f = MockTransactionFactory::default(); + let mut pool = PendingPool::new(MockOrdering::default()); + + let a = address!("000000000000000000000000000000000000000a"); + let b = address!("000000000000000000000000000000000000000b"); + let c = address!("000000000000000000000000000000000000000c"); + let d = address!("000000000000000000000000000000000000000d"); + + // TODO: make creating these mock tx chains easier + // create a chain of transactions by sender A, B, C + let a1 = MockTransaction::eip1559().with_sender(a); + let a2 = a1.next(); + let a3 = a2.next(); + let a4 = a3.next(); + + let b1 = MockTransaction::eip1559().with_sender(b); + let b2 = b1.next(); + let b3 = b2.next(); + + // C has the same number of txs as B + let c1 = MockTransaction::eip1559().with_sender(c); + let c2 = c1.next(); + let c3 = c2.next(); + + let d1 = MockTransaction::eip1559().with_sender(d); + + // just construct a list of all txs to add + let expected_pending = vec![a1.clone(), b1.clone(), c1.clone(), a2.clone()] + .into_iter() + .map(|tx| (tx.sender(), tx.nonce())) + .collect::>(); + let expected_removed = vec![ + d1.clone(), + c3.clone(), + b3.clone(), + a4.clone(), + c2.clone(), + b2.clone(), + a3.clone(), + ] + .into_iter() + .map(|tx| (tx.sender(), tx.nonce())) + .collect::>(); + let all_txs = vec![a1, a2, a3, a4.clone(), b1, b2, b3, c1, c2, c3, d1]; + + // add all the transactions to the pool + for tx in all_txs { + pool.add_transaction(f.validated_arc(tx), 0); + } + + // sanity check, make sure everything checks out + pool.assert_invariants(); + + // let's set the max total txs to 4, since we remove txs for each sender first, we remove + // in this order: + // * d1, c3, b3, a4 + // * c2, b2, a3 + // + // and we are left with: + // * a1, a2 + // * b1 + // * c1 + let pool_limit = SubPoolLimit { max_txs: 4, max_size: usize::MAX }; + + // truncate the pool + let removed = pool.truncate_pool(pool_limit); + pool.assert_invariants(); + assert_eq!(removed.len(), expected_removed.len()); + + // get the inner txs from the removed txs + let removed = + removed.into_iter().map(|tx| (tx.sender(), tx.nonce())).collect::>(); + assert_eq!(removed, expected_removed); + + // get the pending pool + let pending = pool.all().collect::>(); + assert_eq!(pending.len(), expected_pending.len()); + + // get the inner txs from the pending txs + let pending = + pending.into_iter().map(|tx| (tx.sender(), tx.nonce())).collect::>(); + assert_eq!(pending, expected_pending); } } diff --git a/crates/transaction-pool/src/pool/txpool.rs b/crates/transaction-pool/src/pool/txpool.rs index dfc2bf6c7c0d..e9b5acadbcdb 100644 --- a/crates/transaction-pool/src/pool/txpool.rs +++ b/crates/transaction-pool/src/pool/txpool.rs @@ -793,18 +793,9 @@ impl TxPool { .$limit .is_exceeded($this.$pool.len(), $this.$pool.size()) { - // pops the worst transaction from the sub-pool - if let Some(tx) = $this.$pool.pop_worst() { - let id = tx.transaction_id; - - // now that the tx is removed from the sub-pool, we need to remove it also from the total set - $this.all_transactions.remove_transaction(&id); - - // record the removed transaction - removed.push(tx); - - // this might have introduced a nonce gap, so we also discard any descendants - $this.remove_descendants(&id, &mut $removed); + removed = $this.$pool.truncate_pool($this.config.$limit.clone()); + for tx in removed.clone().iter() { + $this.remove_descendants(tx.id(), &mut $removed); } } diff --git a/crates/transaction-pool/src/test_utils/mock.rs b/crates/transaction-pool/src/test_utils/mock.rs index d289597d2b65..ea1a0509d1d1 100644 --- a/crates/transaction-pool/src/test_utils/mock.rs +++ b/crates/transaction-pool/src/test_utils/mock.rs @@ -898,8 +898,8 @@ impl proptest::arbitrary::Arbitrary for MockTransaction { fn arbitrary_with(_: Self::Parameters) -> Self::Strategy { use proptest::prelude::{any, Strategy}; - any::<(Transaction, Address, B256, BlobTransactionSidecar)>() - .prop_map(|(tx, sender, tx_hash, sidecar)| match &tx { + any::<(Transaction, Address, B256)>() + .prop_map(|(tx, sender, tx_hash)| match &tx { Transaction::Legacy(TxLegacy { nonce, gas_price, @@ -972,7 +972,9 @@ impl proptest::arbitrary::Arbitrary for MockTransaction { value: (*value).into(), input: (*input).clone(), accesslist: (*access_list).clone(), - sidecar, + // only generate a sidecar if it is a 4844 tx - also for the sake of + // performance just use a default sidecar + sidecar: BlobTransactionSidecar::default(), }, _ => unimplemented!(), }) From 471c28e889083ec5e8f5bbdc0b57ede8023471dc Mon Sep 17 00:00:00 2001 From: Alexey Shekhirin Date: Wed, 29 Nov 2023 19:29:39 +0000 Subject: [PATCH 054/277] feat(pruner): limit number of blocks to prune per run (#5627) --- bin/reth/src/node/mod.rs | 6 +++- .../consensus/beacon/src/engine/test_utils.rs | 1 + crates/prune/src/pruner.rs | 31 +++++++++++++------ 3 files changed, 28 insertions(+), 10 deletions(-) diff --git a/bin/reth/src/node/mod.rs b/bin/reth/src/node/mod.rs index de078146f2a3..815b77a71cbb 100644 --- a/bin/reth/src/node/mod.rs +++ b/bin/reth/src/node/mod.rs @@ -289,9 +289,10 @@ impl NodeCommand { Arc::clone(&consensus), EvmProcessorFactory::new(self.chain.clone()), ); + let tree_config = BlockchainTreeConfig::default(); let tree = BlockchainTree::new( tree_externals, - BlockchainTreeConfig::default(), + tree_config, prune_config.clone().map(|config| config.segments), )? .with_sync_metrics_tx(sync_metrics_tx.clone()); @@ -473,6 +474,7 @@ impl NodeCommand { let mut pruner = self.build_pruner( &prune_config, db.clone(), + tree_config, snapshotter.highest_snapshot_receiver(), ); @@ -975,6 +977,7 @@ impl NodeCommand { &self, config: &PruneConfig, db: DB, + tree_config: BlockchainTreeConfig, highest_snapshots_rx: HighestSnapshotsTracker, ) -> Pruner { let segments = SegmentSet::default() @@ -1012,6 +1015,7 @@ impl NodeCommand { segments.into_vec(), config.block_interval, self.chain.prune_delete_limit, + tree_config.max_reorg_depth() as usize, highest_snapshots_rx, ) } diff --git a/crates/consensus/beacon/src/engine/test_utils.rs b/crates/consensus/beacon/src/engine/test_utils.rs index 781161bb3a02..0622c7734223 100644 --- a/crates/consensus/beacon/src/engine/test_utils.rs +++ b/crates/consensus/beacon/src/engine/test_utils.rs @@ -533,6 +533,7 @@ where vec![], 5, self.base_config.chain_spec.prune_delete_limit, + config.max_reorg_depth() as usize, watch::channel(None).1, ); diff --git a/crates/prune/src/pruner.rs b/crates/prune/src/pruner.rs index 12214d6d3e26..a74825fd4b90 100644 --- a/crates/prune/src/pruner.rs +++ b/crates/prune/src/pruner.rs @@ -34,6 +34,9 @@ pub struct Pruner { previous_tip_block_number: Option, /// Maximum total entries to prune (delete from database) per block. delete_limit: usize, + /// Maximum number of blocks to be pruned per run, as an additional restriction to + /// `previous_tip_block_number`. + prune_max_blocks_per_run: usize, #[allow(dead_code)] highest_snapshots_tracker: HighestSnapshotsTracker, metrics: Metrics, @@ -48,6 +51,7 @@ impl Pruner { segments: Vec>>, min_block_interval: usize, delete_limit: usize, + prune_max_blocks_per_run: usize, highest_snapshots_tracker: HighestSnapshotsTracker, ) -> Self { Self { @@ -56,6 +60,7 @@ impl Pruner { min_block_interval, previous_tip_block_number: None, delete_limit, + prune_max_blocks_per_run, highest_snapshots_tracker, metrics: Metrics::default(), listeners: Default::default(), @@ -87,14 +92,22 @@ impl Pruner { // TODO(alexey): prune snapshotted segments of data (headers, transactions) let highest_snapshots = *self.highest_snapshots_tracker.borrow(); - // Multiply `delete_limit` (number of row to delete per block) by number of blocks since - // last pruner run. `previous_tip_block_number` is close to `tip_block_number`, usually - // within `self.block_interval` blocks, so `delete_limit` will not be too high. Also see - // docs for `self.previous_tip_block_number`. - let mut delete_limit = self.delete_limit * - self.previous_tip_block_number - .map_or(1, |previous_tip_block_number| tip_block_number - previous_tip_block_number) - as usize; + // Multiply `self.delete_limit` (number of rows to delete per block) by number of blocks + // since last pruner run. `self.previous_tip_block_number` is close to + // `tip_block_number`, usually within `self.block_interval` blocks, so + // `delete_limit` will not be too high. If it's too high, we additionally limit it by + // `self.prune_max_blocks_per_run`. + // + // Also see docs for `self.previous_tip_block_number`. + let blocks_since_last_run = + (self.previous_tip_block_number.map_or(1, |previous_tip_block_number| { + // Saturating subtraction is needed for the case when the chain was reverted, + // meaning current block number might be less than the previous tip + // block number. + tip_block_number.saturating_sub(previous_tip_block_number) as usize + })) + .min(self.prune_max_blocks_per_run); + let mut delete_limit = self.delete_limit * blocks_since_last_run; for segment in &self.segments { if delete_limit == 0 { @@ -259,7 +272,7 @@ mod tests { #[test] fn is_pruning_needed() { let db = create_test_rw_db(); - let mut pruner = Pruner::new(db, MAINNET.clone(), vec![], 5, 0, watch::channel(None).1); + let mut pruner = Pruner::new(db, MAINNET.clone(), vec![], 5, 0, 5, watch::channel(None).1); // No last pruned block number was set before let first_block_number = 1; From e9dfd059dd90510e6124650ec8f256d93ae22236 Mon Sep 17 00:00:00 2001 From: Vitaly Drogan Date: Wed, 29 Nov 2023 22:22:04 +0100 Subject: [PATCH 055/277] perf(trie): prealloc in `Nibble::unpack` (#5629) --- crates/primitives/Cargo.toml | 4 ++++ crates/primitives/benches/nibbles.rs | 19 +++++++++++++++++++ crates/primitives/src/trie/nibbles.rs | 12 +++++------- 3 files changed, 28 insertions(+), 7 deletions(-) create mode 100644 crates/primitives/benches/nibbles.rs diff --git a/crates/primitives/Cargo.toml b/crates/primitives/Cargo.toml index 8204636cec5b..892040c3a340 100644 --- a/crates/primitives/Cargo.toml +++ b/crates/primitives/Cargo.toml @@ -99,3 +99,7 @@ harness = false name = "trie_root" required-features = ["arbitrary", "test-utils"] harness = false + +[[bench]] +name = "nibbles" +harness = false diff --git a/crates/primitives/benches/nibbles.rs b/crates/primitives/benches/nibbles.rs new file mode 100644 index 000000000000..abe9801a34c1 --- /dev/null +++ b/crates/primitives/benches/nibbles.rs @@ -0,0 +1,19 @@ +use criterion::{criterion_group, criterion_main, Criterion}; +use reth_primitives::trie::Nibbles; + +/// Benchmarks the nibble unpacking. +pub fn nibbles_benchmark(c: &mut Criterion) { + c.bench_function("Nibbles unpack", |b| { + let raw = (1..=32).collect::>(); + b.iter(|| { + Nibbles::unpack(&raw); + }) + }); +} + +criterion_group! { + name = benches; + config = Criterion::default(); + targets = nibbles_benchmark +} +criterion_main!(benches); diff --git a/crates/primitives/src/trie/nibbles.rs b/crates/primitives/src/trie/nibbles.rs index 876b9ff9a652..d3d862966df5 100644 --- a/crates/primitives/src/trie/nibbles.rs +++ b/crates/primitives/src/trie/nibbles.rs @@ -94,14 +94,12 @@ impl Nibbles { /// Take a byte array (slice or vector) as input and convert it into a [Nibbles] struct /// containing the nibbles (half-bytes or 4 bits) that make up the input byte data. pub fn unpack>(data: T) -> Self { - Nibbles { - hex_data: Bytes::from( - data.as_ref() - .iter() - .flat_map(|item| vec![item / 16, item % 16]) - .collect::>(), - ), + let mut vec = Vec::with_capacity(data.as_ref().len() * 2); + for byte in data.as_ref() { + vec.push(byte / 16); + vec.push(byte % 16); } + Nibbles { hex_data: Bytes::from(vec) } } /// Packs the nibbles stored in the struct into a byte vector. From 2c5a748c55906d38076147fc4070748cba8a0204 Mon Sep 17 00:00:00 2001 From: Dan Cline <6798349+Rjected@users.noreply.github.com> Date: Wed, 29 Nov 2023 18:57:04 -0500 Subject: [PATCH 056/277] fix: add recover_signer that checks according to EIP-2 (#5618) --- crates/primitives/src/lib.rs | 2 +- crates/primitives/src/revm/env.rs | 7 ++- .../primitives/src/transaction/signature.rs | 60 +++++++++++++++++-- crates/primitives/src/transaction/util.rs | 7 ++- crates/stages/src/stages/sender_recovery.rs | 7 ++- 5 files changed, 71 insertions(+), 12 deletions(-) diff --git a/crates/primitives/src/lib.rs b/crates/primitives/src/lib.rs index 2f40f2a197b1..ca6a5c1d4fed 100644 --- a/crates/primitives/src/lib.rs +++ b/crates/primitives/src/lib.rs @@ -95,7 +95,7 @@ pub use transaction::{ }; pub use transaction::{ - util::secp256k1::{public_key_to_address, recover_signer, sign_message}, + util::secp256k1::{public_key_to_address, recover_signer_unchecked, sign_message}, AccessList, AccessListItem, FromRecoveredTransaction, IntoRecoveredTransaction, InvalidTransactionError, Signature, Transaction, TransactionKind, TransactionMeta, TransactionSigned, TransactionSignedEcRecovered, TransactionSignedNoHash, TxEip1559, TxEip2930, diff --git a/crates/primitives/src/revm/env.rs b/crates/primitives/src/revm/env.rs index 634cc6db1ce7..3d890bc71e5b 100644 --- a/crates/primitives/src/revm/env.rs +++ b/crates/primitives/src/revm/env.rs @@ -1,6 +1,6 @@ use crate::{ constants::{BEACON_ROOTS_ADDRESS, SYSTEM_ADDRESS}, - recover_signer, + recover_signer_unchecked, revm::config::revm_spec, revm_primitives::{AnalysisKind, BlockEnv, CfgEnv, Env, SpecId, TransactTo, TxEnv}, Address, Bytes, Chain, ChainSpec, Head, Header, Transaction, TransactionKind, @@ -129,7 +129,10 @@ pub fn recover_header_signer(header: &Header) -> Result) -> u64 { #[cfg(feature = "optimism")] if self.r.is_zero() && self.s.is_zero() { - return 0 + return 0; } if let Some(chain_id) = chain_id { @@ -100,7 +107,7 @@ impl Signature { } else { // non-EIP-155 legacy scheme, v = 27 for even y-parity, v = 28 for odd y-parity if v != 27 && v != 28 { - return Err(RlpError::Custom("invalid Ethereum signature (V is not 27 or 28)")) + return Err(RlpError::Custom("invalid Ethereum signature (V is not 27 or 28)")); } let odd_y_parity = v == 28; Ok((Signature { r, s, odd_y_parity }, None)) @@ -128,8 +135,13 @@ impl Signature { }) } - /// Recover signer address from message hash. - pub fn recover_signer(&self, hash: B256) -> Option
{ + /// Recover signer from message hash, _without ensuring that the signature has a low `s` + /// value_. + /// + /// Using this for signature validation will succeed, even if the signature is malleable or not + /// compliant with EIP-2. This is provided for compatibility with old signatures which have + /// large `s` values. + pub fn recover_signer_unchecked(&self, hash: B256) -> Option
{ let mut sig: [u8; 65] = [0; 65]; sig[0..32].copy_from_slice(&self.r.to_be_bytes::<32>()); @@ -138,7 +150,20 @@ impl Signature { // NOTE: we are removing error from underlying crypto library as it will restrain primitive // errors and we care only if recovery is passing or not. - secp256k1::recover_signer(&sig, &hash.0).ok() + secp256k1::recover_signer_unchecked(&sig, &hash.0).ok() + } + + /// Recover signer address from message hash. This ensures that the signature S value is + /// greater than `secp256k1n / 2`, as specified in + /// [EIP-2](https://eips.ethereum.org/EIPS/eip-2). + /// + /// If the S value is too large, then this will return `None` + pub fn recover_signer(&self, hash: B256) -> Option
{ + if self.s > SECP256K1N_HALF { + return None; + } + + self.recover_signer_unchecked(hash) } /// Turn this signature into its byte @@ -166,7 +191,8 @@ impl Signature { #[cfg(test)] mod tests { - use crate::{Address, Signature, B256, U256}; + use crate::{transaction::signature::SECP256K1N_HALF, Address, Signature, B256, U256}; + use alloy_primitives::hex; use bytes::BytesMut; use std::str::FromStr; @@ -286,4 +312,26 @@ mod tests { assert!(signature.size() >= 65); } + + #[test] + fn eip_2_reject_high_s_value() { + // This pre-homestead transaction has a high `s` value and should be rejected by the + // `recover_signer` method: + // https://etherscan.io/getRawTx?tx=0x9e6e19637bb625a8ff3d052b7c2fe57dc78c55a15d258d77c43d5a9c160b0384 + // + // Block number: 46170 + let raw_tx = hex!("f86d8085746a52880082520894c93f2250589a6563f5359051c1ea25746549f0d889208686e75e903bc000801ba034b6fdc33ea520e8123cf5ac4a9ff476f639cab68980cd9366ccae7aef437ea0a0e517caa5f50e27ca0d1e9a92c503b4ccb039680c6d9d0c71203ed611ea4feb33"); + let tx = crate::transaction::TransactionSigned::decode_enveloped(&mut &raw_tx[..]).unwrap(); + let signature = tx.signature(); + + // make sure we know it's greater than SECP256K1N_HALF + assert!(signature.s > SECP256K1N_HALF); + + // recover signer, expect failure + let hash = tx.hash(); + assert!(signature.recover_signer(hash).is_none()); + + // use unchecked, ensure it succeeds (the signature is valid if not for EIP-2) + assert!(signature.recover_signer_unchecked(hash).is_some()); + } } diff --git a/crates/primitives/src/transaction/util.rs b/crates/primitives/src/transaction/util.rs index ce180e2dd35b..638064c12f10 100644 --- a/crates/primitives/src/transaction/util.rs +++ b/crates/primitives/src/transaction/util.rs @@ -11,7 +11,10 @@ pub(crate) mod secp256k1 { /// Recovers the address of the sender using secp256k1 pubkey recovery. /// /// Converts the public key into an ethereum address by hashing the public key with keccak256. - pub fn recover_signer(sig: &[u8; 65], msg: &[u8; 32]) -> Result { + /// + /// This does not ensure that the `s` value in the signature is low, and _just_ wraps the + /// underlying secp256k1 library. + pub fn recover_signer_unchecked(sig: &[u8; 65], msg: &[u8; 32]) -> Result { let sig = RecoverableSignature::from_compact(&sig[0..64], RecoveryId::from_i32(sig[64] as i32)?)?; @@ -55,6 +58,6 @@ mod tests { let hash = hex!("47173285a8d7341e5e972fc677286384f802f8ef42a5ec5f03bbfa254cb01fad"); let out = address!("c08b5542d177ac6686946920409741463a15dddb"); - assert_eq!(secp256k1::recover_signer(&sig, &hash), Ok(out)); + assert_eq!(secp256k1::recover_signer_unchecked(&sig, &hash), Ok(out)); } } diff --git a/crates/stages/src/stages/sender_recovery.rs b/crates/stages/src/stages/sender_recovery.rs index a7b19ca57a2d..a758b9b6bc09 100644 --- a/crates/stages/src/stages/sender_recovery.rs +++ b/crates/stages/src/stages/sender_recovery.rs @@ -198,9 +198,14 @@ fn recover_sender( let tx = transaction.value().expect("value to be formated"); tx.transaction.encode_without_signature(rlp_buf); + // We call [Signature::recover_signer_unchecked] because transactions run in the pipeline are + // known to be valid - this means that we do not need to check whether or not the `s` value is + // greater than `secp256k1n / 2` if past EIP-2. There are transactions pre-homestead which have + // large `s` values, so using [Signature::recover_signer] here would not be + // backwards-compatible. let sender = tx .signature - .recover_signer(keccak256(rlp_buf)) + .recover_signer_unchecked(keccak256(rlp_buf)) .ok_or(SenderRecoveryStageError::FailedRecovery(FailedSenderRecoveryError { tx: tx_id }))?; Ok((tx_id, sender)) From afebbe64b7153b2f26b4096f7faa870257244d17 Mon Sep 17 00:00:00 2001 From: Roman Krasiuk Date: Thu, 30 Nov 2023 02:41:58 -0800 Subject: [PATCH 057/277] fix: bench builds (#5635) --- crates/stages/benches/criterion.rs | 16 ++- .../stages/benches/setup/account_hashing.rs | 5 +- crates/stages/benches/setup/mod.rs | 39 ++++--- crates/storage/db/src/lib.rs | 9 +- .../provider/src/providers/database/mod.rs | 101 +++++++++--------- 5 files changed, 88 insertions(+), 82 deletions(-) diff --git a/crates/stages/benches/criterion.rs b/crates/stages/benches/criterion.rs index 2f73ec71f9f1..2a45c2521f4a 100644 --- a/crates/stages/benches/criterion.rs +++ b/crates/stages/benches/criterion.rs @@ -3,10 +3,9 @@ use criterion::{ BenchmarkGroup, Criterion, }; use pprof::criterion::{Output, PProfProfiler}; -use reth_db::DatabaseEnv; +use reth_db::{test_utils::TempDatabase, DatabaseEnv}; use reth_interfaces::test_utils::TestConsensus; -use reth_primitives::{stage::StageCheckpoint, MAINNET}; -use reth_provider::ProviderFactory; +use reth_primitives::stage::StageCheckpoint; use reth_stages::{ stages::{MerkleStage, SenderRecoveryStage, TotalDifficultyStage, TransactionLookupStage}, test_utils::TestStageDB, @@ -122,22 +121,21 @@ fn measure_stage_with_path( stage_range: StageRange, label: String, ) where - S: Clone + Stage, + S: Clone + Stage>>, F: Fn(S, &TestStageDB, StageRange), { - let tx = TestStageDB::new(&path); + let db = TestStageDB::new(&path); let (input, _) = stage_range; group.bench_function(label, move |b| { b.to_async(FuturesExecutor).iter_with_setup( || { // criterion setup does not support async, so we have to use our own runtime - setup(stage.clone(), &tx, stage_range) + setup(stage.clone(), &db, stage_range) }, |_| async { let mut stage = stage.clone(); - let factory = ProviderFactory::new(tx.factory.db(), MAINNET.clone()); - let provider = factory.provider_rw().unwrap(); + let provider = db.factory.provider_rw().unwrap(); stage .execute_ready(input) .await @@ -156,7 +154,7 @@ fn measure_stage( block_interval: std::ops::Range, label: String, ) where - S: Clone + Stage, + S: Clone + Stage>>, F: Fn(S, &TestStageDB, StageRange), { let path = setup::txs_testdata(block_interval.end); diff --git a/crates/stages/benches/setup/account_hashing.rs b/crates/stages/benches/setup/account_hashing.rs index a94a8250aede..175678242465 100644 --- a/crates/stages/benches/setup/account_hashing.rs +++ b/crates/stages/benches/setup/account_hashing.rs @@ -33,6 +33,7 @@ fn find_stage_range(db: &Path) -> StageRange { let mut stage_range = None; TestStageDB::new(db) .factory + .db_ref() .view(|tx| { let mut cursor = tx.cursor_read::()?; let from = cursor.first()?.unwrap().0; @@ -62,8 +63,8 @@ fn generate_testdata_db(num_blocks: u64) -> (PathBuf, StageRange) { // create the dirs std::fs::create_dir_all(&path).unwrap(); println!("Account Hashing testdata not found, generating to {:?}", path.display()); - let tx = TestStageDB::new(&path); - let provider = tx.provider_rw(); + let db = TestStageDB::new(&path); + let provider = db.factory.provider_rw().unwrap(); let _accounts = AccountHashingStage::seed(&provider, opts); provider.commit().expect("failed to commit"); } diff --git a/crates/stages/benches/setup/mod.rs b/crates/stages/benches/setup/mod.rs index 3850ca44fa55..4b972afcf48b 100644 --- a/crates/stages/benches/setup/mod.rs +++ b/crates/stages/benches/setup/mod.rs @@ -2,6 +2,7 @@ use itertools::concat; use reth_db::{ cursor::DbCursorRO, tables, + test_utils::TempDatabase, transaction::{DbTx, DbTxMut}, DatabaseEnv, }; @@ -12,8 +13,7 @@ use reth_interfaces::test_utils::{ random_eoa_account_range, }, }; -use reth_primitives::{Account, Address, SealedBlock, B256, MAINNET}; -use reth_provider::ProviderFactory; +use reth_primitives::{Account, Address, SealedBlock, B256, U256}; use reth_stages::{ stages::{AccountHashingStage, StorageHashingStage}, test_utils::TestStageDB, @@ -23,6 +23,7 @@ use reth_trie::StateRoot; use std::{ collections::BTreeMap, path::{Path, PathBuf}, + sync::Arc, }; mod constants; @@ -32,7 +33,7 @@ pub use account_hashing::*; pub(crate) type StageRange = (ExecInput, UnwindInput); -pub(crate) fn stage_unwind>( +pub(crate) fn stage_unwind>>>( stage: S, db: &TestStageDB, range: StageRange, @@ -41,8 +42,7 @@ pub(crate) fn stage_unwind>( tokio::runtime::Runtime::new().unwrap().block_on(async { let mut stage = stage.clone(); - let factory = ProviderFactory::new(db.factory.db(), MAINNET.clone()); - let provider = factory.provider_rw().unwrap(); + let provider = db.factory.provider_rw().unwrap(); // Clear previous run stage @@ -50,7 +50,7 @@ pub(crate) fn stage_unwind>( .map_err(|e| { format!( "{e}\nMake sure your test database at `{}` isn't too old and incompatible with newer stage changes.", - db.path.as_ref().unwrap().display() + db.factory.db_ref().path().display() ) }) .unwrap(); @@ -59,7 +59,7 @@ pub(crate) fn stage_unwind>( }); } -pub(crate) fn unwind_hashes>( +pub(crate) fn unwind_hashes>>>( stage: S, db: &TestStageDB, range: StageRange, @@ -67,8 +67,7 @@ pub(crate) fn unwind_hashes>( let (input, unwind) = range; let mut stage = stage.clone(); - let factory = ProviderFactory::new(db.factory.db(), MAINNET.clone()); - let provider = factory.provider_rw().unwrap(); + let provider = db.factory.provider_rw().unwrap(); StorageHashingStage::default().unwind(&provider, unwind).unwrap(); AccountHashingStage::default().unwind(&provider, unwind).unwrap(); @@ -105,7 +104,7 @@ pub(crate) fn txs_testdata(num_blocks: u64) -> PathBuf { // create the dirs std::fs::create_dir_all(&path).unwrap(); println!("Transactions testdata not found, generating to {:?}", path.display()); - let tx = TestStageDB::new(&path); + let db = TestStageDB::new(&path); let accounts: BTreeMap = concat([ random_eoa_account_range(&mut rng, 0..n_eoa), @@ -124,11 +123,11 @@ pub(crate) fn txs_testdata(num_blocks: u64) -> PathBuf { key_range.clone(), ); - tx.insert_accounts_and_storages(start_state.clone()).unwrap(); + db.insert_accounts_and_storages(start_state.clone()).unwrap(); // make first block after genesis have valid state root let (root, updates) = - StateRoot::new(tx.provider_rw().tx_ref()).root_with_updates().unwrap(); + StateRoot::new(db.factory.provider_rw().unwrap().tx_ref()).root_with_updates().unwrap(); let second_block = blocks.get_mut(1).unwrap(); let cloned_second = second_block.clone(); let mut updated_header = cloned_second.header.unseal(); @@ -137,8 +136,8 @@ pub(crate) fn txs_testdata(num_blocks: u64) -> PathBuf { let offset = transitions.len() as u64; - tx.insert_changesets(transitions, None).unwrap(); - tx.commit(|tx| updates.flush(tx)).unwrap(); + db.insert_changesets(transitions, None).unwrap(); + db.commit(|tx| Ok(updates.flush(tx)?)).unwrap(); let (transitions, final_state) = random_changeset_range( &mut rng, @@ -148,13 +147,13 @@ pub(crate) fn txs_testdata(num_blocks: u64) -> PathBuf { key_range, ); - tx.insert_changesets(transitions, Some(offset)).unwrap(); + db.insert_changesets(transitions, Some(offset)).unwrap(); - tx.insert_accounts_and_storages(final_state).unwrap(); + db.insert_accounts_and_storages(final_state).unwrap(); // make last block have valid state root let root = { - let tx_mut = tx.provider_rw(); + let tx_mut = db.factory.provider_rw().unwrap(); let root = StateRoot::new(tx_mut.tx_ref()).root().unwrap(); tx_mut.commit().unwrap(); root @@ -166,12 +165,12 @@ pub(crate) fn txs_testdata(num_blocks: u64) -> PathBuf { updated_header.state_root = root; *last_block = SealedBlock { header: updated_header.seal_slow(), ..cloned_last }; - tx.insert_blocks(blocks.iter(), None).unwrap(); + db.insert_blocks(blocks.iter(), None).unwrap(); // initialize TD - tx.commit(|tx| { + db.commit(|tx| { let (head, _) = tx.cursor_read::()?.first()?.unwrap_or_default(); - tx.put::(head, reth_primitives::U256::from(0).into()) + Ok(tx.put::(head, U256::from(0).into())?) }) .unwrap(); } diff --git a/crates/storage/db/src/lib.rs b/crates/storage/db/src/lib.rs index e813bf0d1169..0c8078a69b62 100644 --- a/crates/storage/db/src/lib.rs +++ b/crates/storage/db/src/lib.rs @@ -182,12 +182,17 @@ pub mod test_utils { } impl TempDatabase { - /// returns the ref of inner db + /// Returns the reference to inner db. pub fn db(&self) -> &DB { self.db.as_ref().unwrap() } - /// returns the inner db + /// Returns the path to the database. + pub fn path(&self) -> &Path { + &self.path + } + + /// Convert temp database into inner. pub fn into_inner_db(mut self) -> DB { self.db.take().unwrap() // take out db to avoid clean path in drop fn } diff --git a/crates/storage/provider/src/providers/database/mod.rs b/crates/storage/provider/src/providers/database/mod.rs index 0d1ca70ab465..f2f6141b8971 100644 --- a/crates/storage/provider/src/providers/database/mod.rs +++ b/crates/storage/provider/src/providers/database/mod.rs @@ -22,7 +22,7 @@ use reth_primitives::{ use revm::primitives::{BlockEnv, CfgEnv}; use std::{ ops::{RangeBounds, RangeInclusive}, - path::PathBuf, + path::{Path, PathBuf}, sync::Arc, }; use tokio::sync::watch; @@ -46,42 +46,39 @@ pub struct ProviderFactory { snapshot_provider: Option>, } -impl ProviderFactory { - /// Returns a provider with a created `DbTx` inside, which allows fetching data from the - /// database using different types of providers. Example: [`HeaderProvider`] - /// [`BlockHashReader`]. This may fail if the inner read database transaction fails to open. - pub fn provider(&self) -> ProviderResult> { - let mut provider = DatabaseProvider::new(self.db.tx()?, self.chain_spec.clone()); - - if let Some(snapshot_provider) = &self.snapshot_provider { - provider = provider.with_snapshot_provider(snapshot_provider.clone()); - } - - Ok(provider) - } - - /// Returns a provider with a created `DbTxMut` inside, which allows fetching and updating - /// data from the database using different types of providers. Example: [`HeaderProvider`] - /// [`BlockHashReader`]. This may fail if the inner read/write database transaction fails to - /// open. - pub fn provider_rw(&self) -> ProviderResult> { - let mut provider = DatabaseProvider::new_rw(self.db.tx_mut()?, self.chain_spec.clone()); - - if let Some(snapshot_provider) = &self.snapshot_provider { - provider = provider.with_snapshot_provider(snapshot_provider.clone()); +impl Clone for ProviderFactory { + fn clone(&self) -> Self { + Self { + db: self.db.clone(), + chain_spec: Arc::clone(&self.chain_spec), + snapshot_provider: self.snapshot_provider.clone(), } - - Ok(DatabaseProviderRW(provider)) } } +impl ProviderFactory {} + impl ProviderFactory { - /// create new database provider + /// Create new database provider factory. pub fn new(db: DB, chain_spec: Arc) -> Self { Self { db, chain_spec, snapshot_provider: None } } - /// database provider comes with a shared snapshot provider + /// Create new database provider by passing a path. [`ProviderFactory`] will own the database + /// instance. + pub fn new_with_database_path>( + path: P, + chain_spec: Arc, + log_level: Option, + ) -> RethResult> { + Ok(ProviderFactory:: { + db: init_db(path, log_level).map_err(|e| RethError::Custom(e.to_string()))?, + chain_spec, + snapshot_provider: None, + }) + } + + /// Database provider that comes with a shared snapshot provider. pub fn with_snapshots( mut self, snapshots_path: PathBuf, @@ -93,35 +90,41 @@ impl ProviderFactory { )); self } + + /// Returns reference to the underlying database. + pub fn db_ref(&self) -> &DB { + &self.db + } } impl ProviderFactory { - /// create new database provider by passing a path. [`ProviderFactory`] will own the database - /// instance. - pub fn new_with_database_path>( - path: P, - chain_spec: Arc, - log_level: Option, - ) -> RethResult> { - Ok(ProviderFactory:: { - db: init_db(path, log_level).map_err(|e| RethError::Custom(e.to_string()))?, - chain_spec, - snapshot_provider: None, - }) + /// Returns a provider with a created `DbTx` inside, which allows fetching data from the + /// database using different types of providers. Example: [`HeaderProvider`] + /// [`BlockHashReader`]. This may fail if the inner read database transaction fails to open. + pub fn provider(&self) -> ProviderResult> { + let mut provider = DatabaseProvider::new(self.db.tx()?, self.chain_spec.clone()); + + if let Some(snapshot_provider) = &self.snapshot_provider { + provider = provider.with_snapshot_provider(snapshot_provider.clone()); + } + + Ok(provider) } -} -impl Clone for ProviderFactory { - fn clone(&self) -> Self { - Self { - db: self.db.clone(), - chain_spec: Arc::clone(&self.chain_spec), - snapshot_provider: self.snapshot_provider.clone(), + /// Returns a provider with a created `DbTxMut` inside, which allows fetching and updating + /// data from the database using different types of providers. Example: [`HeaderProvider`] + /// [`BlockHashReader`]. This may fail if the inner read/write database transaction fails to + /// open. + pub fn provider_rw(&self) -> ProviderResult> { + let mut provider = DatabaseProvider::new_rw(self.db.tx_mut()?, self.chain_spec.clone()); + + if let Some(snapshot_provider) = &self.snapshot_provider { + provider = provider.with_snapshot_provider(snapshot_provider.clone()); } + + Ok(DatabaseProviderRW(provider)) } -} -impl ProviderFactory { /// Storage provider for latest block pub fn latest(&self) -> ProviderResult { trace!(target: "providers::db", "Returning latest state provider"); From d93eac21931a6809692b267bad5157c477991c82 Mon Sep 17 00:00:00 2001 From: Matthias Seitz Date: Thu, 30 Nov 2023 12:39:45 +0100 Subject: [PATCH 058/277] ci: run clippy on all targets (#5636) --- .github/workflows/lint.yml | 2 +- examples/db-access.rs | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index 60341fcb805b..293831dcc2d5 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -28,7 +28,7 @@ jobs: with: cache-on-failure: true - run: - cargo clippy --bin "${{ matrix.binary }}" --workspace --features "${{ matrix.network }}" + cargo clippy --bin "${{ matrix.binary }}" --workspace --features "${{ matrix.network }}" --lib --tests --benches --examples env: RUSTFLAGS: -D warnings diff --git a/examples/db-access.rs b/examples/db-access.rs index dd9c74e58016..7fd888bb783e 100644 --- a/examples/db-access.rs +++ b/examples/db-access.rs @@ -1,12 +1,11 @@ use reth_db::open_db_read_only; -use reth_primitives::{Address, ChainSpecBuilder, B256, U256}; +use reth_primitives::{Address, ChainSpecBuilder, B256}; use reth_provider::{ AccountReader, BlockReader, BlockSource, HeaderProvider, ProviderFactory, ReceiptProvider, StateProvider, TransactionsProvider, }; use reth_rpc_types::{Filter, FilteredParams}; use reth_rpc_types_compat::log::from_primitive_log; - use std::path::Path; // Providers are zero cost abstractions on top of an opened MDBX Transaction From a7f474cb0b7b497cbb7f9fa9878bd0147355e8ee Mon Sep 17 00:00:00 2001 From: Dan Cline <6798349+Rjected@users.noreply.github.com> Date: Thu, 30 Nov 2023 06:41:28 -0500 Subject: [PATCH 059/277] feat: truncate blob pool when discarding worst (#5634) --- bin/reth/src/args/txpool_args.rs | 4 ++ crates/transaction-pool/src/config.rs | 3 ++ crates/transaction-pool/src/pool/txpool.rs | 54 ++++++++++++---------- 3 files changed, 36 insertions(+), 25 deletions(-) diff --git a/bin/reth/src/args/txpool_args.rs b/bin/reth/src/args/txpool_args.rs index 9a4843d2c9ef..40912c209e0e 100644 --- a/bin/reth/src/args/txpool_args.rs +++ b/bin/reth/src/args/txpool_args.rs @@ -65,6 +65,10 @@ impl TxPoolArgs { max_txs: self.queued_max_count, max_size: self.queued_max_size * 1024 * 1024, }, + blob_limit: SubPoolLimit { + max_txs: self.queued_max_count, + max_size: self.queued_max_size * 1024 * 1024, + }, max_account_slots: self.max_account_slots, price_bumps: PriceBumpConfig { default_price_bump: self.price_bump, diff --git a/crates/transaction-pool/src/config.rs b/crates/transaction-pool/src/config.rs index fae191136109..de9cebe639d8 100644 --- a/crates/transaction-pool/src/config.rs +++ b/crates/transaction-pool/src/config.rs @@ -26,6 +26,8 @@ pub struct PoolConfig { pub basefee_limit: SubPoolLimit, /// Max number of transaction in the queued sub-pool pub queued_limit: SubPoolLimit, + /// Max number of transactions in the blob sub-pool + pub blob_limit: SubPoolLimit, /// Max number of executable transaction slots guaranteed per account pub max_account_slots: usize, /// Price bump (in %) for the transaction pool underpriced check. @@ -41,6 +43,7 @@ impl Default for PoolConfig { pending_limit: Default::default(), basefee_limit: Default::default(), queued_limit: Default::default(), + blob_limit: Default::default(), max_account_slots: TXPOOL_MAX_ACCOUNT_SLOTS_PER_SENDER, price_bumps: Default::default(), local_transactions_config: Default::default(), diff --git a/crates/transaction-pool/src/pool/txpool.rs b/crates/transaction-pool/src/pool/txpool.rs index e9b5acadbcdb..bcebb4d69507 100644 --- a/crates/transaction-pool/src/pool/txpool.rs +++ b/crates/transaction-pool/src/pool/txpool.rs @@ -92,8 +92,13 @@ pub struct TxPool { /// Holds all parked transactions that currently violate the dynamic fee requirement but could /// be moved to pending if the base fee changes in their favor (decreases) in future blocks. basefee_pool: ParkedPool>, - /// All blob transactions in the pool - blob_transactions: BlobTransactions, + /// Blob transactions in the pool that are __not pending__. + /// + /// This means they either do not satisfy the dynamic fee requirement or the blob fee + /// requirement. These transactions can be moved to pending if the base fee or blob fee changes + /// in their favor (decreases) in future blocks. The transaction may need both the base fee and + /// blob fee to decrease to become executable. + blob_pool: BlobTransactions, /// All transactions in the pool. all_transactions: AllTransactions, /// Transaction pool metrics @@ -110,7 +115,7 @@ impl TxPool { pending_pool: PendingPool::new(ordering), queued_pool: Default::default(), basefee_pool: Default::default(), - blob_transactions: Default::default(), + blob_pool: Default::default(), all_transactions: AllTransactions::new(&config), config, metrics: Default::default(), @@ -136,8 +141,8 @@ impl TxPool { basefee_size: self.basefee_pool.size(), queued: self.queued_pool.len(), queued_size: self.queued_pool.size(), - blob: self.blob_transactions.len(), - blob_size: self.blob_transactions.size(), + blob: self.blob_pool.len(), + blob_size: self.blob_pool.size(), total: self.all_transactions.len(), } } @@ -182,9 +187,8 @@ impl TxPool { (Ordering::Less, Ordering::Equal) | (_, Ordering::Less) => { // decreased blob fee or base fee: recheck blob pool and promote all that are now // valid - let removed = self - .blob_transactions - .enforce_pending_fees(&self.all_transactions.pending_fees); + let removed = + self.blob_pool.enforce_pending_fees(&self.all_transactions.pending_fees); for tx in removed { let to = { let tx = @@ -216,9 +220,8 @@ impl TxPool { // decreased blob fee or base fee: recheck blob pool and promote all that are now // valid - let removed = self - .blob_transactions - .enforce_pending_fees(&self.all_transactions.pending_fees); + let removed = + self.blob_pool.enforce_pending_fees(&self.all_transactions.pending_fees); for tx in removed { let to = { let tx = @@ -355,7 +358,7 @@ impl TxPool { // base fee decreased, we need to move transactions from the basefee pool to the // pending pool and satisfy blob fee transactions as well let unlocked_with_blob = - self.blob_transactions.satisfy_attributes(best_transactions_attributes); + self.blob_pool.satisfy_attributes(best_transactions_attributes); Box::new(self.pending_pool.best_with_unlocked( unlocked_with_blob, @@ -389,7 +392,7 @@ impl TxPool { SubPool::Queued => self.queued_pool.contains(id), SubPool::Pending => self.pending_pool.contains(id), SubPool::BaseFee => self.basefee_pool.contains(id), - SubPool::Blob => self.blob_transactions.contains(id), + SubPool::Blob => self.blob_pool.contains(id), } } @@ -695,7 +698,7 @@ impl TxPool { SubPool::Queued => self.queued_pool.remove_transaction(tx), SubPool::Pending => self.pending_pool.remove_transaction(tx), SubPool::BaseFee => self.basefee_pool.remove_transaction(tx), - SubPool::Blob => self.blob_transactions.remove_transaction(tx), + SubPool::Blob => self.blob_pool.remove_transaction(tx), } } @@ -710,7 +713,7 @@ impl TxPool { SubPool::Pending => self.pending_pool.prune_transaction(tx), SubPool::Queued => self.queued_pool.remove_transaction(tx), SubPool::BaseFee => self.basefee_pool.remove_transaction(tx), - SubPool::Blob => self.blob_transactions.remove_transaction(tx), + SubPool::Blob => self.blob_pool.remove_transaction(tx), } } @@ -756,7 +759,7 @@ impl TxPool { self.basefee_pool.add_transaction(tx); } SubPool::Blob => { - self.blob_transactions.add_transaction(tx); + self.blob_pool.add_transaction(tx); } } } @@ -807,6 +810,7 @@ impl TxPool { self, removed, [ pending_limit => pending_pool, basefee_limit => basefee_pool, + blob_limit => blob_pool, queued_limit => queued_pool ] ); @@ -840,7 +844,7 @@ impl TxPool { self.pending_pool.assert_invariants(); self.basefee_pool.assert_invariants(); self.queued_pool.assert_invariants(); - self.blob_transactions.assert_invariants(); + self.blob_pool.assert_invariants(); } } @@ -1912,7 +1916,7 @@ mod tests { pool.add_transaction(validated, on_chain_balance, on_chain_nonce).unwrap(); // assert pool lengths - assert!(pool.blob_transactions.is_empty()); + assert!(pool.blob_pool.is_empty()); assert_eq!(pool.pending_pool.len(), 1); // check tx state and derived subpool @@ -1930,7 +1934,7 @@ mod tests { assert_eq!(internal_tx.subpool, SubPool::Blob); // make sure the blob transaction was promoted into the pending pool - assert_eq!(pool.blob_transactions.len(), 1); + assert_eq!(pool.blob_pool.len(), 1); assert!(pool.pending_pool.is_empty()); } @@ -1953,7 +1957,7 @@ mod tests { // assert pool lengths assert!(pool.pending_pool.is_empty()); - assert_eq!(pool.blob_transactions.len(), 1); + assert_eq!(pool.blob_pool.len(), 1); // check tx state and derived subpool let internal_tx = pool.all_transactions.txs.get(&id).unwrap(); @@ -1971,7 +1975,7 @@ mod tests { // make sure the blob transaction was promoted into the pending pool assert_eq!(pool.pending_pool.len(), 1); - assert!(pool.blob_transactions.is_empty()); + assert!(pool.blob_pool.is_empty()); } /// A struct representing a txpool promotion test instance @@ -2012,25 +2016,25 @@ mod tests { ) { match check_subpool { SubPool::Blob => { - assert_eq!(pool.blob_transactions.len(), 1, "{failure_message}"); + assert_eq!(pool.blob_pool.len(), 1, "{failure_message}"); assert!(pool.pending_pool.is_empty(), "{failure_message}"); assert!(pool.basefee_pool.is_empty(), "{failure_message}"); assert!(pool.queued_pool.is_empty(), "{failure_message}"); } SubPool::Pending => { - assert!(pool.blob_transactions.is_empty(), "{failure_message}"); + assert!(pool.blob_pool.is_empty(), "{failure_message}"); assert_eq!(pool.pending_pool.len(), 1, "{failure_message}"); assert!(pool.basefee_pool.is_empty(), "{failure_message}"); assert!(pool.queued_pool.is_empty(), "{failure_message}"); } SubPool::BaseFee => { - assert!(pool.blob_transactions.is_empty(), "{failure_message}"); + assert!(pool.blob_pool.is_empty(), "{failure_message}"); assert!(pool.pending_pool.is_empty(), "{failure_message}"); assert_eq!(pool.basefee_pool.len(), 1, "{failure_message}"); assert!(pool.queued_pool.is_empty(), "{failure_message}"); } SubPool::Queued => { - assert!(pool.blob_transactions.is_empty(), "{failure_message}"); + assert!(pool.blob_pool.is_empty(), "{failure_message}"); assert!(pool.pending_pool.is_empty(), "{failure_message}"); assert!(pool.basefee_pool.is_empty(), "{failure_message}"); assert_eq!(pool.queued_pool.len(), 1, "{failure_message}"); From 00804191ff434034f757003c3026ba3b66dc5a36 Mon Sep 17 00:00:00 2001 From: Aditya Pandey Date: Thu, 30 Nov 2023 17:16:44 +0530 Subject: [PATCH 060/277] Add stateOverrides-to-estimate-gas-call (#5604) --- crates/rpc/rpc-api/src/eth.rs | 1 + crates/rpc/rpc-builder/tests/it/http.rs | 2 +- crates/rpc/rpc/src/eth/api/call.rs | 21 ++++++++++++++++----- crates/rpc/rpc/src/eth/api/server.rs | 2 ++ crates/rpc/rpc/src/eth/api/transactions.rs | 1 + crates/rpc/rpc/src/eth/revm_utils.rs | 5 ++++- 6 files changed, 25 insertions(+), 7 deletions(-) diff --git a/crates/rpc/rpc-api/src/eth.rs b/crates/rpc/rpc-api/src/eth.rs index 01aa8fa0cfa4..c301864ee022 100644 --- a/crates/rpc/rpc-api/src/eth.rs +++ b/crates/rpc/rpc-api/src/eth.rs @@ -190,6 +190,7 @@ pub trait EthApi { &self, request: CallRequest, block_number: Option, + state_override: Option, ) -> RpcResult; /// Returns the current price per gas in wei. diff --git a/crates/rpc/rpc-builder/tests/it/http.rs b/crates/rpc/rpc-builder/tests/it/http.rs index ffe39b7667f2..dd86a0a149b1 100644 --- a/crates/rpc/rpc-builder/tests/it/http.rs +++ b/crates/rpc/rpc-builder/tests/it/http.rs @@ -160,7 +160,7 @@ where EthApiClient::create_access_list(client, call_request.clone(), Some(block_number.into())) .await .unwrap(); - EthApiClient::estimate_gas(client, call_request.clone(), Some(block_number.into())) + EthApiClient::estimate_gas(client, call_request.clone(), Some(block_number.into()), None) .await .unwrap(); EthApiClient::call(client, call_request.clone(), Some(block_number.into()), None, None) diff --git a/crates/rpc/rpc/src/eth/api/call.rs b/crates/rpc/rpc/src/eth/api/call.rs index a90d64ecc5cf..0ae826de7aa4 100644 --- a/crates/rpc/rpc/src/eth/api/call.rs +++ b/crates/rpc/rpc/src/eth/api/call.rs @@ -4,8 +4,9 @@ use crate::{ eth::{ error::{ensure_success, EthApiError, EthResult, RevertError, RpcInvalidTransactionError}, revm_utils::{ - build_call_evm_env, caller_gas_allowance, cap_tx_gas_limit_with_caller_allowance, - get_precompiles, inspect, prepare_call_env, transact, EvmOverrides, + apply_state_overrides, build_call_evm_env, caller_gas_allowance, + cap_tx_gas_limit_with_caller_allowance, get_precompiles, inspect, prepare_call_env, + transact, EvmOverrides, }, EthTransactions, }, @@ -41,12 +42,17 @@ where Network: NetworkInfo + Send + Sync + 'static, { /// Estimate gas needed for execution of the `request` at the [BlockId]. - pub async fn estimate_gas_at(&self, request: CallRequest, at: BlockId) -> EthResult { + pub async fn estimate_gas_at( + &self, + request: CallRequest, + at: BlockId, + state_override: Option, + ) -> EthResult { let (cfg, block_env, at) = self.evm_env_at(at).await?; self.on_blocking_task(|this| async move { let state = this.state_at(at)?; - this.estimate_gas_with(cfg, block_env, request, state) + this.estimate_gas_with(cfg, block_env, request, state, state_override) }) .await } @@ -171,6 +177,7 @@ where block: BlockEnv, request: CallRequest, state: S, + state_override: Option, ) -> EthResult where S: StateProvider, @@ -197,6 +204,10 @@ where let mut env = build_call_evm_env(cfg, block, request)?; let mut db = CacheDB::new(StateProviderDatabase::new(state)); + if let Some(state_override) = state_override { + // apply state overrides + apply_state_overrides(state_override, &mut db)?; + } // if the request is a simple transfer we can optimize if env.tx.data.is_empty() { if let TransactTo::Call(to) = env.tx.transact_to { @@ -409,7 +420,7 @@ where // calculate the gas used using the access list request.access_list = Some(access_list.clone()); - let gas_used = self.estimate_gas_with(env.cfg, env.block, request, db.db.state())?; + let gas_used = self.estimate_gas_with(env.cfg, env.block, request, db.db.state(), None)?; Ok(AccessListWithGasUsed { access_list, gas_used }) } diff --git a/crates/rpc/rpc/src/eth/api/server.rs b/crates/rpc/rpc/src/eth/api/server.rs index c63de87e710b..6d20f79123ab 100644 --- a/crates/rpc/rpc/src/eth/api/server.rs +++ b/crates/rpc/rpc/src/eth/api/server.rs @@ -266,12 +266,14 @@ where &self, request: CallRequest, block_number: Option, + state_override: Option, ) -> Result { trace!(target: "rpc::eth", ?request, ?block_number, "Serving eth_estimateGas"); Ok(self .estimate_gas_at( request, block_number.unwrap_or(BlockId::Number(BlockNumberOrTag::Latest)), + state_override, ) .await?) } diff --git a/crates/rpc/rpc/src/eth/api/transactions.rs b/crates/rpc/rpc/src/eth/api/transactions.rs index 93577db24037..d6d1a80c5bab 100644 --- a/crates/rpc/rpc/src/eth/api/transactions.rs +++ b/crates/rpc/rpc/src/eth/api/transactions.rs @@ -530,6 +530,7 @@ where max_fee_per_blob_gas: None, }, BlockId::Number(BlockNumberOrTag::Pending), + None, ) .await?; let gas_limit = estimated_gas; diff --git a/crates/rpc/rpc/src/eth/revm_utils.rs b/crates/rpc/rpc/src/eth/revm_utils.rs index 4393272fc8ce..8d016ec6b59f 100644 --- a/crates/rpc/rpc/src/eth/revm_utils.rs +++ b/crates/rpc/rpc/src/eth/revm_utils.rs @@ -520,7 +520,10 @@ fn apply_block_overrides(overrides: BlockOverrides, env: &mut BlockEnv) { } /// Applies the given state overrides (a set of [AccountOverride]) to the [CacheDB]. -fn apply_state_overrides(overrides: StateOverride, db: &mut CacheDB) -> EthResult<()> +pub(crate) fn apply_state_overrides( + overrides: StateOverride, + db: &mut CacheDB, +) -> EthResult<()> where DB: DatabaseRef, EthApiError: From<::Error>, From aabb58dc8a2afc7f794f760eb5059c18a55fafba Mon Sep 17 00:00:00 2001 From: Dan Cline <6798349+Rjected@users.noreply.github.com> Date: Thu, 30 Nov 2023 10:05:55 -0500 Subject: [PATCH 061/277] feat: add MockTransactionSet for easier dependent mock txs (#5633) --- crates/transaction-pool/src/pool/parked.rs | 121 ++++++++---------- crates/transaction-pool/src/pool/pending.rs | 52 ++++---- .../transaction-pool/src/test_utils/mock.rs | 91 ++++++++++++- 3 files changed, 172 insertions(+), 92 deletions(-) diff --git a/crates/transaction-pool/src/pool/parked.rs b/crates/transaction-pool/src/pool/parked.rs index da656381346c..d985ea4cf8ee 100644 --- a/crates/transaction-pool/src/pool/parked.rs +++ b/crates/transaction-pool/src/pool/parked.rs @@ -152,7 +152,7 @@ impl ParkedPool { ) -> Vec>> { if self.len() <= limit.max_txs { // if we are below the limits, we don't need to drop anything - return Vec::new() + return Vec::new(); } let mut removed = Vec::new(); @@ -173,7 +173,7 @@ impl ParkedPool { } } drop -= list.len(); - continue + continue; } // Otherwise drop only last few transactions @@ -252,7 +252,7 @@ impl ParkedPool> { // still parked -> skip descendant transactions 'this: while let Some((peek, _)) = iter.peek() { if peek.sender != id.sender { - break 'this + break 'this; } iter.next(); } @@ -465,8 +465,8 @@ impl Ord for QueuedOrd { #[cfg(test)] mod tests { use super::*; - use crate::test_utils::{MockTransaction, MockTransactionFactory}; - use reth_primitives::address; + use crate::test_utils::{MockTransaction, MockTransactionFactory, MockTransactionSet}; + use reth_primitives::{address, TxType}; use std::collections::HashSet; #[test] @@ -530,31 +530,29 @@ mod tests { let mut f = MockTransactionFactory::default(); let mut pool = ParkedPool::>::default(); - let a = address!("000000000000000000000000000000000000000a"); - let b = address!("000000000000000000000000000000000000000b"); - let c = address!("000000000000000000000000000000000000000c"); - let d = address!("000000000000000000000000000000000000000d"); + let a_sender = address!("000000000000000000000000000000000000000a"); + let b_sender = address!("000000000000000000000000000000000000000b"); + let c_sender = address!("000000000000000000000000000000000000000c"); + let d_sender = address!("000000000000000000000000000000000000000d"); - // TODO: make creating these mock tx chains easier // create a chain of transactions by sender A, B, C - let a1 = MockTransaction::eip1559().with_sender(a); - let a2 = a1.next(); - let a3 = a2.next(); - let a4 = a3.next(); + let mut tx_set = MockTransactionSet::dependent(a_sender, 0, 4, TxType::EIP1559); + let a = tx_set.clone().into_vec(); - let b1 = MockTransaction::eip1559().with_sender(b); - let b2 = b1.next(); - let b3 = b2.next(); + let b = MockTransactionSet::dependent(b_sender, 0, 3, TxType::EIP1559).into_vec(); + tx_set.extend(b.clone()); // C has the same number of txs as B - let c1 = MockTransaction::eip1559().with_sender(c); - let c2 = c1.next(); - let c3 = c2.next(); + let c = MockTransactionSet::dependent(c_sender, 0, 3, TxType::EIP1559).into_vec(); + tx_set.extend(c.clone()); - let d1 = MockTransaction::eip1559().with_sender(d); + let d = MockTransactionSet::dependent(d_sender, 0, 1, TxType::EIP1559).into_vec(); + tx_set.extend(d.clone()); + + let all_txs = tx_set.into_vec(); // just construct a list of all txs to add - let expected_parked = vec![c1.clone(), c2.clone(), c3.clone(), d1.clone()] + let expected_parked = vec![c[0].clone(), c[1].clone(), c[2].clone(), d[0].clone()] .into_iter() .map(|tx| (tx.sender(), tx.nonce())) .collect::>(); @@ -563,18 +561,17 @@ mod tests { // txs based on when they were submitted, removing the oldest txs first, until the pool is // not over the limit let expected_removed = vec![ - a1.clone(), - a2.clone(), - a3.clone(), - a4.clone(), - b1.clone(), - b2.clone(), - b3.clone(), + a[0].clone(), + a[1].clone(), + a[2].clone(), + a[3].clone(), + b[0].clone(), + b[1].clone(), + b[2].clone(), ] .into_iter() .map(|tx| (tx.sender(), tx.nonce())) .collect::>(); - let all_txs = vec![a1, a2, a3, a4, b1, b2, b3, c1, c2, c3, d1]; // add all the transactions to the pool for tx in all_txs { @@ -608,41 +605,30 @@ mod tests { let mut f = MockTransactionFactory::default(); let mut pool = ParkedPool::>::default(); - let a = address!("000000000000000000000000000000000000000a"); - let b = address!("000000000000000000000000000000000000000b"); - let c = address!("000000000000000000000000000000000000000c"); - let d = address!("000000000000000000000000000000000000000d"); + let a_sender = address!("000000000000000000000000000000000000000a"); + let b_sender = address!("000000000000000000000000000000000000000b"); + let c_sender = address!("000000000000000000000000000000000000000c"); + let d_sender = address!("000000000000000000000000000000000000000d"); // create a chain of transactions by sender A, B, C - let a1 = MockTransaction::eip1559().with_sender(a); - let a2 = a1.next(); - let a3 = a2.next(); - let a4 = a3.next(); + let mut tx_set = + MockTransactionSet::dependent(a_sender, 0, 4, reth_primitives::TxType::EIP1559); + let a = tx_set.clone().into_vec(); - let b1 = MockTransaction::eip1559().with_sender(b); - let b2 = b1.next(); - let b3 = b2.next(); + let b = MockTransactionSet::dependent(b_sender, 0, 3, reth_primitives::TxType::EIP1559) + .into_vec(); + tx_set.extend(b.clone()); // C has the same number of txs as B - let c1 = MockTransaction::eip1559().with_sender(c); - let c2 = c1.next(); - let c3 = c2.next(); - - let d1 = MockTransaction::eip1559().with_sender(d); - - let all_txs = vec![ - a1.clone(), - a2.clone(), - a3.clone(), - a4.clone(), - b1.clone(), - b2.clone(), - b3.clone(), - c1.clone(), - c2.clone(), - c3.clone(), - d1.clone(), - ]; + let c = MockTransactionSet::dependent(c_sender, 0, 3, reth_primitives::TxType::EIP1559) + .into_vec(); + tx_set.extend(c.clone()); + + let d = MockTransactionSet::dependent(d_sender, 0, 1, reth_primitives::TxType::EIP1559) + .into_vec(); + tx_set.extend(d.clone()); + + let all_txs = tx_set.into_vec(); // add all the transactions to the pool for tx in all_txs { @@ -656,12 +642,15 @@ mod tests { .map(|s| s.sender_id) .collect::>(); assert_eq!(senders.len(), 4); - let expected_senders = - vec![d, c, b, a].into_iter().map(|s| f.ids.sender_id(&s).unwrap()).collect::>(); + let expected_senders = vec![d_sender, c_sender, b_sender, a_sender] + .into_iter() + .map(|s| f.ids.sender_id(&s).unwrap()) + .collect::>(); assert_eq!(senders, expected_senders); + // manually order the txs let mut pool = ParkedPool::>::default(); - let all_txs = vec![a1, b1, c1, d1, a2, b2, c2, a3, b3, c3, a4]; + let all_txs = vec![d[0].clone(), b[0].clone(), c[0].clone(), a[0].clone()]; // add all the transactions to the pool for tx in all_txs { @@ -674,8 +663,10 @@ mod tests { .map(|s| s.sender_id) .collect::>(); assert_eq!(senders.len(), 4); - let expected_senders = - vec![a, c, b, d].into_iter().map(|s| f.ids.sender_id(&s).unwrap()).collect::>(); + let expected_senders = vec![a_sender, c_sender, b_sender, d_sender] + .into_iter() + .map(|s| f.ids.sender_id(&s).unwrap()) + .collect::>(); assert_eq!(senders, expected_senders); } } diff --git a/crates/transaction-pool/src/pool/pending.rs b/crates/transaction-pool/src/pool/pending.rs index 34c2d0f711db..44409c68543f 100644 --- a/crates/transaction-pool/src/pool/pending.rs +++ b/crates/transaction-pool/src/pool/pending.rs @@ -185,7 +185,7 @@ impl PendingPool { // Remove all dependent transactions. 'this: while let Some((next_id, next_tx)) = transactions_iter.peek() { if next_id.sender != id.sender { - break 'this + break 'this; } removed.push(Arc::clone(&next_tx.transaction)); transactions_iter.next(); @@ -228,7 +228,7 @@ impl PendingPool { // Remove all dependent transactions. 'this: while let Some((next_id, next_tx)) = transactions_iter.peek() { if next_id.sender != id.sender { - break 'this + break 'this; } removed.push(Arc::clone(&next_tx.transaction)); transactions_iter.next(); @@ -420,12 +420,12 @@ impl PendingPool { } } - return + return; } if !remove_locals && tx.transaction.is_local() { non_local_senders -= 1; - continue + continue; } total_size += tx.transaction.size(); @@ -460,7 +460,7 @@ impl PendingPool { self.remove_to_limit(&limit, false, &mut removed); if self.size() <= limit.max_size && self.len() <= limit.max_txs { - return removed + return removed; } // now repeat for local transactions @@ -570,7 +570,7 @@ mod tests { use super::*; use crate::{ - test_utils::{MockOrdering, MockTransaction, MockTransactionFactory}, + test_utils::{MockOrdering, MockTransaction, MockTransactionFactory, MockTransactionSet}, PoolTransaction, }; @@ -661,31 +661,31 @@ mod tests { let mut f = MockTransactionFactory::default(); let mut pool = PendingPool::new(MockOrdering::default()); - let a = address!("000000000000000000000000000000000000000a"); - let b = address!("000000000000000000000000000000000000000b"); - let c = address!("000000000000000000000000000000000000000c"); - let d = address!("000000000000000000000000000000000000000d"); + let a_sender = address!("000000000000000000000000000000000000000a"); + let b_sender = address!("000000000000000000000000000000000000000b"); + let c_sender = address!("000000000000000000000000000000000000000c"); + let d_sender = address!("000000000000000000000000000000000000000d"); // create a chain of transactions by sender A, B, C - let a1 = MockTransaction::eip1559().with_sender(a); - let a2 = a1.next(); - let a3 = a2.next(); - let a4 = a3.next(); + let mut tx_set = + MockTransactionSet::dependent(a_sender, 0, 4, reth_primitives::TxType::EIP1559); + let a = tx_set.clone().into_vec(); - let b1 = MockTransaction::eip1559().with_sender(b); - let b2 = b1.next(); - let b3 = b2.next(); + let b = MockTransactionSet::dependent(b_sender, 0, 3, reth_primitives::TxType::EIP1559) + .into_vec(); + tx_set.extend(b.clone()); // C has the same number of txs as B - let c1 = MockTransaction::eip1559().with_sender(c); - let c2 = c1.next(); - let c3 = c2.next(); + let c = MockTransactionSet::dependent(c_sender, 0, 3, reth_primitives::TxType::EIP1559) + .into_vec(); + tx_set.extend(c.clone()); - let d1 = MockTransaction::eip1559().with_sender(d); + let d = MockTransactionSet::dependent(d_sender, 0, 1, reth_primitives::TxType::EIP1559) + .into_vec(); + tx_set.extend(d.clone()); // add all the transactions to the pool - let all_txs = - vec![a1, a2, a3, a4.clone(), b1, b2, b3.clone(), c1, c2, c3.clone(), d1.clone()]; + let all_txs = tx_set.into_vec(); for tx in all_txs { pool.add_transaction(f.validated_arc(tx), 0); } @@ -694,8 +694,10 @@ mod tests { // the independent set is the roots of each of these tx chains, these are the highest // nonces for each sender - let expected_highest_nonces = - vec![d1, c3, b3, a4].iter().map(|tx| (tx.sender(), tx.nonce())).collect::>(); + let expected_highest_nonces = vec![d[0].clone(), c[2].clone(), b[2].clone(), a[3].clone()] + .iter() + .map(|tx| (tx.sender(), tx.nonce())) + .collect::>(); let actual_highest_nonces = pool .highest_nonces .iter() diff --git a/crates/transaction-pool/src/test_utils/mock.rs b/crates/transaction-pool/src/test_utils/mock.rs index ea1a0509d1d1..a1c778af8857 100644 --- a/crates/transaction-pool/src/test_utils/mock.rs +++ b/crates/transaction-pool/src/test_utils/mock.rs @@ -246,6 +246,55 @@ impl MockTransaction { } } + /// Returns a new EIP2930 transaction with random address and hash and empty values + pub fn eip2930() -> Self { + MockTransaction::Eip2930 { + hash: B256::random(), + sender: Address::random(), + nonce: 0, + to: TransactionKind::Call(Address::random()), + gas_limit: 0, + input: Bytes::new(), + value: Default::default(), + gas_price: 0, + accesslist: Default::default(), + } + } + + /// Returns a new deposit transaction with random address and hash and empty values + #[cfg(feature = "optimism")] + pub fn deposit() -> Self { + MockTransaction::Deposit(TxDeposit { + source_hash: B256::random(), + from: Address::random(), + to: TransactionKind::Call(Address::random()), + mint: Some(0), + value: Default::default(), + gas_limit: 0, + is_system_transaction: false, + input: Bytes::new(), + }) + } + + /// Creates a new transaction with the given [TxType]. + /// + /// See the default constructors for each of the transaction types: + /// + /// * [MockTransaction::legacy] + /// * [MockTransaction::eip2930] + /// * [MockTransaction::eip1559] + /// * [MockTransaction::eip4844] + pub fn new_from_type(tx_type: TxType) -> Self { + match tx_type { + TxType::Legacy => Self::legacy(), + TxType::EIP2930 => Self::eip2930(), + TxType::EIP1559 => Self::eip1559(), + TxType::EIP4844 => Self::eip4844(), + #[cfg(feature = "optimism")] + TxType::DEPOSIT => Self::deposit(), + } + } + /// Sets the max fee per blob gas for EIP-4844 transactions, pub fn with_blob_fee(mut self, val: u128) -> Self { self.set_blob_fee(val); @@ -588,12 +637,12 @@ impl PoolTransaction for MockTransaction { let base_fee = base_fee as u128; let max_fee_per_gas = self.max_fee_per_gas(); if max_fee_per_gas < base_fee { - return None + return None; } let fee = max_fee_per_gas - base_fee; if let Some(priority_fee) = self.max_priority_fee_per_gas() { - return Some(fee.min(priority_fee)) + return Some(fee.min(priority_fee)); } Some(fee) @@ -1000,6 +1049,7 @@ impl MockTransactionFactory { pub fn validated(&mut self, transaction: MockTransaction) -> MockValidTx { self.validated_with_origin(TransactionOrigin::External, transaction) } + pub fn validated_arc(&mut self, transaction: MockTransaction) -> Arc { Arc::new(self.validated(transaction)) } @@ -1083,6 +1133,43 @@ impl MockTransactionDistribution { } } +/// A set of [MockTransaction]s that can be modified at once +#[derive(Debug, Clone)] +pub struct MockTransactionSet { + pub(crate) transactions: Vec, +} + +impl MockTransactionSet { + /// Create a new [MockTransactionSet] from a list of transactions + fn new(transactions: Vec) -> Self { + Self { transactions } + } + + /// Create a list of dependent transactions with a common sender. The transactions start at the + /// given nonce, and the sender is incremented by the given tx_count. + pub fn dependent(sender: Address, from_nonce: u64, tx_count: usize, tx_type: TxType) -> Self { + let mut txs = Vec::with_capacity(tx_count); + let mut curr_tx = MockTransaction::new_from_type(tx_type).with_nonce(from_nonce); + for i in 0..tx_count { + let nonce = from_nonce + i as u64; + curr_tx = curr_tx.next().with_sender(sender); + txs.push(curr_tx.clone()); + } + + MockTransactionSet::new(txs) + } + + /// Add transactions to the [MockTransactionSet] + pub fn extend>(&mut self, txs: T) { + self.transactions.extend(txs); + } + + /// Extract the inner [Vec] of [MockTransaction]s + pub fn into_vec(self) -> Vec { + self.transactions + } +} + #[test] fn test_mock_priority() { let o = MockOrdering; From c29fcf3b162e737c5f20cf52dc672a8d30678ff3 Mon Sep 17 00:00:00 2001 From: Matthias Seitz Date: Thu, 30 Nov 2023 16:10:02 +0100 Subject: [PATCH 062/277] docs: more tracing type docs (#5638) --- .../rpc/rpc-types/src/eth/trace/geth/call.rs | 13 ++++++ .../rpc/rpc-types/src/eth/trace/geth/mod.rs | 8 +++- crates/rpc/rpc-types/src/eth/trace/parity.rs | 46 ++++++++++++++++++- 3 files changed, 65 insertions(+), 2 deletions(-) diff --git a/crates/rpc/rpc-types/src/eth/trace/geth/call.rs b/crates/rpc/rpc-types/src/eth/trace/geth/call.rs index 1d2d75419471..bf6910cf19a2 100644 --- a/crates/rpc/rpc-types/src/eth/trace/geth/call.rs +++ b/crates/rpc/rpc-types/src/eth/trace/geth/call.rs @@ -7,30 +7,43 @@ use serde::{Deserialize, Serialize}; /// #[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")] diff --git a/crates/rpc/rpc-types/src/eth/trace/geth/mod.rs b/crates/rpc/rpc-types/src/eth/trace/geth/mod.rs index adc2f7a8e31c..a95a3dc2dade 100644 --- a/crates/rpc/rpc-types/src/eth/trace/geth/mod.rs +++ b/crates/rpc/rpc-types/src/eth/trace/geth/mod.rs @@ -1,5 +1,5 @@ -//! Geth tracing types #![allow(missing_docs)] +//! Geth tracing types use crate::{state::StateOverride, BlockOverrides}; use alloy_primitives::{Bytes, B256, U256}; @@ -43,10 +43,14 @@ pub struct BlockTraceResult { #[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, } @@ -250,6 +254,7 @@ impl From for GethDebugTracerConfig { #[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. @@ -460,6 +465,7 @@ impl GethDefaultTracingOptions { #[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 diff --git a/crates/rpc/rpc-types/src/eth/trace/parity.rs b/crates/rpc/rpc-types/src/eth/trace/parity.rs index 5a24a151d8db..2fbc36137a18 100644 --- a/crates/rpc/rpc-types/src/eth/trace/parity.rs +++ b/crates/rpc/rpc-types/src/eth/trace/parity.rs @@ -1,4 +1,3 @@ -#![allow(missing_docs)] //! Types for trace module. //! //! See @@ -58,26 +57,38 @@ impl TraceResults { #[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), } @@ -111,12 +122,17 @@ impl Delta { } } +/// 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>, } @@ -139,16 +155,20 @@ impl DerefMut for StateDiff { } } +/// 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), } @@ -249,13 +269,17 @@ pub struct CreateAction { 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 { @@ -279,18 +303,25 @@ pub struct SelfdestructAction { 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, } @@ -328,15 +359,24 @@ impl TraceOutput { #[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 { @@ -429,6 +469,7 @@ pub struct VmTrace { 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 { @@ -443,6 +484,7 @@ pub struct VmInstruction { /// 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, } @@ -475,7 +517,9 @@ pub struct MemoryDelta { #[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, } From 02a07f6480c4dfd1ce3c6aaa2a1d312595c62705 Mon Sep 17 00:00:00 2001 From: Matthias Seitz Date: Thu, 30 Nov 2023 16:11:03 +0100 Subject: [PATCH 063/277] chore: apply same impl order (#5639) --- .../src/providers/database/provider.rs | 254 +++++++++--------- 1 file changed, 127 insertions(+), 127 deletions(-) diff --git a/crates/storage/provider/src/providers/database/provider.rs b/crates/storage/provider/src/providers/database/provider.rs index b6e436a97248..7ff06228acc6 100644 --- a/crates/storage/provider/src/providers/database/provider.rs +++ b/crates/storage/provider/src/providers/database/provider.rs @@ -1574,22 +1574,22 @@ impl StageCheckpointReader for DatabaseProvider { } impl StageCheckpointWriter for DatabaseProvider { - /// Save stage checkpoint progress. - fn save_stage_checkpoint_progress( + /// Save stage checkpoint. + fn save_stage_checkpoint( &self, id: StageId, - checkpoint: Vec, + checkpoint: StageCheckpoint, ) -> ProviderResult<()> { - Ok(self.tx.put::(id.to_string(), checkpoint)?) + Ok(self.tx.put::(id.to_string(), checkpoint)?) } - /// Save stage checkpoint. - fn save_stage_checkpoint( + /// Save stage checkpoint progress. + fn save_stage_checkpoint_progress( &self, id: StageId, - checkpoint: StageCheckpoint, + checkpoint: Vec, ) -> ProviderResult<()> { - Ok(self.tx.put::(id.to_string(), checkpoint)?) + Ok(self.tx.put::(id.to_string(), checkpoint)?) } fn update_pipeline_stages( @@ -1678,76 +1678,73 @@ impl StorageReader for DatabaseProvider { } impl HashingWriter for DatabaseProvider { - fn insert_hashes( + fn unwind_account_hashing( &self, range: RangeInclusive, - end_block_hash: B256, - expected_state_root: B256, - ) -> ProviderResult<()> { - // Initialize prefix sets. - let mut account_prefix_set = PrefixSetMut::default(); - let mut storage_prefix_set: HashMap = HashMap::default(); - let mut destroyed_accounts = HashSet::default(); + ) -> ProviderResult>> { + let mut hashed_accounts_cursor = self.tx.cursor_write::()?; - let mut durations_recorder = metrics::DurationsRecorder::default(); + // Aggregate all block changesets and make a list of accounts that have been changed. + let hashed_accounts = self + .tx + .cursor_read::()? + .walk_range(range)? + .collect::, _>>()? + .into_iter() + .rev() + // fold all account to get the old balance/nonces and account that needs to be removed + .fold( + BTreeMap::new(), + |mut accounts: BTreeMap>, (_, account_before)| { + accounts.insert(account_before.address, account_before.info); + accounts + }, + ) + .into_iter() + // hash addresses and collect it inside sorted BTreeMap. + // We are doing keccak only once per address. + .map(|(address, account)| (keccak256(address), account)) + .collect::>(); - // storage hashing stage - { - let lists = self.changed_storages_with_range(range.clone())?; - let storages = self.plain_state_storages(lists)?; - let storage_entries = self.insert_storage_for_hashing(storages)?; - for (hashed_address, hashed_slots) in storage_entries { - account_prefix_set.insert(Nibbles::unpack(hashed_address)); - for slot in hashed_slots { - storage_prefix_set - .entry(hashed_address) - .or_default() - .insert(Nibbles::unpack(slot)); + hashed_accounts + .iter() + // Apply values to HashedState (if Account is None remove it); + .try_for_each(|(hashed_address, account)| -> ProviderResult<()> { + if let Some(account) = account { + hashed_accounts_cursor.upsert(*hashed_address, *account)?; + } else if hashed_accounts_cursor.seek_exact(*hashed_address)?.is_some() { + hashed_accounts_cursor.delete_current()?; } - } - } - durations_recorder.record_relative(metrics::Action::InsertStorageHashing); + Ok(()) + })?; - // account hashing stage - { - let lists = self.changed_accounts_with_range(range.clone())?; - let accounts = self.basic_accounts(lists)?; - let hashed_addresses = self.insert_account_for_hashing(accounts)?; - for (hashed_address, account) in hashed_addresses { - account_prefix_set.insert(Nibbles::unpack(hashed_address)); - if account.is_none() { - destroyed_accounts.insert(hashed_address); - } - } - } - durations_recorder.record_relative(metrics::Action::InsertAccountHashing); + Ok(hashed_accounts) + } - // merkle tree - { - // This is the same as `StateRoot::incremental_root_with_updates`, only the prefix sets - // are pre-loaded. - let (state_root, trie_updates) = StateRoot::new(&self.tx) - .with_changed_account_prefixes(account_prefix_set.freeze()) - .with_changed_storage_prefixes( - storage_prefix_set.into_iter().map(|(k, v)| (k, v.freeze())).collect(), - ) - .with_destroyed_accounts(destroyed_accounts) - .root_with_updates() - .map_err(Into::::into)?; - if state_root != expected_state_root { - return Err(ProviderError::StateRootMismatch(Box::new(RootMismatch { - root: GotExpected { got: state_root, expected: expected_state_root }, - block_number: *range.end(), - block_hash: end_block_hash, - }))) - } - trie_updates.flush(&self.tx)?; - } - durations_recorder.record_relative(metrics::Action::InsertMerkleTree); + fn insert_account_for_hashing( + &self, + accounts: impl IntoIterator)>, + ) -> ProviderResult>> { + let mut hashed_accounts_cursor = self.tx.cursor_write::()?; - debug!(target: "providers::db", ?range, actions = ?durations_recorder.actions, "Inserted hashes"); + let hashed_accounts = accounts.into_iter().fold( + BTreeMap::new(), + |mut map: BTreeMap>, (address, account)| { + map.insert(keccak256(address), account); + map + }, + ); - Ok(()) + hashed_accounts.iter().try_for_each(|(hashed_address, account)| -> ProviderResult<()> { + if let Some(account) = account { + hashed_accounts_cursor.upsert(*hashed_address, *account)? + } else if hashed_accounts_cursor.seek_exact(*hashed_address)?.is_some() { + hashed_accounts_cursor.delete_current()?; + } + Ok(()) + })?; + + Ok(hashed_accounts) } fn unwind_storage_hashing( @@ -1848,73 +1845,76 @@ impl HashingWriter for DatabaseProvider { Ok(hashed_storage_keys) } - fn unwind_account_hashing( + fn insert_hashes( &self, range: RangeInclusive, - ) -> ProviderResult>> { - let mut hashed_accounts_cursor = self.tx.cursor_write::()?; + end_block_hash: B256, + expected_state_root: B256, + ) -> ProviderResult<()> { + // Initialize prefix sets. + let mut account_prefix_set = PrefixSetMut::default(); + let mut storage_prefix_set: HashMap = HashMap::default(); + let mut destroyed_accounts = HashSet::default(); - // Aggregate all block changesets and make a list of accounts that have been changed. - let hashed_accounts = self - .tx - .cursor_read::()? - .walk_range(range)? - .collect::, _>>()? - .into_iter() - .rev() - // fold all account to get the old balance/nonces and account that needs to be removed - .fold( - BTreeMap::new(), - |mut accounts: BTreeMap>, (_, account_before)| { - accounts.insert(account_before.address, account_before.info); - accounts - }, - ) - .into_iter() - // hash addresses and collect it inside sorted BTreeMap. - // We are doing keccak only once per address. - .map(|(address, account)| (keccak256(address), account)) - .collect::>(); + let mut durations_recorder = metrics::DurationsRecorder::default(); - hashed_accounts - .iter() - // Apply values to HashedState (if Account is None remove it); - .try_for_each(|(hashed_address, account)| -> ProviderResult<()> { - if let Some(account) = account { - hashed_accounts_cursor.upsert(*hashed_address, *account)?; - } else if hashed_accounts_cursor.seek_exact(*hashed_address)?.is_some() { - hashed_accounts_cursor.delete_current()?; + // storage hashing stage + { + let lists = self.changed_storages_with_range(range.clone())?; + let storages = self.plain_state_storages(lists)?; + let storage_entries = self.insert_storage_for_hashing(storages)?; + for (hashed_address, hashed_slots) in storage_entries { + account_prefix_set.insert(Nibbles::unpack(hashed_address)); + for slot in hashed_slots { + storage_prefix_set + .entry(hashed_address) + .or_default() + .insert(Nibbles::unpack(slot)); } - Ok(()) - })?; - - Ok(hashed_accounts) - } - - fn insert_account_for_hashing( - &self, - accounts: impl IntoIterator)>, - ) -> ProviderResult>> { - let mut hashed_accounts_cursor = self.tx.cursor_write::()?; + } + } + durations_recorder.record_relative(metrics::Action::InsertStorageHashing); - let hashed_accounts = accounts.into_iter().fold( - BTreeMap::new(), - |mut map: BTreeMap>, (address, account)| { - map.insert(keccak256(address), account); - map - }, - ); + // account hashing stage + { + let lists = self.changed_accounts_with_range(range.clone())?; + let accounts = self.basic_accounts(lists)?; + let hashed_addresses = self.insert_account_for_hashing(accounts)?; + for (hashed_address, account) in hashed_addresses { + account_prefix_set.insert(Nibbles::unpack(hashed_address)); + if account.is_none() { + destroyed_accounts.insert(hashed_address); + } + } + } + durations_recorder.record_relative(metrics::Action::InsertAccountHashing); - hashed_accounts.iter().try_for_each(|(hashed_address, account)| -> ProviderResult<()> { - if let Some(account) = account { - hashed_accounts_cursor.upsert(*hashed_address, *account)? - } else if hashed_accounts_cursor.seek_exact(*hashed_address)?.is_some() { - hashed_accounts_cursor.delete_current()?; + // merkle tree + { + // This is the same as `StateRoot::incremental_root_with_updates`, only the prefix sets + // are pre-loaded. + let (state_root, trie_updates) = StateRoot::new(&self.tx) + .with_changed_account_prefixes(account_prefix_set.freeze()) + .with_changed_storage_prefixes( + storage_prefix_set.into_iter().map(|(k, v)| (k, v.freeze())).collect(), + ) + .with_destroyed_accounts(destroyed_accounts) + .root_with_updates() + .map_err(Into::::into)?; + if state_root != expected_state_root { + return Err(ProviderError::StateRootMismatch(Box::new(RootMismatch { + root: GotExpected { got: state_root, expected: expected_state_root }, + block_number: *range.end(), + block_hash: end_block_hash, + }))) } - Ok(()) - })?; + trie_updates.flush(&self.tx)?; + } + durations_recorder.record_relative(metrics::Action::InsertMerkleTree); - Ok(hashed_accounts) + debug!(target: "providers::db", ?range, actions = ?durations_recorder.actions, "Inserted hashes"); + + Ok(()) } } From 12d955933359c500cddecfb2ee55c13f5b2c2dd7 Mon Sep 17 00:00:00 2001 From: Matthias Seitz Date: Thu, 30 Nov 2023 16:12:17 +0100 Subject: [PATCH 064/277] fix: fetch missing cached blocks (#5612) --- crates/rpc/rpc/src/eth/api/fee_history.rs | 142 ++++++++++++++++------ 1 file changed, 103 insertions(+), 39 deletions(-) diff --git a/crates/rpc/rpc/src/eth/api/fee_history.rs b/crates/rpc/rpc/src/eth/api/fee_history.rs index b78c7f04ec09..8c01d672c1b2 100644 --- a/crates/rpc/rpc/src/eth/api/fee_history.rs +++ b/crates/rpc/rpc/src/eth/api/fee_history.rs @@ -1,40 +1,25 @@ //! Consist of types adjacent to the fee history cache and its configs use crate::eth::{cache::EthStateCache, error::EthApiError, gas_oracle::MAX_HEADER_HISTORY}; -use futures::{Stream, StreamExt}; +use futures::{ + future::{Fuse, FusedFuture}, + FutureExt, Stream, StreamExt, +}; use metrics::atomics::AtomicU64; use reth_primitives::{Receipt, SealedBlock, TransactionSigned, B256, U256}; use reth_provider::{BlockReaderIdExt, CanonStateNotification, ChainSpecProvider}; use reth_rpc_types::TxGasAndReward; use serde::{Deserialize, Serialize}; use std::{ - collections::BTreeMap, + collections::{BTreeMap, VecDeque}, fmt::Debug, sync::{atomic::Ordering::SeqCst, Arc}, }; +use tracing::trace; -/// Settings for the [FeeHistoryCache]. -#[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize)] -#[serde(rename_all = "camelCase")] -pub struct FeeHistoryCacheConfig { - /// Max number of blocks in cache. - /// - /// Default is [MAX_HEADER_HISTORY] plus some change to also serve slightly older blocks from - /// cache, since fee_history supports the entire range - pub max_blocks: u64, - /// Percentile approximation resolution - /// - /// Default is 4 which means 0.25 - pub resolution: u64, -} - -impl Default for FeeHistoryCacheConfig { - fn default() -> Self { - FeeHistoryCacheConfig { max_blocks: MAX_HEADER_HISTORY + 100, resolution: 4 } - } -} - -/// Wrapper struct for BTreeMap +/// Contains cached fee history entries for blocks. +/// +/// Purpose for this is to provide cached data for `eth_feeHistory`. #[derive(Debug, Clone)] pub struct FeeHistoryCache { inner: Arc, @@ -65,7 +50,24 @@ impl FeeHistoryCache { self.config().resolution } - /// Processing of the arriving blocks + /// Returns all blocks that are missing in the cache in the [lower_bound, upper_bound] range. + /// + /// This function is used to populate the cache with missing blocks, which can happen if the + /// node switched to stage sync node. + async fn missing_consecutive_blocks(&self) -> VecDeque { + let mut missing_blocks = VecDeque::new(); + let lower_bound = self.lower_bound(); + let upper_bound = self.upper_bound(); + let entries = self.inner.entries.read().await; + for block_number in (lower_bound..upper_bound).rev() { + if !entries.contains_key(&block_number) { + missing_blocks.push_back(block_number); + } + } + missing_blocks + } + + /// Insert block data into the cache. async fn insert_blocks(&self, blocks: I) where I: Iterator)>, @@ -99,6 +101,13 @@ impl FeeHistoryCache { } let upper_bound = *entries.last_entry().expect("Contains at least one entry").key(); + + // also enforce proper lower bound in case we have gaps + let target_lower = upper_bound.saturating_sub(self.inner.config.max_blocks); + while entries.len() > 1 && *entries.first_key_value().unwrap().0 < target_lower { + entries.pop_first(); + } + let lower_bound = *entries.first_entry().expect("Contains at least one entry").key(); self.inner.upper_bound.store(upper_bound, SeqCst); self.inner.lower_bound.store(lower_bound, SeqCst); @@ -130,7 +139,7 @@ impl FeeHistoryCache { if start_block >= lower_bound && end_block <= upper_bound { let entries = self.inner.entries.read().await; let result = entries - .range(start_block..=end_block + 1) + .range(start_block..=end_block) .map(|(_, fee_entry)| fee_entry.clone()) .collect::>(); @@ -153,18 +162,39 @@ impl FeeHistoryCache { } } +/// Settings for the [FeeHistoryCache]. +#[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct FeeHistoryCacheConfig { + /// Max number of blocks in cache. + /// + /// Default is [MAX_HEADER_HISTORY] plus some change to also serve slightly older blocks from + /// cache, since fee_history supports the entire range + pub max_blocks: u64, + /// Percentile approximation resolution + /// + /// Default is 4 which means 0.25 + pub resolution: u64, +} + +impl Default for FeeHistoryCacheConfig { + fn default() -> Self { + FeeHistoryCacheConfig { max_blocks: MAX_HEADER_HISTORY + 100, resolution: 4 } + } +} + /// Container type for shared state in [FeeHistoryCache] #[derive(Debug)] struct FeeHistoryCacheInner { /// Stores the lower bound of the cache lower_bound: AtomicU64, + /// Stores the upper bound of the cache upper_bound: AtomicU64, /// Config for FeeHistoryCache, consists of resolution for percentile approximation /// and max number of blocks config: FeeHistoryCacheConfig, /// Stores the entries of the cache entries: tokio::sync::RwLock>, - #[allow(unused)] eth_cache: EthStateCache, } @@ -173,22 +203,56 @@ struct FeeHistoryCacheInner { pub async fn fee_history_cache_new_blocks_task( fee_history_cache: FeeHistoryCache, mut events: St, - _provider: Provider, + provider: Provider, ) where St: Stream + Unpin + 'static, Provider: BlockReaderIdExt + ChainSpecProvider + 'static, { - // TODO: keep track of gaps in the chain and fill them from disk - - while let Some(event) = events.next().await { - if let Some(committed) = event.committed() { - let (blocks, receipts): (Vec<_>, Vec<_>) = committed - .blocks_and_receipts() - .map(|(block, receipts)| { - (block.block.clone(), receipts.iter().flatten().cloned().collect::>()) - }) - .unzip(); - fee_history_cache.insert_blocks(blocks.into_iter().zip(receipts)).await; + // We're listening for new blocks emitted when the node is in live sync. + // If the node transitions to stage sync, we need to fetch the missing blocks + let mut missing_blocks = VecDeque::new(); + let mut fetch_missing_block = Fuse::terminated(); + + loop { + if fetch_missing_block.is_terminated() { + if let Some(block_number) = missing_blocks.pop_front() { + trace!(target: "rpc::fee", ?block_number, "Fetching missing block for fee history cache"); + if let Ok(Some(hash)) = provider.block_hash(block_number) { + // fetch missing block + fetch_missing_block = fee_history_cache + .inner + .eth_cache + .get_block_and_receipts(hash) + .boxed() + .fuse(); + } + } + } + + tokio::select! { + res = &mut fetch_missing_block => { + if let Ok(res) = res { + fee_history_cache.insert_blocks(res.into_iter()).await; + } + } + event = events.next() => { + let Some(event) = event else { + // the stream ended, we are done + break; + }; + if let Some(committed) = event.committed() { + let (blocks, receipts): (Vec<_>, Vec<_>) = committed + .blocks_and_receipts() + .map(|(block, receipts)| { + (block.block.clone(), receipts.iter().flatten().cloned().collect::>()) + }) + .unzip(); + fee_history_cache.insert_blocks(blocks.into_iter().zip(receipts)).await; + + // keep track of missing blocks + missing_blocks = fee_history_cache.missing_consecutive_blocks().await; + } + } } } } From a4ed76d05844c07959cbc7a23f16c0f4982e5dcb Mon Sep 17 00:00:00 2001 From: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Date: Thu, 30 Nov 2023 20:14:47 +0100 Subject: [PATCH 065/277] perf: misc Codec improvements (#5642) --- crates/storage/codecs/src/lib.rs | 18 +++++------------- 1 file changed, 5 insertions(+), 13 deletions(-) diff --git a/crates/storage/codecs/src/lib.rs b/crates/storage/codecs/src/lib.rs index 5b7a5ee1610e..aeb0f2c5bece 100644 --- a/crates/storage/codecs/src/lib.rs +++ b/crates/storage/codecs/src/lib.rs @@ -89,7 +89,7 @@ impl_uint_compact!(u8, u64, u128); impl Compact for Vec where - T: Compact + Default, + T: Compact, { /// Returns 0 since we won't include it in the `StructFlags`. fn to_compact(self, buf: &mut B) -> usize @@ -116,15 +116,11 @@ where fn from_compact(buf: &[u8], _: usize) -> (Self, &[u8]) { let (length, mut buf) = decode_varuint(buf); let mut list = Vec::with_capacity(length); - #[allow(unused_assignments)] - let mut len = 0; for _ in 0..length { - #[allow(unused_assignments)] - let mut element = T::default(); - + let len; (len, buf) = decode_varuint(buf); - (element, _) = T::from_compact(&buf[..len], len); + let (element, _) = T::from_compact(&buf[..len], len); buf.advance(len); list.push(element); @@ -139,7 +135,6 @@ where B: bytes::BufMut + AsMut<[u8]>, { encode_varuint(self.len(), buf); - for element in self { element.to_compact(buf); } @@ -152,11 +147,8 @@ where let mut list = Vec::with_capacity(length); for _ in 0..length { - #[allow(unused_assignments)] - let mut element = T::default(); - + let element; (element, buf) = T::from_compact(buf, len); - list.push(element); } @@ -331,7 +323,7 @@ where fn decode_varuint(mut buf: &[u8]) -> (usize, &[u8]) { let mut value: usize = 0; - for i in 0..33 { + for i in 0usize..33 { let byte = buf.get_u8(); if byte < 128 { value |= usize::from(byte) << (i * 7); From 0d522e84729038e3ff50a57e1fc79f019e82a5b2 Mon Sep 17 00:00:00 2001 From: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Date: Thu, 30 Nov 2023 20:28:50 +0100 Subject: [PATCH 066/277] chore: improve Nibbles-related code (#5631) --- bin/reth/src/debug_cmd/in_memory_merkle.rs | 2 +- bin/reth/src/debug_cmd/merkle.rs | 5 +- crates/primitives/benches/nibbles.rs | 13 +- .../primitives/src/trie/hash_builder/mod.rs | 31 +- crates/primitives/src/trie/mask.rs | 13 +- crates/primitives/src/trie/mod.rs | 2 +- crates/primitives/src/trie/nibbles.rs | 274 ++++++++++++------ crates/primitives/src/trie/nodes/branch.rs | 4 +- crates/primitives/src/trie/nodes/leaf.rs | 15 +- crates/primitives/src/trie/nodes/mod.rs | 22 +- .../storage/db/src/tables/codecs/compact.rs | 2 +- crates/storage/db/src/tables/mod.rs | 4 +- crates/storage/db/src/tables/models/mod.rs | 8 +- crates/transaction-pool/benches/truncate.rs | 6 +- crates/trie/benches/prefix_set.rs | 13 +- crates/trie/src/prefix_set/mod.rs | 31 +- crates/trie/src/trie.rs | 24 +- crates/trie/src/trie_cursor/account_cursor.rs | 18 +- crates/trie/src/trie_cursor/storage_cursor.rs | 4 +- crates/trie/src/trie_cursor/subnode.rs | 6 +- crates/trie/src/updates.rs | 21 +- crates/trie/src/walker.rs | 20 +- docs/design/database.md | 2 +- 23 files changed, 310 insertions(+), 230 deletions(-) diff --git a/bin/reth/src/debug_cmd/in_memory_merkle.rs b/bin/reth/src/debug_cmd/in_memory_merkle.rs index 223a104efabf..357da8817129 100644 --- a/bin/reth/src/debug_cmd/in_memory_merkle.rs +++ b/bin/reth/src/debug_cmd/in_memory_merkle.rs @@ -223,7 +223,7 @@ impl Command { (Some(in_mem), Some(incr)) => { pretty_assertions::assert_eq!(in_mem.0, incr.0, "Nibbles don't match"); if in_mem.1 != incr.1 && - matches!(in_mem.0, TrieKey::AccountNode(ref nibbles) if nibbles.inner.len() > self.skip_node_depth.unwrap_or_default()) + matches!(in_mem.0, TrieKey::AccountNode(ref nibbles) if nibbles.len() > self.skip_node_depth.unwrap_or_default()) { in_mem_mismatched.push(in_mem); incremental_mismatched.push(incr); diff --git a/bin/reth/src/debug_cmd/merkle.rs b/bin/reth/src/debug_cmd/merkle.rs index fd6a9c0c5109..3745efc9f761 100644 --- a/bin/reth/src/debug_cmd/merkle.rs +++ b/bin/reth/src/debug_cmd/merkle.rs @@ -311,7 +311,7 @@ impl Command { "Nibbles don't match" ); if incremental.1 != clean.1 && - clean.0.inner.len() > self.skip_node_depth.unwrap_or_default() + clean.0.len() > self.skip_node_depth.unwrap_or_default() { incremental_account_mismatched.push(incremental); clean_account_mismatched.push(clean); @@ -340,8 +340,7 @@ impl Command { match (incremental_storage_trie_iter.next(), clean_storage_trie_iter.next()) { (Some(incremental), Some(clean)) => { if incremental != clean && - clean.1.nibbles.inner.len() > - self.skip_node_depth.unwrap_or_default() + clean.1.nibbles.len() > self.skip_node_depth.unwrap_or_default() { first_mismatched_storage = Some((incremental, clean)); break diff --git a/crates/primitives/benches/nibbles.rs b/crates/primitives/benches/nibbles.rs index abe9801a34c1..3d839fd7a688 100644 --- a/crates/primitives/benches/nibbles.rs +++ b/crates/primitives/benches/nibbles.rs @@ -3,17 +3,12 @@ use reth_primitives::trie::Nibbles; /// Benchmarks the nibble unpacking. pub fn nibbles_benchmark(c: &mut Criterion) { - c.bench_function("Nibbles unpack", |b| { + let mut g = c.benchmark_group("nibbles"); + g.bench_function("unpack", |b| { let raw = (1..=32).collect::>(); - b.iter(|| { - Nibbles::unpack(&raw); - }) + b.iter(|| Nibbles::unpack(&raw)) }); } -criterion_group! { - name = benches; - config = Criterion::default(); - targets = nibbles_benchmark -} +criterion_group!(benches, nibbles_benchmark); criterion_main!(benches); diff --git a/crates/primitives/src/trie/hash_builder/mod.rs b/crates/primitives/src/trie/hash_builder/mod.rs index 3e4a7a7265b5..89559819ecbf 100644 --- a/crates/primitives/src/trie/hash_builder/mod.rs +++ b/crates/primitives/src/trie/hash_builder/mod.rs @@ -1,8 +1,9 @@ use super::{ - nodes::{rlp_hash, BranchNode, ExtensionNode, LeafNode}, + nodes::{word_rlp, BranchNode, ExtensionNode, LeafNode}, BranchNodeCompact, Nibbles, TrieMask, }; use crate::{constants::EMPTY_ROOT_HASH, keccak256, Bytes, B256}; +use itertools::Itertools; use std::{ collections::{BTreeMap, HashMap}, fmt::Debug, @@ -62,7 +63,7 @@ pub struct HashBuilder { impl From for HashBuilder { fn from(state: HashBuilderState) -> Self { Self { - key: Nibbles::from_hex(state.key), + key: Nibbles::new_unchecked(state.key), stack: state.stack, value: state.value, groups: state.groups, @@ -79,7 +80,7 @@ impl From for HashBuilder { impl From for HashBuilderState { fn from(state: HashBuilder) -> Self { Self { - key: state.key.hex_data.to_vec(), + key: state.key.to_vec(), stack: state.stack, value: state.value, groups: state.groups, @@ -155,7 +156,7 @@ impl HashBuilder { if !self.key.is_empty() { self.update(&key); } else if key.is_empty() { - self.stack.push(rlp_hash(value)); + self.stack.push(word_rlp(&value)); } self.set_key_value(key, value); self.stored_in_database = stored_in_database; @@ -166,7 +167,7 @@ impl HashBuilder { // Clears the internal state if !self.key.is_empty() { self.update(&Nibbles::default()); - self.key.hex_data.0.clear(); + self.key.clear(); self.value = HashBuilderValue::Bytes(vec![]); } self.current_root() @@ -209,7 +210,7 @@ impl HashBuilder { tracing::Level::TRACE, "loop", i, - current = crate::hex::encode(¤t.hex_data), + ?current, ?build_extensions ); let _enter = span.enter(); @@ -217,7 +218,7 @@ impl HashBuilder { let preceding_exists = !self.groups.is_empty(); let preceding_len: usize = self.groups.len().saturating_sub(1); - let common_prefix_len = succeeding.common_prefix_length(¤t); + let common_prefix_len = succeeding.common_prefix_length(current.as_slice()); let len = std::cmp::max(preceding_len, common_prefix_len); assert!(len < current.len()); @@ -241,7 +242,7 @@ impl HashBuilder { trace!( target: "trie::hash_builder", ?extra_digit, - groups = self.groups.iter().map(|x| format!("{x:?}")).collect::>().join(","), + groups = ?self.groups.iter().format(", "), ); // Adjust the tree masks for exporting to the DB @@ -256,7 +257,7 @@ impl HashBuilder { trace!(target: "trie::hash_builder", "skipping {} nibbles", len_from); // The key without the common prefix - let short_node_key = current.slice_from(len_from); + let short_node_key = current.slice(len_from..); trace!(target: "trie::hash_builder", ?short_node_key); // Concatenate the 2 nodes together @@ -276,7 +277,7 @@ impl HashBuilder { } HashBuilderValue::Hash(hash) => { trace!(target: "trie::hash_builder", ?hash, "pushing branch node hash"); - self.stack.push(rlp_hash(*hash)); + self.stack.push(word_rlp(hash)); if self.stored_in_database { self.tree_masks[current.len() - 1] |= @@ -302,7 +303,7 @@ impl HashBuilder { }, "extension node rlp"); self.rlp_buf.clear(); self.stack.push(extension_node.rlp(&mut self.rlp_buf)); - self.retain_proof_from_buf(¤t.slice(0, len_from)); + self.retain_proof_from_buf(¤t.slice(..len_from)); self.resize_masks(len_from); } @@ -353,7 +354,7 @@ impl HashBuilder { self.rlp_buf.clear(); let rlp = branch_node.rlp(state_mask, &mut self.rlp_buf); - self.retain_proof_from_buf(¤t.slice(0, len)); + self.retain_proof_from_buf(¤t.slice(..len)); // Clears the stack from the branch node elements let first_child_idx = self.stack.len() - state_mask.count_ones() as usize; @@ -403,7 +404,7 @@ impl HashBuilder { // Send it over to the provided channel which will handle it on the // other side of the HashBuilder trace!(target: "trie::hash_builder", node = ?n, "intermediate node"); - let common_prefix = current.slice(0, len); + let common_prefix = current.slice(..len); if let Some(nodes) = self.updated_branch_nodes.as_mut() { nodes.insert(common_prefix, n); } @@ -563,7 +564,7 @@ mod tests { let (_, updates) = hb.split(); - let update = updates.get(&Nibbles::from(hex!("01").as_slice())).unwrap(); + let update = updates.get(&Nibbles::new_unchecked(hex!("01"))).unwrap(); assert_eq!(update.state_mask, TrieMask::new(0b1111)); // 1st nibble: 0, 1, 2, 3 assert_eq!(update.tree_mask, TrieMask::new(0)); assert_eq!(update.hash_mask, TrieMask::new(6)); // in the 1st nibble, the ones with 1 and 2 are branches with `hashes` @@ -633,7 +634,7 @@ mod tests { let mut hb2 = HashBuilder::default(); // Insert the branch with the `0x6` shared prefix. - hb2.add_branch(Nibbles::from_hex(vec![0x6]), branch_node_hash, false); + hb2.add_branch(Nibbles::new_unchecked([0x6]), branch_node_hash, false); let expected = trie_root(raw_input.clone()); assert_eq!(hb.root(), expected); diff --git a/crates/primitives/src/trie/mask.rs b/crates/primitives/src/trie/mask.rs index 152be03c936d..16fdd69597b9 100644 --- a/crates/primitives/src/trie/mask.rs +++ b/crates/primitives/src/trie/mask.rs @@ -32,27 +32,32 @@ pub struct TrieMask(u16); impl TrieMask { /// Creates a new `TrieMask` from the given inner value. + #[inline] pub fn new(inner: u16) -> Self { Self(inner) } /// Creates a new `TrieMask` from the given nibble. + #[inline] pub fn from_nibble(nibble: u8) -> Self { Self(1u16 << nibble) } /// Returns `true` if the current `TrieMask` is a subset of `other`. - pub fn is_subset_of(&self, other: &Self) -> bool { - *self & *other == *self + #[inline] + pub fn is_subset_of(self, other: Self) -> bool { + self & other == self } /// Returns `true` if a given bit is set in a mask. - pub fn is_bit_set(&self, index: u8) -> bool { + #[inline] + pub fn is_bit_set(self, index: u8) -> bool { self.0 & (1u16 << index) != 0 } /// Returns `true` if the mask is empty. - pub fn is_empty(&self) -> bool { + #[inline] + pub fn is_empty(self) -> bool { self.0 == 0 } } diff --git a/crates/primitives/src/trie/mod.rs b/crates/primitives/src/trie/mod.rs index f388fc6d1329..b531d8fdac36 100644 --- a/crates/primitives/src/trie/mod.rs +++ b/crates/primitives/src/trie/mod.rs @@ -19,7 +19,7 @@ mod subnode; pub use self::{ mask::TrieMask, - nibbles::{Nibbles, StoredNibbles, StoredNibblesSubKey}, + nibbles::{Nibbles, StoredNibblesSubKey}, storage::StorageTrieEntry, subnode::StoredSubNode, }; diff --git a/crates/primitives/src/trie/nibbles.rs b/crates/primitives/src/trie/nibbles.rs index d3d862966df5..aa36f2955c69 100644 --- a/crates/primitives/src/trie/nibbles.rs +++ b/crates/primitives/src/trie/nibbles.rs @@ -3,28 +3,30 @@ use alloy_rlp::RlpEncodableWrapper; use derive_more::{Deref, From, Index}; use reth_codecs::{main_codec, Compact}; use serde::{Deserialize, Serialize}; +use std::{borrow::Borrow, ops::RangeBounds}; -/// The nibbles are the keys for the AccountsTrie and the subkeys for the StorageTrie. -#[main_codec] -#[derive(Debug, Default, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] -pub struct StoredNibbles { - /// The inner nibble bytes - pub inner: Bytes, -} +/// The representation of nibbles of the merkle trie stored in the database. +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, PartialOrd, Ord, Hash, Deref)] +pub struct StoredNibblesSubKey(pub Nibbles); -impl From> for StoredNibbles { - fn from(inner: Vec) -> Self { - Self { inner: inner.into() } +impl From for StoredNibblesSubKey { + #[inline] + fn from(value: Nibbles) -> Self { + Self(value) } } -/// The representation of nibbles of the merkle trie stored in the database. -#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, PartialOrd, Ord, Hash, Deref)] -pub struct StoredNibblesSubKey(StoredNibbles); - impl From> for StoredNibblesSubKey { - fn from(inner: Vec) -> Self { - Self(StoredNibbles { inner: inner.into() }) + #[inline] + fn from(value: Vec) -> Self { + Self(Nibbles::new_unchecked(value)) + } +} + +impl From for Nibbles { + #[inline] + fn from(value: StoredNibblesSubKey) -> Self { + value.0 } } @@ -33,73 +35,122 @@ impl Compact for StoredNibblesSubKey { where B: bytes::BufMut + AsMut<[u8]>, { - assert!(self.inner.len() <= 64); - let mut padded = vec![0; 64]; - padded[..self.inner.len()].copy_from_slice(&self.inner[..]); - buf.put_slice(&padded); - buf.put_u8(self.inner.len() as u8); + assert!(self.0.len() <= 64); + + // right-pad with zeros + buf.put_slice(&self.0[..]); + static ZERO: &[u8; 64] = &[0; 64]; + buf.put_slice(&ZERO[self.0.len()..]); + + buf.put_u8(self.0.len() as u8); 64 + 1 } fn from_compact(buf: &[u8], _len: usize) -> (Self, &[u8]) { let len = buf[64] as usize; - let inner = Vec::from(&buf[..len]).into(); - (Self(StoredNibbles { inner }), &buf[65..]) + (Self(Nibbles::new_unchecked(buf[..len].to_vec())), &buf[65..]) } } /// Structure representing a sequence of nibbles. /// -/// A nibble is a 4-bit value, and this structure is used to store -/// the nibble sequence representing the keys in a Merkle Patricia Trie (MPT). -/// Using nibbles simplifies trie operations and enables consistent key -/// representation in the MPT. +/// A nibble is a 4-bit value, and this structure is used to store the nibble sequence representing +/// the keys in a Merkle Patricia Trie (MPT). +/// Using nibbles simplifies trie operations and enables consistent key representation in the MPT. /// -/// The `hex_data` field is a `Vec` that stores the nibbles, with each -/// `u8` value containing a single nibble. This means that each byte in -/// `hex_data` has its upper 4 bits set to zero and the lower 4 bits +/// The internal representation is a shared heap-allocated vector ([`Bytes`]) that stores one nibble +/// per byte. This means that each byte has its upper 4 bits set to zero and the lower 4 bits /// representing the nibble value. +#[main_codec] #[derive( - Default, Clone, Eq, PartialEq, RlpEncodableWrapper, PartialOrd, Ord, Hash, Index, From, Deref, + Clone, + Debug, + Default, + PartialEq, + Eq, + PartialOrd, + Ord, + Hash, + RlpEncodableWrapper, + Index, + From, + Deref, )] -pub struct Nibbles { - /// The inner representation of the nibble sequence. - pub hex_data: Bytes, +pub struct Nibbles(Bytes); + +impl From> for Nibbles { + #[inline] + fn from(value: Vec) -> Self { + Self::new_unchecked(value) + } } -impl From<&[u8]> for Nibbles { - fn from(slice: &[u8]) -> Self { - Nibbles::from_hex(slice.to_vec()) +impl From for Vec { + #[inline] + fn from(value: Nibbles) -> Self { + value.0.into() } } -impl From<&[u8; N]> for Nibbles { - fn from(arr: &[u8; N]) -> Self { - Nibbles::from_hex(arr.to_vec()) +impl From for Bytes { + #[inline] + fn from(value: Nibbles) -> Self { + value.into_bytes() } } -impl std::fmt::Debug for Nibbles { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - f.debug_struct("Nibbles").field("hex_data", &crate::hex::encode(&self.hex_data)).finish() +impl PartialEq<[u8]> for Nibbles { + #[inline] + fn eq(&self, other: &[u8]) -> bool { + self.as_slice() == other + } +} + +impl PartialEq for [u8] { + #[inline] + fn eq(&self, other: &Nibbles) -> bool { + self == other.as_slice() + } +} + +impl PartialOrd<[u8]> for Nibbles { + #[inline] + fn partial_cmp(&self, other: &[u8]) -> Option { + self.as_slice().partial_cmp(other) + } +} + +impl PartialOrd for [u8] { + #[inline] + fn partial_cmp(&self, other: &Nibbles) -> Option { + self.partial_cmp(other.as_slice()) + } +} + +impl Borrow<[u8]> for Nibbles { + #[inline] + fn borrow(&self) -> &[u8] { + self.as_slice() } } impl Nibbles { - /// Creates a new [Nibbles] instance from bytes. - pub fn from_hex>(hex: T) -> Self { - Nibbles { hex_data: hex.into() } + /// Creates a new [`Nibbles`] instance from nibble bytes, without checking their validity. + #[inline] + pub fn new_unchecked>(nibbles: T) -> Self { + Self(nibbles.into()) } - /// Take a byte array (slice or vector) as input and convert it into a [Nibbles] struct - /// containing the nibbles (half-bytes or 4 bits) that make up the input byte data. + /// Converts a byte slice into a [`Nibbles`] instance containing the nibbles (half-bytes or 4 + /// bits) that make up the input byte data. pub fn unpack>(data: T) -> Self { - let mut vec = Vec::with_capacity(data.as_ref().len() * 2); - for byte in data.as_ref() { - vec.push(byte / 16); - vec.push(byte % 16); + let data = data.as_ref(); + let mut nibbles = Vec::with_capacity(data.len() * 2); + for &byte in data { + nibbles.push(byte >> 4); + nibbles.push(byte & 0x0f); } - Nibbles { hex_data: Bytes::from(vec) } + Self(nibbles.into()) } /// Packs the nibbles stored in the struct into a byte vector. @@ -109,22 +160,14 @@ impl Nibbles { /// If the number of nibbles is odd, the last nibble is shifted left by 4 bits and /// added to the packed byte vector. pub fn pack(&self) -> Vec { - let length = (self.len() + 1) / 2; - if length == 0 { - Vec::new() - } else { - self.iter() - .enumerate() - .filter_map(|(index, nibble)| { - if index % 2 == 0 { - let next_nibble = self.get(index + 1).unwrap_or(&0); - Some((*nibble << 4) + *next_nibble) - } else { - None - } - }) - .collect() + let packed_len = (self.len() + 1) / 2; + let mut v = Vec::with_capacity(packed_len); + for i in 0..packed_len { + let hi = *unsafe { self.get_unchecked(i * 2) }; + let lo = self.get(i * 2 + 1).copied().unwrap_or(0); + v.push((hi << 4) | lo); } + v } /// Encodes a given path leaf as a compact array of bytes, where each byte represents two @@ -155,19 +198,19 @@ impl Nibbles { /// # use reth_primitives::trie::Nibbles; /// /// // Extension node with an even path length: - /// let nibbles = Nibbles::from_hex(vec![0x0A, 0x0B, 0x0C, 0x0D]); + /// let nibbles = Nibbles::new_unchecked(&[0x0A, 0x0B, 0x0C, 0x0D]); /// assert_eq!(nibbles.encode_path_leaf(false), vec![0x00, 0xAB, 0xCD]); /// /// // Extension node with an odd path length: - /// let nibbles = Nibbles::from_hex(vec![0x0A, 0x0B, 0x0C]); + /// let nibbles = Nibbles::new_unchecked(&[0x0A, 0x0B, 0x0C]); /// assert_eq!(nibbles.encode_path_leaf(false), vec![0x1A, 0xBC]); /// /// // Leaf node with an even path length: - /// let nibbles = Nibbles::from_hex(vec![0x0A, 0x0B, 0x0C, 0x0D]); + /// let nibbles = Nibbles::new_unchecked(&[0x0A, 0x0B, 0x0C, 0x0D]); /// assert_eq!(nibbles.encode_path_leaf(true), vec![0x20, 0xAB, 0xCD]); /// /// // Leaf node with an odd path length: - /// let nibbles = Nibbles::from_hex(vec![0x0A, 0x0B, 0x0C]); + /// let nibbles = Nibbles::new_unchecked(&[0x0A, 0x0B, 0x0C]); /// assert_eq!(nibbles.encode_path_leaf(true), vec![0x3A, 0xBC]); /// ``` pub fn encode_path_leaf(&self, is_leaf: bool) -> Vec { @@ -192,14 +235,14 @@ impl Nibbles { } /// Increments the nibble sequence by one. - pub fn increment(&self) -> Option { - let mut incremented = self.hex_data.to_vec(); + pub fn increment(&self) -> Option { + let mut incremented = self.0.to_vec(); for nibble in incremented.iter_mut().rev() { - assert!(*nibble < 0x10); + debug_assert!(*nibble < 0x10); if *nibble < 0xf { *nibble += 1; - return Some(Nibbles::from_hex(incremented)) + return Some(Self::new_unchecked(incremented)) } else { *nibble = 0; } @@ -210,27 +253,37 @@ impl Nibbles { /// The last element of the hex vector is used to determine whether the nibble sequence /// represents a leaf or an extension node. If the last element is 0x10 (16), then it's a leaf. + #[inline] pub fn is_leaf(&self) -> bool { - self.hex_data[self.hex_data.len() - 1] == 16 + self.last() == Some(16) } /// Returns `true` if the current nibble sequence starts with the given prefix. - pub fn has_prefix(&self, other: &Self) -> bool { + #[inline] + pub fn has_prefix(&self, other: &[u8]) -> bool { self.starts_with(other) } /// Returns the nibble at the given index. + /// + /// # Panics + /// + /// Panics if the index is out of bounds. + #[inline] + #[track_caller] pub fn at(&self, i: usize) -> usize { - self.hex_data[i] as usize + self.0[i] as usize } /// Returns the last nibble of the current nibble sequence. + #[inline] pub fn last(&self) -> Option { - self.hex_data.last().copied() + self.0.last().copied() } /// Returns the length of the common prefix between the current nibble sequence and the given. - pub fn common_prefix_length(&self, other: &Nibbles) -> usize { + #[inline] + pub fn common_prefix_length(&self, other: &[u8]) -> usize { let len = std::cmp::min(self.len(), other.len()); for i in 0..len { if self[i] != other[i] { @@ -240,34 +293,67 @@ impl Nibbles { len } - /// Slice the current nibbles from the given start index to the end. - pub fn slice_from(&self, index: usize) -> Nibbles { - self.slice(index, self.hex_data.len()) + /// Returns a reference to the underlying [`Bytes`]. + #[inline] + pub fn as_bytes(&self) -> &Bytes { + &self.0 + } + + /// Returns the nibbles as a byte slice. + #[inline] + pub fn as_slice(&self) -> &[u8] { + &self.0 + } + + /// Returns the underlying [`Bytes`]. + #[inline] + pub fn into_bytes(self) -> Bytes { + self.0 } /// Slice the current nibbles within the provided index range. - pub fn slice(&self, start: usize, end: usize) -> Nibbles { - Nibbles::from_hex(self.hex_data[start..end].to_vec()) + #[inline] + pub fn slice(&self, range: impl RangeBounds) -> Self { + Self(self.0.slice(range)) } /// Join two nibbles together. - pub fn join(&self, b: &Nibbles) -> Nibbles { + #[inline] + pub fn join(&self, b: &Self) -> Self { let mut hex_data = Vec::with_capacity(self.len() + b.len()); hex_data.extend_from_slice(self); hex_data.extend_from_slice(b); - Nibbles::from_hex(hex_data) + Self::new_unchecked(hex_data) + } + + /// Pushes a nibble to the end of the current nibbles. + /// + /// **Note**: This method re-allocates on each call. + #[inline] + pub fn push(&mut self, nibble: u8) { + self.extend([nibble]); } /// Extend the current nibbles with another nibbles. + /// + /// **Note**: This method re-allocates on each call. + #[inline] pub fn extend(&mut self, b: impl AsRef<[u8]>) { - let mut bytes = self.hex_data.to_vec(); + let mut bytes = self.0.to_vec(); bytes.extend_from_slice(b.as_ref()); - self.hex_data = bytes.into(); + self.0 = bytes.into(); } - /// Truncate the current nibbles to the given length. + /// Truncates the current nibbles to the given length. + #[inline] pub fn truncate(&mut self, len: usize) { - self.hex_data.0.truncate(len) + self.0.truncate(len); + } + + /// Clears the current nibbles. + #[inline] + pub fn clear(&mut self) { + self.0.clear(); } } @@ -279,7 +365,7 @@ mod tests { #[test] fn hashed_regression() { - let nibbles = Nibbles::from_hex(hex!("05010406040a040203030f010805020b050c04070003070e0909070f010b0a0805020301070c0a0902040b0f000f0006040a04050f020b090701000a0a040b")); + let nibbles = Nibbles::new_unchecked(hex!("05010406040a040203030f010805020b050c04070003070e0909070f010b0a0805020301070c0a0902040b0f000f0006040a04050f020b090701000a0a040b")); let path = nibbles.encode_path_leaf(true); let expected = hex!("351464a4233f1852b5c47037e997f1ba852317ca924bf0f064a45f2b9710aa4b"); assert_eq!(path, expected); @@ -295,7 +381,7 @@ mod tests { (vec![0xa, 0xb, 0x2, 0x0], vec![0xab, 0x20]), (vec![0xa, 0xb, 0x2, 0x7], vec![0xab, 0x27]), ] { - let nibbles = Nibbles::from_hex(input); + let nibbles = Nibbles::new_unchecked(input); let encoded = nibbles.pack(); assert_eq!(encoded, expected); } diff --git a/crates/primitives/src/trie/nodes/branch.rs b/crates/primitives/src/trie/nodes/branch.rs index 2771adfa40c8..961050be3f8d 100644 --- a/crates/primitives/src/trie/nodes/branch.rs +++ b/crates/primitives/src/trie/nodes/branch.rs @@ -123,8 +123,8 @@ impl BranchNodeCompact { ) -> Self { let (state_mask, tree_mask, hash_mask) = (state_mask.into(), tree_mask.into(), hash_mask.into()); - assert!(tree_mask.is_subset_of(&state_mask)); - assert!(hash_mask.is_subset_of(&state_mask)); + assert!(tree_mask.is_subset_of(state_mask)); + assert!(hash_mask.is_subset_of(state_mask)); assert_eq!(hash_mask.count_ones() as usize, hashes.len()); Self { state_mask, tree_mask, hash_mask, hashes, root_hash } } diff --git a/crates/primitives/src/trie/nodes/leaf.rs b/crates/primitives/src/trie/nodes/leaf.rs index 9fb016e1505a..684bf913d6ff 100644 --- a/crates/primitives/src/trie/nodes/leaf.rs +++ b/crates/primitives/src/trie/nodes/leaf.rs @@ -54,25 +54,22 @@ impl std::fmt::Debug for LeafNode<'_> { #[cfg(test)] mod tests { use super::*; - use crate::hex_literal::hex; + use crate::hex; // From manual regression test #[test] fn encode_leaf_node_nibble() { - let nibble = Nibbles { hex_data: hex!("0604060f").into() }; + let nibble = Nibbles::new_unchecked(hex!("0604060f")); let encoded = nibble.encode_path_leaf(true); - let expected = hex!("20646f").to_vec(); - assert_eq!(encoded, expected); + assert_eq!(encoded, hex!("20646f")); } #[test] fn rlp_leaf_node_roundtrip() { - let nibble = Nibbles { hex_data: hex!("0604060f").into() }; - let val = hex!("76657262").to_vec(); + let nibble = Nibbles::new_unchecked(hex!("0604060f")); + let val = hex!("76657262"); let leaf = LeafNode::new(&nibble, &val); let rlp = leaf.rlp(&mut vec![]); - - let expected = hex!("c98320646f8476657262").to_vec(); - assert_eq!(rlp, expected); + assert_eq!(rlp, hex!("c98320646f8476657262")); } } diff --git a/crates/primitives/src/trie/nodes/mod.rs b/crates/primitives/src/trie/nodes/mod.rs index 78c413a92516..98e7d494af7e 100644 --- a/crates/primitives/src/trie/nodes/mod.rs +++ b/crates/primitives/src/trie/nodes/mod.rs @@ -3,28 +3,30 @@ use alloy_rlp::EMPTY_STRING_CODE; use std::ops::Range; mod branch; +pub use branch::{BranchNode, BranchNodeCompact}; + mod extension; -mod leaf; +pub use extension::ExtensionNode; -pub use self::{ - branch::{BranchNode, BranchNodeCompact}, - extension::ExtensionNode, - leaf::LeafNode, -}; +mod leaf; +pub use leaf::LeafNode; /// The range of valid child indexes. pub const CHILD_INDEX_RANGE: Range = 0..16; /// Given an RLP encoded node, returns either RLP(node) or RLP(keccak(RLP(node))) +#[inline] fn rlp_node(rlp: &[u8]) -> Vec { if rlp.len() < B256::len_bytes() { rlp.to_vec() } else { - rlp_hash(keccak256(rlp)) + word_rlp(&keccak256(rlp)) } } -/// Optimization for quick encoding of a hash as RLP -pub fn rlp_hash(hash: B256) -> Vec { - [[EMPTY_STRING_CODE + B256::len_bytes() as u8].as_slice(), hash.0.as_slice()].concat() +/// Optimization for quick encoding of a 32-byte word as RLP. +// TODO: this could return [u8; 33] but Vec is needed everywhere this function is used +#[inline] +pub fn word_rlp(word: &B256) -> Vec { + [&[EMPTY_STRING_CODE + B256::len_bytes() as u8][..], &word[..]].concat() } diff --git a/crates/storage/db/src/tables/codecs/compact.rs b/crates/storage/db/src/tables/codecs/compact.rs index c420e3105360..b005cc961ee6 100644 --- a/crates/storage/db/src/tables/codecs/compact.rs +++ b/crates/storage/db/src/tables/codecs/compact.rs @@ -37,7 +37,7 @@ impl_compression_for_compact!( Receipt, TxType, StorageEntry, - StoredNibbles, + Nibbles, BranchNodeCompact, StoredNibblesSubKey, StorageTrieEntry, diff --git a/crates/storage/db/src/tables/mod.rs b/crates/storage/db/src/tables/mod.rs index 275271f7bc39..f8d4457ff658 100644 --- a/crates/storage/db/src/tables/mod.rs +++ b/crates/storage/db/src/tables/mod.rs @@ -36,7 +36,7 @@ use crate::{ }; use reth_primitives::{ stage::StageCheckpoint, - trie::{BranchNodeCompact, StorageTrieEntry, StoredNibbles, StoredNibblesSubKey}, + trie::{BranchNodeCompact, Nibbles, StorageTrieEntry, StoredNibblesSubKey}, Account, Address, BlockHash, BlockNumber, Bytecode, Header, IntegerList, PruneCheckpoint, PruneSegment, Receipt, StorageEntry, TransactionSignedNoHash, TxHash, TxNumber, B256, }; @@ -384,7 +384,7 @@ dupsort!( table!( /// Stores the current state's Merkle Patricia Tree. - ( AccountsTrie ) StoredNibbles | BranchNodeCompact + ( AccountsTrie ) Nibbles | BranchNodeCompact ); dupsort!( diff --git a/crates/storage/db/src/tables/models/mod.rs b/crates/storage/db/src/tables/models/mod.rs index 507151797d4b..3fe332a60bbd 100644 --- a/crates/storage/db/src/tables/models/mod.rs +++ b/crates/storage/db/src/tables/models/mod.rs @@ -5,7 +5,7 @@ use crate::{ }; use reth_codecs::Compact; use reth_primitives::{ - trie::{StoredNibbles, StoredNibblesSubKey}, + trie::{Nibbles, StoredNibblesSubKey}, Address, PruneSegment, B256, }; @@ -102,18 +102,18 @@ impl Decode for String { } } -impl Encode for StoredNibbles { +impl Encode for Nibbles { type Encoded = Vec; // Delegate to the Compact implementation fn encode(self) -> Self::Encoded { - let mut buf = Vec::with_capacity(self.inner.len()); + let mut buf = Vec::with_capacity(self.len()); self.to_compact(&mut buf); buf } } -impl Decode for StoredNibbles { +impl Decode for Nibbles { fn decode>(value: B) -> Result { let buf = value.as_ref(); Ok(Self::from_compact(buf, buf.len()).0) diff --git a/crates/transaction-pool/benches/truncate.rs b/crates/transaction-pool/benches/truncate.rs index e96ed753b3e2..ca25d731d699 100644 --- a/crates/transaction-pool/benches/truncate.rs +++ b/crates/transaction-pool/benches/truncate.rs @@ -35,8 +35,7 @@ fn create_transactions_for_sender( .unwrap() .current(); - let mut nonce = 0; - for tx in txs.iter_mut() { + for (nonce, tx) in txs.iter_mut().enumerate() { // reject pre-eip1559 tx types, if there is a legacy tx, replace it with an eip1559 tx if tx.is_legacy() || tx.is_eip2930() { *tx = MockTransaction::eip1559(); @@ -47,8 +46,7 @@ fn create_transactions_for_sender( } tx.set_sender(sender); - tx.set_nonce(nonce); - nonce += 1; + tx.set_nonce(nonce as u64); } txs diff --git a/crates/trie/benches/prefix_set.rs b/crates/trie/benches/prefix_set.rs index 95782fe890f1..c9e4dbbd699f 100644 --- a/crates/trie/benches/prefix_set.rs +++ b/crates/trie/benches/prefix_set.rs @@ -16,14 +16,13 @@ pub trait PrefixSetAbstraction: Default { fn contains(&mut self, key: Nibbles) -> bool; } -/// Abstractions used for benching impl PrefixSetAbstraction for PrefixSetMut { fn insert(&mut self, key: Nibbles) { - self.insert(key) + PrefixSetMut::insert(self, key) } fn contains(&mut self, key: Nibbles) -> bool { - PrefixSetMut::contains(self, key) + PrefixSetMut::contains(self, &key) } } @@ -95,12 +94,12 @@ fn generate_test_data(size: usize) -> (Vec, Vec, Vec) { let mut preload = vec(vec(any::(), 32), size).new_tree(&mut runner).unwrap().current(); preload.dedup(); preload.sort(); - let preload = preload.into_iter().map(|hash| Nibbles::from(&hash[..])).collect::>(); + let preload = preload.into_iter().map(Nibbles::new_unchecked).collect::>(); let mut input = vec(vec(any::(), 0..=32), size).new_tree(&mut runner).unwrap().current(); input.dedup(); input.sort(); - let input = input.into_iter().map(|bytes| Nibbles::from(&bytes[..])).collect::>(); + let input = input.into_iter().map(Nibbles::new_unchecked).collect::>(); let expected = input .iter() @@ -145,11 +144,11 @@ mod implementations { fn contains(&mut self, prefix: Nibbles) -> bool { let range = match self.last_checked.as_ref() { // presumably never hit - Some(last) if &prefix < last => (Bound::Unbounded, Bound::Excluded(last)), + Some(last) if prefix < *last => (Bound::Unbounded, Bound::Excluded(last)), Some(last) => (Bound::Included(last), Bound::Unbounded), None => (Bound::Unbounded, Bound::Unbounded), }; - for key in self.keys.range(range) { + for key in self.keys.range::(range) { if key.has_prefix(&prefix) { self.last_checked = Some(prefix); return true diff --git a/crates/trie/src/prefix_set/mod.rs b/crates/trie/src/prefix_set/mod.rs index 7fe8cb9c17d2..0c262759a3c1 100644 --- a/crates/trie/src/prefix_set/mod.rs +++ b/crates/trie/src/prefix_set/mod.rs @@ -22,13 +22,14 @@ pub use loader::{LoadedPrefixSets, PrefixSetLoader}; /// # Examples /// /// ``` +/// use reth_primitives::trie::Nibbles; /// use reth_trie::prefix_set::PrefixSetMut; /// /// let mut prefix_set = PrefixSetMut::default(); -/// prefix_set.insert(b"key1"); -/// prefix_set.insert(b"key2"); -/// -/// assert_eq!(prefix_set.contains(b"key"), true); +/// prefix_set.insert(Nibbles::new_unchecked(&[0xa, 0xb])); +/// prefix_set.insert(Nibbles::new_unchecked(&[0xa, 0xb, 0xc])); +/// assert!(prefix_set.contains(&[0xa, 0xb])); +/// assert!(prefix_set.contains(&[0xa, 0xb, 0xc])); /// ``` #[derive(Debug, Default, Clone)] pub struct PrefixSetMut { @@ -49,26 +50,24 @@ where impl PrefixSetMut { /// Returns `true` if any of the keys in the set has the given prefix or /// if the given prefix is a prefix of any key in the set. - pub fn contains>(&mut self, prefix: T) -> bool { + pub fn contains(&mut self, prefix: &[u8]) -> bool { if !self.sorted { self.keys.sort(); self.keys.dedup(); self.sorted = true; } - let prefix = prefix.into(); - - while self.index > 0 && self.keys[self.index] > prefix { + while self.index > 0 && self.keys[self.index] > *prefix { self.index -= 1; } for (idx, key) in self.keys[self.index..].iter().enumerate() { - if key.has_prefix(&prefix) { + if key.has_prefix(prefix) { self.index += idx; return true } - if key > &prefix { + if *key > *prefix { self.index += idx; return false } @@ -78,9 +77,9 @@ impl PrefixSetMut { } /// Inserts the given `nibbles` into the set. - pub fn insert>(&mut self, nibbles: T) { + pub fn insert(&mut self, nibbles: Nibbles) { self.sorted = false; - self.keys.push(nibbles.into()); + self.keys.push(nibbles); } /// Returns the number of elements in the set. @@ -159,10 +158,10 @@ mod tests { #[test] fn test_contains_with_multiple_inserts_and_duplicates() { let mut prefix_set = PrefixSetMut::default(); - prefix_set.insert(b"123"); - prefix_set.insert(b"124"); - prefix_set.insert(b"456"); - prefix_set.insert(b"123"); // Duplicate + prefix_set.insert(Nibbles::new_unchecked(b"123")); + prefix_set.insert(Nibbles::new_unchecked(b"124")); + prefix_set.insert(Nibbles::new_unchecked(b"456")); + prefix_set.insert(Nibbles::new_unchecked(b"123")); // Duplicate assert!(prefix_set.contains(b"12")); assert!(prefix_set.contains(b"45")); diff --git a/crates/trie/src/trie.rs b/crates/trie/src/trie.rs index 4fdc41a7dd66..d145c66348f8 100644 --- a/crates/trie/src/trie.rs +++ b/crates/trie/src/trie.rs @@ -937,7 +937,7 @@ mod tests { assert_eq!(account_updates.len(), 2); let (nibbles1a, node1a) = account_updates.first().unwrap(); - assert_eq!(nibbles1a.inner[..], [0xB]); + assert_eq!(nibbles1a[..], [0xB]); assert_eq!(node1a.state_mask, TrieMask::new(0b1011)); assert_eq!(node1a.tree_mask, TrieMask::new(0b0001)); assert_eq!(node1a.hash_mask, TrieMask::new(0b1001)); @@ -945,7 +945,7 @@ mod tests { assert_eq!(node1a.hashes.len(), 2); let (nibbles2a, node2a) = account_updates.last().unwrap(); - assert_eq!(nibbles2a.inner[..], [0xB, 0x0]); + assert_eq!(nibbles2a[..], [0xB, 0x0]); assert_eq!(node2a.state_mask, TrieMask::new(0b10001)); assert_eq!(node2a.tree_mask, TrieMask::new(0b00000)); assert_eq!(node2a.hash_mask, TrieMask::new(0b10000)); @@ -963,7 +963,7 @@ mod tests { assert_eq!(storage_updates.len(), 1); let (nibbles3, node3) = storage_updates.first().unwrap(); - assert!(nibbles3.inner.is_empty()); + assert!(nibbles3.is_empty()); assert_eq!(node3.state_mask, TrieMask::new(0b1010)); assert_eq!(node3.tree_mask, TrieMask::new(0b0000)); assert_eq!(node3.hash_mask, TrieMask::new(0b0010)); @@ -1004,7 +1004,7 @@ mod tests { assert_eq!(account_updates.len(), 2); let (nibbles1b, node1b) = account_updates.first().unwrap(); - assert_eq!(nibbles1b.inner[..], [0xB]); + assert_eq!(nibbles1b[..], [0xB]); assert_eq!(node1b.state_mask, TrieMask::new(0b1011)); assert_eq!(node1b.tree_mask, TrieMask::new(0b0001)); assert_eq!(node1b.hash_mask, TrieMask::new(0b1011)); @@ -1014,7 +1014,7 @@ mod tests { assert_eq!(node1a.hashes[1], node1b.hashes[2]); let (nibbles2b, node2b) = account_updates.last().unwrap(); - assert_eq!(nibbles2b.inner[..], [0xB, 0x0]); + assert_eq!(nibbles2b[..], [0xB, 0x0]); assert_eq!(node2a, node2b); tx.commit().unwrap(); let tx = factory.provider_rw().unwrap(); @@ -1057,7 +1057,7 @@ mod tests { assert_eq!(account_updates.len(), 1); let (nibbles1c, node1c) = account_updates.first().unwrap(); - assert_eq!(nibbles1c.inner[..], [0xB]); + assert_eq!(nibbles1c[..], [0xB]); assert_eq!(node1c.state_mask, TrieMask::new(0b1011)); assert_eq!(node1c.tree_mask, TrieMask::new(0b0000)); @@ -1114,7 +1114,7 @@ mod tests { assert_eq!(account_updates.len(), 1); let (nibbles1d, node1d) = account_updates.first().unwrap(); - assert_eq!(nibbles1d.inner[..], [0xB]); + assert_eq!(nibbles1d[..], [0xB]); assert_eq!(node1d.state_mask, TrieMask::new(0b1011)); assert_eq!(node1d.tree_mask, TrieMask::new(0b0000)); @@ -1143,7 +1143,7 @@ mod tests { .iter() .filter_map(|entry| match entry { (TrieKey::AccountNode(nibbles), TrieOp::Update(node)) => { - Some((nibbles.inner[..].into(), node.clone())) + Some((nibbles.clone(), node.clone())) } _ => None, }) @@ -1170,7 +1170,7 @@ mod tests { let mut account_updates = HashMap::new(); for item in walker { let (key, node) = item.unwrap(); - account_updates.insert(key.inner[..].into(), node); + account_updates.insert(key, node); } assert_trie_updates(&account_updates); @@ -1231,7 +1231,7 @@ mod tests { .iter() .filter_map(|entry| match entry { (TrieKey::StorageNode(_, nibbles), TrieOp::Update(node)) => { - Some((nibbles.inner[..].into(), node.clone())) + Some((nibbles.clone().into(), node.clone())) } _ => None, }) @@ -1296,11 +1296,11 @@ mod tests { fn assert_trie_updates(account_updates: &HashMap) { assert_eq!(account_updates.len(), 2); - let node = account_updates.get(&vec![0x3].as_slice().into()).unwrap(); + let node = account_updates.get(&[0x3][..]).unwrap(); let expected = BranchNodeCompact::new(0b0011, 0b0001, 0b0000, vec![], None); assert_eq!(node, &expected); - let node = account_updates.get(&vec![0x3, 0x0, 0xA, 0xF].as_slice().into()).unwrap(); + let node = account_updates.get(&[0x3, 0x0, 0xA, 0xF][..]).unwrap(); assert_eq!(node.state_mask, TrieMask::new(0b101100000)); assert_eq!(node.tree_mask, TrieMask::new(0b000000000)); assert_eq!(node.hash_mask, TrieMask::new(0b001000000)); diff --git a/crates/trie/src/trie_cursor/account_cursor.rs b/crates/trie/src/trie_cursor/account_cursor.rs index 0fe241760a06..b98be8bd2ba3 100644 --- a/crates/trie/src/trie_cursor/account_cursor.rs +++ b/crates/trie/src/trie_cursor/account_cursor.rs @@ -1,7 +1,7 @@ use super::TrieCursor; use crate::updates::TrieKey; use reth_db::{cursor::DbCursorRO, tables, DatabaseError}; -use reth_primitives::trie::{BranchNodeCompact, StoredNibbles}; +use reth_primitives::trie::{BranchNodeCompact, Nibbles}; /// A cursor over the account trie. #[derive(Debug)] @@ -18,20 +18,20 @@ impl TrieCursor for AccountTrieCursor where C: DbCursorRO, { - type Key = StoredNibbles; + type Key = Nibbles; fn seek_exact( &mut self, key: Self::Key, ) -> Result, BranchNodeCompact)>, DatabaseError> { - Ok(self.0.seek_exact(key)?.map(|value| (value.0.inner.to_vec(), value.1))) + Ok(self.0.seek_exact(key)?.map(|value| (value.0.to_vec(), value.1))) } fn seek( &mut self, key: Self::Key, ) -> Result, BranchNodeCompact)>, DatabaseError> { - Ok(self.0.seek(key)?.map(|value| (value.0.inner.to_vec(), value.1))) + Ok(self.0.seek(key)?.map(|value| (value.0.to_vec(), value.1))) } fn current(&mut self) -> Result, DatabaseError> { @@ -80,13 +80,13 @@ mod tests { } let db_data = cursor.walk_range(..).unwrap().collect::, _>>().unwrap(); - assert_eq!(db_data[0].0.inner.to_vec(), data[0]); - assert_eq!(db_data[1].0.inner.to_vec(), data[1]); - assert_eq!(db_data[2].0.inner.to_vec(), data[2]); - assert_eq!(db_data[3].0.inner.to_vec(), data[3]); + assert_eq!(db_data[0].0.to_vec(), data[0]); + assert_eq!(db_data[1].0.to_vec(), data[1]); + assert_eq!(db_data[2].0.to_vec(), data[2]); + assert_eq!(db_data[3].0.to_vec(), data[3]); assert_eq!( - cursor.seek(hex!("0303040f").to_vec().into()).unwrap().map(|(k, _)| k.inner.to_vec()), + cursor.seek(hex!("0303040f").to_vec().into()).unwrap().map(|(k, _)| k.to_vec()), Some(data[1].clone()) ); } diff --git a/crates/trie/src/trie_cursor/storage_cursor.rs b/crates/trie/src/trie_cursor/storage_cursor.rs index 19fe1b281914..04b82ecbe472 100644 --- a/crates/trie/src/trie_cursor/storage_cursor.rs +++ b/crates/trie/src/trie_cursor/storage_cursor.rs @@ -38,7 +38,7 @@ where .cursor .seek_by_key_subkey(self.hashed_address, key.clone())? .filter(|e| e.nibbles == key) - .map(|value| (value.nibbles.inner.to_vec(), value.node))) + .map(|value| (value.nibbles.to_vec(), value.node))) } fn seek( @@ -48,7 +48,7 @@ where Ok(self .cursor .seek_by_key_subkey(self.hashed_address, key)? - .map(|value| (value.nibbles.inner.to_vec(), value.node))) + .map(|value| (value.nibbles.to_vec(), value.node))) } fn current(&mut self) -> Result, DatabaseError> { diff --git a/crates/trie/src/trie_cursor/subnode.rs b/crates/trie/src/trie_cursor/subnode.rs index a0e30dd93790..d49ccfe7f5dc 100644 --- a/crates/trie/src/trie_cursor/subnode.rs +++ b/crates/trie/src/trie_cursor/subnode.rs @@ -39,14 +39,14 @@ impl From for CursorSubNode { Some(n) => n as i8, None => -1, }; - Self { key: Nibbles::from_hex(value.key), nibble, node: value.node } + Self { key: Nibbles::new_unchecked(value.key), nibble, node: value.node } } } impl From for StoredSubNode { fn from(value: CursorSubNode) -> Self { let nibble = if value.nibble >= 0 { Some(value.nibble as u8) } else { None }; - Self { key: value.key.hex_data.to_vec(), nibble, node: value.node } + Self { key: value.key.to_vec(), nibble, node: value.node } } } @@ -67,7 +67,7 @@ impl CursorSubNode { pub fn full_key(&self) -> Nibbles { let mut out = self.key.clone(); if self.nibble >= 0 { - out.extend([self.nibble as u8]); + out.push(self.nibble as u8); } out } diff --git a/crates/trie/src/updates.rs b/crates/trie/src/updates.rs index 11cfb82204a3..9a3da6c54268 100644 --- a/crates/trie/src/updates.rs +++ b/crates/trie/src/updates.rs @@ -5,7 +5,7 @@ use reth_db::{ transaction::{DbTx, DbTxMut}, }; use reth_primitives::{ - trie::{BranchNodeCompact, Nibbles, StorageTrieEntry, StoredNibbles, StoredNibblesSubKey}, + trie::{BranchNodeCompact, Nibbles, StorageTrieEntry, StoredNibblesSubKey}, B256, }; use std::collections::{hash_map::IntoIter, HashMap}; @@ -14,7 +14,7 @@ use std::collections::{hash_map::IntoIter, HashMap}; #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] pub enum TrieKey { /// A node in the account trie. - AccountNode(StoredNibbles), + AccountNode(Nibbles), /// A node in the storage trie. StorageNode(B256, StoredNibblesSubKey), /// Storage trie of an account. @@ -78,9 +78,11 @@ impl TrieUpdates { /// Extend the updates with account trie updates. pub fn extend_with_account_updates(&mut self, updates: HashMap) { - self.extend(updates.into_iter().map(|(nibbles, node)| { - (TrieKey::AccountNode(nibbles.hex_data.to_vec().into()), TrieOp::Update(node)) - })); + self.extend( + updates + .into_iter() + .map(|(nibbles, node)| (TrieKey::AccountNode(nibbles), TrieOp::Update(node))), + ); } /// Extend the updates with storage trie updates. @@ -90,10 +92,7 @@ impl TrieUpdates { updates: HashMap, ) { self.extend(updates.into_iter().map(|(nibbles, node)| { - ( - TrieKey::StorageNode(hashed_address, nibbles.hex_data.to_vec().into()), - TrieOp::Update(node), - ) + (TrieKey::StorageNode(hashed_address, nibbles.into()), TrieOp::Update(node)) })); } @@ -122,7 +121,7 @@ impl TrieUpdates { } } TrieOp::Update(node) => { - if !nibbles.inner.is_empty() { + if !nibbles.is_empty() { account_trie_cursor.upsert(nibbles, node)?; } } @@ -136,7 +135,7 @@ impl TrieUpdates { TrieOp::Update(..) => unreachable!("Cannot update full storage trie."), }, TrieKey::StorageNode(hashed_address, nibbles) => { - if !nibbles.inner.is_empty() { + if !nibbles.is_empty() { // Delete the old entry if it exists. if storage_trie_cursor .seek_by_key_subkey(hashed_address, nibbles.clone())? diff --git a/crates/trie/src/walker.rs b/crates/trie/src/walker.rs index 4ad38fe190cc..54e7fc7ec270 100644 --- a/crates/trie/src/walker.rs +++ b/crates/trie/src/walker.rs @@ -122,16 +122,16 @@ impl TrieWalker { fn node(&mut self, exact: bool) -> Result, DatabaseError> { let key = self.key().expect("key must exist"); let entry = if exact { - self.cursor.seek_exact(key.hex_data.to_vec().into())? + self.cursor.seek_exact(key.to_vec().into())? } else { - self.cursor.seek(key.hex_data.to_vec().into())? + self.cursor.seek(key.to_vec().into())? }; if let Some((_, node)) = &entry { assert!(!node.state_mask.is_empty()); } - Ok(entry.map(|(k, v)| (Nibbles::from_hex(k), v))) + Ok(entry.map(|(k, v)| (Nibbles::new_unchecked(k), v))) } /// Consumes the next node in the trie, updating the stack. @@ -313,7 +313,7 @@ mod tests { // We're traversing the path in lexigraphical order. for expected in expected { let got = walker.advance().unwrap(); - assert_eq!(got.unwrap(), Nibbles::from(&expected[..])); + assert_eq!(got.unwrap(), Nibbles::new_unchecked(expected.clone())); } // There should be 8 paths traversed in total from 3 branches. @@ -361,26 +361,26 @@ mod tests { // No changes let mut cursor = TrieWalker::new(&mut trie, Default::default()); - assert_eq!(cursor.key(), Some(Nibbles::from_hex(vec![]))); // root + assert_eq!(cursor.key(), Some(Nibbles::new_unchecked([]))); // root assert!(cursor.can_skip_current_node); // due to root_hash cursor.advance().unwrap(); // skips to the end of trie assert_eq!(cursor.key(), None); // We insert something that's not part of the existing trie/prefix. let mut changed = PrefixSetMut::default(); - changed.insert(&[0xF, 0x1]); + changed.insert(Nibbles::new_unchecked([0xF, 0x1])); let mut cursor = TrieWalker::new(&mut trie, changed.freeze()); // Root node - assert_eq!(cursor.key(), Some(Nibbles::from_hex(vec![]))); + assert_eq!(cursor.key(), Some(Nibbles::new_unchecked([]))); // Should not be able to skip state due to the changed values assert!(!cursor.can_skip_current_node); cursor.advance().unwrap(); - assert_eq!(cursor.key(), Some(Nibbles::from_hex(vec![0x2]))); + assert_eq!(cursor.key(), Some(Nibbles::new_unchecked([0x2]))); cursor.advance().unwrap(); - assert_eq!(cursor.key(), Some(Nibbles::from_hex(vec![0x2, 0x1]))); + assert_eq!(cursor.key(), Some(Nibbles::new_unchecked([0x2, 0x1]))); cursor.advance().unwrap(); - assert_eq!(cursor.key(), Some(Nibbles::from_hex(vec![0x4]))); + assert_eq!(cursor.key(), Some(Nibbles::new_unchecked([0x4]))); cursor.advance().unwrap(); assert_eq!(cursor.key(), None); // the end of trie diff --git a/docs/design/database.md b/docs/design/database.md index 4c51b613e02b..42ec8ba56036 100644 --- a/docs/design/database.md +++ b/docs/design/database.md @@ -113,7 +113,7 @@ HashedStorage { U256 StorageValue } AccountsTrie { - StoredNibbles Nibbles "PK" + Nibbles "PK" BranchNodeCompact Node } StoragesTrie { From 8bdcce535b0043964add563bfd15a655533944b7 Mon Sep 17 00:00:00 2001 From: Roman Krasiuk Date: Fri, 1 Dec 2023 03:06:52 -0800 Subject: [PATCH 067/277] chore(txpool): query header on txpool maintenance bootup (#5648) --- crates/transaction-pool/src/maintain.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/transaction-pool/src/maintain.rs b/crates/transaction-pool/src/maintain.rs index a4c924bef1e7..db74f50f2f8b 100644 --- a/crates/transaction-pool/src/maintain.rs +++ b/crates/transaction-pool/src/maintain.rs @@ -85,7 +85,7 @@ pub async fn maintain_transaction_pool( let metrics = MaintainPoolMetrics::default(); let MaintainPoolConfig { max_update_depth, max_reload_accounts } = config; // ensure the pool points to latest state - if let Ok(Some(latest)) = client.block_by_number_or_tag(BlockNumberOrTag::Latest) { + if let Ok(Some(latest)) = client.header_by_number_or_tag(BlockNumberOrTag::Latest) { let latest = latest.seal_slow(); let chain_spec = client.chain_spec(); let info = BlockInfo { From 4a0497ddb1baa58f54d0e0a1947c6f648d151286 Mon Sep 17 00:00:00 2001 From: Dan Cline <6798349+Rjected@users.noreply.github.com> Date: Fri, 1 Dec 2023 06:11:47 -0500 Subject: [PATCH 068/277] feat: add Default for RpcServerArgs (#5646) --- bin/reth/src/args/rpc_server_args.rs | 35 ++++++++++++++++++++++++++++ bin/reth/src/args/types.rs | 20 +++++++++++++++- 2 files changed, 54 insertions(+), 1 deletion(-) diff --git a/bin/reth/src/args/rpc_server_args.rs b/bin/reth/src/args/rpc_server_args.rs index fb66122456c8..261b3515e18b 100644 --- a/bin/reth/src/args/rpc_server_args.rs +++ b/bin/reth/src/args/rpc_server_args.rs @@ -459,6 +459,41 @@ impl RethRpcConfig for RpcServerArgs { } } +impl Default for RpcServerArgs { + fn default() -> Self { + Self { + http: true, + http_addr: Ipv4Addr::LOCALHOST.into(), + http_port: constants::DEFAULT_HTTP_RPC_PORT, + http_api: None, + http_corsdomain: None, + ws: false, + ws_addr: Ipv4Addr::LOCALHOST.into(), + ws_port: constants::DEFAULT_WS_RPC_PORT, + ws_allowed_origins: None, + ws_api: None, + ipcdisable: false, + ipcpath: constants::DEFAULT_IPC_ENDPOINT.to_string(), + auth_addr: Ipv4Addr::LOCALHOST.into(), + auth_port: constants::DEFAULT_AUTH_PORT, + auth_jwtsecret: None, + rpc_jwtsecret: None, + rpc_max_request_size: RPC_DEFAULT_MAX_REQUEST_SIZE_MB.into(), + rpc_max_response_size: RPC_DEFAULT_MAX_RESPONSE_SIZE_MB.into(), + rpc_max_subscriptions_per_connection: RPC_DEFAULT_MAX_SUBS_PER_CONN.into(), + rpc_max_connections: RPC_DEFAULT_MAX_CONNECTIONS.into(), + rpc_max_tracing_requests: constants::DEFAULT_MAX_TRACING_REQUESTS, + rpc_max_blocks_per_filter: constants::DEFAULT_MAX_BLOCKS_PER_FILTER.into(), + rpc_max_logs_per_response: (constants::DEFAULT_MAX_LOGS_PER_RESPONSE as u64).into(), + rpc_gas_cap: RPC_DEFAULT_GAS_CAP.into(), + gas_price_oracle: GasPriceOracleArgs::default(), + block_cache_len: DEFAULT_BLOCK_CACHE_MAX_LEN, + receipt_cache_len: DEFAULT_RECEIPT_CACHE_MAX_LEN, + env_cache_len: DEFAULT_ENV_CACHE_MAX_LEN, + } + } +} + /// clap value parser for [RpcModuleSelection]. #[derive(Clone, Debug, Default)] #[non_exhaustive] diff --git a/bin/reth/src/args/types.rs b/bin/reth/src/args/types.rs index 4696e5b32346..b7fcbe5298ba 100644 --- a/bin/reth/src/args/types.rs +++ b/bin/reth/src/args/types.rs @@ -31,12 +31,19 @@ macro_rules! zero_as_none { } } + impl From<$inner_type> for $type_name { + #[inline] + fn from(value: $inner_type) -> Self { + Self(if value == 0 { None } else { Some(value) }) + } + } + impl std::str::FromStr for $type_name { type Err = std::num::ParseIntError; fn from_str(s: &str) -> Result { let value = s.parse::<$inner_type>()?; - Ok(Self(if value == 0 { None } else { Some(value) })) + Ok(Self::from(value)) } } }; @@ -99,4 +106,15 @@ mod tests { assert_eq!(val, ZeroAsNoneU64(None)); assert_eq!(val.unwrap_or_max(), u64::MAX); } + + #[test] + fn test_from_u64() { + let original = 1u64; + let expected = ZeroAsNoneU64(Some(1u64)); + assert_eq!(ZeroAsNoneU64::from(original), expected); + + let original = 0u64; + let expected = ZeroAsNoneU64(None); + assert_eq!(ZeroAsNoneU64::from(original), expected); + } } From 1067bb32d0fe34f68602f5105de5ba6852f931eb Mon Sep 17 00:00:00 2001 From: Yash Atreya <44857776+yash-atreya@users.noreply.github.com> Date: Fri, 1 Dec 2023 07:00:07 -0500 Subject: [PATCH 069/277] perf(rpc): Use block_with_senders in tracing (#5630) Co-authored-by: Matthias Seitz --- crates/rpc/rpc/src/eth/api/call.rs | 16 +++++++--------- crates/rpc/rpc/src/eth/api/transactions.rs | 15 ++++++--------- 2 files changed, 13 insertions(+), 18 deletions(-) diff --git a/crates/rpc/rpc/src/eth/api/call.rs b/crates/rpc/rpc/src/eth/api/call.rs index 0ae826de7aa4..661d14d4c690 100644 --- a/crates/rpc/rpc/src/eth/api/call.rs +++ b/crates/rpc/rpc/src/eth/api/call.rs @@ -19,8 +19,7 @@ use reth_provider::{ }; use reth_revm::{access_list::AccessListInspector, database::StateProviderDatabase}; use reth_rpc_types::{ - state::StateOverride, AccessListWithGasUsed, BlockError, Bundle, CallRequest, EthCallResponse, - StateContext, + state::StateOverride, AccessListWithGasUsed, Bundle, CallRequest, EthCallResponse, StateContext, }; use reth_transaction_pool::TransactionPool; use revm::{ @@ -93,10 +92,12 @@ where let target_block = block_number.unwrap_or(BlockId::Number(BlockNumberOrTag::Latest)); - let ((cfg, block_env, _), block) = - futures::try_join!(self.evm_env_at(target_block), self.block_by_id(target_block))?; + let ((cfg, block_env, _), block) = futures::try_join!( + self.evm_env_at(target_block), + self.block_with_senders(target_block) + )?; - let block = block.ok_or_else(|| EthApiError::UnknownBlockNumber)?; + let Some(block) = block else { return Err(EthApiError::UnknownBlockNumber) }; let gas_limit = self.inner.gas_cap; // we're essentially replaying the transactions in the block here, hence we need the state @@ -118,11 +119,8 @@ where if replay_block_txs { // only need to replay the transactions in the block if not all transactions are // to be replayed - let transactions = block.body.into_iter().take(num_txs); - - // Execute all transactions until index + let transactions = block.into_transactions_ecrecovered().take(num_txs); for tx in transactions { - let tx = tx.into_ecrecovered().ok_or(BlockError::InvalidSignature)?; let tx = tx_env_with_recovered(&tx); let env = Env { cfg: cfg.clone(), block: block_env.clone(), tx }; let (res, _) = transact(&mut db, env)?; diff --git a/crates/rpc/rpc/src/eth/api/transactions.rs b/crates/rpc/rpc/src/eth/api/transactions.rs index d6d1a80c5bab..af0966162afb 100644 --- a/crates/rpc/rpc/src/eth/api/transactions.rs +++ b/crates/rpc/rpc/src/eth/api/transactions.rs @@ -1038,19 +1038,16 @@ where block_id: impl Into, index: Index, ) -> EthResult> { - let block_id = block_id.into(); - - if let Some(block) = self.block(block_id).await? { + if let Some(block) = self.block_with_senders(block_id.into()).await? { let block_hash = block.hash; - let block = block.unseal(); - if let Some(tx_signed) = block.body.into_iter().nth(index.into()) { - let tx = - tx_signed.into_ecrecovered().ok_or(EthApiError::InvalidTransactionSignature)?; + let block_number = block.number; + let base_fee_per_gas = block.base_fee_per_gas; + if let Some(tx) = block.into_transactions_ecrecovered().nth(index.into()) { return Ok(Some(from_recovered_with_block_context( tx, block_hash, - block.header.number, - block.header.base_fee_per_gas, + block_number, + base_fee_per_gas, index.into(), ))) } From 2144b97df0e515e069d4eea4fc9f314f4fb865f2 Mon Sep 17 00:00:00 2001 From: Arindam Singh <98768062+Arindam2407@users.noreply.github.com> Date: Fri, 1 Dec 2023 12:07:32 +0000 Subject: [PATCH 070/277] reth-ethereum-forks crate (#5621) Co-authored-by: root Co-authored-by: Matthias Seitz --- Cargo.lock | 237 ++++++++++++------ Cargo.toml | 2 + crates/ethereum-forks/Cargo.toml | 41 +++ .../src/forkid.rs | 11 +- .../src/hardfork.rs | 52 ---- crates/ethereum-forks/src/head.rs | 24 ++ crates/ethereum-forks/src/lib.rs | 28 +++ crates/net/dns/src/lib.rs | 2 +- crates/net/eth-wire/src/builder.rs | 4 +- crates/net/eth-wire/src/types/status.rs | 4 +- crates/net/network/src/session/active.rs | 4 +- crates/primitives/Cargo.toml | 54 ++-- crates/primitives/src/chain/spec.rs | 61 ++++- crates/primitives/src/header.rs | 22 -- crates/primitives/src/lib.rs | 7 +- examples/manual-p2p/src/main.rs | 2 +- 16 files changed, 357 insertions(+), 198 deletions(-) create mode 100644 crates/ethereum-forks/Cargo.toml rename crates/{primitives => ethereum-forks}/src/forkid.rs (99%) rename crates/{primitives => ethereum-forks}/src/hardfork.rs (70%) create mode 100644 crates/ethereum-forks/src/head.rs create mode 100644 crates/ethereum-forks/src/lib.rs diff --git a/Cargo.lock b/Cargo.lock index 4947be0261de..12c76d02a04e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -140,9 +140,9 @@ checksum = "0942ffc6dcaadf03badf6e6a2d0228460359d5e34b57ccdc720b7382dfbd5ec5" [[package]] name = "alloy-dyn-abi" -version = "0.5.0" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c9fe14fb6dded4be3f44d053e59402a405bb231561e36a88bc2283a9829d81fe" +checksum = "2a80060a7b7cbcb40c78fe763130078f6e8b49d7d9a5b0d0f9c508d28b094b60" dependencies = [ "alloy-json-abi", "alloy-primitives", @@ -158,9 +158,9 @@ dependencies = [ [[package]] name = "alloy-json-abi" -version = "0.5.0" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85cbbc8fcae58f39dbfbdc94ead48de0779910528889aebc074aed75959bffe7" +checksum = "ef4dff5c682e16e398783e8da63956bdf22ce48841e6ca367f8a8a6b1f2ed989" dependencies = [ "alloy-primitives", "alloy-sol-type-parser", @@ -170,9 +170,9 @@ dependencies = [ [[package]] name = "alloy-primitives" -version = "0.5.0" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f5def4b5e1bb8fe7ea37eeac1063246d4ef26f56cbdccf864a5a6bdcb80e91f4" +checksum = "3a3877a2fa7474d9367d01ab43e33a4a4eeedf9bf7ec481533520686e956703b" dependencies = [ "alloy-rlp", "arbitrary", @@ -218,9 +218,9 @@ dependencies = [ [[package]] name = "alloy-sol-macro" -version = "0.5.0" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0acd5b8d2699b095a57a0ecea6a6a2045b8e5ed6f2607bfa3382961d2889e82" +checksum = "123b602fbffdfb514183bcca2e5e7428b41ed5d59580ae70bf8eca937d165427" dependencies = [ "const-hex", "dunce", @@ -236,18 +236,18 @@ dependencies = [ [[package]] name = "alloy-sol-type-parser" -version = "0.5.0" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc77699fb68c8a2cf18efb2c51df43e09b42b53886c6eb552951b19c41ebfc84" +checksum = "e4648000cc0f043d0cdded1650bf4a1c075da0367cd9c80deda48db870985cbd" dependencies = [ "winnow", ] [[package]] name = "alloy-sol-types" -version = "0.5.0" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97d483e9c6db659cdb24fc736684ef68b743705fbdb0677c6404815361871b92" +checksum = "8c4c333ed7d5a4d87e3b8ad2ce230d4f9347b9e43faf98ad16ba58bf318cd023" dependencies = [ "alloy-primitives", "alloy-sol-macro", @@ -946,7 +946,7 @@ checksum = "f3e5afa991908cfbe79bd3109b824e473a1dc5f74f31fab91bb44c9e245daa77" dependencies = [ "boa_gc", "boa_macros", - "hashbrown 0.14.2", + "hashbrown 0.14.3", "indexmap 2.1.0", "once_cell", "phf", @@ -999,9 +999,9 @@ checksum = "3190f92dfe48224adc92881c620f08ccf37ff62b91a094bb357fe53bd5e84647" [[package]] name = "boyer-moore-magiclen" -version = "0.2.18" +version = "0.2.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "116d76fee857b03ecdd95d5f9555e46aa0cd34e5bb348a520e9445d151182a7e" +checksum = "854f4a08ebbcc4d3d2b8b3863fdffdc9f2046bb1c43610ff9841a51dd5b70f55" dependencies = [ "debug-helper", ] @@ -1239,9 +1239,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.4.8" +version = "4.4.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2275f18819641850fa26c89acc84d465c1bf91ce57bc2748b28c420473352f64" +checksum = "41fffed7514f420abec6d183b1d3acfd9099c79c3a10a06ade4f8203f1411272" dependencies = [ "clap_builder", "clap_derive", @@ -1249,9 +1249,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.4.8" +version = "4.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07cdf1b148b25c1e1f7a42225e30a0d99a615cd4637eae7365548dd4529b95bc" +checksum = "63361bae7eef3771745f02d8d892bec2fee5f6e34af316ba556e7f97a7069ff1" dependencies = [ "anstream", "anstyle", @@ -1450,9 +1450,9 @@ dependencies = [ [[package]] name = "core-foundation" -version = "0.9.3" +version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "194a7a9e6de53fa55116934067c844d9d749312f75c6f6d0980e8c252f8c2146" +checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f" dependencies = [ "core-foundation-sys", "libc", @@ -1460,9 +1460,9 @@ dependencies = [ [[package]] name = "core-foundation-sys" -version = "0.8.4" +version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e496a50fda8aacccc86d7529e2c1e0892dbd0f898a6b5645b5561b89c3210efa" +checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f" [[package]] name = "cpp_demangle" @@ -1842,7 +1842,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "978747c1d849a7d2ee5e8adc0159961c48fb7e5db2f06af6723b80123bb53856" dependencies = [ "cfg-if", - "hashbrown 0.14.2", + "hashbrown 0.14.3", "lock_api", "once_cell", "parking_lot_core 0.9.9", @@ -2328,12 +2328,12 @@ checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" [[package]] name = "errno" -version = "0.3.7" +version = "0.3.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f258a7194e7f7c2a7837a8913aeab7fd8c383457034fa20ce4dd3dcb813e8eb8" +checksum = "a258e46cdc063eb8519c00b9fc845fc47bcfca4130e2f08e88665ceda8474245" dependencies = [ "libc", - "windows-sys 0.48.0", + "windows-sys 0.52.0", ] [[package]] @@ -3068,9 +3068,9 @@ dependencies = [ [[package]] name = "hashbrown" -version = "0.14.2" +version = "0.14.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f93e7192158dbcda357bdec5fb5788eebf8bbac027f3f33e719d29135ae84156" +checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604" dependencies = [ "ahash", "allocator-api2", @@ -3092,7 +3092,7 @@ version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e8094feaf31ff591f651a2664fb9cfd92bba7a60ce3197265e9482ebe753c8f7" dependencies = [ - "hashbrown 0.14.2", + "hashbrown 0.14.3", ] [[package]] @@ -3606,7 +3606,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d530e1a18b1cb4c484e6e34556a0d948706958449fca0cab753d649f2bce3d1f" dependencies = [ "equivalent", - "hashbrown 0.14.2", + "hashbrown 0.14.3", "serde", ] @@ -3758,9 +3758,9 @@ dependencies = [ [[package]] name = "js-sys" -version = "0.3.65" +version = "0.3.66" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "54c0c35952f67de54bb584e9fd912b3023117cbafc0a77d8f3dee1fb5f572fe8" +checksum = "cee9c64da59eae3b50095c18d3e74f8b73c0b86d2792824ff01bbce68ba229ca" dependencies = [ "wasm-bindgen", ] @@ -4071,7 +4071,7 @@ version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a4a83fb7698b3643a0e34f9ae6f2e8f0178c0fd42f8b59d493aa271ff3a5bf21" dependencies = [ - "hashbrown 0.14.2", + "hashbrown 0.14.3", ] [[package]] @@ -4080,7 +4080,7 @@ version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2994eeba8ed550fd9b47a0b38f0242bc3344e496483c6180b69139cc2fa5d1d7" dependencies = [ - "hashbrown 0.14.2", + "hashbrown 0.14.3", ] [[package]] @@ -4161,9 +4161,9 @@ dependencies = [ [[package]] name = "memmap2" -version = "0.8.0" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43a5a03cefb0d953ec0be133036f14e109412fa594edc2f77227249db66cc3ed" +checksum = "deaba38d7abf1d4cca21cc89e932e542ba2b9258664d2a9ef0e61512039c9375" dependencies = [ "libc", ] @@ -4658,9 +4658,9 @@ dependencies = [ [[package]] name = "parity-scale-codec" -version = "3.6.5" +version = "3.6.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0dec8a8073036902368c2cdc0387e85ff9a37054d7e7c98e592145e0c92cd4fb" +checksum = "881331e34fa842a2fb61cc2db9643a8fedc615e47cfcc52597d1af0db9a7e8fe" dependencies = [ "arrayvec", "bitvec", @@ -4673,11 +4673,11 @@ dependencies = [ [[package]] name = "parity-scale-codec-derive" -version = "3.6.5" +version = "3.6.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "312270ee71e1cd70289dacf597cab7b207aa107d2f28191c2ae45b2ece18a260" +checksum = "be30eaf4b0a9fba5336683b38de57bb86d179a35862ba6bfcf57625d006bde5b" dependencies = [ - "proc-macro-crate 1.3.1", + "proc-macro-crate 2.0.0", "proc-macro2", "quote", "syn 1.0.109", @@ -5143,9 +5143,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.69" +version = "1.0.70" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "134c189feb4956b20f6f547d2cf727d4c0fe06722b20a0eec87ed445a97f92da" +checksum = "39278fbbf5fb4f646ce651690877f89d1c5811a3d4acb27700c1cb3cdb78fd3b" dependencies = [ "unicode-ident", ] @@ -5921,6 +5921,22 @@ dependencies = [ "tracing", ] +[[package]] +name = "reth-ethereum-forks" +version = "0.1.0-alpha.12" +dependencies = [ + "alloy-primitives", + "alloy-rlp", + "arbitrary", + "crc", + "proptest", + "proptest-derive", + "rand 0.8.5", + "reth-codecs", + "serde", + "thiserror", +] + [[package]] name = "reth-interfaces" version = "0.1.0-alpha.12" @@ -6177,6 +6193,7 @@ dependencies = [ "rand 0.8.5", "rayon", "reth-codecs", + "reth-ethereum-forks", "reth-rpc-types", "revm", "revm-primitives", @@ -6641,7 +6658,7 @@ dependencies = [ "c-kzg", "derive_more", "enumn", - "hashbrown 0.14.2", + "hashbrown 0.14.3", "hex", "once_cell", "serde", @@ -6683,9 +6700,9 @@ dependencies = [ [[package]] name = "ring" -version = "0.17.5" +version = "0.17.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb0205304757e5d899b9c2e448b867ffd03ae7f988002e47cd24954391394d0b" +checksum = "684d5e6e18f669ccebf64a92236bb7db9a34f07be010e3627368182027180866" dependencies = [ "cc", "getrandom 0.2.11", @@ -6849,7 +6866,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "629648aced5775d558af50b2b4c7b02983a04b312126d45eeead26e7caa498b9" dependencies = [ "log", - "ring 0.17.5", + "ring 0.17.6", "rustls-webpki", "sct", ] @@ -6881,7 +6898,7 @@ version = "0.101.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8b6275d1ee7a1cd780b64aca7726599a1dbc893b1e64144529e55c3c2f745765" dependencies = [ - "ring 0.17.5", + "ring 0.17.6", "untrusted 0.9.0", ] @@ -7001,7 +7018,7 @@ version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "da046153aa2352493d6cb7da4b6e5c0c057d8a1d0a9aa8560baffdd945acd414" dependencies = [ - "ring 0.17.5", + "ring 0.17.6", "untrusted 0.9.0", ] @@ -7492,9 +7509,9 @@ dependencies = [ [[package]] name = "spki" -version = "0.7.2" +version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d1e996ef02c474957d681f1b05213dfb0abab947b446a62d37770b23500184a" +checksum = "d91ed6c858b01f942cd56b37a94b3e0a1798290327d1236e4d9cf4eaca44d29d" dependencies = [ "base64ct", "der", @@ -7608,21 +7625,21 @@ dependencies = [ [[package]] name = "symbolic-common" -version = "12.7.0" +version = "12.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "39eac77836da383d35edbd9ff4585b4fc1109929ff641232f2e9a1aefdfc9e91" +checksum = "9ba238da6058509cda85e388e8bf1caf522894ec38da490de9694fab9238c715" dependencies = [ "debugid", - "memmap2 0.8.0", + "memmap2 0.9.0", "stable_deref_trait", "uuid 1.6.1", ] [[package]] name = "symbolic-demangle" -version = "12.7.0" +version = "12.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ee1608a1d13061fb0e307a316de29f6c6e737b05459fe6bbf5dd8d7837c4fb7" +checksum = "9b7f36fed2b0d1f3db882e5edb9d1b6595dd8cc095f52f265b17291f09062b70" dependencies = [ "cpp_demangle", "rustc-demangle", @@ -7653,9 +7670,9 @@ dependencies = [ [[package]] name = "syn-solidity" -version = "0.5.0" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e349ed2ca56eff703d7c3ea013771bcbab9ad2ad39dddf863fc51d820329dc41" +checksum = "cf07302c875aa7e1ee4fb811fdf2a0436bb04640639539003dcab2e049a9d1fe" dependencies = [ "paste", "proc-macro2", @@ -8615,9 +8632,9 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "wasm-bindgen" -version = "0.2.88" +version = "0.2.89" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7daec296f25a1bae309c0cd5c29c4b260e510e6d813c286b19eaadf409d40fce" +checksum = "0ed0d4f68a3015cc185aff4db9506a015f4b96f95303897bfa23f846db54064e" dependencies = [ "cfg-if", "wasm-bindgen-macro", @@ -8625,9 +8642,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-backend" -version = "0.2.88" +version = "0.2.89" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e397f4664c0e4e428e8313a469aaa58310d302159845980fd23b0f22a847f217" +checksum = "1b56f625e64f3a1084ded111c4d5f477df9f8c92df113852fa5a374dbda78826" dependencies = [ "bumpalo", "log", @@ -8640,9 +8657,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-futures" -version = "0.4.38" +version = "0.4.39" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9afec9963e3d0994cac82455b2b3502b81a7f40f9a0d32181f7528d9f4b43e02" +checksum = "ac36a15a220124ac510204aec1c3e5db8a22ab06fd6706d881dc6149f8ed9a12" dependencies = [ "cfg-if", "js-sys", @@ -8652,9 +8669,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.88" +version = "0.2.89" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5961017b3b08ad5f3fe39f1e79877f8ee7c23c5e5fd5eb80de95abc41f1f16b2" +checksum = "0162dbf37223cd2afce98f3d0785506dcb8d266223983e4b5b525859e6e182b2" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -8662,9 +8679,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.88" +version = "0.2.89" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c5353b8dab669f5e10f5bd76df26a9360c748f054f862ff5f3f8aae0c7fb3907" +checksum = "f0eb82fcb7930ae6219a7ecfd55b217f5f0893484b7a13022ebb2b2bf20b5283" dependencies = [ "proc-macro2", "quote", @@ -8675,9 +8692,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-shared" -version = "0.2.88" +version = "0.2.89" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d046c5d029ba91a1ed14da14dca44b68bf2f124cfbaf741c54151fdb3e0750b" +checksum = "7ab9b36309365056cd639da3134bf87fa8f3d86008abf99e612384a6eecd459f" [[package]] name = "wasm-streams" @@ -8694,9 +8711,9 @@ dependencies = [ [[package]] name = "web-sys" -version = "0.3.65" +version = "0.3.66" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5db499c5f66323272151db0e666cd34f78617522fb0c1604d31a27c50c206a85" +checksum = "50c24a44ec86bb68fbecd1b3efed7e85ea5621b39b35ef2766b66cd984f8010f" dependencies = [ "js-sys", "wasm-bindgen", @@ -8800,6 +8817,15 @@ dependencies = [ "windows-targets 0.48.5", ] +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets 0.52.0", +] + [[package]] name = "windows-targets" version = "0.42.2" @@ -8830,6 +8856,21 @@ dependencies = [ "windows_x86_64_msvc 0.48.5", ] +[[package]] +name = "windows-targets" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a18201040b24831fbb9e4eb208f8892e1f50a37feb53cc7ff887feb8f50e7cd" +dependencies = [ + "windows_aarch64_gnullvm 0.52.0", + "windows_aarch64_msvc 0.52.0", + "windows_i686_gnu 0.52.0", + "windows_i686_msvc 0.52.0", + "windows_x86_64_gnu 0.52.0", + "windows_x86_64_gnullvm 0.52.0", + "windows_x86_64_msvc 0.52.0", +] + [[package]] name = "windows_aarch64_gnullvm" version = "0.42.2" @@ -8842,6 +8883,12 @@ version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb7764e35d4db8a7921e09562a0304bf2f93e0a51bfccee0bd0bb0b666b015ea" + [[package]] name = "windows_aarch64_msvc" version = "0.42.2" @@ -8854,6 +8901,12 @@ version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbaa0368d4f1d2aaefc55b6fcfee13f41544ddf36801e793edbbfd7d7df075ef" + [[package]] name = "windows_i686_gnu" version = "0.42.2" @@ -8866,6 +8919,12 @@ version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" +[[package]] +name = "windows_i686_gnu" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a28637cb1fa3560a16915793afb20081aba2c92ee8af57b4d5f28e4b3e7df313" + [[package]] name = "windows_i686_msvc" version = "0.42.2" @@ -8878,6 +8937,12 @@ version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" +[[package]] +name = "windows_i686_msvc" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ffe5e8e31046ce6230cc7215707b816e339ff4d4d67c65dffa206fd0f7aa7b9a" + [[package]] name = "windows_x86_64_gnu" version = "0.42.2" @@ -8890,6 +8955,12 @@ version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d6fa32db2bc4a2f5abeacf2b69f7992cd09dca97498da74a151a3132c26befd" + [[package]] name = "windows_x86_64_gnullvm" version = "0.42.2" @@ -8902,6 +8973,12 @@ version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a657e1e9d3f514745a572a6846d3c7aa7dbe1658c056ed9c3344c4109a6949e" + [[package]] name = "windows_x86_64_msvc" version = "0.42.2" @@ -8914,6 +8991,12 @@ version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04" + [[package]] name = "winnow" version = "0.5.19" @@ -9029,18 +9112,18 @@ dependencies = [ [[package]] name = "zerocopy" -version = "0.7.26" +version = "0.7.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e97e415490559a91254a2979b4829267a57d2fcd741a98eee8b722fb57289aa0" +checksum = "f43de342578a3a14a9314a2dab1942cbfcbe5686e1f91acdc513058063eafe18" dependencies = [ "zerocopy-derive", ] [[package]] name = "zerocopy-derive" -version = "0.7.26" +version = "0.7.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd7e48ccf166952882ca8bd778a43502c64f33bf94c12ebe2a7f08e5a0f6689f" +checksum = "e1012d89e3acb79fad7a799ce96866cfb8098b74638465ea1b1533d35900ca90" dependencies = [ "proc-macro2", "quote", diff --git a/Cargo.toml b/Cargo.toml index 6adee94d89d4..f895fd82f46c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,6 +6,7 @@ members = [ "crates/consensus/auto-seal/", "crates/consensus/beacon/", "crates/consensus/common/", + "crates/ethereum-forks/", "crates/interfaces/", "crates/metrics/", "crates/metrics/metrics-derive/", @@ -102,6 +103,7 @@ reth-dns-discovery = { path = "crates/net/dns" } reth-downloaders = { path = "crates/net/downloaders" } reth-ecies = { path = "crates/net/ecies" } reth-eth-wire = { path = "crates/net/eth-wire" } +reth-ethereum-forks = { path = "crates/ethereum-forks" } reth-interfaces = { path = "crates/interfaces" } reth-ipc = { path = "crates/rpc/ipc" } reth-libmdbx = { path = "crates/storage/libmdbx-rs" } diff --git a/crates/ethereum-forks/Cargo.toml b/crates/ethereum-forks/Cargo.toml new file mode 100644 index 000000000000..d4ee55f14cb4 --- /dev/null +++ b/crates/ethereum-forks/Cargo.toml @@ -0,0 +1,41 @@ +[package] +name = "reth-ethereum-forks" +version.workspace = true +edition.workspace = true +rust-version.workspace = true +license.workspace = true +homepage.workspace = true +repository.workspace = true +description = "Ethereum fork types used in reth." + +[dependencies] +# reth +reth-codecs.workspace = true + +# ethereum +alloy-primitives = { workspace = true, features = ["rand", "rlp"] } +alloy-rlp = { workspace = true, features = ["arrayvec"] } + +# used for forkid +crc = "3" + +# misc +serde.workspace = true +thiserror.workspace = true + + +# arbitrary utils +arbitrary = { workspace = true, features = ["derive"], optional = true } +proptest = { workspace = true, optional = true } +proptest-derive = { workspace = true, optional = true } + +[dev-dependencies] +rand.workspace = true +arbitrary = { workspace = true, features = ["derive"] } +proptest.workspace = true +proptest-derive.workspace = true + + +[features] +arbitrary = ["dep:arbitrary", "dep:proptest", "dep:proptest-derive"] +optimism = ["reth-codecs/optimism"] diff --git a/crates/primitives/src/forkid.rs b/crates/ethereum-forks/src/forkid.rs similarity index 99% rename from crates/primitives/src/forkid.rs rename to crates/ethereum-forks/src/forkid.rs index 89ef34a52bdb..a10574817470 100644 --- a/crates/primitives/src/forkid.rs +++ b/crates/ethereum-forks/src/forkid.rs @@ -2,9 +2,8 @@ //! //! Previously version of Apache licenced [`ethereum-forkid`](https://crates.io/crates/ethereum-forkid). -#![deny(missing_docs)] - -use crate::{hex, BlockNumber, Head, B256}; +use crate::Head; +use alloy_primitives::{hex, BlockNumber, B256}; use alloy_rlp::*; use crc::*; use reth_codecs::derive_arbitrary; @@ -72,10 +71,12 @@ where } } -// TODO: Move +/// How to filter forks. #[derive(Clone, Copy, Debug, Eq, PartialEq, Serialize, Deserialize)] pub enum ForkFilterKey { + /// By block number activation. Block(BlockNumber), + /// By timestamp activation. Time(u64), } @@ -379,7 +380,7 @@ impl Cache { #[cfg(test)] mod tests { use super::*; - use crate::{hex_literal::hex, revm_primitives::b256}; + use alloy_primitives::b256; const GENESIS_HASH: B256 = b256!("d4e56740f876aef8c010b86a40d5f56745a118d0906a34e69aec8c0db1cb8fa3"); diff --git a/crates/primitives/src/hardfork.rs b/crates/ethereum-forks/src/hardfork.rs similarity index 70% rename from crates/primitives/src/hardfork.rs rename to crates/ethereum-forks/src/hardfork.rs index 793499610187..38a54811b382 100644 --- a/crates/primitives/src/hardfork.rs +++ b/crates/ethereum-forks/src/hardfork.rs @@ -1,6 +1,5 @@ use serde::{Deserialize, Serialize}; -use crate::{ChainSpec, ForkCondition, ForkFilter, ForkId}; use std::{fmt::Display, str::FromStr}; /// The name of an Ethereum hardfork. @@ -52,25 +51,6 @@ pub enum Hardfork { Cancun, } -impl Hardfork { - /// Get the [ForkId] for this hardfork in the given spec, if the fork is activated at any point. - pub fn fork_id(&self, spec: &ChainSpec) -> Option { - match spec.fork(*self) { - ForkCondition::Never => None, - _ => Some(spec.fork_id(&spec.satisfy(spec.fork(*self)))), - } - } - - /// Get the [ForkFilter] for this hardfork in the given spec, if the fork is activated at any - /// point. - pub fn fork_filter(&self, spec: &ChainSpec) -> Option { - match spec.fork(*self) { - ForkCondition::Never => None, - _ => Some(spec.fork_filter(spec.satisfy(spec.fork(*self)))), - } - } -} - impl FromStr for Hardfork { type Err = String; @@ -115,8 +95,6 @@ impl Display for Hardfork { #[cfg(test)] mod tests { use super::*; - use crate::{Chain, Genesis}; - use std::collections::BTreeMap; #[test] fn check_hardfork_from_str() { @@ -181,34 +159,4 @@ mod tests { fn check_nonexistent_hardfork_from_str() { assert!(Hardfork::from_str("not a hardfork").is_err()); } - - #[test] - fn check_fork_id_chainspec_with_fork_condition_never() { - let spec = ChainSpec { - chain: Chain::mainnet(), - genesis: Genesis::default(), - genesis_hash: None, - hardforks: BTreeMap::from([(Hardfork::Frontier, ForkCondition::Never)]), - paris_block_and_final_difficulty: None, - deposit_contract: None, - ..Default::default() - }; - - assert_eq!(Hardfork::Frontier.fork_id(&spec), None); - } - - #[test] - fn check_fork_filter_chainspec_with_fork_condition_never() { - let spec = ChainSpec { - chain: Chain::mainnet(), - genesis: Genesis::default(), - genesis_hash: None, - hardforks: BTreeMap::from([(Hardfork::Shanghai, ForkCondition::Never)]), - paris_block_and_final_difficulty: None, - deposit_contract: None, - ..Default::default() - }; - - assert_eq!(Hardfork::Shanghai.fork_filter(&spec), None); - } } diff --git a/crates/ethereum-forks/src/head.rs b/crates/ethereum-forks/src/head.rs new file mode 100644 index 000000000000..ac526807da08 --- /dev/null +++ b/crates/ethereum-forks/src/head.rs @@ -0,0 +1,24 @@ +use alloy_primitives::{BlockNumber, B256, U256}; +use serde::{Deserialize, Serialize}; + +/// Describes the current head block. +/// +/// The head block is the highest fully synced block. +/// +/// Note: This is a slimmed down version of Header, primarily for communicating the highest block +/// with the P2P network and the RPC. +#[derive( + Debug, Clone, Copy, Default, PartialEq, Eq, Hash, PartialOrd, Ord, Serialize, Deserialize, +)] +pub struct Head { + /// The number of the head block. + pub number: BlockNumber, + /// The hash of the head block. + pub hash: B256, + /// The difficulty of the head block. + pub difficulty: U256, + /// The total difficulty at the head block. + pub total_difficulty: U256, + /// The timestamp of the head block. + pub timestamp: u64, +} diff --git a/crates/ethereum-forks/src/lib.rs b/crates/ethereum-forks/src/lib.rs new file mode 100644 index 000000000000..49aed4605856 --- /dev/null +++ b/crates/ethereum-forks/src/lib.rs @@ -0,0 +1,28 @@ +//! Ethereum fork types used in reth. +//! +//! This crate contains Ethereum fork types and helper functions. +//! +//! ## Feature Flags +//! +//! - `arbitrary`: Adds `proptest` and `arbitrary` support for primitive types. + +#![doc( + html_logo_url = "https://raw.githubusercontent.com/paradigmxyz/reth/main/assets/reth-docs.png", + html_favicon_url = "https://avatars0.githubusercontent.com/u/97369466?s=256", + issue_tracker_base_url = "https://github.com/paradigmxyz/reth/issues/" +)] +#![warn(missing_debug_implementations, missing_docs, unreachable_pub, rustdoc::all)] +#![deny(unused_must_use, rust_2018_idioms, unused_crate_dependencies)] +#![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))] +#![allow(clippy::non_canonical_clone_impl)] + +mod forkid; +mod hardfork; +mod head; + +pub use forkid::{ForkFilter, ForkFilterKey, ForkHash, ForkId, ForkTransition, ValidationError}; +pub use hardfork::Hardfork; +pub use head::Head; + +#[cfg(any(test, feature = "arbitrary"))] +pub use arbitrary; diff --git a/crates/net/dns/src/lib.rs b/crates/net/dns/src/lib.rs index cc33e4d9f769..878ec20240c7 100644 --- a/crates/net/dns/src/lib.rs +++ b/crates/net/dns/src/lib.rs @@ -462,7 +462,7 @@ mod tests { let mut builder = EnrBuilder::new("v4"); let mut buf = Vec::new(); - let fork_id = Hardfork::Frontier.fork_id(&MAINNET).unwrap(); + let fork_id = MAINNET.hardfork_fork_id(Hardfork::Frontier).unwrap(); fork_id.encode(&mut buf); builder.ip4(Ipv4Addr::LOCALHOST).udp4(30303).tcp4(30303).add_value(b"eth", &buf); let enr = builder.build(&secret_key).unwrap(); diff --git a/crates/net/eth-wire/src/builder.rs b/crates/net/eth-wire/src/builder.rs index 0d360a4dd558..df3e771cf512 100644 --- a/crates/net/eth-wire/src/builder.rs +++ b/crates/net/eth-wire/src/builder.rs @@ -17,7 +17,7 @@ use reth_primitives::{Chain, ForkId, B256, U256}; /// .total_difficulty(U256::from(100)) /// .blockhash(B256::from(MAINNET_GENESIS_HASH)) /// .genesis(B256::from(MAINNET_GENESIS_HASH)) -/// .forkid(Hardfork::Paris.fork_id(&MAINNET).unwrap()) +/// .forkid(MAINNET.hardfork_fork_id(Hardfork::Paris).unwrap()) /// .build(); /// /// assert_eq!( @@ -28,7 +28,7 @@ use reth_primitives::{Chain, ForkId, B256, U256}; /// total_difficulty: U256::from(100), /// blockhash: B256::from(MAINNET_GENESIS_HASH), /// genesis: B256::from(MAINNET_GENESIS_HASH), -/// forkid: Hardfork::Paris.fork_id(&MAINNET).unwrap(), +/// forkid: MAINNET.hardfork_fork_id(Hardfork::Paris).unwrap(), /// } /// ); /// ``` diff --git a/crates/net/eth-wire/src/types/status.rs b/crates/net/eth-wire/src/types/status.rs index c112fe58ccb5..5f80449440fc 100644 --- a/crates/net/eth-wire/src/types/status.rs +++ b/crates/net/eth-wire/src/types/status.rs @@ -137,8 +137,8 @@ impl Default for Status { total_difficulty: U256::from(17_179_869_184u64), blockhash: mainnet_genesis, genesis: mainnet_genesis, - forkid: Hardfork::Frontier - .fork_id(&MAINNET) + forkid: MAINNET + .hardfork_fork_id(Hardfork::Frontier) .expect("The Frontier hardfork should always exist"), } } diff --git a/crates/net/network/src/session/active.rs b/crates/net/network/src/session/active.rs index a7a731a128bc..e737d6191f00 100644 --- a/crates/net/network/src/session/active.rs +++ b/crates/net/network/src/session/active.rs @@ -926,8 +926,8 @@ mod tests { secret_key, local_peer_id, status: StatusBuilder::default().build(), - fork_filter: Hardfork::Frontier - .fork_filter(&MAINNET) + fork_filter: MAINNET + .hardfork_fork_filter(Hardfork::Frontier) .expect("The Frontier fork filter should exist on mainnet"), bandwidth_meter: BandwidthMeter::default(), } diff --git a/crates/primitives/Cargo.toml b/crates/primitives/Cargo.toml index 892040c3a340..e89ccf8e5d65 100644 --- a/crates/primitives/Cargo.toml +++ b/crates/primitives/Cargo.toml @@ -2,18 +2,19 @@ name = "reth-primitives" version.workspace = true edition.workspace = true -rust-version.workspace = true -license.workspace = true homepage.workspace = true +license.workspace = true repository.workspace = true +rust-version.workspace = true description = "Commonly used types in reth." [dependencies] # reth reth-codecs.workspace = true +reth-ethereum-forks.workspace = true reth-rpc-types.workspace = true -revm-primitives.workspace = true revm.workspace = true +revm-primitives.workspace = true # ethereum alloy-primitives = { workspace = true, features = ["rand", "rlp"] } @@ -33,28 +34,28 @@ crc = "3" tracing.workspace = true # misc -bytes.workspace = true byteorder = "1" +bytes.workspace = true clap = { workspace = true, features = ["derive"], optional = true } +derive_more = "0.99" +itertools = "0.11" +modular-bitfield = "0.11.2" +num_enum = "0.7" +once_cell.workspace = true +rayon.workspace = true serde.workspace = true serde_json.workspace = true serde_with = "3.3.0" -thiserror.workspace = true +sha2 = "0.10.7" sucds = "~0.6" -modular-bitfield = "0.11.2" -derive_more = "0.99" +tempfile.workspace = true +thiserror.workspace = true url = "2.3" -once_cell.workspace = true zstd = { version = "0.12", features = ["experimental"] } -rayon.workspace = true -tempfile.workspace = true -sha2 = "0.10.7" -itertools = "0.11" -num_enum = "0.7" # `test-utils` feature -plain_hasher = { version = "0.2", optional = true } hash-db = { version = "~0.15", optional = true } +plain_hasher = { version = "0.2", optional = true } # arbitrary utils arbitrary = { workspace = true, features = ["derive"], optional = true } @@ -63,32 +64,39 @@ proptest-derive = { workspace = true, optional = true } strum = { workspace = true, features = ["derive"] } [dev-dependencies] -serde_json.workspace = true -test-fuzz = "4" -rand.workspace = true -revm-primitives = { workspace = true, features = ["arbitrary"] } arbitrary = { workspace = true, features = ["derive"] } +assert_matches.workspace = true proptest.workspace = true proptest-derive.workspace = true -assert_matches.workspace = true +rand.workspace = true +revm-primitives = { workspace = true, features = ["arbitrary"] } +serde_json.workspace = true +test-fuzz = "4" toml.workspace = true triehash = "0.8" -plain_hasher = "0.2" hash-db = "~0.15" +plain_hasher = "0.2" # necessary so we don't hit a "undeclared 'std'": # https://github.com/paradigmxyz/reth/pull/177#discussion_r1021172198 -secp256k1.workspace = true criterion.workspace = true pprof = { workspace = true, features = ["flamegraph", "frame-pointer", "criterion"] } +secp256k1.workspace = true [features] default = ["c-kzg"] -arbitrary = ["revm-primitives/arbitrary", "reth-rpc-types/arbitrary", "dep:arbitrary", "dep:proptest", "dep:proptest-derive"] +arbitrary = [ + "revm-primitives/arbitrary", + "reth-rpc-types/arbitrary", + "reth-ethereum-forks/arbitrary", + "dep:arbitrary", + "dep:proptest", + "dep:proptest-derive", +] c-kzg = ["dep:c-kzg", "revm/c-kzg", "revm-primitives/c-kzg"] clap = ["dep:clap"] -optimism = ["reth-codecs/optimism", "revm-primitives/optimism", "revm/optimism"] +optimism = ["reth-codecs/optimism", "revm-primitives/optimism", "reth-ethereum-forks/optimism", "revm/optimism"] test-utils = ["dep:plain_hasher", "dep:hash-db", "dep:ethers-core"] [[bench]] diff --git a/crates/primitives/src/chain/spec.rs b/crates/primitives/src/chain/spec.rs index a1d5d96a8a9f..7430ce92a0f4 100644 --- a/crates/primitives/src/chain/spec.rs +++ b/crates/primitives/src/chain/spec.rs @@ -3,12 +3,10 @@ use crate::{ EIP1559_DEFAULT_BASE_FEE_MAX_CHANGE_DENOMINATOR, EIP1559_DEFAULT_ELASTICITY_MULTIPLIER, EIP1559_INITIAL_BASE_FEE, EMPTY_RECEIPTS, EMPTY_TRANSACTIONS, EMPTY_WITHDRAWALS, }, - forkid::ForkFilterKey, - header::Head, proofs::genesis_state_root, revm_primitives::{address, b256}, - Address, BlockNumber, Chain, ForkFilter, ForkHash, ForkId, Genesis, Hardfork, Header, - SealedHeader, B256, EMPTY_OMMER_ROOT_HASH, U256, + Address, BlockNumber, Chain, ForkFilter, ForkFilterKey, ForkHash, ForkId, Genesis, Hardfork, + Head, Header, SealedHeader, B256, EMPTY_OMMER_ROOT_HASH, U256, }; use once_cell::sync::Lazy; use serde::{Deserialize, Serialize}; @@ -561,6 +559,7 @@ impl ChainSpec { } /// Returns `true` if this chain contains Optimism configuration. + #[inline] pub fn is_optimism(&self) -> bool { self.chain.is_optimism() } @@ -671,6 +670,7 @@ impl ChainSpec { /// /// Note: technically this would also be valid for the block before the paris upgrade, but this /// edge case is omitted here. + #[inline] pub fn final_paris_total_difficulty(&self, block_number: u64) -> Option { self.paris_block_and_final_difficulty.and_then(|(activated_at, final_difficulty)| { if block_number >= activated_at { @@ -681,19 +681,38 @@ impl ChainSpec { }) } + /// Get the fork filter for the given hardfork + pub fn hardfork_fork_filter(&self, fork: Hardfork) -> Option { + match self.fork(fork) { + ForkCondition::Never => None, + _ => Some(self.fork_filter(self.satisfy(self.fork(fork)))), + } + } + /// Returns the forks in this specification and their activation conditions. pub fn hardforks(&self) -> &BTreeMap { &self.hardforks } /// Get the fork id for the given hardfork. + #[inline] pub fn hardfork_fork_id(&self, fork: Hardfork) -> Option { - fork.fork_id(self) + match self.fork(fork) { + ForkCondition::Never => None, + _ => Some(self.fork_id(&self.satisfy(self.fork(fork)))), + } } /// Convenience method to get the fork id for [Hardfork::Shanghai] from a given chainspec. + #[inline] pub fn shanghai_fork_id(&self) -> Option { - Hardfork::Shanghai.fork_id(self) + self.hardfork_fork_id(Hardfork::Shanghai) + } + + /// Convenience method to get the fork id for [Hardfork::Cancun] from a given chainspec. + #[inline] + pub fn cancun_fork_id(&self) -> Option { + self.hardfork_fork_id(Hardfork::Cancun) } /// Get the fork condition for the given fork. @@ -2749,4 +2768,34 @@ Post-merge hard forks (timestamp based): serde_json::from_str(&serialized_chain_spec).unwrap(); assert!(matches!(deserialized_chain_spec, AllGenesisFormats::Reth(_))) } + + #[test] + fn check_fork_id_chainspec_with_fork_condition_never() { + let spec = ChainSpec { + chain: Chain::mainnet(), + genesis: Genesis::default(), + genesis_hash: None, + hardforks: BTreeMap::from([(Hardfork::Frontier, ForkCondition::Never)]), + paris_block_and_final_difficulty: None, + deposit_contract: None, + ..Default::default() + }; + + assert_eq!(spec.hardfork_fork_id(Hardfork::Frontier), None); + } + + #[test] + fn check_fork_filter_chainspec_with_fork_condition_never() { + let spec = ChainSpec { + chain: Chain::mainnet(), + genesis: Genesis::default(), + genesis_hash: None, + hardforks: BTreeMap::from([(Hardfork::Shanghai, ForkCondition::Never)]), + paris_block_and_final_difficulty: None, + deposit_contract: None, + ..Default::default() + }; + + assert_eq!(spec.hardfork_fork_filter(Hardfork::Shanghai), None); + } } diff --git a/crates/primitives/src/header.rs b/crates/primitives/src/header.rs index 36b3322f3237..f86c9f205986 100644 --- a/crates/primitives/src/header.rs +++ b/crates/primitives/src/header.rs @@ -14,28 +14,6 @@ use std::{ ops::{Deref, DerefMut}, }; -/// Describes the current head block. -/// -/// The head block is the highest fully synced block. -/// -/// Note: This is a slimmed down version of [Header], primarily for communicating the highest block -/// with the P2P network and the RPC. -#[derive( - Debug, Clone, Copy, Default, PartialEq, Eq, Hash, PartialOrd, Ord, Serialize, Deserialize, -)] -pub struct Head { - /// The number of the head block. - pub number: BlockNumber, - /// The hash of the head block. - pub hash: B256, - /// The difficulty of the head block. - pub difficulty: U256, - /// The total difficulty at the head block. - pub total_difficulty: U256, - /// The timestamp of the head block. - pub timestamp: u64, -} - /// Block header #[main_codec] #[derive(Debug, Clone, PartialEq, Eq, Hash)] diff --git a/crates/primitives/src/lib.rs b/crates/primitives/src/lib.rs index ca6a5c1d4fed..36e55c6d2cc1 100644 --- a/crates/primitives/src/lib.rs +++ b/crates/primitives/src/lib.rs @@ -25,10 +25,8 @@ mod compression; pub mod constants; pub mod eip4844; mod error; -mod forkid; pub mod fs; mod genesis; -mod hardfork; mod header; mod integer_list; mod log; @@ -67,10 +65,8 @@ pub use constants::{ KECCAK_EMPTY, MAINNET_GENESIS_HASH, SEPOLIA_GENESIS_HASH, }; pub use error::{GotExpected, GotExpectedBoxed}; -pub use forkid::{ForkFilter, ForkHash, ForkId, ForkTransition, ValidationError}; pub use genesis::{ChainConfig, Genesis, GenesisAccount}; -pub use hardfork::Hardfork; -pub use header::{Head, Header, HeadersDirection, SealedHeader}; +pub use header::{Header, HeadersDirection, SealedHeader}; pub use integer_list::IntegerList; pub use log::{logs_bloom, Log}; pub use net::{ @@ -113,6 +109,7 @@ pub use alloy_primitives::{ Address, BlockHash, BlockNumber, Bloom, BloomInput, Bytes, ChainId, Selector, StorageKey, StorageValue, TxHash, TxIndex, TxNumber, B128, B256, B512, B64, U128, U256, U64, U8, }; +pub use reth_ethereum_forks::*; pub use revm_primitives::{self, JumpMap}; #[doc(hidden)] diff --git a/examples/manual-p2p/src/main.rs b/examples/manual-p2p/src/main.rs index d8b98875d204..3ff90bf75dca 100644 --- a/examples/manual-p2p/src/main.rs +++ b/examples/manual-p2p/src/main.rs @@ -102,7 +102,7 @@ async fn handshake_eth(p2p_stream: AuthedP2PStream) -> eyre::Result<(AuthedEthSt let status = Status::builder() .chain(Chain::mainnet()) .genesis(MAINNET_GENESIS_HASH) - .forkid(Hardfork::Shanghai.fork_id(&MAINNET).unwrap()) + .forkid(MAINNET.hardfork_fork_id(Hardfork::Shanghai).unwrap()) .build(); let status = Status { version: p2p_stream.shared_capabilities().eth()?.version(), ..status }; From 67d93e822ee1339cf961e27819a7352b34a2bbc2 Mon Sep 17 00:00:00 2001 From: Roman Krasiuk Date: Fri, 1 Dec 2023 04:35:45 -0800 Subject: [PATCH 071/277] chore(evm): use provider errors (#5649) --- crates/interfaces/src/executor.rs | 9 ++++++--- crates/payload/basic/src/lib.rs | 8 +++++--- crates/payload/basic/src/optimism.rs | 4 +--- crates/payload/builder/src/error.rs | 9 +++++---- crates/revm/src/database.rs | 4 ++-- crates/revm/src/optimism/mod.rs | 6 +++--- crates/revm/src/optimism/processor.rs | 10 ++++++++-- crates/revm/src/processor.rs | 15 +++++++-------- 8 files changed, 37 insertions(+), 28 deletions(-) diff --git a/crates/interfaces/src/executor.rs b/crates/interfaces/src/executor.rs index fa677ad814c8..e6d2d1f3f831 100644 --- a/crates/interfaces/src/executor.rs +++ b/crates/interfaces/src/executor.rs @@ -1,4 +1,4 @@ -use crate::RethError; +use crate::provider::ProviderError; use reth_primitives::{ revm_primitives::EVMError, BlockNumHash, Bloom, GotExpected, GotExpectedBoxed, PruneSegmentError, B256, @@ -15,7 +15,7 @@ pub enum BlockValidationError { hash: B256, /// The EVM error. #[source] - error: Box>, + error: Box>, }, /// Error when recovering the sender for a transaction #[error("failed to recover sender for transaction")] @@ -127,11 +127,14 @@ pub enum BlockExecutionError { #[derive(Error, Debug, Clone, PartialEq, Eq)] pub enum OptimismBlockExecutionError { /// Error when trying to parse L1 block info - #[error("Could not get L1 block info from L2 block: {message:?}")] + #[error("could not get L1 block info from L2 block: {message:?}")] L1BlockInfoError { /// The inner error message message: String, }, + /// Thrown when force deploy of create2deployer code fails. + #[error("failed to force create2deployer account code")] + ForceCreate2DeployerFail, } impl BlockExecutionError { diff --git a/crates/payload/basic/src/lib.rs b/crates/payload/basic/src/lib.rs index db3b9491c0b0..a275ad5d78a1 100644 --- a/crates/payload/basic/src/lib.rs +++ b/crates/payload/basic/src/lib.rs @@ -13,7 +13,7 @@ use crate::metrics::PayloadBuilderMetrics; use alloy_rlp::Encodable; use futures_core::ready; use futures_util::FutureExt; -use reth_interfaces::{RethError, RethResult}; +use reth_interfaces::RethResult; use reth_payload_builder::{ database::CachedReads, error::PayloadBuilderError, BuiltPayload, KeepPayloadJobAlive, PayloadBuilderAttributes, PayloadId, PayloadJob, PayloadJobGenerator, @@ -30,7 +30,9 @@ use reth_primitives::{ Block, BlockNumberOrTag, Bytes, ChainSpec, Header, IntoRecoveredTransaction, Receipt, Receipts, SealedBlock, Withdrawal, B256, EMPTY_OMMER_ROOT_HASH, U256, }; -use reth_provider::{BlockReaderIdExt, BlockSource, BundleStateWithReceipts, StateProviderFactory}; +use reth_provider::{ + BlockReaderIdExt, BlockSource, BundleStateWithReceipts, ProviderError, StateProviderFactory, +}; use reth_revm::{ database::StateProviderDatabase, state_change::{apply_beacon_root_contract_call, post_block_withdrawals_balance_increments}, @@ -1159,7 +1161,7 @@ impl WithdrawalsOutcome { /// Returns the withdrawals root. /// /// Returns `None` values pre shanghai -fn commit_withdrawals>( +fn commit_withdrawals>( db: &mut State, chain_spec: &ChainSpec, timestamp: u64, diff --git a/crates/payload/basic/src/optimism.rs b/crates/payload/basic/src/optimism.rs index d316676d139b..2f27854586c2 100644 --- a/crates/payload/basic/src/optimism.rs +++ b/crates/payload/basic/src/optimism.rs @@ -60,9 +60,7 @@ where // the above check for empty blocks will never be hit on OP chains. reth_revm::optimism::ensure_create2_deployer(chain_spec.clone(), attributes.timestamp, &mut db) .map_err(|_| { - PayloadBuilderError::Internal(RethError::Custom( - "Failed to force create2deployer account code".to_string(), - )) + PayloadBuilderError::Optimism(OptimismPayloadBuilderError::ForceCreate2DeployerFail) })?; let mut receipts = Vec::new(); diff --git a/crates/payload/builder/src/error.rs b/crates/payload/builder/src/error.rs index d42366d121cc..cd998f156a4a 100644 --- a/crates/payload/builder/src/error.rs +++ b/crates/payload/builder/src/error.rs @@ -22,7 +22,7 @@ pub enum PayloadBuilderError { Internal(#[from] RethError), /// Unrecoverable error during evm execution. #[error("evm execution error: {0}")] - EvmExecutionError(EVMError), + EvmExecutionError(EVMError), /// Thrown if the payload requests withdrawals before Shanghai activation. #[error("withdrawals set before Shanghai activation")] WithdrawalsBeforeShanghai, @@ -39,22 +39,23 @@ impl From for PayloadBuilderError { } /// Optimism specific payload building errors. +#[cfg(feature = "optimism")] #[derive(Debug, thiserror::Error)] pub enum OptimismPayloadBuilderError { /// Thrown when a transaction fails to convert to a /// [reth_primitives::TransactionSignedEcRecovered]. - #[cfg(feature = "optimism")] #[error("failed to convert deposit transaction to TransactionSignedEcRecovered")] TransactionEcRecoverFailed, /// Thrown when the L1 block info could not be parsed from the calldata of the /// first transaction supplied in the payload attributes. - #[cfg(feature = "optimism")] #[error("failed to parse L1 block info from L1 info tx calldata")] L1BlockInfoParseFailed, /// Thrown when a database account could not be loaded. #[error("failed to load account {0:?}")] - #[cfg(feature = "optimism")] AccountLoadFailed(revm_primitives::Address), + /// Thrown when force deploy of create2deployer code fails. + #[error("failed to force create2deployer account code")] + ForceCreate2DeployerFail, } impl From for PayloadBuilderError { diff --git a/crates/revm/src/database.rs b/crates/revm/src/database.rs index 6712020b4306..0486043a5ae4 100644 --- a/crates/revm/src/database.rs +++ b/crates/revm/src/database.rs @@ -1,6 +1,6 @@ use reth_interfaces::RethError; use reth_primitives::{Address, B256, KECCAK_EMPTY, U256}; -use reth_provider::StateProvider; +use reth_provider::{ProviderError, StateProvider}; use revm::{ db::{CacheDB, DatabaseRef}, primitives::{AccountInfo, Bytecode}, @@ -40,7 +40,7 @@ impl StateProviderDatabase { } impl Database for StateProviderDatabase { - type Error = RethError; + type Error = ProviderError; fn basic(&mut self, address: Address) -> Result, Self::Error> { Ok(self.0.basic_account(address)?.map(|account| AccountInfo { diff --git a/crates/revm/src/optimism/mod.rs b/crates/revm/src/optimism/mod.rs index 3ecb632a6c33..812b8f4695ca 100644 --- a/crates/revm/src/optimism/mod.rs +++ b/crates/revm/src/optimism/mod.rs @@ -180,9 +180,9 @@ where // If the canyon hardfork is active at the current timestamp, and it was not active at the // previous block timestamp (heuristically, block time is not perfectly constant at 2s), and the // chain is an optimism chain, then we need to force-deploy the create2 deployer contract. - if chain_spec.is_fork_active_at_timestamp(Hardfork::Canyon, timestamp) && - !chain_spec.is_fork_active_at_timestamp(Hardfork::Canyon, timestamp - 2) && - chain_spec.is_optimism() + if chain_spec.is_optimism() && + chain_spec.is_fork_active_at_timestamp(Hardfork::Canyon, timestamp) && + !chain_spec.is_fork_active_at_timestamp(Hardfork::Canyon, timestamp - 2) { trace!(target: "evm", "Forcing create2 deployer contract deployment on Canyon transition"); diff --git a/crates/revm/src/optimism/processor.rs b/crates/revm/src/optimism/processor.rs index c0f3ba6a57ee..cef7cc204eac 100644 --- a/crates/revm/src/optimism/processor.rs +++ b/crates/revm/src/optimism/processor.rs @@ -1,5 +1,7 @@ use crate::processor::{verify_receipt, EVMProcessor}; -use reth_interfaces::executor::{BlockExecutionError, BlockValidationError}; +use reth_interfaces::executor::{ + BlockExecutionError, BlockValidationError, OptimismBlockExecutionError, +}; use reth_primitives::{ revm::compat::into_reth_log, revm_primitives::ResultAndState, Address, Block, Hardfork, Receipt, U256, @@ -74,7 +76,11 @@ impl<'a> BlockExecutor for EVMProcessor<'a> { // so we can safely assume that this will always be triggered upon the transition and that // the above check for empty blocks will never be hit on OP chains. super::ensure_create2_deployer(self.chain_spec().clone(), block.timestamp, self.db_mut()) - .map_err(|_| BlockExecutionError::ProviderError)?; + .map_err(|_| { + BlockExecutionError::OptimismBlockExecution( + OptimismBlockExecutionError::ForceCreate2DeployerFail, + ) + })?; let mut cumulative_gas_used = 0; let mut receipts = Vec::with_capacity(block.body.len()); diff --git a/crates/revm/src/processor.rs b/crates/revm/src/processor.rs index 0a4806e7d461..708943d88b77 100644 --- a/crates/revm/src/processor.rs +++ b/crates/revm/src/processor.rs @@ -4,17 +4,16 @@ use crate::{ stack::{InspectorStack, InspectorStackConfig}, state_change::{apply_beacon_root_contract_call, post_block_balance_increments}, }; -use reth_interfaces::{ - executor::{BlockExecutionError, BlockValidationError}, - RethError, -}; +use reth_interfaces::executor::{BlockExecutionError, BlockValidationError}; use reth_primitives::{ revm::env::{fill_cfg_and_block_env, fill_tx_env}, Address, Block, BlockNumber, Bloom, ChainSpec, GotExpected, Hardfork, Header, PruneMode, PruneModes, PruneSegmentError, Receipt, ReceiptWithBloom, Receipts, TransactionSigned, B256, MINIMUM_PRUNING_DISTANCE, U256, }; -use reth_provider::{BlockExecutor, BlockExecutorStats, PrunableBlockExecutor, StateProvider}; +use reth_provider::{ + BlockExecutor, BlockExecutorStats, ProviderError, PrunableBlockExecutor, StateProvider, +}; use revm::{ db::{states::bundle_state::BundleRetention, StateDBBox}, primitives::ResultAndState, @@ -53,7 +52,7 @@ pub struct EVMProcessor<'a> { /// The configured chain-spec pub(crate) chain_spec: Arc, /// revm instance that contains database and env environment. - pub(crate) evm: EVM>, + pub(crate) evm: EVM>, /// Hook and inspector stack that we want to invoke on that hook. stack: InspectorStack, /// The collection of receipts. @@ -115,7 +114,7 @@ impl<'a> EVMProcessor<'a> { /// Create a new EVM processor with the given revm state. pub fn new_with_state( chain_spec: Arc, - revm_state: StateDBBox<'a, RethError>, + revm_state: StateDBBox<'a, ProviderError>, ) -> Self { let mut evm = EVM::new(); evm.database(revm_state); @@ -143,7 +142,7 @@ impl<'a> EVMProcessor<'a> { } /// Returns a reference to the database - pub fn db_mut(&mut self) -> &mut StateDBBox<'a, RethError> { + pub fn db_mut(&mut self) -> &mut StateDBBox<'a, ProviderError> { // Option will be removed from EVM in the future. // as it is always some. // https://github.com/bluealloy/revm/issues/697 From a3f77d275bbaf9ea02f30da5c0051b046bf629d9 Mon Sep 17 00:00:00 2001 From: Matthias Seitz Date: Fri, 1 Dec 2023 14:39:13 +0100 Subject: [PATCH 072/277] chore: remove unused deps in primitives (#5651) --- Cargo.lock | 3 --- crates/primitives/Cargo.toml | 5 ----- 2 files changed, 8 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 12c76d02a04e..0547931d5364 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -6177,7 +6177,6 @@ dependencies = [ "bytes", "c-kzg", "clap", - "crc", "criterion", "derive_more", "ethers-core", @@ -6200,7 +6199,6 @@ dependencies = [ "secp256k1 0.27.0", "serde", "serde_json", - "serde_with", "sha2", "strum", "sucds 0.6.0", @@ -6210,7 +6208,6 @@ dependencies = [ "toml 0.8.8", "tracing", "triehash", - "url", "zstd 0.12.4", ] diff --git a/crates/primitives/Cargo.toml b/crates/primitives/Cargo.toml index e89ccf8e5d65..7d33f2ad077d 100644 --- a/crates/primitives/Cargo.toml +++ b/crates/primitives/Cargo.toml @@ -27,9 +27,6 @@ secp256k1 = { workspace = true, features = ["global-context", "recovery"] } # for eip-4844 c-kzg = { workspace = true, features = ["serde"], optional = true } -# used for forkid -crc = "3" - # tracing tracing.workspace = true @@ -45,12 +42,10 @@ once_cell.workspace = true rayon.workspace = true serde.workspace = true serde_json.workspace = true -serde_with = "3.3.0" sha2 = "0.10.7" sucds = "~0.6" tempfile.workspace = true thiserror.workspace = true -url = "2.3" zstd = { version = "0.12", features = ["experimental"] } # `test-utils` feature From cb52a4ca3967b9d9f619d15573e1b293350b1e62 Mon Sep 17 00:00:00 2001 From: Matthias Seitz Date: Fri, 1 Dec 2023 14:39:21 +0100 Subject: [PATCH 073/277] chore: cleanup codecs deps (#5650) --- Cargo.lock | 2 +- crates/primitives/Cargo.toml | 2 +- crates/storage/codecs/Cargo.toml | 29 ++++++++++++----------------- crates/storage/codecs/src/lib.rs | 17 ++++++++++++----- crates/storage/db/Cargo.toml | 3 +-- 5 files changed, 27 insertions(+), 26 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 0547931d5364..efff0ef3dba7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5700,13 +5700,13 @@ dependencies = [ name = "reth-codecs" version = "0.1.0-alpha.12" dependencies = [ + "alloy-primitives", "arbitrary", "bytes", "codecs-derive", "modular-bitfield", "proptest", "proptest-derive", - "revm-primitives", "serde", "test-fuzz", ] diff --git a/crates/primitives/Cargo.toml b/crates/primitives/Cargo.toml index 7d33f2ad077d..9487a1d46d77 100644 --- a/crates/primitives/Cargo.toml +++ b/crates/primitives/Cargo.toml @@ -14,7 +14,7 @@ reth-codecs.workspace = true reth-ethereum-forks.workspace = true reth-rpc-types.workspace = true revm.workspace = true -revm-primitives.workspace = true +revm-primitives = { workspace = true, features = ["serde"] } # ethereum alloy-primitives = { workspace = true, features = ["rand", "rlp"] } diff --git a/crates/storage/codecs/Cargo.toml b/crates/storage/codecs/Cargo.toml index 4e51d617a7d7..18a028691b06 100644 --- a/crates/storage/codecs/Cargo.toml +++ b/crates/storage/codecs/Cargo.toml @@ -7,31 +7,26 @@ license.workspace = true homepage.workspace = true repository.workspace = true -[features] -default = ["compact"] -compact = ["codecs-derive/compact"] -scale = ["codecs-derive/scale"] -postcard = ["codecs-derive/postcard"] -no_codec = ["codecs-derive/no_codec"] -arbitrary = ["revm-primitives/arbitrary", "dep:arbitrary", "dep:proptest", "dep:proptest-derive"] -optimism = ["codecs-derive/optimism"] - [dependencies] -bytes.workspace = true codecs-derive = { path = "./derive", default-features = false } -revm-primitives = { workspace = true, features = ["serde"] } -# arbitrary utils -arbitrary = { workspace = true, features = ["derive"], optional = true } -proptest = { workspace = true, optional = true } -proptest-derive = { workspace = true, optional = true } +alloy-primitives.workspace = true +bytes.workspace = true [dev-dependencies] -revm-primitives = { workspace = true, features = ["serde", "arbitrary"] } -serde = "1.0" +alloy-primitives = { workspace = true, features = ["arbitrary", "serde"] } +serde.workspace = true modular-bitfield = "0.11.2" test-fuzz = "4" arbitrary = { workspace = true, features = ["derive"] } proptest.workspace = true proptest-derive.workspace = true + +[features] +default = ["compact"] +compact = ["codecs-derive/compact"] +scale = ["codecs-derive/scale"] +postcard = ["codecs-derive/postcard"] +no_codec = ["codecs-derive/no_codec"] +optimism = ["codecs-derive/optimism"] \ No newline at end of file diff --git a/crates/storage/codecs/src/lib.rs b/crates/storage/codecs/src/lib.rs index aeb0f2c5bece..bfe096d120d2 100644 --- a/crates/storage/codecs/src/lib.rs +++ b/crates/storage/codecs/src/lib.rs @@ -5,14 +5,21 @@ html_favicon_url = "https://avatars0.githubusercontent.com/u/97369466?s=256", issue_tracker_base_url = "https://github.com/paradigmxyz/reth/issues/" )] +#![warn( + missing_debug_implementations, + missing_docs, + unused_crate_dependencies, + unreachable_pub, + rustdoc::all +)] +#![deny(unused_must_use, rust_2018_idioms)] +#![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))] +#![allow(clippy::non_canonical_clone_impl)] pub use codecs_derive::*; +use alloy_primitives::{Address, Bloom, Bytes, B256, B512, U256}; use bytes::Buf; -use revm_primitives::{ - alloy_primitives::{Bloom, B512}, - Address, Bytes, B256, U256, -}; /// Trait that implements the `Compact` codec. /// @@ -338,7 +345,7 @@ fn decode_varuint(mut buf: &[u8]) -> (usize, &[u8]) { #[cfg(test)] mod tests { use super::*; - use revm_primitives::{Address, Bytes}; + use alloy_primitives::{Address, Bytes}; #[test] fn compact_bytes() { diff --git a/crates/storage/db/Cargo.toml b/crates/storage/db/Cargo.toml index 60e13ad2f67f..46cc2a215808 100644 --- a/crates/storage/db/Cargo.toml +++ b/crates/storage/db/Cargo.toml @@ -56,7 +56,7 @@ proptest-derive = { workspace = true, optional = true } [dev-dependencies] # reth libs with arbitrary reth-primitives = { workspace = true, features = ["arbitrary"] } -reth-codecs = { workspace = true, features = ["arbitrary"] } +reth-codecs.workspace = true reth-interfaces.workspace = true tempfile.workspace = true @@ -93,7 +93,6 @@ mdbx = ["reth-libmdbx"] bench = [] arbitrary = [ "reth-primitives/arbitrary", - "reth-codecs/arbitrary", "dep:arbitrary", "dep:proptest", "dep:proptest-derive", From 0d70bf45d6ecdcd34623962a631f546a645389f9 Mon Sep 17 00:00:00 2001 From: Matthias Seitz Date: Fri, 1 Dec 2023 15:46:02 +0100 Subject: [PATCH 074/277] chore: group optimism reexports (#5652) --- crates/primitives/src/lib.rs | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/crates/primitives/src/lib.rs b/crates/primitives/src/lib.rs index 36e55c6d2cc1..a8f3705dbcc1 100644 --- a/crates/primitives/src/lib.rs +++ b/crates/primitives/src/lib.rs @@ -57,8 +57,6 @@ pub use chain::{ ChainSpecBuilder, DisplayHardforks, ForkBaseFeeParams, ForkCondition, ForkTimestamps, NamedChain, DEV, GOERLI, HOLESKY, MAINNET, SEPOLIA, }; -#[cfg(feature = "optimism")] -pub use chain::{BASE_GOERLI, BASE_MAINNET, OP_GOERLI}; pub use compression::*; pub use constants::{ DEV_GENESIS_HASH, EMPTY_OMMER_ROOT_HASH, GOERLI_GENESIS_HASH, HOLESKY_GENESIS_HASH, @@ -98,8 +96,6 @@ pub use transaction::{ TxEip4844, TxHashOrNumber, TxLegacy, TxType, TxValue, EIP1559_TX_TYPE_ID, EIP2930_TX_TYPE_ID, EIP4844_TX_TYPE_ID, LEGACY_TX_TYPE_ID, }; -#[cfg(feature = "optimism")] -pub use transaction::{TxDeposit, DEPOSIT_TX_TYPE_ID}; pub use withdrawal::Withdrawal; // Re-exports @@ -133,3 +129,15 @@ pub use arbitrary; #[cfg(feature = "c-kzg")] pub use c_kzg as kzg; + +/// Optimism specific re-exports +#[cfg(feature = "optimism")] +mod optimism { + pub use crate::{ + chain::{BASE_GOERLI, BASE_MAINNET, OP_GOERLI}, + transaction::{TxDeposit, DEPOSIT_TX_TYPE_ID}, + }; +} + +#[cfg(feature = "optimism")] +pub use optimism::*; From 346f135c56207d7a228d0c2cb51f3107f5d26875 Mon Sep 17 00:00:00 2001 From: solidoracle <105349716+solidoracle@users.noreply.github.com> Date: Fri, 1 Dec 2023 16:07:07 +0100 Subject: [PATCH 075/277] Granular canonicalization metrics dashboard update (#5454) --- etc/grafana/dashboards/overview.json | 4663 +++++++++++++++++++++++++- 1 file changed, 4655 insertions(+), 8 deletions(-) diff --git a/etc/grafana/dashboards/overview.json b/etc/grafana/dashboards/overview.json index 4949632a7d11..5c1adb8d68fc 100644 --- a/etc/grafana/dashboards/overview.json +++ b/etc/grafana/dashboards/overview.json @@ -1,5 +1,5 @@ { - "__inputs": [ +"__inputs": [ { "name": "DS_PROMETHEUS", "label": "Prometheus", @@ -218,9 +218,7 @@ "minVizWidth": 0, "orientation": "horizontal", "reduceOptions": { - "calcs": [ - "last" - ], + "calcs": ["last"], "fields": "", "values": false }, @@ -5443,7 +5441,7 @@ "type": "prometheus", "uid": "${DS_PROMETHEUS}" }, - "description": "", +"description": "", "fieldConfig": { "defaults": { "color": { @@ -5529,7 +5527,7 @@ "legendFormat": "Resident", "range": true, "refId": "A" - } + } ], "title": "Memory", "type": "timeseries" @@ -5696,7 +5694,7 @@ "x": 12, "y": 219 }, - "id": 100, + "id": 100, "options": { "legend": { "calcs": [], @@ -6343,6 +6341,4655 @@ ], "title": "Call Latency time", "type": "heatmap" + }, + { + "collapsed": false, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 253 + }, + "id": 123, + "panels": [], + "repeat": "instance", + "repeatDirection": "h", + "title": "Database - Canonicalization Metrics", + "type": "row" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "description": "", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "ms" + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 254 + }, + "id": 167, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "expr": "reth_storage_providers_database_duration{action=\"insert storage hashing\", quantile=\"0.5\"}", + "instant": false, + "legendFormat": "{{quantile}} percentile", + "range": true, + "refId": "A" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "expr": "reth_storage_providers_database_duration{action=\"insert storage hashing\", quantile=\"0.9\"} ", + "hide": false, + "instant": false, + "legendFormat": "{{quantile}} percentile", + "range": true, + "refId": "B" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "expr": "reth_storage_providers_database_duration{action=\"insert storage hashing\", quantile=\"0.95\"} ", + "hide": false, + "instant": false, + "legendFormat": "{{quantile}} percentile", + "range": true, + "refId": "C" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "expr": "reth_storage_providers_database_duration{action=\"insert storage hashing\", quantile=\"0.99\"} ", + "hide": false, + "instant": false, + "legendFormat": "{{quantile}} percentile", + "range": true, + "refId": "D" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "exemplar": false, + "expr": "reth_storage_providers_database_duration{action=\"insert storage hashing\", quantile=\"0.999\"} ", + "hide": false, + "instant": false, + "legendFormat": "{{quantile}} percentile", + "range": true, + "refId": "E" + } + ], + "title": "Insert Storage Hashing", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "description": "", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "ms" + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 254 + }, + "id": 168, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "expr": "reth_storage_providers_database_duration{action=\"insert account hashing\", quantile=\"0.5\"}", + "instant": false, + "legendFormat": "{{quantile}} percentile", + "range": true, + "refId": "A" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "expr": "reth_storage_providers_database_duration{action=\"insert account hashing\", quantile=\"0.9\"} ", + "hide": false, + "instant": false, + "legendFormat": "{{quantile}} percentile", + "range": true, + "refId": "B" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "expr": "reth_storage_providers_database_duration{action=\"insert account hashing\", quantile=\"0.95\"} ", + "hide": false, + "instant": false, + "legendFormat": "{{quantile}} percentile", + "range": true, + "refId": "C" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "expr": "reth_storage_providers_database_duration{action=\"insert account hashing\", quantile=\"0.99\"} ", + "hide": false, + "instant": false, + "legendFormat": "{{quantile}} percentile", + "range": true, + "refId": "D" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "exemplar": false, + "expr": "reth_storage_providers_database_duration{action=\"insert account hashing\", quantile=\"0.999\"} ", + "hide": false, + "instant": false, + "legendFormat": "{{quantile}} percentile", + "range": true, + "refId": "E" + } + ], + "title": "Insert Account Hashing", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "description": "", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "ms" + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 262 + }, + "id": 169, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "expr": "reth_storage_providers_database_duration{action=\"insert merkle tree\", quantile=\"0.5\"}", + "instant": false, + "legendFormat": "{{quantile}} percentile", + "range": true, + "refId": "A" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "expr": "reth_storage_providers_database_duration{action=\"insert merkle tree\", quantile=\"0.9\"} ", + "hide": false, + "instant": false, + "legendFormat": "{{quantile}} percentile", + "range": true, + "refId": "B" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "expr": "reth_storage_providers_database_duration{action=\"insert merkle tree\", quantile=\"0.95\"} ", + "hide": false, + "instant": false, + "legendFormat": "{{quantile}} percentile", + "range": true, + "refId": "C" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "expr": "reth_storage_providers_database_duration{action=\"insert merkle tree\", quantile=\"0.99\"} ", + "hide": false, + "instant": false, + "legendFormat": "{{quantile}} percentile", + "range": true, + "refId": "D" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "exemplar": false, + "expr": "reth_storage_providers_database_duration{action=\"insert merkle tree\", quantile=\"0.999\"} ", + "hide": false, + "instant": false, + "legendFormat": "{{quantile}} percentile", + "range": true, + "refId": "E" + } + ], + "title": "Insert Merkle Tree", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "description": "", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "ms" + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 262 + }, + "id": 170, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "expr": "reth_storage_providers_database_duration{action=\"insert block\", quantile=\"0.5\"}", + "instant": false, + "legendFormat": "{{quantile}} percentile", + "range": true, + "refId": "A" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "expr": "reth_storage_providers_database_duration{action=\"insert block\", quantile=\"0.9\"} ", + "hide": false, + "instant": false, + "legendFormat": "{{quantile}} percentile", + "range": true, + "refId": "B" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "expr": "reth_storage_providers_database_duration{action=\"insert block\", quantile=\"0.95\"} ", + "hide": false, + "instant": false, + "legendFormat": "{{quantile}} percentile", + "range": true, + "refId": "C" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "expr": "reth_storage_providers_database_duration{action=\"insert block\", quantile=\"0.99\"} ", + "hide": false, + "instant": false, + "legendFormat": "{{quantile}} percentile", + "range": true, + "refId": "D" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "exemplar": false, + "expr": "reth_storage_providers_database_duration{action=\"insert block\", quantile=\"0.999\"} ", + "hide": false, + "instant": false, + "legendFormat": "{{quantile}} percentile", + "range": true, + "refId": "E" + } + ], + "title": "Insert Block", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "description": "", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "ms" + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 270 + }, + "id": 171, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "expr": "reth_storage_providers_database_duration{action=\"insert state\", quantile=\"0.5\"}", + "instant": false, + "legendFormat": "{{quantile}} percentile", + "range": true, + "refId": "A" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "expr": "reth_storage_providers_database_duration{action=\"insert state\", quantile=\"0.9\"} ", + "hide": false, + "instant": false, + "legendFormat": "{{quantile}} percentile", + "range": true, + "refId": "B" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "expr": "reth_storage_providers_database_duration{action=\"insert state\", quantile=\"0.95\"} ", + "hide": false, + "instant": false, + "legendFormat": "{{quantile}} percentile", + "range": true, + "refId": "C" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "expr": "reth_storage_providers_database_duration{action=\"insert state\", quantile=\"0.99\"} ", + "hide": false, + "instant": false, + "legendFormat": "{{quantile}} percentile", + "range": true, + "refId": "D" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "exemplar": false, + "expr": "reth_storage_providers_database_duration{action=\"insert state\", quantile=\"0.999\"} ", + "hide": false, + "instant": false, + "legendFormat": "{{quantile}} percentile", + "range": true, + "refId": "E" + } + ], + "title": "Insert State", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "description": "", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "ms" + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 270 + }, + "id": 172, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "expr": "reth_storage_providers_database_duration{action=\"insert hashes\", quantile=\"0.5\"}", + "instant": false, + "legendFormat": "{{quantile}} percentile", + "range": true, + "refId": "A" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "expr": "reth_storage_providers_database_duration{action=\"insert hashes\", quantile=\"0.9\"} ", + "hide": false, + "instant": false, + "legendFormat": "{{quantile}} percentile", + "range": true, + "refId": "B" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "expr": "reth_storage_providers_database_duration{action=\"insert hashes\", quantile=\"0.95\"} ", + "hide": false, + "instant": false, + "legendFormat": "{{quantile}} percentile", + "range": true, + "refId": "C" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "expr": "reth_storage_providers_database_duration{action=\"insert hashes\", quantile=\"0.99\"} ", + "hide": false, + "instant": false, + "legendFormat": "{{quantile}} percentile", + "range": true, + "refId": "D" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "exemplar": false, + "expr": "reth_storage_providers_database_duration{action=\"insert hashes\", quantile=\"0.999\"} ", + "hide": false, + "instant": false, + "legendFormat": "{{quantile}} percentile", + "range": true, + "refId": "E" + } + ], + "title": "Insert Hashes", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "description": "", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "ms" + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 278 + }, + "id": 173, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "expr": "reth_storage_providers_database_duration{action=\"insert history indices\", quantile=\"0.5\"}", + "instant": false, + "legendFormat": "{{quantile}} percentile", + "range": true, + "refId": "A" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "expr": "reth_storage_providers_database_duration{action=\"insert history indices\", quantile=\"0.9\"} ", + "hide": false, + "instant": false, + "legendFormat": "{{quantile}} percentile", + "range": true, + "refId": "B" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "expr": "reth_storage_providers_database_duration{action=\"insert history indices\", quantile=\"0.95\"} ", + "hide": false, + "instant": false, + "legendFormat": "{{quantile}} percentile", + "range": true, + "refId": "C" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "expr": "reth_storage_providers_database_duration{action=\"insert history indices\", quantile=\"0.99\"} ", + "hide": false, + "instant": false, + "legendFormat": "{{quantile}} percentile", + "range": true, + "refId": "D" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "exemplar": false, + "expr": "reth_storage_providers_database_duration{action=\"insert history indices\", quantile=\"0.999\"} ", + "hide": false, + "instant": false, + "legendFormat": "{{quantile}} percentile", + "range": true, + "refId": "E" + } + ], + "title": "Insert History Indices", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "description": "", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "ms" + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 278 + }, + "id": 174, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "expr": "reth_storage_providers_database_duration{action=\"update pipeline stages\", quantile=\"0.5\"}", + "instant": false, + "legendFormat": "{{quantile}} percentile", + "range": true, + "refId": "A" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "expr": "reth_storage_providers_database_duration{action=\"update pipeline stages\", quantile=\"0.9\"} ", + "hide": false, + "instant": false, + "legendFormat": "{{quantile}} percentile", + "range": true, + "refId": "B" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "expr": "reth_storage_providers_database_duration{action=\"update pipeline stages\", quantile=\"0.95\"} ", + "hide": false, + "instant": false, + "legendFormat": "{{quantile}} percentile", + "range": true, + "refId": "C" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "expr": "reth_storage_providers_database_duration{action=\"update pipeline stages\", quantile=\"0.99\"} ", + "hide": false, + "instant": false, + "legendFormat": "{{quantile}} percentile", + "range": true, + "refId": "D" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "exemplar": false, + "expr": "reth_storage_providers_database_duration{action=\"update pipeline stages\", quantile=\"0.999\"} ", + "hide": false, + "instant": false, + "legendFormat": "{{quantile}} percentile", + "range": true, + "refId": "E" + } + ], + "title": "Update Pipeline Stages", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "description": "", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "ms" + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 286 + }, + "id": 175, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "expr": "reth_storage_providers_database_duration{action=\"insert canonical headers\", quantile=\"0.5\"}", + "instant": false, + "legendFormat": "{{quantile}} percentile", + "range": true, + "refId": "A" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "expr": "reth_storage_providers_database_duration{action=\"insert canonical headers\", quantile=\"0.9\"} ", + "hide": false, + "instant": false, + "legendFormat": "{{quantile}} percentile", + "range": true, + "refId": "B" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "expr": "reth_storage_providers_database_duration{action=\"insert canonical headers\", quantile=\"0.95\"} ", + "hide": false, + "instant": false, + "legendFormat": "{{quantile}} percentile", + "range": true, + "refId": "C" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "expr": "reth_storage_providers_database_duration{action=\"insert canonical headers\", quantile=\"0.99\"} ", + "hide": false, + "instant": false, + "legendFormat": "{{quantile}} percentile", + "range": true, + "refId": "D" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "exemplar": false, + "expr": "reth_storage_providers_database_duration{action=\"insert canonical headers\", quantile=\"0.999\"} ", + "hide": false, + "instant": false, + "legendFormat": "{{quantile}} percentile", + "range": true, + "refId": "E" + } + ], + "title": "Insert Canonical Headers", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "description": "", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "ms" + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 286 + }, + "id": 176, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "expr": "reth_storage_providers_database_duration{action=\"insert headers\", quantile=\"0.5\"}", + "instant": false, + "legendFormat": "{{quantile}} percentile", + "range": true, + "refId": "A" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "expr": "reth_storage_providers_database_duration{action=\"insert headers\", quantile=\"0.9\"} ", + "hide": false, + "instant": false, + "legendFormat": "{{quantile}} percentile", + "range": true, + "refId": "B" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "expr": "reth_storage_providers_database_duration{action=\"insert headers\", quantile=\"0.95\"} ", + "hide": false, + "instant": false, + "legendFormat": "{{quantile}} percentile", + "range": true, + "refId": "C" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "expr": "reth_storage_providers_database_duration{action=\"insert headers\", quantile=\"0.99\"} ", + "hide": false, + "instant": false, + "legendFormat": "{{quantile}} percentile", + "range": true, + "refId": "D" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "exemplar": false, + "expr": "reth_storage_providers_database_duration{action=\"insert headers\", quantile=\"0.999\"} ", + "hide": false, + "instant": false, + "legendFormat": "{{quantile}} percentile", + "range": true, + "refId": "E" + } + ], + "title": "Insert Headers", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "description": "", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "ms" + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 294 + }, + "id": 177, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "expr": "reth_storage_providers_database_duration{action=\"insert header numbers\", quantile=\"0.5\"}", + "instant": false, + "legendFormat": "{{quantile}} percentile", + "range": true, + "refId": "A" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "expr": "reth_storage_providers_database_duration{action=\"insert header numbers\", quantile=\"0.9\"} ", + "hide": false, + "instant": false, + "legendFormat": "{{quantile}} percentile", + "range": true, + "refId": "B" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "expr": "reth_storage_providers_database_duration{action=\"insert header numbers\", quantile=\"0.95\"} ", + "hide": false, + "instant": false, + "legendFormat": "{{quantile}} percentile", + "range": true, + "refId": "C" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "expr": "reth_storage_providers_database_duration{action=\"insert header numbers\", quantile=\"0.99\"} ", + "hide": false, + "instant": false, + "legendFormat": "{{quantile}} percentile", + "range": true, + "refId": "D" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "exemplar": false, + "expr": "reth_storage_providers_database_duration{action=\"insert header numbers\", quantile=\"0.999\"} ", + "hide": false, + "instant": false, + "legendFormat": "{{quantile}} percentile", + "range": true, + "refId": "E" + } + ], + "title": "Insert Header Numbers", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "description": "", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "ms" + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 294 + }, + "id": 178, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "expr": "reth_storage_providers_database_duration{action=\"insert header TD\", quantile=\"0.5\"}", + "instant": false, + "legendFormat": "{{quantile}} percentile", + "range": true, + "refId": "A" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "expr": "reth_storage_providers_database_duration{action=\"insert headers numbers\", quantile=\"0.9\"} ", + "hide": false, + "instant": false, + "legendFormat": "{{quantile}} percentile", + "range": true, + "refId": "B" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "expr": "reth_storage_providers_database_duration{action=\"insert header TD\", quantile=\"0.95\"} ", + "hide": false, + "instant": false, + "legendFormat": "{{quantile}} percentile", + "range": true, + "refId": "C" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "expr": "reth_storage_providers_database_duration{action=\"insert header TD\", quantile=\"0.99\"} ", + "hide": false, + "instant": false, + "legendFormat": "{{quantile}} percentile", + "range": true, + "refId": "D" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "exemplar": false, + "expr": "reth_storage_providers_database_duration{action=\"insert header TD\", quantile=\"0.999\"} ", + "hide": false, + "instant": false, + "legendFormat": "{{quantile}} percentile", + "range": true, + "refId": "E" + } + ], + "title": "Insert Header TD", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "description": "", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "ms" + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 302 + }, + "id": 179, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "expr": "reth_storage_providers_database_duration{action=\"insert block ommers\", quantile=\"0.5\"}", + "instant": false, + "legendFormat": "{{quantile}} percentile", + "range": true, + "refId": "A" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "expr": "reth_storage_providers_database_duration{action=\"insert block ommers\", quantile=\"0.9\"} ", + "hide": false, + "instant": false, + "legendFormat": "{{quantile}} percentile", + "range": true, + "refId": "B" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "expr": "reth_storage_providers_database_duration{action=\"insert block ommers\", quantile=\"0.95\"} ", + "hide": false, + "instant": false, + "legendFormat": "{{quantile}} percentile", + "range": true, + "refId": "C" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "expr": "reth_storage_providers_database_duration{action=\"insert block ommers\", quantile=\"0.99\"} ", + "hide": false, + "instant": false, + "legendFormat": "{{quantile}} percentile", + "range": true, + "refId": "D" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "exemplar": false, + "expr": "reth_storage_providers_database_duration{action=\"insert block ommers\", quantile=\"0.999\"} ", + "hide": false, + "instant": false, + "legendFormat": "{{quantile}} percentile", + "range": true, + "refId": "E" + } + ], + "title": "Insert Block Ommers", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "description": "", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "ms" + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 302 + }, + "id": 180, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "expr": "reth_storage_providers_database_duration{action=\"insert tx senders\", quantile=\"0.5\"}", + "instant": false, + "legendFormat": "{{quantile}} percentile", + "range": true, + "refId": "A" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "expr": "reth_storage_providers_database_duration{action=\"insert tx senders\", quantile=\"0.9\"} ", + "hide": false, + "instant": false, + "legendFormat": "{{quantile}} percentile", + "range": true, + "refId": "B" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "expr": "reth_storage_providers_database_duration{action=\"insert tx senders\", quantile=\"0.95\"} ", + "hide": false, + "instant": false, + "legendFormat": "{{quantile}} percentile", + "range": true, + "refId": "C" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "expr": "reth_storage_providers_database_duration{action=\"insert tx senders\", quantile=\"0.99\"} ", + "hide": false, + "instant": false, + "legendFormat": "{{quantile}} percentile", + "range": true, + "refId": "D" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "exemplar": false, + "expr": "reth_storage_providers_database_duration{action=\"insert tx senders\", quantile=\"0.999\"} ", + "hide": false, + "instant": false, + "legendFormat": "{{quantile}} percentile", + "range": true, + "refId": "E" + } + ], + "title": "Insert Tx Senders", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "description": "", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "ms" + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 310 + }, + "id": 181, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "expr": "reth_storage_providers_database_duration{action=\"insert transactions\", quantile=\"0.5\"}", + "instant": false, + "legendFormat": "{{quantile}} percentile", + "range": true, + "refId": "A" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "expr": "reth_storage_providers_database_duration{action=\"insert transactions\", quantile=\"0.9\"} ", + "hide": false, + "instant": false, + "legendFormat": "{{quantile}} percentile", + "range": true, + "refId": "B" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "expr": "reth_storage_providers_database_duration{action=\"insert transactions\", quantile=\"0.95\"} ", + "hide": false, + "instant": false, + "legendFormat": "{{quantile}} percentile", + "range": true, + "refId": "C" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "expr": "reth_storage_providers_database_duration{action=\"insert transactions\", quantile=\"0.99\"} ", + "hide": false, + "instant": false, + "legendFormat": "{{quantile}} percentile", + "range": true, + "refId": "D" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "exemplar": false, + "expr": "reth_storage_providers_database_duration{action=\"insert transactions\", quantile=\"0.999\"} ", + "hide": false, + "instant": false, + "legendFormat": "{{quantile}} percentile", + "range": true, + "refId": "E" + } + ], + "title": "Insert Transactions", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "description": "", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "ms" + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 310 + }, + "id": 182, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "expr": "reth_storage_providers_database_duration{action=\"insert tx hash numbers\", quantile=\"0.5\"}", + "instant": false, + "legendFormat": "{{quantile}} percentile", + "range": true, + "refId": "A" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "expr": "reth_storage_providers_database_duration{action=\"insert tx hash numbers\", quantile=\"0.9\"} ", + "hide": false, + "instant": false, + "legendFormat": "{{quantile}} percentile", + "range": true, + "refId": "B" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "expr": "reth_storage_providers_database_duration{action=\"insert tx hash numbers\", quantile=\"0.95\"} ", + "hide": false, + "instant": false, + "legendFormat": "{{quantile}} percentile", + "range": true, + "refId": "C" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "expr": "reth_storage_providers_database_duration{action=\"insert tx hash numbers\", quantile=\"0.99\"} ", + "hide": false, + "instant": false, + "legendFormat": "{{quantile}} percentile", + "range": true, + "refId": "D" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "exemplar": false, + "expr": "reth_storage_providers_database_duration{action=\"insert tx hash numbers\", quantile=\"0.999\"} ", + "hide": false, + "instant": false, + "legendFormat": "{{quantile}} percentile", + "range": true, + "refId": "E" + } + ], + "title": "Insert Tx Hash Numbers", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "description": "", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "ms" + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 318 + }, + "id": 183, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "expr": "reth_storage_providers_database_duration{action=\"insert block withdrawals\", quantile=\"0.5\"}", + "instant": false, + "legendFormat": "{{quantile}} percentile", + "range": true, + "refId": "A" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "expr": "reth_storage_providers_database_duration{action=\"insert block withdrawals\", quantile=\"0.9\"} ", + "hide": false, + "instant": false, + "legendFormat": "{{quantile}} percentile", + "range": true, + "refId": "B" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "expr": "reth_storage_providers_database_duration{action=\"insert block withdrawals\", quantile=\"0.95\"} ", + "hide": false, + "instant": false, + "legendFormat": "{{quantile}} percentile", + "range": true, + "refId": "C" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "expr": "reth_storage_providers_database_duration{action=\"insert block withdrawals\", quantile=\"0.99\"} ", + "hide": false, + "instant": false, + "legendFormat": "{{quantile}} percentile", + "range": true, + "refId": "D" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "exemplar": false, + "expr": "reth_storage_providers_database_duration{action=\"insert block withdrawals\", quantile=\"0.999\"} ", + "hide": false, + "instant": false, + "legendFormat": "{{quantile}} percentile", + "range": true, + "refId": "E" + } + ], + "title": "Insert Block Withdrawals", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "description": "", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "ms" + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 318 + }, + "id": 184, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "expr": "reth_storage_providers_database_duration{action=\"insert block body indices\", quantile=\"0.5\"}", + "instant": false, + "legendFormat": "{{quantile}} percentile", + "range": true, + "refId": "A" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "expr": "reth_storage_providers_database_duration{action=\"insert block body indices\", quantile=\"0.9\"} ", + "hide": false, + "instant": false, + "legendFormat": "{{quantile}} percentile", + "range": true, + "refId": "B" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "expr": "reth_storage_providers_database_duration{action=\"insert block body indices\", quantile=\"0.95\"} ", + "hide": false, + "instant": false, + "legendFormat": "{{quantile}} percentile", + "range": true, + "refId": "C" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "expr": "reth_storage_providers_database_duration{action=\"insert block body indices\", quantile=\"0.99\"} ", + "hide": false, + "instant": false, + "legendFormat": "{{quantile}} percentile", + "range": true, + "refId": "D" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "exemplar": false, + "expr": "reth_storage_providers_database_duration{action=\"insert block body indices\", quantile=\"0.999\"} ", + "hide": false, + "instant": false, + "legendFormat": "{{quantile}} percentile", + "range": true, + "refId": "E" + } + ], + "title": "Insert Block Body Indices", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "description": "", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "ms" + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 326 + }, + "id": 185, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "expr": "reth_storage_providers_database_duration{action=\"insert transaction block\", quantile=\"0.5\"}", + "instant": false, + "legendFormat": "{{quantile}} percentile", + "range": true, + "refId": "A" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "expr": "reth_storage_providers_database_duration{action=\"insert transaction block\", quantile=\"0.9\"} ", + "hide": false, + "instant": false, + "legendFormat": "{{quantile}} percentile", + "range": true, + "refId": "B" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "expr": "reth_storage_providers_database_duration{action=\"insert transaction block\", quantile=\"0.95\"} ", + "hide": false, + "instant": false, + "legendFormat": "{{quantile}} percentile", + "range": true, + "refId": "C" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "expr": "reth_storage_providers_database_duration{action=\"insert transaction block\", quantile=\"0.99\"} ", + "hide": false, + "instant": false, + "legendFormat": "{{quantile}} percentile", + "range": true, + "refId": "D" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "exemplar": false, + "expr": "reth_storage_providers_database_duration{action=\"insert transaction block\", quantile=\"0.999\"} ", + "hide": false, + "instant": false, + "legendFormat": "{{quantile}} percentile", + "range": true, + "refId": "E" + } + ], + "title": "Insert Transaction Block", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "description": "", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "ms" + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 326 + }, + "id": 186, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "expr": "reth_storage_providers_database_duration{action=\"recover signers\", quantile=\"0.5\"}", + "instant": false, + "legendFormat": "{{quantile}} percentile", + "range": true, + "refId": "A" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "expr": "reth_storage_providers_database_duration{action=\"recover signers\", quantile=\"0.9\"} ", + "hide": false, + "instant": false, + "legendFormat": "{{quantile}} percentile", + "range": true, + "refId": "B" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "expr": "reth_storage_providers_database_duration{action=\"recover signers\", quantile=\"0.95\"} ", + "hide": false, + "instant": false, + "legendFormat": "{{quantile}} percentile", + "range": true, + "refId": "C" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "expr": "reth_storage_providers_database_duration{action=\"recover signers\", quantile=\"0.99\"} ", + "hide": false, + "instant": false, + "legendFormat": "{{quantile}} percentile", + "range": true, + "refId": "D" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "exemplar": false, + "expr": "reth_storage_providers_database_duration{action=\"recover signers\", quantile=\"0.999\"} ", + "hide": false, + "instant": false, + "legendFormat": "{{quantile}} percentile", + "range": true, + "refId": "E" + } + ], + "title": "Recover Signers", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "description": "", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "ms" + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 334 + }, + "id": 187, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "expr": "reth_storage_providers_database_duration{action=\"get next tx num\", quantile=\"0.5\"}", + "instant": false, + "legendFormat": "{{quantile}} percentile", + "range": true, + "refId": "A" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "expr": "reth_storage_providers_database_duration{action=\"get next tx num\", quantile=\"0.9\"} ", + "hide": false, + "instant": false, + "legendFormat": "{{quantile}} percentile", + "range": true, + "refId": "B" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "expr": "reth_storage_providers_database_duration{action=\"get next tx num\", quantile=\"0.95\"} ", + "hide": false, + "instant": false, + "legendFormat": "{{quantile}} percentile", + "range": true, + "refId": "C" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "expr": "reth_storage_providers_database_duration{action=\"get next tx num\", quantile=\"0.99\"} ", + "hide": false, + "instant": false, + "legendFormat": "{{quantile}} percentile", + "range": true, + "refId": "D" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "exemplar": false, + "expr": "reth_storage_providers_database_duration{action=\"get next tx num\", quantile=\"0.999\"} ", + "hide": false, + "instant": false, + "legendFormat": "{{quantile}} percentile", + "range": true, + "refId": "E" + } + ], + "title": "Get Next Tx Num", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "description": "", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "ms" + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 334 + }, + "id": 188, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "expr": "reth_storage_providers_database_duration{action=\"get parent TD\", quantile=\"0.5\"}", + "instant": false, + "legendFormat": "{{quantile}} percentile", + "range": true, + "refId": "A" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "expr": "reth_storage_providers_database_duration{action=\"get parent TD\", quantile=\"0.9\"} ", + "hide": false, + "instant": false, + "legendFormat": "{{quantile}} percentile", + "range": true, + "refId": "B" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "expr": "reth_storage_providers_database_duration{action=\"get parent TD\", quantile=\"0.95\"} ", + "hide": false, + "instant": false, + "legendFormat": "{{quantile}} percentile", + "range": true, + "refId": "C" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "expr": "reth_storage_providers_database_duration{action=\"get parent TD\", quantile=\"0.99\"} ", + "hide": false, + "instant": false, + "legendFormat": "{{quantile}} percentile", + "range": true, + "refId": "D" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "exemplar": false, + "expr": "reth_storage_providers_database_duration{action=\"get parent TD\", quantile=\"0.999\"} ", + "hide": false, + "instant": false, + "legendFormat": "{{quantile}} percentile", + "range": true, + "refId": "E" + } + ], + "title": "Get Parent TD", + "type": "timeseries" + }, + { + "collapsed": false, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 342 + }, + "id": 133, + "panels": [], + "repeat": "instance", + "repeatDirection": "h", + "title": "Blockchain Tree - Canonicalization Metrics", + "type": "row" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "description": "", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "ms" + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 343 + }, + "id": 158, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "expr": "reth_blockchain_tree_make_canonical_duration{action=\"clone old blocks\", quantile=\"0.5\"} ", + "instant": false, + "legendFormat": "{{quantile}} percentile", + "range": true, + "refId": "A" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "expr": "reth_blockchain_tree_make_canonical_duration{action=\"clone old blocks\", quantile=\"0.9\"} ", + "hide": false, + "instant": false, + "legendFormat": "{{quantile}} percentile", + "range": true, + "refId": "B" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "expr": "reth_blockchain_tree_make_canonical_duration{action=\"clone old blocks\", quantile=\"0.95\"} ", + "hide": false, + "instant": false, + "legendFormat": "{{quantile}} percentile", + "range": true, + "refId": "C" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "expr": "reth_blockchain_tree_make_canonical_duration{action=\"clone old blocks\", quantile=\"0.99\"} ", + "hide": false, + "instant": false, + "legendFormat": "{{quantile}} percentile", + "range": true, + "refId": "D" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "exemplar": false, + "expr": "reth_blockchain_tree_make_canonical_duration{action=\"clone old blocks\", quantile=\"0.999\"} ", + "hide": false, + "instant": false, + "legendFormat": "{{quantile}} percentile", + "range": true, + "refId": "E" + } + ], + "title": "Clone Old Blocks", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "description": "", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "ms" + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 343 + }, + "id": 159, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "expr": "reth_blockchain_tree_make_canonical_duration{action=\"find canonical header\", quantile=\"0.5\"} ", + "instant": false, + "legendFormat": "{{quantile}} percentile", + "range": true, + "refId": "A" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "expr": "reth_blockchain_tree_make_canonical_duration{action=\"find canonical header\", quantile=\"0.9\"} ", + "hide": false, + "instant": false, + "legendFormat": "{{quantile}} percentile", + "range": true, + "refId": "B" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "expr": "reth_blockchain_tree_make_canonical_duration{action=\"find canonical header\", quantile=\"0.95\"} ", + "hide": false, + "instant": false, + "legendFormat": "{{quantile}} percentile", + "range": true, + "refId": "C" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "expr": "reth_blockchain_tree_make_canonical_duration{action=\"find canonical header\", quantile=\"0.99\"} ", + "hide": false, + "instant": false, + "legendFormat": "{{quantile}} percentile", + "range": true, + "refId": "D" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "exemplar": false, + "expr": "reth_blockchain_tree_make_canonical_duration{action=\"find canonical header\", quantile=\"0.999\"} ", + "hide": false, + "instant": false, + "legendFormat": "{{quantile}} percentile", + "range": true, + "refId": "E" + } + ], + "title": "Find Canonical Header", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "description": "", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "ms" + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 351 + }, + "id": 160, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "expr": "reth_blockchain_tree_make_canonical_duration{action=\"split chain\", quantile=\"0.5\"} ", + "instant": false, + "legendFormat": "{{quantile}} percentile", + "range": true, + "refId": "A" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "expr": "reth_blockchain_tree_make_canonical_duration{action=\"split chain\", quantile=\"0.9\"} ", + "hide": false, + "instant": false, + "legendFormat": "{{quantile}} percentile", + "range": true, + "refId": "B" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "expr": "reth_blockchain_tree_make_canonical_duration{action=\"split chain\", quantile=\"0.95\"} ", + "hide": false, + "instant": false, + "legendFormat": "{{quantile}} percentile", + "range": true, + "refId": "C" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "expr": "reth_blockchain_tree_make_canonical_duration{action=\"split chain\", quantile=\"0.99\"} ", + "hide": false, + "instant": false, + "legendFormat": "{{quantile}} percentile", + "range": true, + "refId": "D" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "exemplar": false, + "expr": "reth_blockchain_tree_make_canonical_duration{action=\"split chain\", quantile=\"0.999\"} ", + "hide": false, + "instant": false, + "legendFormat": "{{quantile}} percentile", + "range": true, + "refId": "E" + } + ], + "title": "Split Chain", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "description": "", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "ms" + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 351 + }, + "id": 162, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "expr": "reth_blockchain_tree_make_canonical_duration{action=\"split chain forks\", quantile=\"0.5\"} ", + "instant": false, + "legendFormat": "{{quantile}} percentile", + "range": true, + "refId": "A" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "expr": "reth_blockchain_tree_make_canonical_duration{action=\"split chain forks\", quantile=\"0.9\"} ", + "hide": false, + "instant": false, + "legendFormat": "{{quantile}} percentile", + "range": true, + "refId": "B" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "expr": "reth_blockchain_tree_make_canonical_duration{action=\"split chain forks\", quantile=\"0.95\"} ", + "hide": false, + "instant": false, + "legendFormat": "{{quantile}} percentile", + "range": true, + "refId": "C" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "expr": "reth_blockchain_tree_make_canonical_duration{action=\"split chain forks\", quantile=\"0.99\"} ", + "hide": false, + "instant": false, + "legendFormat": "{{quantile}} percentile", + "range": true, + "refId": "D" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "exemplar": false, + "expr": "reth_blockchain_tree_make_canonical_duration{action=\"split chain forks\", quantile=\"0.999\"} ", + "hide": false, + "instant": false, + "legendFormat": "{{quantile}} percentile", + "range": true, + "refId": "E" + } + ], + "title": "Split Chain Forks", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "description": "", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "ms" + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 359 + }, + "id": 163, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "expr": "reth_blockchain_tree_make_canonical_duration{action=\"merge all chains\", quantile=\"0.5\"} ", + "instant": false, + "legendFormat": "{{quantile}} percentile", + "range": true, + "refId": "A" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "expr": "reth_blockchain_tree_make_canonical_duration{action=\"merge all chains\", quantile=\"0.9\"} ", + "hide": false, + "instant": false, + "legendFormat": "{{quantile}} percentile", + "range": true, + "refId": "B" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "expr": "reth_blockchain_tree_make_canonical_duration{action=\"merge all chains\", quantile=\"0.95\"} ", + "hide": false, + "instant": false, + "legendFormat": "{{quantile}} percentile", + "range": true, + "refId": "C" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "expr": "reth_blockchain_tree_make_canonical_duration{action=\"merge all chains\", quantile=\"0.99\"} ", + "hide": false, + "instant": false, + "legendFormat": "{{quantile}} percentile", + "range": true, + "refId": "D" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "exemplar": false, + "expr": "reth_blockchain_tree_make_canonical_duration{action=\"merge all chains\", quantile=\"0.999\"} ", + "hide": false, + "instant": false, + "legendFormat": "{{quantile}} percentile", + "range": true, + "refId": "E" + } + ], + "title": "Merge All Chains", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "description": "", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "ms" + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 359 + }, + "id": 164, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "expr": "reth_blockchain_tree_make_canonical_duration{action=\"update canonical index\", quantile=\"0.5\"} ", + "instant": false, + "legendFormat": "{{quantile}} percentile", + "range": true, + "refId": "A" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "expr": "reth_blockchain_tree_make_canonical_duration{action=\"update canonical index\", quantile=\"0.9\"} ", + "hide": false, + "instant": false, + "legendFormat": "{{quantile}} percentile", + "range": true, + "refId": "B" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "expr": "reth_blockchain_tree_make_canonical_duration{action=\"update canonical index\", quantile=\"0.95\"} ", + "hide": false, + "instant": false, + "legendFormat": "{{quantile}} percentile", + "range": true, + "refId": "C" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "expr": "reth_blockchain_tree_make_canonical_duration{action=\"update canonical index\", quantile=\"0.99\"} ", + "hide": false, + "instant": false, + "legendFormat": "{{quantile}} percentile", + "range": true, + "refId": "D" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "exemplar": false, + "expr": "reth_blockchain_tree_make_canonical_duration{action=\"update canonical index\", quantile=\"0.999\"} ", + "hide": false, + "instant": false, + "legendFormat": "{{quantile}} percentile", + "range": true, + "refId": "E" + } + ], + "title": "Update Canonical Index", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "description": "", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "ms" + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 367 + }, + "id": 165, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "expr": "reth_blockchain_tree_make_canonical_duration{action=\"commit canonical chain to database\", quantile=\"0.5\"} ", + "instant": false, + "legendFormat": "{{quantile}} percentile", + "range": true, + "refId": "A" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "expr": "reth_blockchain_tree_make_canonical_duration{action=\"commit canonical chain to database\", quantile=\"0.9\"} ", + "hide": false, + "instant": false, + "legendFormat": "{{quantile}} percentile", + "range": true, + "refId": "B" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "expr": "reth_blockchain_tree_make_canonical_duration{action=\"commit canonical chain to database\", quantile=\"0.95\"} ", + "hide": false, + "instant": false, + "legendFormat": "{{quantile}} percentile", + "range": true, + "refId": "C" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "expr": "reth_blockchain_tree_make_canonical_duration{action=\"commit canonical chain to database\", quantile=\"0.99\"} ", + "hide": false, + "instant": false, + "legendFormat": "{{quantile}} percentile", + "range": true, + "refId": "D" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "exemplar": false, + "expr": "reth_blockchain_tree_make_canonical_duration{action=\"commit canonical chain to database\", quantile=\"0.999\"} ", + "hide": false, + "instant": false, + "legendFormat": "{{quantile}} percentile", + "range": true, + "refId": "E" + } + ], + "title": "Commit Canonical Chain to Database", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "description": "", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "ms" + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 367 + }, + "id": 166, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "expr": "reth_blockchain_tree_make_canonical_duration{action=\"revert canonical chain from database\", quantile=\"0.5\"} ", + "instant": false, + "legendFormat": "{{quantile}} percentile", + "range": true, + "refId": "A" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "expr": "reth_blockchain_tree_make_canonical_duration{action=\"revert canonical chain from database\", quantile=\"0.9\"} ", + "hide": false, + "instant": false, + "legendFormat": "{{quantile}} percentile", + "range": true, + "refId": "B" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "expr": "reth_blockchain_tree_make_canonical_duration{action=\"revert canonical chain from database\", quantile=\"0.95\"} ", + "hide": false, + "instant": false, + "legendFormat": "{{quantile}} percentile", + "range": true, + "refId": "C" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "expr": "reth_blockchain_tree_make_canonical_duration{action=\"revert canonical chain from database\", quantile=\"0.99\"} ", + "hide": false, + "instant": false, + "legendFormat": "{{quantile}} percentile", + "range": true, + "refId": "D" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "exemplar": false, + "expr": "reth_blockchain_tree_make_canonical_duration{action=\"revert canonical chain from database\", quantile=\"0.999\"} ", + "hide": false, + "instant": false, + "legendFormat": "{{quantile}} percentile", + "range": true, + "refId": "E" + } + ], + "title": "Revert Canonical Chain From Database", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "description": "", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "ms" + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 375 + }, + "id": 161, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "expr": "reth_blockchain_tree_make_canonical_duration{action=\"insert old canonical chain\", quantile=\"0.5\"} ", + "instant": false, + "legendFormat": "{{quantile}} percentile", + "range": true, + "refId": "A" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "expr": "reth_blockchain_tree_make_canonical_duration{action=\"insert old canonical chain\", quantile=\"0.9\"} ", + "hide": false, + "instant": false, + "legendFormat": "{{quantile}} percentile", + "range": true, + "refId": "B" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "expr": "reth_blockchain_tree_make_canonical_duration{action=\"insert old canonical chain\", quantile=\"0.95\"} ", + "hide": false, + "instant": false, + "legendFormat": "{{quantile}} percentile", + "range": true, + "refId": "C" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "expr": "reth_blockchain_tree_make_canonical_duration{action=\"insert old canonical chain\", quantile=\"0.99\"} ", + "hide": false, + "instant": false, + "legendFormat": "{{quantile}} percentile", + "range": true, + "refId": "D" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "exemplar": false, + "expr": "reth_blockchain_tree_make_canonical_duration{action=\"insert old canonical chain\", quantile=\"0.999\"} ", + "hide": false, + "instant": false, + "legendFormat": "{{quantile}} percentile", + "range": true, + "refId": "E" + } + ], + "title": "Insert Old Canonical Chain", + "type": "timeseries" } ], "refresh": "30s", @@ -6386,4 +11033,4 @@ "uid": "2k8BXz24x", "version": 12, "weekStart": "" -} \ No newline at end of file +} From ba544d746cf3913dc65249be94dbbb58de8c449c Mon Sep 17 00:00:00 2001 From: Matthias Seitz Date: Fri, 1 Dec 2023 19:05:44 +0100 Subject: [PATCH 076/277] fix: invert is error check (#5657) --- crates/revm/revm-inspectors/src/tracing/types.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/revm/revm-inspectors/src/tracing/types.rs b/crates/revm/revm-inspectors/src/tracing/types.rs index b5e6415bf435..7f10a204f338 100644 --- a/crates/revm/revm-inspectors/src/tracing/types.rs +++ b/crates/revm/revm-inspectors/src/tracing/types.rs @@ -61,7 +61,7 @@ impl CallTrace { /// Returns true if the status code is an error or revert, See [InstructionResult::Revert] #[inline] pub fn is_error(&self) -> bool { - self.status.is_error() + !self.status.is_ok() } /// Returns true if the status code is a revert From a53e2c0666c2a4c7d4a80170d60164b45183aaa2 Mon Sep 17 00:00:00 2001 From: Dan Cline <6798349+Rjected@users.noreply.github.com> Date: Fri, 1 Dec 2023 17:07:53 -0500 Subject: [PATCH 077/277] feat: add Default for NetworkArgs and DiscoveryArgs (#5658) --- bin/reth/src/args/network_args.rs | 32 +++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/bin/reth/src/args/network_args.rs b/bin/reth/src/args/network_args.rs index 44fd62282599..387096aa5b9f 100644 --- a/bin/reth/src/args/network_args.rs +++ b/bin/reth/src/args/network_args.rs @@ -125,6 +125,26 @@ impl NetworkArgs { } } +impl Default for NetworkArgs { + fn default() -> Self { + Self { + discovery: DiscoveryArgs::default(), + trusted_peers: vec![], + trusted_only: false, + bootnodes: None, + peers_file: None, + identity: P2P_CLIENT_VERSION.to_string(), + p2p_secret_key: None, + no_persist_peers: false, + nat: NatResolver::Any, + addr: DEFAULT_DISCOVERY_ADDR, + port: DEFAULT_DISCOVERY_PORT, + max_outbound_peers: None, + max_inbound_peers: None, + } + } +} + /// Arguments to setup discovery #[derive(Debug, Args)] pub struct DiscoveryArgs { @@ -166,6 +186,18 @@ impl DiscoveryArgs { } } +impl Default for DiscoveryArgs { + fn default() -> Self { + Self { + disable_discovery: false, + disable_dns_discovery: false, + disable_discv4_discovery: false, + addr: DEFAULT_DISCOVERY_ADDR, + port: DEFAULT_DISCOVERY_PORT, + } + } +} + #[cfg(test)] mod tests { use super::*; From 5ac4a3d4cb33fd2f6c9219f0da83088a4186da59 Mon Sep 17 00:00:00 2001 From: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Date: Fri, 1 Dec 2023 23:30:43 +0100 Subject: [PATCH 078/277] chore: simplify `Consensus::validate_header_range` with let-else (#5659) --- crates/interfaces/src/consensus.rs | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/crates/interfaces/src/consensus.rs b/crates/interfaces/src/consensus.rs index bdb1c04eaf87..a1412fa44499 100644 --- a/crates/interfaces/src/consensus.rs +++ b/crates/interfaces/src/consensus.rs @@ -37,18 +37,16 @@ pub trait Consensus: Debug + Send + Sync { /// /// Note: this expects that the headers are in natural order (ascending block number) fn validate_header_range(&self, headers: &[SealedHeader]) -> Result<(), ConsensusError> { - if headers.is_empty() { - return Ok(()) - } - let first = headers.first().expect("checked empty"); - self.validate_header(first)?; - let mut parent = first; - for child in headers.iter().skip(1) { + let mut headers = headers.iter(); + let Some(mut parent) = headers.next() else { + return Ok(()); + }; + self.validate_header(parent)?; + for child in headers { self.validate_header(child)?; self.validate_header_against_parent(child, parent)?; parent = child; } - Ok(()) } From 542639cc6ff6d3c54867d8844522d085c942fb6d Mon Sep 17 00:00:00 2001 From: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Date: Fri, 1 Dec 2023 23:45:15 +0100 Subject: [PATCH 079/277] perf(trie): use smallvec as the Nibbles representation (#5641) --- Cargo.lock | 5 + crates/primitives/Cargo.toml | 22 +- crates/primitives/benches/nibbles.rs | 56 ++- .../primitives/src/trie/hash_builder/mod.rs | 6 +- crates/primitives/src/trie/nibbles.rs | 418 +++++++++++++----- crates/primitives/src/trie/nodes/extension.rs | 5 +- crates/primitives/src/trie/nodes/leaf.rs | 13 +- crates/storage/codecs/src/lib.rs | 59 +-- crates/trie/benches/prefix_set.rs | 4 +- crates/trie/src/prefix_set/mod.rs | 12 +- crates/trie/src/trie_cursor/account_cursor.rs | 1 - crates/trie/src/trie_cursor/subnode.rs | 2 +- crates/trie/src/walker.rs | 16 +- 13 files changed, 454 insertions(+), 165 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index efff0ef3dba7..0a61aed99f31 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -6200,6 +6200,7 @@ dependencies = [ "serde", "serde_json", "sha2", + "smallvec", "strum", "sucds 0.6.0", "tempfile", @@ -7437,6 +7438,10 @@ name = "smallvec" version = "1.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4dccd0940a2dcdf68d092b8cbab7dc0ad8fa938bf95787e1b916b0e3d0e8e970" +dependencies = [ + "arbitrary", + "serde", +] [[package]] name = "smol_str" diff --git a/crates/primitives/Cargo.toml b/crates/primitives/Cargo.toml index 9487a1d46d77..eef82c7f025a 100644 --- a/crates/primitives/Cargo.toml +++ b/crates/primitives/Cargo.toml @@ -31,8 +31,9 @@ c-kzg = { workspace = true, features = ["serde"], optional = true } tracing.workspace = true # misc -byteorder = "1" +smallvec = { version = "1.11", features = ["arbitrary", "serde", "union", "const_new"] } bytes.workspace = true +byteorder = "1" clap = { workspace = true, features = ["derive"], optional = true } derive_more = "0.99" itertools = "0.11" @@ -82,16 +83,21 @@ secp256k1.workspace = true [features] default = ["c-kzg"] arbitrary = [ - "revm-primitives/arbitrary", - "reth-rpc-types/arbitrary", - "reth-ethereum-forks/arbitrary", - "dep:arbitrary", - "dep:proptest", - "dep:proptest-derive", + "revm-primitives/arbitrary", + "reth-rpc-types/arbitrary", + "reth-ethereum-forks/arbitrary", + "dep:arbitrary", + "dep:proptest", + "dep:proptest-derive", ] c-kzg = ["dep:c-kzg", "revm/c-kzg", "revm-primitives/c-kzg"] clap = ["dep:clap"] -optimism = ["reth-codecs/optimism", "revm-primitives/optimism", "reth-ethereum-forks/optimism", "revm/optimism"] +optimism = [ + "reth-codecs/optimism", + "revm-primitives/optimism", + "reth-ethereum-forks/optimism", + "revm/optimism", +] test-utils = ["dep:plain_hasher", "dep:hash-db", "dep:ethers-core"] [[bench]] diff --git a/crates/primitives/benches/nibbles.rs b/crates/primitives/benches/nibbles.rs index 3d839fd7a688..1db4f837882d 100644 --- a/crates/primitives/benches/nibbles.rs +++ b/crates/primitives/benches/nibbles.rs @@ -1,13 +1,63 @@ use criterion::{criterion_group, criterion_main, Criterion}; +use proptest::{prelude::*, strategy::ValueTree}; use reth_primitives::trie::Nibbles; +use std::{hint::black_box, time::Duration}; /// Benchmarks the nibble unpacking. pub fn nibbles_benchmark(c: &mut Criterion) { let mut g = c.benchmark_group("nibbles"); - g.bench_function("unpack", |b| { - let raw = (1..=32).collect::>(); - b.iter(|| Nibbles::unpack(&raw)) + g.warm_up_time(Duration::from_secs(1)); + g.noise_threshold(0.02); + + g.bench_function("unpack/32", |b| { + let bytes = get_bytes(32); + b.iter(|| Nibbles::unpack(black_box(&bytes[..]))) + }); + g.bench_function("unpack/256", |b| { + let bytes = get_bytes(256); + b.iter(|| Nibbles::unpack(black_box(&bytes[..]))) + }); + g.bench_function("unpack/2048", |b| { + let bytes = get_bytes(2048); + b.iter(|| Nibbles::unpack(black_box(&bytes[..]))) + }); + + g.bench_function("pack/32", |b| { + let nibbles = get_nibbles(32); + b.iter(|| black_box(&nibbles).pack()) + }); + g.bench_function("pack/256", |b| { + let nibbles = get_nibbles(256); + b.iter(|| black_box(&nibbles).pack()) }); + g.bench_function("pack/2048", |b| { + let nibbles = get_nibbles(2048); + b.iter(|| black_box(&nibbles).pack()) + }); + + g.bench_function("encode_path_leaf/31", |b| { + let nibbles = get_nibbles(31); + b.iter(|| black_box(&nibbles).encode_path_leaf(false)) + }); + g.bench_function("encode_path_leaf/256", |b| { + let nibbles = get_nibbles(256); + b.iter(|| black_box(&nibbles).encode_path_leaf(false)) + }); + g.bench_function("encode_path_leaf/2048", |b| { + let nibbles = get_nibbles(2048); + b.iter(|| black_box(&nibbles).encode_path_leaf(false)) + }); +} + +fn get_nibbles(len: usize) -> Nibbles { + Nibbles::from_nibbles_unchecked(get_bytes(len)) +} + +fn get_bytes(len: usize) -> Vec { + proptest::collection::vec(proptest::arbitrary::any::(), len) + .new_tree(&mut Default::default()) + .unwrap() + .current() } criterion_group!(benches, nibbles_benchmark); diff --git a/crates/primitives/src/trie/hash_builder/mod.rs b/crates/primitives/src/trie/hash_builder/mod.rs index 89559819ecbf..ed2c86de77ed 100644 --- a/crates/primitives/src/trie/hash_builder/mod.rs +++ b/crates/primitives/src/trie/hash_builder/mod.rs @@ -63,7 +63,7 @@ pub struct HashBuilder { impl From for HashBuilder { fn from(state: HashBuilderState) -> Self { Self { - key: Nibbles::new_unchecked(state.key), + key: Nibbles::from_nibbles_unchecked(state.key), stack: state.stack, value: state.value, groups: state.groups, @@ -564,7 +564,7 @@ mod tests { let (_, updates) = hb.split(); - let update = updates.get(&Nibbles::new_unchecked(hex!("01"))).unwrap(); + let update = updates.get(&Nibbles::from_nibbles_unchecked(hex!("01"))).unwrap(); assert_eq!(update.state_mask, TrieMask::new(0b1111)); // 1st nibble: 0, 1, 2, 3 assert_eq!(update.tree_mask, TrieMask::new(0)); assert_eq!(update.hash_mask, TrieMask::new(6)); // in the 1st nibble, the ones with 1 and 2 are branches with `hashes` @@ -634,7 +634,7 @@ mod tests { let mut hb2 = HashBuilder::default(); // Insert the branch with the `0x6` shared prefix. - hb2.add_branch(Nibbles::new_unchecked([0x6]), branch_node_hash, false); + hb2.add_branch(Nibbles::from_nibbles_unchecked([0x6]), branch_node_hash, false); let expected = trie_root(raw_input.clone()); assert_eq!(hb.root(), expected); diff --git a/crates/primitives/src/trie/nibbles.rs b/crates/primitives/src/trie/nibbles.rs index aa36f2955c69..7c6d92a2bfc8 100644 --- a/crates/primitives/src/trie/nibbles.rs +++ b/crates/primitives/src/trie/nibbles.rs @@ -1,9 +1,15 @@ use crate::Bytes; -use alloy_rlp::RlpEncodableWrapper; +use bytes::Buf; use derive_more::{Deref, From, Index}; -use reth_codecs::{main_codec, Compact}; +use reth_codecs::Compact; use serde::{Deserialize, Serialize}; -use std::{borrow::Borrow, ops::RangeBounds}; +use smallvec::SmallVec; +use std::{ + borrow::Borrow, + fmt, + mem::MaybeUninit, + ops::{Bound, RangeBounds}, +}; /// The representation of nibbles of the merkle trie stored in the database. #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, PartialOrd, Ord, Hash, Deref)] @@ -19,7 +25,7 @@ impl From for StoredNibblesSubKey { impl From> for StoredNibblesSubKey { #[inline] fn from(value: Vec) -> Self { - Self(Nibbles::new_unchecked(value)) + Self(Nibbles::from_nibbles_unchecked(value)) } } @@ -48,7 +54,7 @@ impl Compact for StoredNibblesSubKey { fn from_compact(buf: &[u8], _len: usize) -> (Self, &[u8]) { let len = buf[64] as usize; - (Self(Nibbles::new_unchecked(buf[..len].to_vec())), &buf[65..]) + (Self(Nibbles::from_nibbles_unchecked(&buf[..len])), &buf[65..]) } } @@ -58,44 +64,92 @@ impl Compact for StoredNibblesSubKey { /// the keys in a Merkle Patricia Trie (MPT). /// Using nibbles simplifies trie operations and enables consistent key representation in the MPT. /// -/// The internal representation is a shared heap-allocated vector ([`Bytes`]) that stores one nibble -/// per byte. This means that each byte has its upper 4 bits set to zero and the lower 4 bits -/// representing the nibble value. -#[main_codec] +/// The internal representation is a [`SmallVec`] that stores one nibble per byte. This means that +/// each byte has its upper 4 bits set to zero and the lower 4 bits representing the nibble value. #[derive( Clone, - Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash, - RlpEncodableWrapper, Index, From, Deref, + serde::Serialize, + serde::Deserialize, )] -pub struct Nibbles(Bytes); +#[cfg_attr(any(test, feature = "arbitrary"), derive(arbitrary::Arbitrary))] +pub struct Nibbles(SmallVec<[u8; 64]>); + +impl alloy_rlp::Encodable for Nibbles { + #[inline] + fn length(&self) -> usize { + alloy_rlp::Encodable::length(self.as_slice()) + } + + #[inline] + fn encode(&self, out: &mut dyn alloy_rlp::BufMut) { + alloy_rlp::Encodable::encode(self.as_slice(), out) + } +} + +#[cfg(any(test, feature = "arbitrary"))] +impl proptest::arbitrary::Arbitrary for Nibbles { + type Parameters = (); + type Strategy = proptest::strategy::Map< + proptest::collection::VecStrategy>, + fn(Vec) -> Self, + >; + + #[inline] + fn arbitrary_with((): ()) -> Self::Strategy { + use proptest::prelude::*; + proptest::collection::vec(0x0..=0xf, 0..64).prop_map(Self::from_nibbles_unchecked) + } +} + +impl Compact for Nibbles { + fn to_compact(self, buf: &mut B) -> usize + where + B: bytes::BufMut + AsMut<[u8]>, + { + buf.put_slice(self.as_slice()); + self.len() + } + + fn from_compact(mut buf: &[u8], len: usize) -> (Self, &[u8]) { + let nibbles = &buf[..len]; + buf.advance(len); + (Nibbles::from_nibbles_unchecked(nibbles), buf) + } +} + +impl fmt::Debug for Nibbles { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_tuple("Nibbles").field(&crate::hex::encode(self.as_slice())).finish() + } +} impl From> for Nibbles { #[inline] fn from(value: Vec) -> Self { - Self::new_unchecked(value) + Self(SmallVec::from_vec(value)) } } impl From for Vec { #[inline] fn from(value: Nibbles) -> Self { - value.0.into() + value.0.into_vec() } } impl From for Bytes { #[inline] fn from(value: Nibbles) -> Self { - value.into_bytes() + value.0.into_vec().into() } } @@ -134,23 +188,81 @@ impl Borrow<[u8]> for Nibbles { } } +impl Extend for Nibbles { + #[inline] + fn extend>(&mut self, iter: T) { + self.0.extend(iter) + } +} + impl Nibbles { + /// Creates a new empty [`Nibbles`] instance. + #[inline] + pub const fn new() -> Self { + Self(SmallVec::new_const()) + } + + /// Creates a new [`Nibbles`] instance with the given capacity. + #[inline] + pub fn with_capacity(capacity: usize) -> Self { + Self(SmallVec::with_capacity(capacity)) + } + /// Creates a new [`Nibbles`] instance from nibble bytes, without checking their validity. #[inline] - pub fn new_unchecked>(nibbles: T) -> Self { - Self(nibbles.into()) + pub fn from_nibbles_unchecked>(nibbles: T) -> Self { + Self(SmallVec::from_slice(nibbles.as_ref())) } /// Converts a byte slice into a [`Nibbles`] instance containing the nibbles (half-bytes or 4 /// bits) that make up the input byte data. + #[inline] pub fn unpack>(data: T) -> Self { let data = data.as_ref(); - let mut nibbles = Vec::with_capacity(data.len() * 2); - for &byte in data { - nibbles.push(byte >> 4); - nibbles.push(byte & 0x0f); + if data.len() <= 32 { + // SAFETY: checked length. + unsafe { Self::unpack_stack(data) } + } else { + Self::unpack_heap(data) + } + } + + /// Unpacks on the stack. + /// + /// # Safety + /// + /// `data.len()` must be less than or equal to 32. + unsafe fn unpack_stack(data: &[u8]) -> Self { + let mut nibbles = MaybeUninit::<[u8; 64]>::uninit(); + Self::unpack_to(data, nibbles.as_mut_ptr().cast()); + let unpacked_len = data.len() * 2; + Self(SmallVec::from_buf_and_len_unchecked(nibbles, unpacked_len)) + } + + /// Unpacks on the heap. + fn unpack_heap(data: &[u8]) -> Self { + // Collect into a vec directly to avoid the smallvec overhead since we know this is going on + // the heap. + let unpacked_len = data.len() * 2; + let mut nibbles = Vec::with_capacity(unpacked_len); + // SAFETY: enough capacity. + unsafe { Self::unpack_to(data, nibbles.as_mut_ptr()) }; + // SAFETY: within capacity and `unpack_to` initialized the memory. + unsafe { nibbles.set_len(unpacked_len) }; + Self(SmallVec::from_vec(nibbles)) + } + + /// Unpacks into the given pointer. + /// + /// # Safety + /// + /// `ptr` must be valid for at least `data.len() * 2` bytes. + #[inline] + unsafe fn unpack_to(data: &[u8], ptr: *mut u8) { + for (i, &byte) in data.iter().enumerate() { + ptr.add(i * 2).write(byte >> 4); + ptr.add(i * 2 + 1).write(byte & 0x0f); } - Self(nibbles.into()) } /// Packs the nibbles stored in the struct into a byte vector. @@ -159,15 +271,68 @@ impl Nibbles { /// effectively reducing the size of the data by a factor of two. /// If the number of nibbles is odd, the last nibble is shifted left by 4 bits and /// added to the packed byte vector. - pub fn pack(&self) -> Vec { + #[inline] + pub fn pack(&self) -> SmallVec<[u8; 32]> { + if self.len() <= 64 { + // SAFETY: checked length. + unsafe { self.pack_stack() } + } else { + self.pack_heap() + } + } + + /// Packs on the stack. + /// + /// # Safety + /// + /// `self.len()` must be less than or equal to 32. + unsafe fn pack_stack(&self) -> SmallVec<[u8; 32]> { + let mut nibbles = MaybeUninit::<[u8; 32]>::uninit(); + self.pack_to(nibbles.as_mut_ptr().cast()); + let packed_len = (self.len() + 1) / 2; + SmallVec::from_buf_and_len_unchecked(nibbles, packed_len) + } + + /// Packs on the heap. + fn pack_heap(&self) -> SmallVec<[u8; 32]> { + // Collect into a vec directly to avoid the smallvec overhead since we know this is going on + // the heap. let packed_len = (self.len() + 1) / 2; - let mut v = Vec::with_capacity(packed_len); - for i in 0..packed_len { - let hi = *unsafe { self.get_unchecked(i * 2) }; - let lo = self.get(i * 2 + 1).copied().unwrap_or(0); - v.push((hi << 4) | lo); + let mut vec = Vec::with_capacity(packed_len); + // SAFETY: enough capacity. + unsafe { self.pack_to(vec.as_mut_ptr()) }; + // SAFETY: within capacity and `pack_to` initialized the memory. + unsafe { vec.set_len(packed_len) }; + SmallVec::from_vec(vec) + } + + /// Packs into the given pointer. + /// + /// # Safety + /// + /// `ptr` must be valid for at least `self.len() / 2 + IS_ODD` bytes. + #[inline] + unsafe fn pack_to(&self, ptr: *mut u8) { + for i in 0..self.len() / 2 { + ptr.add(i).write(self.get_byte_unchecked(i * 2)); + } + if self.len() % 2 != 0 { + let i = self.len() / 2; + ptr.add(i).write(self.last().unwrap_unchecked() << 4); } - v + } + + /// Gets the byte at the given index by combining two consecutive nibbles. + /// + /// # Safety + /// + /// `i..i + 1` must be in range. + #[inline] + unsafe fn get_byte_unchecked(&self, i: usize) -> u8 { + debug_assert!(i + 1 < self.len(), "index {i}..{} out of bounds of {}", i + 1, self.len()); + let hi = *self.get_unchecked(i); + let lo = *self.get_unchecked(i + 1); + (hi << 4) | lo } /// Encodes a given path leaf as a compact array of bytes, where each byte represents two @@ -189,60 +354,77 @@ impl Nibbles { /// /// # Returns /// - /// A `Vec` containing the compact byte representation of the nibble sequence, including the + /// A vector containing the compact byte representation of the nibble sequence, including the /// header byte. /// - /// # Example + /// This vector's length is `self.len() / 2 + 1`. For stack-allocated nibbles, this is at most + /// 33 bytes, so 36 was chosen as the stack capacity to round up to the next usize-aligned + /// size. + /// + /// # Examples /// /// ``` /// # use reth_primitives::trie::Nibbles; /// /// // Extension node with an even path length: - /// let nibbles = Nibbles::new_unchecked(&[0x0A, 0x0B, 0x0C, 0x0D]); - /// assert_eq!(nibbles.encode_path_leaf(false), vec![0x00, 0xAB, 0xCD]); + /// let nibbles = Nibbles::from_nibbles_unchecked(&[0x0A, 0x0B, 0x0C, 0x0D]); + /// assert_eq!(nibbles.encode_path_leaf(false)[..], [0x00, 0xAB, 0xCD]); /// /// // Extension node with an odd path length: - /// let nibbles = Nibbles::new_unchecked(&[0x0A, 0x0B, 0x0C]); - /// assert_eq!(nibbles.encode_path_leaf(false), vec![0x1A, 0xBC]); + /// let nibbles = Nibbles::from_nibbles_unchecked(&[0x0A, 0x0B, 0x0C]); + /// assert_eq!(nibbles.encode_path_leaf(false)[..], [0x1A, 0xBC]); /// /// // Leaf node with an even path length: - /// let nibbles = Nibbles::new_unchecked(&[0x0A, 0x0B, 0x0C, 0x0D]); - /// assert_eq!(nibbles.encode_path_leaf(true), vec![0x20, 0xAB, 0xCD]); + /// let nibbles = Nibbles::from_nibbles_unchecked(&[0x0A, 0x0B, 0x0C, 0x0D]); + /// assert_eq!(nibbles.encode_path_leaf(true)[..], [0x20, 0xAB, 0xCD]); /// /// // Leaf node with an odd path length: - /// let nibbles = Nibbles::new_unchecked(&[0x0A, 0x0B, 0x0C]); - /// assert_eq!(nibbles.encode_path_leaf(true), vec![0x3A, 0xBC]); + /// let nibbles = Nibbles::from_nibbles_unchecked(&[0x0A, 0x0B, 0x0C]); + /// assert_eq!(nibbles.encode_path_leaf(true)[..], [0x3A, 0xBC]); /// ``` - pub fn encode_path_leaf(&self, is_leaf: bool) -> Vec { - let mut encoded = vec![0u8; self.len() / 2 + 1]; + pub fn encode_path_leaf(&self, is_leaf: bool) -> SmallVec<[u8; 36]> { + let encoded_len = self.len() / 2 + 1; + let mut encoded = SmallVec::with_capacity(encoded_len); + // SAFETY: enough capacity. + unsafe { self.encode_path_leaf_to(is_leaf, encoded.as_mut_ptr()) }; + // SAFETY: within capacity and `encode_path_leaf_to` initialized the memory. + unsafe { encoded.set_len(encoded_len) }; + encoded + } + + /// # Safety + /// + /// `ptr` must be valid for at least `self.len() / 2 + 1` bytes. + #[inline] + unsafe fn encode_path_leaf_to(&self, is_leaf: bool, ptr: *mut u8) { let odd_nibbles = self.len() % 2 != 0; + *ptr = self.encode_path_leaf_first_byte(is_leaf, odd_nibbles); + let mut nibble_idx = if odd_nibbles { 1 } else { 0 }; + for i in 0..self.len() / 2 { + ptr.add(i + 1).write(self.get_byte_unchecked(nibble_idx)); + nibble_idx += 2; + } + } - // Set the first byte of the encoded vector. - encoded[0] = match (is_leaf, odd_nibbles) { + #[inline] + fn encode_path_leaf_first_byte(&self, is_leaf: bool, odd_nibbles: bool) -> u8 { + match (is_leaf, odd_nibbles) { (true, true) => 0x30 | self[0], (true, false) => 0x20, (false, true) => 0x10 | self[0], (false, false) => 0x00, - }; - - let mut nibble_idx = if odd_nibbles { 1 } else { 0 }; - for byte in encoded.iter_mut().skip(1) { - *byte = (self[nibble_idx] << 4) + self[nibble_idx + 1]; - nibble_idx += 2; } - - encoded } /// Increments the nibble sequence by one. pub fn increment(&self) -> Option { - let mut incremented = self.0.to_vec(); + let mut incremented = self.clone(); - for nibble in incremented.iter_mut().rev() { - debug_assert!(*nibble < 0x10); + for nibble in incremented.0.iter_mut().rev() { + debug_assert!(*nibble <= 0xf); if *nibble < 0xf { *nibble += 1; - return Some(Self::new_unchecked(incremented)) + return Some(incremented); } else { *nibble = 0; } @@ -272,7 +454,13 @@ impl Nibbles { #[inline] #[track_caller] pub fn at(&self, i: usize) -> usize { - self.0[i] as usize + self[i] as usize + } + + /// Returns the first nibble of the current nibble sequence. + #[inline] + pub fn first(&self) -> Option { + self.0.first().copied() } /// Returns the last nibble of the current nibble sequence. @@ -293,61 +481,58 @@ impl Nibbles { len } - /// Returns a reference to the underlying [`Bytes`]. - #[inline] - pub fn as_bytes(&self) -> &Bytes { - &self.0 - } - /// Returns the nibbles as a byte slice. #[inline] pub fn as_slice(&self) -> &[u8] { &self.0 } - /// Returns the underlying [`Bytes`]. - #[inline] - pub fn into_bytes(self) -> Bytes { - self.0 - } - /// Slice the current nibbles within the provided index range. + /// + /// # Panics + /// + /// Panics if the range is out of bounds. #[inline] + #[track_caller] pub fn slice(&self, range: impl RangeBounds) -> Self { - Self(self.0.slice(range)) + let start = match range.start_bound() { + Bound::Included(&n) => n, + Bound::Excluded(&n) => n.checked_add(1).expect("out of range"), + Bound::Unbounded => 0, + }; + let end = match range.end_bound() { + Bound::Included(&n) => n.checked_add(1).expect("out of range"), + Bound::Excluded(&n) => n, + Bound::Unbounded => self.len(), + }; + Self::from_nibbles_unchecked(&self[start..end]) } /// Join two nibbles together. #[inline] pub fn join(&self, b: &Self) -> Self { - let mut hex_data = Vec::with_capacity(self.len() + b.len()); - hex_data.extend_from_slice(self); - hex_data.extend_from_slice(b); - Self::new_unchecked(hex_data) + let mut nibbles = SmallVec::with_capacity(self.len() + b.len()); + nibbles.extend_from_slice(self); + nibbles.extend_from_slice(b); + Self(nibbles) } /// Pushes a nibble to the end of the current nibbles. - /// - /// **Note**: This method re-allocates on each call. #[inline] pub fn push(&mut self, nibble: u8) { - self.extend([nibble]); + self.0.push(nibble); } /// Extend the current nibbles with another nibbles. - /// - /// **Note**: This method re-allocates on each call. #[inline] - pub fn extend(&mut self, b: impl AsRef<[u8]>) { - let mut bytes = self.0.to_vec(); - bytes.extend_from_slice(b.as_ref()); - self.0 = bytes.into(); + pub fn extend_from_slice(&mut self, b: impl AsRef<[u8]>) { + self.0.extend_from_slice(b.as_ref()); } /// Truncates the current nibbles to the given length. #[inline] - pub fn truncate(&mut self, len: usize) { - self.0.truncate(len); + pub fn truncate(&mut self, new_len: usize) { + self.0.truncate(new_len); } /// Clears the current nibbles. @@ -365,40 +550,75 @@ mod tests { #[test] fn hashed_regression() { - let nibbles = Nibbles::new_unchecked(hex!("05010406040a040203030f010805020b050c04070003070e0909070f010b0a0805020301070c0a0902040b0f000f0006040a04050f020b090701000a0a040b")); + let nibbles = Nibbles::from_nibbles_unchecked(hex!("05010406040a040203030f010805020b050c04070003070e0909070f010b0a0805020301070c0a0902040b0f000f0006040a04050f020b090701000a0a040b")); let path = nibbles.encode_path_leaf(true); let expected = hex!("351464a4233f1852b5c47037e997f1ba852317ca924bf0f064a45f2b9710aa4b"); - assert_eq!(path, expected); + assert_eq!(path[..], expected); } #[test] fn pack_nibbles() { - for (input, expected) in [ - (vec![], vec![]), - (vec![0xa], vec![0xa0]), - (vec![0xa, 0xb], vec![0xab]), - (vec![0xa, 0xb, 0x2], vec![0xab, 0x20]), - (vec![0xa, 0xb, 0x2, 0x0], vec![0xab, 0x20]), - (vec![0xa, 0xb, 0x2, 0x7], vec![0xab, 0x27]), - ] { - let nibbles = Nibbles::new_unchecked(input); + let tests = [ + (&[][..], &[][..]), + (&[0xa], &[0xa0]), + (&[0xa, 0x0], &[0xa0]), + (&[0xa, 0xb], &[0xab]), + (&[0xa, 0xb, 0x2], &[0xab, 0x20]), + (&[0xa, 0xb, 0x2, 0x0], &[0xab, 0x20]), + (&[0xa, 0xb, 0x2, 0x7], &[0xab, 0x27]), + ]; + for (input, expected) in tests { + assert!(input.iter().all(|&x| x <= 0xf)); + let nibbles = Nibbles::from_nibbles_unchecked(input); let encoded = nibbles.pack(); - assert_eq!(encoded, expected); + assert_eq!(&encoded[..], expected); } } + #[test] + fn slice() { + const RAW: &[u8] = &hex!("05010406040a040203030f010805020b050c04070003070e0909070f010b0a0805020301070c0a0902040b0f000f0006040a04050f020b090701000a0a040b"); + + #[track_caller] + fn test_slice(range: impl RangeBounds, expected: &[u8]) { + let nibbles = Nibbles::from_nibbles_unchecked(RAW); + let sliced = nibbles.slice(range); + assert_eq!(sliced, Nibbles::from_nibbles_unchecked(expected)); + assert_eq!(sliced.as_slice(), expected); + } + + test_slice(0..0, &[]); + test_slice(0..1, &[0x05]); + test_slice(1..1, &[]); + test_slice(1..=1, &[0x01]); + test_slice(0..=1, &[0x05, 0x01]); + test_slice(0..2, &[0x05, 0x01]); + + test_slice(..0, &[]); + test_slice(..1, &[0x05]); + test_slice(..=1, &[0x05, 0x01]); + test_slice(..2, &[0x05, 0x01]); + + test_slice(.., RAW); + test_slice(..RAW.len(), RAW); + test_slice(0.., RAW); + test_slice(0..RAW.len(), RAW); + } + proptest! { #[test] fn pack_unpack_roundtrip(input in any::>()) { let nibbles = Nibbles::unpack(&input); + prop_assert!(nibbles.iter().all(|&nibble| nibble <= 0xf)); let packed = nibbles.pack(); - prop_assert_eq!(packed, input); + prop_assert_eq!(&packed[..], input); } #[test] fn encode_path_first_byte(input in any::>()) { prop_assume!(!input.is_empty()); let input = Nibbles::unpack(input); + prop_assert!(input.iter().all(|&nibble| nibble <= 0xf)); let input_is_odd = input.len() % 2 == 1; let compact_leaf = input.encode_path_leaf(true); @@ -407,7 +627,7 @@ mod tests { assert_ne!(leaf_flag & 0x20, 0); assert_eq!(input_is_odd, (leaf_flag & 0x10) != 0); if input_is_odd { - assert_eq!(leaf_flag & 0x0f, *input.first().unwrap()); + assert_eq!(leaf_flag & 0x0f, input.first().unwrap()); } @@ -417,7 +637,7 @@ mod tests { assert_eq!(extension_flag & 0x20, 0); assert_eq!(input_is_odd, (extension_flag & 0x10) != 0); if input_is_odd { - assert_eq!(extension_flag & 0x0f, *input.first().unwrap()); + assert_eq!(extension_flag & 0x0f, input.first().unwrap()); } } } diff --git a/crates/primitives/src/trie/nodes/extension.rs b/crates/primitives/src/trie/nodes/extension.rs index 09fe529ecfc7..35233fdffa0d 100644 --- a/crates/primitives/src/trie/nodes/extension.rs +++ b/crates/primitives/src/trie/nodes/extension.rs @@ -1,5 +1,6 @@ use super::{super::Nibbles, rlp_node}; use alloy_rlp::{BufMut, Encodable}; +use smallvec::SmallVec; /// An intermediate node that exists solely to compress the trie's paths. It contains a path segment /// (a shared prefix of keys) and a single child pointer. Essentially, an extension node can be @@ -9,8 +10,8 @@ use alloy_rlp::{BufMut, Encodable}; /// with a single child into one node. This simplification reduces the space and computational /// complexity when performing operations on the trie. pub struct ExtensionNode<'a> { - /// A common prefix for keys. - pub prefix: Vec, + /// A common prefix for keys. See [`Nibbles::encode_path_leaf`] for more information. + pub prefix: SmallVec<[u8; 36]>, /// A pointer to the child. pub node: &'a [u8], } diff --git a/crates/primitives/src/trie/nodes/leaf.rs b/crates/primitives/src/trie/nodes/leaf.rs index 684bf913d6ff..3de18de81d1f 100644 --- a/crates/primitives/src/trie/nodes/leaf.rs +++ b/crates/primitives/src/trie/nodes/leaf.rs @@ -1,5 +1,6 @@ use super::{super::Nibbles, rlp_node}; use alloy_rlp::{BufMut, Encodable}; +use smallvec::SmallVec; /// A leaf node represents the endpoint or terminal node in the trie. In other words, a leaf node is /// where actual values are stored. @@ -10,9 +11,9 @@ use alloy_rlp::{BufMut, Encodable}; /// node means that the search has successfully found the value associated with that key. #[derive(Default)] pub struct LeafNode<'a> { - /// The key path. - pub key: Vec, - /// value: SmallVec<[u8; 36]> + /// The key path. See [`Nibbles::encode_path_leaf`] for more information. + pub key: SmallVec<[u8; 36]>, + /// The node value. pub value: &'a [u8], } @@ -59,14 +60,14 @@ mod tests { // From manual regression test #[test] fn encode_leaf_node_nibble() { - let nibble = Nibbles::new_unchecked(hex!("0604060f")); + let nibble = Nibbles::from_nibbles_unchecked(hex!("0604060f")); let encoded = nibble.encode_path_leaf(true); - assert_eq!(encoded, hex!("20646f")); + assert_eq!(encoded[..], hex!("20646f")); } #[test] fn rlp_leaf_node_roundtrip() { - let nibble = Nibbles::new_unchecked(hex!("0604060f")); + let nibble = Nibbles::from_nibbles_unchecked(hex!("0604060f")); let val = hex!("76657262"); let leaf = LeafNode::new(&nibble, &val); let rlp = leaf.rlp(&mut vec![]); diff --git a/crates/storage/codecs/src/lib.rs b/crates/storage/codecs/src/lib.rs index bfe096d120d2..2e9a8d3cb522 100644 --- a/crates/storage/codecs/src/lib.rs +++ b/crates/storage/codecs/src/lib.rs @@ -70,7 +70,9 @@ macro_rules! impl_uint_compact { ($($name:tt),+) => { $( impl Compact for $name { - fn to_compact(self, buf: &mut B) -> usize where B: bytes::BufMut + AsMut<[u8]> { + fn to_compact(self, buf: &mut B) -> usize + where B: bytes::BufMut + AsMut<[u8]> + { let leading = self.leading_zeros() as usize / 8; buf.put_slice(&self.to_be_bytes()[leading..]); std::mem::size_of::<$name>() - leading @@ -262,44 +264,49 @@ impl Compact for Bytes { } } -/// Implements the [`Compact`] trait for fixed size hash types like [`B256`]. +impl Compact for [u8; N] { + fn to_compact(self, buf: &mut B) -> usize + where + B: bytes::BufMut + AsMut<[u8]>, + { + buf.put_slice(&self); + N + } + + fn from_compact(mut buf: &[u8], len: usize) -> (Self, &[u8]) { + if len == 0 { + return ([0; N], buf) + } + + let v = buf[..N].try_into().unwrap(); + buf.advance(N); + (v, buf) + } +} + +/// Implements the [`Compact`] trait for fixed size byte array types like [`B256`]. #[macro_export] -macro_rules! impl_hash_compact { +macro_rules! impl_compact_for_bytes { ($($name:tt),+) => { $( impl Compact for $name { - fn to_compact(self, buf: &mut B) -> usize where B: bytes::BufMut + AsMut<[u8]> { - buf.put_slice(self.as_slice()); - std::mem::size_of::<$name>() - } - - fn from_compact(mut buf: &[u8], len: usize) -> (Self,&[u8]) { - if len == 0 { - return ($name::default(), buf) - } - - let v = $name::from_slice( - buf.get(..std::mem::size_of::<$name>()).expect("size not matching"), - ); - buf.advance(std::mem::size_of::<$name>()); - (v, buf) - } - - fn specialized_to_compact(self, buf: &mut B) -> usize + fn to_compact(self, buf: &mut B) -> usize where - B: bytes::BufMut + AsMut<[u8]> { - self.to_compact(buf) + B: bytes::BufMut + AsMut<[u8]> + { + self.0.to_compact(buf) } - fn specialized_from_compact(buf: &[u8], len: usize) -> (Self, &[u8]) { - Self::from_compact(buf, len) + fn from_compact(buf: &[u8], len: usize) -> (Self, &[u8]) { + let (v, buf) = <[u8; std::mem::size_of::<$name>()]>::from_compact(buf, len); + (Self::from(v), buf) } } )+ }; } -impl_hash_compact!(Address, B256, B512, Bloom); +impl_compact_for_bytes!(Address, B256, B512, Bloom); impl Compact for bool { /// `bool` vars go directly to the `StructFlags` and are not written to the buffer. diff --git a/crates/trie/benches/prefix_set.rs b/crates/trie/benches/prefix_set.rs index c9e4dbbd699f..2fd8ff28d470 100644 --- a/crates/trie/benches/prefix_set.rs +++ b/crates/trie/benches/prefix_set.rs @@ -94,12 +94,12 @@ fn generate_test_data(size: usize) -> (Vec, Vec, Vec) { let mut preload = vec(vec(any::(), 32), size).new_tree(&mut runner).unwrap().current(); preload.dedup(); preload.sort(); - let preload = preload.into_iter().map(Nibbles::new_unchecked).collect::>(); + let preload = preload.into_iter().map(Nibbles::from_nibbles_unchecked).collect::>(); let mut input = vec(vec(any::(), 0..=32), size).new_tree(&mut runner).unwrap().current(); input.dedup(); input.sort(); - let input = input.into_iter().map(Nibbles::new_unchecked).collect::>(); + let input = input.into_iter().map(Nibbles::from_nibbles_unchecked).collect::>(); let expected = input .iter() diff --git a/crates/trie/src/prefix_set/mod.rs b/crates/trie/src/prefix_set/mod.rs index 0c262759a3c1..9b80c703c85f 100644 --- a/crates/trie/src/prefix_set/mod.rs +++ b/crates/trie/src/prefix_set/mod.rs @@ -26,8 +26,8 @@ pub use loader::{LoadedPrefixSets, PrefixSetLoader}; /// use reth_trie::prefix_set::PrefixSetMut; /// /// let mut prefix_set = PrefixSetMut::default(); -/// prefix_set.insert(Nibbles::new_unchecked(&[0xa, 0xb])); -/// prefix_set.insert(Nibbles::new_unchecked(&[0xa, 0xb, 0xc])); +/// prefix_set.insert(Nibbles::from_nibbles_unchecked(&[0xa, 0xb])); +/// prefix_set.insert(Nibbles::from_nibbles_unchecked(&[0xa, 0xb, 0xc])); /// assert!(prefix_set.contains(&[0xa, 0xb])); /// assert!(prefix_set.contains(&[0xa, 0xb, 0xc])); /// ``` @@ -158,10 +158,10 @@ mod tests { #[test] fn test_contains_with_multiple_inserts_and_duplicates() { let mut prefix_set = PrefixSetMut::default(); - prefix_set.insert(Nibbles::new_unchecked(b"123")); - prefix_set.insert(Nibbles::new_unchecked(b"124")); - prefix_set.insert(Nibbles::new_unchecked(b"456")); - prefix_set.insert(Nibbles::new_unchecked(b"123")); // Duplicate + prefix_set.insert(Nibbles::from_nibbles_unchecked(b"123")); + prefix_set.insert(Nibbles::from_nibbles_unchecked(b"124")); + prefix_set.insert(Nibbles::from_nibbles_unchecked(b"456")); + prefix_set.insert(Nibbles::from_nibbles_unchecked(b"123")); // Duplicate assert!(prefix_set.contains(b"12")); assert!(prefix_set.contains(b"45")); diff --git a/crates/trie/src/trie_cursor/account_cursor.rs b/crates/trie/src/trie_cursor/account_cursor.rs index b98be8bd2ba3..94e7be590146 100644 --- a/crates/trie/src/trie_cursor/account_cursor.rs +++ b/crates/trie/src/trie_cursor/account_cursor.rs @@ -41,7 +41,6 @@ where #[cfg(test)] mod tests { - use super::*; use reth_db::{ cursor::{DbCursorRO, DbCursorRW}, diff --git a/crates/trie/src/trie_cursor/subnode.rs b/crates/trie/src/trie_cursor/subnode.rs index d49ccfe7f5dc..27f332fed721 100644 --- a/crates/trie/src/trie_cursor/subnode.rs +++ b/crates/trie/src/trie_cursor/subnode.rs @@ -39,7 +39,7 @@ impl From for CursorSubNode { Some(n) => n as i8, None => -1, }; - Self { key: Nibbles::new_unchecked(value.key), nibble, node: value.node } + Self { key: Nibbles::from_nibbles_unchecked(value.key), nibble, node: value.node } } } diff --git a/crates/trie/src/walker.rs b/crates/trie/src/walker.rs index 54e7fc7ec270..67da36d742c3 100644 --- a/crates/trie/src/walker.rs +++ b/crates/trie/src/walker.rs @@ -131,7 +131,7 @@ impl TrieWalker { assert!(!node.state_mask.is_empty()); } - Ok(entry.map(|(k, v)| (Nibbles::new_unchecked(k), v))) + Ok(entry.map(|(k, v)| (Nibbles::from_nibbles_unchecked(k), v))) } /// Consumes the next node in the trie, updating the stack. @@ -313,7 +313,7 @@ mod tests { // We're traversing the path in lexigraphical order. for expected in expected { let got = walker.advance().unwrap(); - assert_eq!(got.unwrap(), Nibbles::new_unchecked(expected.clone())); + assert_eq!(got.unwrap(), Nibbles::from_nibbles_unchecked(expected.clone())); } // There should be 8 paths traversed in total from 3 branches. @@ -361,26 +361,26 @@ mod tests { // No changes let mut cursor = TrieWalker::new(&mut trie, Default::default()); - assert_eq!(cursor.key(), Some(Nibbles::new_unchecked([]))); // root + assert_eq!(cursor.key(), Some(Nibbles::from_nibbles_unchecked([]))); // root assert!(cursor.can_skip_current_node); // due to root_hash cursor.advance().unwrap(); // skips to the end of trie assert_eq!(cursor.key(), None); // We insert something that's not part of the existing trie/prefix. let mut changed = PrefixSetMut::default(); - changed.insert(Nibbles::new_unchecked([0xF, 0x1])); + changed.insert(Nibbles::from_nibbles_unchecked([0xF, 0x1])); let mut cursor = TrieWalker::new(&mut trie, changed.freeze()); // Root node - assert_eq!(cursor.key(), Some(Nibbles::new_unchecked([]))); + assert_eq!(cursor.key(), Some(Nibbles::from_nibbles_unchecked([]))); // Should not be able to skip state due to the changed values assert!(!cursor.can_skip_current_node); cursor.advance().unwrap(); - assert_eq!(cursor.key(), Some(Nibbles::new_unchecked([0x2]))); + assert_eq!(cursor.key(), Some(Nibbles::from_nibbles_unchecked([0x2]))); cursor.advance().unwrap(); - assert_eq!(cursor.key(), Some(Nibbles::new_unchecked([0x2, 0x1]))); + assert_eq!(cursor.key(), Some(Nibbles::from_nibbles_unchecked([0x2, 0x1]))); cursor.advance().unwrap(); - assert_eq!(cursor.key(), Some(Nibbles::new_unchecked([0x4]))); + assert_eq!(cursor.key(), Some(Nibbles::from_nibbles_unchecked([0x4]))); cursor.advance().unwrap(); assert_eq!(cursor.key(), None); // the end of trie From 585bc31fbdde52ef03f3281b21cba776e0b78969 Mon Sep 17 00:00:00 2001 From: Dan Cline <6798349+Rjected@users.noreply.github.com> Date: Fri, 1 Dec 2023 19:08:51 -0500 Subject: [PATCH 080/277] feat: add sanity tests for Args Default impls (#5660) --- bin/reth/src/args/database_args.rs | 20 +++++++++++ bin/reth/src/args/debug_args.rs | 20 +++++++++++ bin/reth/src/args/dev_args.rs | 9 ++++- bin/reth/src/args/gas_price_oracle_args.rs | 20 ++++++++++- bin/reth/src/args/network_args.rs | 14 ++++++-- bin/reth/src/args/payload_builder_args.rs | 29 ++++++++++++++-- bin/reth/src/args/pruning_args.rs | 20 +++++++++++ bin/reth/src/args/rollup_args.rs | 22 +++++++++++- bin/reth/src/args/rpc_server_args.rs | 12 +++++-- bin/reth/src/args/txpool_args.rs | 39 +++++++++++++++++++++- crates/rpc/rpc/src/layers/jwt_secret.rs | 2 +- 11 files changed, 194 insertions(+), 13 deletions(-) diff --git a/bin/reth/src/args/database_args.rs b/bin/reth/src/args/database_args.rs index 954390cdc211..edaa26bbfaec 100644 --- a/bin/reth/src/args/database_args.rs +++ b/bin/reth/src/args/database_args.rs @@ -11,3 +11,23 @@ pub struct DatabaseArgs { #[arg(long = "db.log-level", value_enum)] pub log_level: Option, } + +#[cfg(test)] +mod tests { + use super::*; + use clap::Parser; + + /// A helper type to parse Args more easily + #[derive(Parser)] + struct CommandParser { + #[clap(flatten)] + args: T, + } + + #[test] + fn test_parse_database_args() { + let default_args = DatabaseArgs::default(); + let args = CommandParser::::parse_from(["reth"]).args; + assert_eq!(args, default_args); + } +} diff --git a/bin/reth/src/args/debug_args.rs b/bin/reth/src/args/debug_args.rs index 3772aa52cb2f..71741ae4ee20 100644 --- a/bin/reth/src/args/debug_args.rs +++ b/bin/reth/src/args/debug_args.rs @@ -58,3 +58,23 @@ pub struct DebugArgs { )] pub hook_all: bool, } + +#[cfg(test)] +mod tests { + use super::*; + use clap::Parser; + + /// A helper type to parse Args more easily + #[derive(Parser)] + struct CommandParser { + #[clap(flatten)] + args: T, + } + + #[test] + fn test_parse_database_args() { + let default_args = DebugArgs::default(); + let args = CommandParser::::parse_from(["reth"]).args; + assert_eq!(args, default_args); + } +} diff --git a/bin/reth/src/args/dev_args.rs b/bin/reth/src/args/dev_args.rs index da046225a89e..1bcfeeff2bbb 100644 --- a/bin/reth/src/args/dev_args.rs +++ b/bin/reth/src/args/dev_args.rs @@ -32,7 +32,7 @@ pub struct DevArgs { /// --dev.block_time 12s #[arg( long = "dev.block-time", - help_heading = "Dev testnet", + help_heading = "Dev testnet", conflicts_with = "block_max_transactions", value_parser = parse_duration, verbatim_doc_comment @@ -96,4 +96,11 @@ mod tests { ]); assert!(args.is_err()); } + + #[test] + fn dev_args_default_sanity_check() { + let default_args = DevArgs::default(); + let args = CommandParser::::parse_from(["reth"]).args; + assert_eq!(args, default_args); + } } diff --git a/bin/reth/src/args/gas_price_oracle_args.rs b/bin/reth/src/args/gas_price_oracle_args.rs index 42237f91276c..001ba9017c97 100644 --- a/bin/reth/src/args/gas_price_oracle_args.rs +++ b/bin/reth/src/args/gas_price_oracle_args.rs @@ -1,7 +1,7 @@ use clap::Args; /// Parameters to configure Gas Price Oracle -#[derive(Debug, Clone, Args, PartialEq, Eq, Default)] +#[derive(Debug, Clone, Args, PartialEq, Eq)] #[clap(next_help_heading = "Gas Price Oracle")] pub struct GasPriceOracleArgs { /// Number of recent blocks to check for gas price @@ -21,6 +21,17 @@ pub struct GasPriceOracleArgs { pub percentile: Option, } +impl Default for GasPriceOracleArgs { + fn default() -> Self { + Self { + blocks: Some(20), + ignore_price: Some(2), + max_price: Some(500000000000), + percentile: Some(60), + } + } +} + #[cfg(test)] mod tests { use super::*; @@ -46,4 +57,11 @@ mod tests { } ); } + + #[test] + fn gpo_args_default_sanity_test() { + let default_args = GasPriceOracleArgs::default(); + let args = CommandParser::::parse_from(["reth"]).args; + assert_eq!(args, default_args); + } } diff --git a/bin/reth/src/args/network_args.rs b/bin/reth/src/args/network_args.rs index 387096aa5b9f..2014468d6134 100644 --- a/bin/reth/src/args/network_args.rs +++ b/bin/reth/src/args/network_args.rs @@ -11,7 +11,7 @@ use secp256k1::SecretKey; use std::{net::Ipv4Addr, path::PathBuf, sync::Arc}; /// Parameters for configuring the network more granularity via CLI -#[derive(Debug, Args)] +#[derive(Debug, Args, PartialEq, Eq)] #[clap(next_help_heading = "Networking")] pub struct NetworkArgs { /// Disable the discovery service. @@ -118,7 +118,7 @@ impl NetworkArgs { /// If `no_persist_peers` is true then this returns the path to the persistent peers file path. pub fn persistent_peers_file(&self, peers_file: PathBuf) -> Option { if self.no_persist_peers { - return None + return None; } Some(peers_file) @@ -146,7 +146,7 @@ impl Default for NetworkArgs { } /// Arguments to setup discovery -#[derive(Debug, Args)] +#[derive(Debug, Args, PartialEq, Eq)] pub struct DiscoveryArgs { /// Disable the discovery service. #[arg(short, long, default_value_if("dev", "true", "true"))] @@ -256,4 +256,12 @@ mod tests { ] ); } + + #[test] + fn network_args_default_sanity_test() { + let default_args = NetworkArgs::default(); + let args = CommandParser::::parse_from(["reth"]).args; + + assert_eq!(args, default_args); + } } diff --git a/bin/reth/src/args/payload_builder_args.rs b/bin/reth/src/args/payload_builder_args.rs index 7de104987c79..1619e7bd979a 100644 --- a/bin/reth/src/args/payload_builder_args.rs +++ b/bin/reth/src/args/payload_builder_args.rs @@ -6,11 +6,13 @@ use clap::{ builder::{RangedU64ValueParser, TypedValueParser}, Arg, Args, Command, }; -use reth_primitives::constants::MAXIMUM_EXTRA_DATA_SIZE; +use reth_primitives::constants::{ + ETHEREUM_BLOCK_GAS_LIMIT, MAXIMUM_EXTRA_DATA_SIZE, SLOT_DURATION, +}; use std::{borrow::Cow, ffi::OsStr, time::Duration}; /// Parameters for configuring the Payload Builder -#[derive(Debug, Args, PartialEq, Default)] +#[derive(Debug, Args, PartialEq)] #[clap(next_help_heading = "Builder")] pub struct PayloadBuilderArgs { /// Block extra data set by the payload builder. @@ -46,6 +48,20 @@ pub struct PayloadBuilderArgs { pub compute_pending_block: bool, } +impl Default for PayloadBuilderArgs { + fn default() -> Self { + Self { + extradata: default_extradata(), + max_gas_limit: ETHEREUM_BLOCK_GAS_LIMIT, + interval: Duration::from_secs(1), + deadline: SLOT_DURATION, + max_payload_tasks: 3, + #[cfg(feature = "optimism")] + compute_pending_block: false, + } + } +} + impl PayloadBuilderConfig for PayloadBuilderArgs { fn extradata(&self) -> Cow<'_, str> { self.extradata.as_str().into() @@ -94,7 +110,7 @@ impl TypedValueParser for ExtradataValueParser { format!( "Payload builder extradata size exceeds {MAXIMUM_EXTRA_DATA_SIZE}bytes limit" ), - )) + )); } Ok(val.to_string()) } @@ -152,4 +168,11 @@ mod tests { ]); assert!(args.is_err()); } + + #[test] + fn payload_builder_args_default_sanity_check() { + let default_args = PayloadBuilderArgs::default(); + let args = CommandParser::::parse_from(["reth"]).args; + assert_eq!(args, default_args); + } } diff --git a/bin/reth/src/args/pruning_args.rs b/bin/reth/src/args/pruning_args.rs index 251e1a918669..ece803261c7f 100644 --- a/bin/reth/src/args/pruning_args.rs +++ b/bin/reth/src/args/pruning_args.rs @@ -47,3 +47,23 @@ impl PruningArgs { }) } } + +#[cfg(test)] +mod tests { + use super::*; + use clap::{Args, Parser}; + + /// A helper type to parse Args more easily + #[derive(Parser)] + struct CommandParser { + #[clap(flatten)] + args: T, + } + + #[test] + fn pruning_args_sanity_check() { + let default_args = PruningArgs::default(); + let args = CommandParser::::parse_from(["reth"]).args; + assert_eq!(args, default_args); + } +} diff --git a/bin/reth/src/args/rollup_args.rs b/bin/reth/src/args/rollup_args.rs index c97fe19147e5..ec2a995113a1 100644 --- a/bin/reth/src/args/rollup_args.rs +++ b/bin/reth/src/args/rollup_args.rs @@ -1,7 +1,7 @@ //! clap [Args](clap::Args) for op-reth rollup configuration /// Parameters for rollup configuration -#[derive(Debug, clap::Args)] +#[derive(Debug, Default, PartialEq, Eq, clap::Args)] #[clap(next_help_heading = "Rollup")] pub struct RollupArgs { /// HTTP endpoint for the sequencer mempool @@ -17,3 +17,23 @@ pub struct RollupArgs { #[arg(long = "rollup.enable-genesis-walkback")] pub enable_genesis_walkback: bool, } + +#[cfg(test)] +mod tests { + use super::*; + use clap::{Args, Parser}; + + /// A helper type to parse Args more easily + #[derive(Parser)] + struct CommandParser { + #[clap(flatten)] + args: T, + } + + #[test] + fn test_parse_database_args() { + let default_args = RollupArgs::default(); + let args = CommandParser::::parse_from(["reth"]).args; + assert_eq!(args, default_args); + } +} diff --git a/bin/reth/src/args/rpc_server_args.rs b/bin/reth/src/args/rpc_server_args.rs index 261b3515e18b..699b7fd1fa9c 100644 --- a/bin/reth/src/args/rpc_server_args.rs +++ b/bin/reth/src/args/rpc_server_args.rs @@ -61,7 +61,7 @@ pub(crate) const RPC_DEFAULT_MAX_RESPONSE_SIZE_MB: u32 = 150; pub(crate) const RPC_DEFAULT_MAX_CONNECTIONS: u32 = 500; /// Parameters for configuring the rpc more granularity via CLI -#[derive(Debug, Clone, Args)] +#[derive(Debug, Clone, Args, PartialEq, Eq)] #[clap(next_help_heading = "RPC")] pub struct RpcServerArgs { /// Enable the HTTP-RPC server @@ -462,7 +462,7 @@ impl RethRpcConfig for RpcServerArgs { impl Default for RpcServerArgs { fn default() -> Self { Self { - http: true, + http: false, http_addr: Ipv4Addr::LOCALHOST.into(), http_port: constants::DEFAULT_HTTP_RPC_PORT, http_api: None, @@ -709,4 +709,12 @@ mod tests { assert_eq!(config.max_blocks_per_filter, Some(100)); assert_eq!(config.max_logs_per_response, Some(200)); } + + #[test] + fn rpc_server_args_default_sanity_test() { + let default_args = RpcServerArgs::default(); + let args = CommandParser::::parse_from(["reth"]).args; + + assert_eq!(args, default_args); + } } diff --git a/bin/reth/src/args/txpool_args.rs b/bin/reth/src/args/txpool_args.rs index 40912c209e0e..12dea777b25f 100644 --- a/bin/reth/src/args/txpool_args.rs +++ b/bin/reth/src/args/txpool_args.rs @@ -8,7 +8,7 @@ use reth_transaction_pool::{ }; /// Parameters for debugging purposes -#[derive(Debug, Args, PartialEq, Default)] +#[derive(Debug, Args, PartialEq)] #[clap(next_help_heading = "TxPool")] pub struct TxPoolArgs { /// Max number of transaction in the pending sub-pool. @@ -48,6 +48,23 @@ pub struct TxPoolArgs { pub no_locals: bool, } +impl Default for TxPoolArgs { + fn default() -> Self { + Self { + pending_max_count: TXPOOL_SUBPOOL_MAX_TXS_DEFAULT, + pending_max_size: TXPOOL_SUBPOOL_MAX_SIZE_MB_DEFAULT, + basefee_max_count: TXPOOL_SUBPOOL_MAX_TXS_DEFAULT, + basefee_max_size: TXPOOL_SUBPOOL_MAX_SIZE_MB_DEFAULT, + queued_max_count: TXPOOL_SUBPOOL_MAX_TXS_DEFAULT, + queued_max_size: TXPOOL_SUBPOOL_MAX_SIZE_MB_DEFAULT, + max_account_slots: TXPOOL_MAX_ACCOUNT_SLOTS_PER_SENDER, + price_bump: DEFAULT_PRICE_BUMP, + blob_transaction_price_bump: REPLACE_BLOB_PRICE_BUMP, + no_locals: false, + } + } +} + impl TxPoolArgs { /// Returns transaction pool configuration. pub fn pool_config(&self) -> PoolConfig { @@ -77,3 +94,23 @@ impl TxPoolArgs { } } } + +#[cfg(test)] +mod tests { + use super::*; + use clap::Parser; + + /// A helper type to parse Args more easily + #[derive(Parser)] + struct CommandParser { + #[clap(flatten)] + args: T, + } + + #[test] + fn txpool_args_default_sanity_test() { + let default_args = TxPoolArgs::default(); + let args = CommandParser::::parse_from(["reth"]).args; + assert_eq!(args, default_args); + } +} diff --git a/crates/rpc/rpc/src/layers/jwt_secret.rs b/crates/rpc/rpc/src/layers/jwt_secret.rs index 53207fcc0c70..6c855cbf507f 100644 --- a/crates/rpc/rpc/src/layers/jwt_secret.rs +++ b/crates/rpc/rpc/src/layers/jwt_secret.rs @@ -56,7 +56,7 @@ const JWT_SIGNATURE_ALGO: Algorithm = Algorithm::HS256; /// for the JWT, which is included in the JWT along with its payload. /// /// See also: [Secret key - Engine API specs](https://github.com/ethereum/execution-apis/blob/main/src/engine/authentication.md#key-distribution) -#[derive(Clone)] +#[derive(Clone, PartialEq, Eq)] pub struct JwtSecret([u8; 32]); impl JwtSecret { From 7fdd9136cc178a349e0ea0c79f6486a6eab1d08d Mon Sep 17 00:00:00 2001 From: Dan Cline <6798349+Rjected@users.noreply.github.com> Date: Sat, 2 Dec 2023 04:10:40 -0500 Subject: [PATCH 081/277] chore: use StageConfig for more reth node methods (#5663) Co-authored-by: Roman Krasiuk --- bin/reth/src/node/mod.rs | 51 ++++++++++++++++++++-------------------- 1 file changed, 26 insertions(+), 25 deletions(-) diff --git a/bin/reth/src/node/mod.rs b/bin/reth/src/node/mod.rs index 815b77a71cbb..1b4eaeb20f59 100644 --- a/bin/reth/src/node/mod.rs +++ b/bin/reth/src/node/mod.rs @@ -34,7 +34,10 @@ use reth_beacon_consensus::{ use reth_blockchain_tree::{ config::BlockchainTreeConfig, externals::TreeExternals, BlockchainTree, ShareableBlockchainTree, }; -use reth_config::{config::PruneConfig, Config}; +use reth_config::{ + config::{PruneConfig, StageConfig}, + Config, +}; use reth_db::{database::Database, init_db, DatabaseEnv}; use reth_downloaders::{ bodies::bodies::BodiesDownloaderBuilder, @@ -419,7 +422,7 @@ impl NodeCommand { let mut pipeline = self .build_networked_pipeline( - &config, + &config.stages, client.clone(), Arc::clone(&consensus), provider_factory, @@ -439,7 +442,7 @@ impl NodeCommand { } else { let pipeline = self .build_networked_pipeline( - &config, + &config.stages, network_client.clone(), Arc::clone(&consensus), provider_factory, @@ -604,7 +607,7 @@ impl NodeCommand { #[allow(clippy::too_many_arguments)] async fn build_networked_pipeline( &self, - config: &Config, + config: &StageConfig, client: Client, consensus: Arc, provider_factory: ProviderFactory, @@ -618,11 +621,11 @@ impl NodeCommand { Client: HeadersClient + BodiesClient + Clone + 'static, { // building network downloaders using the fetch client - let header_downloader = ReverseHeadersDownloaderBuilder::from(config.stages.headers) + let header_downloader = ReverseHeadersDownloaderBuilder::from(config.headers) .build(client.clone(), Arc::clone(&consensus)) .into_task_with(task_executor); - let body_downloader = BodiesDownloaderBuilder::from(config.stages.bodies) + let body_downloader = BodiesDownloaderBuilder::from(config.bodies) .build(client, Arc::clone(&consensus), provider_factory.clone()) .into_task_with(task_executor); @@ -863,7 +866,7 @@ impl NodeCommand { async fn build_pipeline( &self, provider_factory: ProviderFactory, - config: &Config, + config: &StageConfig, header_downloader: H, body_downloader: B, consensus: Arc, @@ -877,8 +880,6 @@ impl NodeCommand { H: HeaderDownloader + 'static, B: BodyDownloader + 'static, { - let stage_config = &config.stages; - let mut builder = Pipeline::builder(); if let Some(max_block) = max_block { @@ -923,47 +924,47 @@ impl NodeCommand { ) .set( TotalDifficultyStage::new(consensus) - .with_commit_threshold(stage_config.total_difficulty.commit_threshold), + .with_commit_threshold(config.total_difficulty.commit_threshold), ) .set(SenderRecoveryStage { - commit_threshold: stage_config.sender_recovery.commit_threshold, + commit_threshold: config.sender_recovery.commit_threshold, }) .set( ExecutionStage::new( factory, ExecutionStageThresholds { - max_blocks: stage_config.execution.max_blocks, - max_changes: stage_config.execution.max_changes, - max_cumulative_gas: stage_config.execution.max_cumulative_gas, + max_blocks: config.execution.max_blocks, + max_changes: config.execution.max_changes, + max_cumulative_gas: config.execution.max_cumulative_gas, }, - stage_config + config .merkle .clean_threshold - .max(stage_config.account_hashing.clean_threshold) - .max(stage_config.storage_hashing.clean_threshold), + .max(config.account_hashing.clean_threshold) + .max(config.storage_hashing.clean_threshold), prune_modes.clone(), ) .with_metrics_tx(metrics_tx), ) .set(AccountHashingStage::new( - stage_config.account_hashing.clean_threshold, - stage_config.account_hashing.commit_threshold, + config.account_hashing.clean_threshold, + config.account_hashing.commit_threshold, )) .set(StorageHashingStage::new( - stage_config.storage_hashing.clean_threshold, - stage_config.storage_hashing.commit_threshold, + config.storage_hashing.clean_threshold, + config.storage_hashing.commit_threshold, )) - .set(MerkleStage::new_execution(stage_config.merkle.clean_threshold)) + .set(MerkleStage::new_execution(config.merkle.clean_threshold)) .set(TransactionLookupStage::new( - stage_config.transaction_lookup.commit_threshold, + config.transaction_lookup.commit_threshold, prune_modes.transaction_lookup, )) .set(IndexAccountHistoryStage::new( - stage_config.index_account_history.commit_threshold, + config.index_account_history.commit_threshold, prune_modes.account_history, )) .set(IndexStorageHistoryStage::new( - stage_config.index_storage_history.commit_threshold, + config.index_storage_history.commit_threshold, prune_modes.storage_history, )), ) From 6b06382d02e8a345be0794b2be3bbb12d769bd4b Mon Sep 17 00:00:00 2001 From: Roman Krasiuk Date: Sat, 2 Dec 2023 03:29:21 -0800 Subject: [PATCH 082/277] feat(cli): eth state cache args (#5664) --- bin/reth/src/args/mod.rs | 4 ++ bin/reth/src/args/rpc_server_args.rs | 40 +++++++---------- bin/reth/src/args/rpc_state_cache_args.rs | 54 +++++++++++++++++++++++ bin/reth/src/cli/config.rs | 8 +++- 4 files changed, 82 insertions(+), 24 deletions(-) create mode 100644 bin/reth/src/args/rpc_state_cache_args.rs diff --git a/bin/reth/src/args/mod.rs b/bin/reth/src/args/mod.rs index 06bf7f66817b..0e41b9443675 100644 --- a/bin/reth/src/args/mod.rs +++ b/bin/reth/src/args/mod.rs @@ -8,6 +8,10 @@ pub use network_args::{DiscoveryArgs, NetworkArgs}; mod rpc_server_args; pub use rpc_server_args::RpcServerArgs; +/// RpcStateCacheArgs struct for configuring RPC state cache +mod rpc_state_cache_args; +pub use rpc_state_cache_args::RpcStateCacheArgs; + /// DebugArgs struct for debugging purposes mod debug_args; pub use debug_args::DebugArgs; diff --git a/bin/reth/src/args/rpc_server_args.rs b/bin/reth/src/args/rpc_server_args.rs index 699b7fd1fa9c..e72807db2f0a 100644 --- a/bin/reth/src/args/rpc_server_args.rs +++ b/bin/reth/src/args/rpc_server_args.rs @@ -3,7 +3,7 @@ use crate::{ args::{ types::{MaxU32, ZeroAsNoneU64}, - GasPriceOracleArgs, + GasPriceOracleArgs, RpcStateCacheArgs, }, cli::{ components::{RethNodeComponents, RethRpcComponents, RethRpcServerHandles}, @@ -23,13 +23,7 @@ use reth_provider::{ EvmEnvProvider, HeaderProvider, StateProviderFactory, }; use reth_rpc::{ - eth::{ - cache::{ - DEFAULT_BLOCK_CACHE_MAX_LEN, DEFAULT_ENV_CACHE_MAX_LEN, DEFAULT_RECEIPT_CACHE_MAX_LEN, - }, - gas_oracle::GasPriceOracleConfig, - RPC_DEFAULT_GAS_CAP, - }, + eth::{cache::EthStateCacheConfig, gas_oracle::GasPriceOracleConfig, RPC_DEFAULT_GAS_CAP}, JwtError, JwtSecret, }; use reth_rpc_builder::{ @@ -175,21 +169,13 @@ pub struct RpcServerArgs { )] pub rpc_gas_cap: u64, + /// State cache configuration. + #[clap(flatten)] + pub rpc_state_cache: RpcStateCacheArgs, + /// Gas price oracle configuration. #[clap(flatten)] pub gas_price_oracle: GasPriceOracleArgs, - - /// Maximum number of block cache entries. - #[arg(long, default_value_t = DEFAULT_BLOCK_CACHE_MAX_LEN)] - pub block_cache_len: u32, - - /// Maximum number of receipt cache entries. - #[arg(long, default_value_t = DEFAULT_RECEIPT_CACHE_MAX_LEN)] - pub receipt_cache_len: u32, - - /// Maximum number of env cache entries. - #[arg(long, default_value_t = DEFAULT_ENV_CACHE_MAX_LEN)] - pub env_cache_len: u32, } impl RpcServerArgs { @@ -350,9 +336,19 @@ impl RethRpcConfig for RpcServerArgs { .max_blocks_per_filter(self.rpc_max_blocks_per_filter.unwrap_or_max()) .max_logs_per_response(self.rpc_max_logs_per_response.unwrap_or_max() as usize) .rpc_gas_cap(self.rpc_gas_cap) + .state_cache(self.state_cache_config()) .gpo_config(self.gas_price_oracle_config()) } + fn state_cache_config(&self) -> EthStateCacheConfig { + EthStateCacheConfig { + max_blocks: self.rpc_state_cache.max_blocks, + max_receipts: self.rpc_state_cache.max_receipts, + max_envs: self.rpc_state_cache.max_envs, + max_concurrent_db_requests: self.rpc_state_cache.max_concurrent_db_requests, + } + } + fn rpc_max_request_size_bytes(&self) -> u32 { self.rpc_max_request_size.get().saturating_mul(1024 * 1024) } @@ -487,9 +483,7 @@ impl Default for RpcServerArgs { rpc_max_logs_per_response: (constants::DEFAULT_MAX_LOGS_PER_RESPONSE as u64).into(), rpc_gas_cap: RPC_DEFAULT_GAS_CAP.into(), gas_price_oracle: GasPriceOracleArgs::default(), - block_cache_len: DEFAULT_BLOCK_CACHE_MAX_LEN, - receipt_cache_len: DEFAULT_RECEIPT_CACHE_MAX_LEN, - env_cache_len: DEFAULT_ENV_CACHE_MAX_LEN, + rpc_state_cache: RpcStateCacheArgs::default(), } } } diff --git a/bin/reth/src/args/rpc_state_cache_args.rs b/bin/reth/src/args/rpc_state_cache_args.rs new file mode 100644 index 000000000000..6396b2483a9f --- /dev/null +++ b/bin/reth/src/args/rpc_state_cache_args.rs @@ -0,0 +1,54 @@ +use clap::{builder::RangedU64ValueParser, Args}; +use reth_rpc::eth::cache::{ + DEFAULT_BLOCK_CACHE_MAX_LEN, DEFAULT_CONCURRENT_DB_REQUESTS, DEFAULT_ENV_CACHE_MAX_LEN, + DEFAULT_RECEIPT_CACHE_MAX_LEN, +}; + +/// Parameters to configure RPC state cache. +#[derive(Debug, Clone, Args, PartialEq, Eq)] +#[clap(next_help_heading = "RPC State Cache")] + +pub struct RpcStateCacheArgs { + /// Max number of blocks in cache. + #[arg( + long = "rpc-cache.max-blocks", + default_value_t = DEFAULT_BLOCK_CACHE_MAX_LEN, + value_parser = RangedU64ValueParser::::new().range(1..) + )] + pub max_blocks: u32, + + /// Max number receipts in cache. + #[arg( + long = "rpc-cache.max-receipts", + default_value_t = DEFAULT_RECEIPT_CACHE_MAX_LEN, + value_parser = RangedU64ValueParser::::new().range(1..) + )] + pub max_receipts: u32, + + /// Max number of bytes for cached env data. + #[arg( + long = "rpc-cache.max-envs", + default_value_t = DEFAULT_ENV_CACHE_MAX_LEN, + value_parser = RangedU64ValueParser::::new().range(1..) + )] + pub max_envs: u32, + + /// Max number of concurrent database requests. + #[arg( + long = "rpc-cache.max-concurrent-db-requests", + default_value_t = DEFAULT_CONCURRENT_DB_REQUESTS, + value_parser = RangedU64ValueParser::::new().range(1..) + )] + pub max_concurrent_db_requests: usize, +} + +impl Default for RpcStateCacheArgs { + fn default() -> Self { + Self { + max_blocks: DEFAULT_BLOCK_CACHE_MAX_LEN, + max_receipts: DEFAULT_RECEIPT_CACHE_MAX_LEN, + max_envs: DEFAULT_ENV_CACHE_MAX_LEN, + max_concurrent_db_requests: DEFAULT_CONCURRENT_DB_REQUESTS, + } + } +} diff --git a/bin/reth/src/cli/config.rs b/bin/reth/src/cli/config.rs index 48c1e2bd5fed..2d3847bbb59c 100644 --- a/bin/reth/src/cli/config.rs +++ b/bin/reth/src/cli/config.rs @@ -3,7 +3,10 @@ use alloy_rlp::Encodable; use reth_network::protocol::IntoRlpxSubProtocol; use reth_primitives::{Bytes, BytesMut}; -use reth_rpc::{eth::gas_oracle::GasPriceOracleConfig, JwtError, JwtSecret}; +use reth_rpc::{ + eth::{cache::EthStateCacheConfig, gas_oracle::GasPriceOracleConfig}, + JwtError, JwtSecret, +}; use reth_rpc_builder::{ auth::AuthServerConfig, error::RpcError, EthConfig, IpcServerBuilder, RpcServerConfig, ServerBuilder, TransportRpcModuleConfig, @@ -24,6 +27,9 @@ pub trait RethRpcConfig { /// The configured ethereum RPC settings. fn eth_config(&self) -> EthConfig; + /// Returns state cache configuration. + fn state_cache_config(&self) -> EthStateCacheConfig; + /// Returns the max request size in bytes. fn rpc_max_request_size_bytes(&self) -> u32; From b3b84c4e33feb91df415be7c6cdeede3c91c5d1d Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Sun, 3 Dec 2023 11:34:06 +0100 Subject: [PATCH 083/277] chore(deps): weekly `cargo update` (#5667) Co-authored-by: github-merge-queue --- Cargo.lock | 70 +++++++++++++++++++++++++++--------------------------- 1 file changed, 35 insertions(+), 35 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 0a61aed99f31..a431aef5b42c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -140,9 +140,9 @@ checksum = "0942ffc6dcaadf03badf6e6a2d0228460359d5e34b57ccdc720b7382dfbd5ec5" [[package]] name = "alloy-dyn-abi" -version = "0.5.1" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a80060a7b7cbcb40c78fe763130078f6e8b49d7d9a5b0d0f9c508d28b094b60" +checksum = "fafc3b20c6d069d9db47037f34acfb0e079c050fa5c1ff9e79855609b359b92b" dependencies = [ "alloy-json-abi", "alloy-primitives", @@ -158,9 +158,9 @@ dependencies = [ [[package]] name = "alloy-json-abi" -version = "0.5.1" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef4dff5c682e16e398783e8da63956bdf22ce48841e6ca367f8a8a6b1f2ed989" +checksum = "8d32061da2f184e5defab8e65a3057f88b7017cfe1ea9e2d6b413edb5ca76a54" dependencies = [ "alloy-primitives", "alloy-sol-type-parser", @@ -170,9 +170,9 @@ dependencies = [ [[package]] name = "alloy-primitives" -version = "0.5.1" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3a3877a2fa7474d9367d01ab43e33a4a4eeedf9bf7ec481533520686e956703b" +checksum = "08ca2c09d5911548a5cb620382ea0e1af99d3c898ce0efecbbd274a4676cf53e" dependencies = [ "alloy-rlp", "arbitrary", @@ -218,9 +218,9 @@ dependencies = [ [[package]] name = "alloy-sol-macro" -version = "0.5.1" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "123b602fbffdfb514183bcca2e5e7428b41ed5d59580ae70bf8eca937d165427" +checksum = "e40cea54ac58080a1b88ea6556866eac1902b321186c77d53ad2b5ebbbf0e038" dependencies = [ "const-hex", "dunce", @@ -236,18 +236,18 @@ dependencies = [ [[package]] name = "alloy-sol-type-parser" -version = "0.5.1" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e4648000cc0f043d0cdded1650bf4a1c075da0367cd9c80deda48db870985cbd" +checksum = "f23cb462613b2046da46dbf69ebaee458b7bfd3e9d7fe05adcce38a8d4b8a14f" dependencies = [ "winnow", ] [[package]] name = "alloy-sol-types" -version = "0.5.1" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c4c333ed7d5a4d87e3b8ad2ce230d4f9347b9e43faf98ad16ba58bf318cd023" +checksum = "f81aa34725607be118c395d62c1d8d97c8a343dd1ada5370ed508ed5232eab6a" dependencies = [ "alloy-primitives", "alloy-sol-macro", @@ -1377,9 +1377,9 @@ dependencies = [ [[package]] name = "concurrent-queue" -version = "2.3.0" +version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f057a694a54f12365049b0958a1685bb52d567f5593b355fbf685838e873d400" +checksum = "d16048cd947b08fa32c24458a22f5dc5e835264f689f4f5653210c69fd107363" dependencies = [ "crossbeam-utils", ] @@ -1891,9 +1891,9 @@ dependencies = [ [[package]] name = "deranged" -version = "0.3.9" +version = "0.3.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f32d04922c60427da6f9fef14d042d9edddef64cb9d4ce0d64d0685fbeb1fd3" +checksum = "8eb30d70a07a3b04884d2677f06bec33509dc67ca60d92949e5535352d3191dc" dependencies = [ "powerfmt", "serde", @@ -4039,9 +4039,9 @@ dependencies = [ [[package]] name = "linux-raw-sys" -version = "0.4.11" +version = "0.4.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "969488b55f8ac402214f3f5fd243ebb7206cf82de60d3172994707a4bcc2b829" +checksum = "c4cd1a83af159aa67994778be9070f0ae1bd732942279cabb14f86f986a21456" [[package]] name = "litemap" @@ -6846,15 +6846,15 @@ dependencies = [ [[package]] name = "rustix" -version = "0.38.25" +version = "0.38.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc99bc2d4f1fed22595588a013687477aedf3cdcfb26558c559edb67b4d9b22e" +checksum = "9470c4bf8246c8daf25f9598dca807fb6510347b1e1cfa55749113850c79d88a" dependencies = [ "bitflags 2.4.1", "errno", "libc", "linux-raw-sys", - "windows-sys 0.48.0", + "windows-sys 0.52.0", ] [[package]] @@ -7627,9 +7627,9 @@ dependencies = [ [[package]] name = "symbolic-common" -version = "12.7.1" +version = "12.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ba238da6058509cda85e388e8bf1caf522894ec38da490de9694fab9238c715" +checksum = "1cccfffbc6bb3bb2d3a26cd2077f4d055f6808d266f9d4d158797a4c60510dfe" dependencies = [ "debugid", "memmap2 0.9.0", @@ -7639,9 +7639,9 @@ dependencies = [ [[package]] name = "symbolic-demangle" -version = "12.7.1" +version = "12.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b7f36fed2b0d1f3db882e5edb9d1b6595dd8cc095f52f265b17291f09062b70" +checksum = "76a99812da4020a67e76c4eb41f08c87364c14170495ff780f30dd519c221a68" dependencies = [ "cpp_demangle", "rustc-demangle", @@ -7672,9 +7672,9 @@ dependencies = [ [[package]] name = "syn-solidity" -version = "0.5.1" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf07302c875aa7e1ee4fb811fdf2a0436bb04640639539003dcab2e049a9d1fe" +checksum = "e2c7ad08db24862d5b787a94714ff6b047935c3e3f60af944ac969404debd7ff" dependencies = [ "paste", "proc-macro2", @@ -8528,15 +8528,15 @@ checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9" [[package]] name = "utf16_iter" -version = "1.0.4" +version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52df8b7fb78e7910d776fccf2e42ceaf3604d55e8e7eb2dbd183cb1441d8a692" +checksum = "c8232dd3cdaed5356e0f716d285e4b40b932ac434100fe9b7e0e8e935b9e6246" [[package]] name = "utf8_iter" -version = "1.0.3" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64a8922555b9500e3d865caed19330172cd67cbf82203f1a3311d8c305cc9f33" +checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" [[package]] name = "utf8parse" @@ -9114,18 +9114,18 @@ dependencies = [ [[package]] name = "zerocopy" -version = "0.7.27" +version = "0.7.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f43de342578a3a14a9314a2dab1942cbfcbe5686e1f91acdc513058063eafe18" +checksum = "7d6f15f7ade05d2a4935e34a457b936c23dc70a05cc1d97133dc99e7a3fe0f0e" dependencies = [ "zerocopy-derive", ] [[package]] name = "zerocopy-derive" -version = "0.7.27" +version = "0.7.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e1012d89e3acb79fad7a799ce96866cfb8098b74638465ea1b1533d35900ca90" +checksum = "dbbad221e3f78500350ecbd7dfa4e63ef945c05f4c61cb7f4d3f84cd0bba649b" dependencies = [ "proc-macro2", "quote", From 270b9e0f69f6db7fb0bcc459139d672e7a0c9bf5 Mon Sep 17 00:00:00 2001 From: Bjerg Date: Mon, 4 Dec 2023 13:30:29 +0100 Subject: [PATCH 084/277] docs: nit in parity trace builder (#5673) --- crates/revm/revm-inspectors/src/tracing/builder/parity.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/revm/revm-inspectors/src/tracing/builder/parity.rs b/crates/revm/revm-inspectors/src/tracing/builder/parity.rs index d091a019b268..cbfcea6b4a14 100644 --- a/crates/revm/revm-inspectors/src/tracing/builder/parity.rs +++ b/crates/revm/revm-inspectors/src/tracing/builder/parity.rs @@ -139,7 +139,7 @@ impl ParityTraceBuilder { }) } - /// Returns an iterator over all recorded traces for `trace_transaction` + /// Returns all recorded traces for `trace_transaction` pub fn into_localized_transaction_traces( self, info: TransactionInfo, From caa7b92518b24147881cf7de0ca8113ac8fd2913 Mon Sep 17 00:00:00 2001 From: Bjerg Date: Mon, 4 Dec 2023 13:25:20 +0100 Subject: [PATCH 085/277] chore: clippy (#5674) --- crates/consensus/auto-seal/src/lib.rs | 2 +- crates/payload/builder/src/payload.rs | 4 ++-- crates/rpc/rpc-types/src/eth/filter.rs | 2 +- crates/storage/nippy-jar/src/lib.rs | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/crates/consensus/auto-seal/src/lib.rs b/crates/consensus/auto-seal/src/lib.rs index 01035e35dc78..823a28bd73d9 100644 --- a/crates/consensus/auto-seal/src/lib.rs +++ b/crates/consensus/auto-seal/src/lib.rs @@ -251,7 +251,7 @@ impl StorageInner { /// transactions. pub(crate) fn build_header_template( &self, - transactions: &Vec, + transactions: &[TransactionSigned], chain_spec: Arc, ) -> Header { let timestamp = SystemTime::now().duration_since(UNIX_EPOCH).unwrap_or_default().as_secs(); diff --git a/crates/payload/builder/src/payload.rs b/crates/payload/builder/src/payload.rs index 4f1252e245a7..da436d9116b6 100644 --- a/crates/payload/builder/src/payload.rs +++ b/crates/payload/builder/src/payload.rs @@ -169,7 +169,7 @@ impl PayloadBuilderAttributes { #[cfg(feature = "optimism")] let (id, transactions) = { - let transactions = attributes + let transactions: Vec<_> = attributes .optimism_payload_attributes .transactions .as_deref() @@ -276,7 +276,7 @@ impl PayloadBuilderAttributes { pub(crate) fn payload_id( parent: &B256, attributes: &PayloadAttributes, - #[cfg(feature = "optimism")] txs: &Vec, + #[cfg(feature = "optimism")] txs: &[TransactionSigned], ) -> PayloadId { use sha2::Digest; let mut hasher = sha2::Sha256::new(); diff --git a/crates/rpc/rpc-types/src/eth/filter.rs b/crates/rpc/rpc-types/src/eth/filter.rs index 0a89e1b2091b..74ead5cfc85b 100644 --- a/crates/rpc/rpc-types/src/eth/filter.rs +++ b/crates/rpc/rpc-types/src/eth/filter.rs @@ -728,7 +728,7 @@ impl FilteredParams { } /// Returns `true` if the bloom matches the topics - pub fn matches_topics(bloom: Bloom, topic_filters: &Vec) -> bool { + pub fn matches_topics(bloom: Bloom, topic_filters: &[BloomFilter]) -> bool { if topic_filters.is_empty() { return true } diff --git a/crates/storage/nippy-jar/src/lib.rs b/crates/storage/nippy-jar/src/lib.rs index 435359e87030..aedc91d568da 100644 --- a/crates/storage/nippy-jar/src/lib.rs +++ b/crates/storage/nippy-jar/src/lib.rs @@ -454,7 +454,7 @@ where /// Safety checks before creating and returning a [`File`] handle to write data to. fn freeze_check( &mut self, - columns: &Vec>>>, + columns: &[impl IntoIterator>>], ) -> Result { if columns.len() != self.columns { return Err(NippyJarError::ColumnLenMismatch(self.columns, columns.len())) From 6f7d6d50160e0dbb84de5b74f9794e14cc04406b Mon Sep 17 00:00:00 2001 From: Bjerg Date: Mon, 4 Dec 2023 13:59:10 +0100 Subject: [PATCH 086/277] chore: make trace builder ctrs public (#5672) --- crates/revm/revm-inspectors/src/tracing/builder/geth.rs | 2 +- crates/revm/revm-inspectors/src/tracing/builder/parity.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/revm/revm-inspectors/src/tracing/builder/geth.rs b/crates/revm/revm-inspectors/src/tracing/builder/geth.rs index 4e456c42b7ff..247b4bb08ad0 100644 --- a/crates/revm/revm-inspectors/src/tracing/builder/geth.rs +++ b/crates/revm/revm-inspectors/src/tracing/builder/geth.rs @@ -24,7 +24,7 @@ pub struct GethTraceBuilder { impl GethTraceBuilder { /// Returns a new instance of the builder - pub(crate) fn new(nodes: Vec, _config: TracingInspectorConfig) -> Self { + pub fn new(nodes: Vec, _config: TracingInspectorConfig) -> Self { Self { nodes, _config } } diff --git a/crates/revm/revm-inspectors/src/tracing/builder/parity.rs b/crates/revm/revm-inspectors/src/tracing/builder/parity.rs index cbfcea6b4a14..daafe0f4f88d 100644 --- a/crates/revm/revm-inspectors/src/tracing/builder/parity.rs +++ b/crates/revm/revm-inspectors/src/tracing/builder/parity.rs @@ -32,7 +32,7 @@ pub struct ParityTraceBuilder { impl ParityTraceBuilder { /// Returns a new instance of the builder - pub(crate) fn new( + pub fn new( nodes: Vec, spec_id: Option, _config: TracingInspectorConfig, From b34b0d3c8de2598b2976f7ee2fc1a166c50b1b94 Mon Sep 17 00:00:00 2001 From: Alexey Shekhirin Date: Mon, 4 Dec 2023 14:07:17 +0000 Subject: [PATCH 087/277] release: v0.1.0-alpha.13 (#5676) --- Cargo.lock | 96 +++++++++++++++++++++++++++--------------------------- Cargo.toml | 2 +- 2 files changed, 49 insertions(+), 49 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index a431aef5b42c..c072cbf05681 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1294,7 +1294,7 @@ checksum = "67ba02a97a2bd10f4b59b25c7973101c79642302776489e030cd13cdab09ed15" [[package]] name = "codecs-derive" -version = "0.1.0-alpha.12" +version = "0.1.0-alpha.13" dependencies = [ "convert_case 0.6.0", "parity-scale-codec", @@ -2184,7 +2184,7 @@ dependencies = [ [[package]] name = "ef-tests" -version = "0.1.0-alpha.12" +version = "0.1.0-alpha.13" dependencies = [ "alloy-rlp", "reth-db", @@ -5523,7 +5523,7 @@ dependencies = [ [[package]] name = "reth" -version = "0.1.0-alpha.12" +version = "0.1.0-alpha.13" dependencies = [ "alloy-rlp", "aquamarine", @@ -5601,7 +5601,7 @@ dependencies = [ [[package]] name = "reth-auto-seal-consensus" -version = "0.1.0-alpha.12" +version = "0.1.0-alpha.13" dependencies = [ "clap", "eyre", @@ -5624,7 +5624,7 @@ dependencies = [ [[package]] name = "reth-basic-payload-builder" -version = "0.1.0-alpha.12" +version = "0.1.0-alpha.13" dependencies = [ "alloy-rlp", "futures-core", @@ -5645,7 +5645,7 @@ dependencies = [ [[package]] name = "reth-beacon-consensus" -version = "0.1.0-alpha.12" +version = "0.1.0-alpha.13" dependencies = [ "assert_matches", "cfg-if", @@ -5678,7 +5678,7 @@ dependencies = [ [[package]] name = "reth-blockchain-tree" -version = "0.1.0-alpha.12" +version = "0.1.0-alpha.13" dependencies = [ "aquamarine", "assert_matches", @@ -5698,7 +5698,7 @@ dependencies = [ [[package]] name = "reth-codecs" -version = "0.1.0-alpha.12" +version = "0.1.0-alpha.13" dependencies = [ "alloy-primitives", "arbitrary", @@ -5713,7 +5713,7 @@ dependencies = [ [[package]] name = "reth-config" -version = "0.1.0-alpha.12" +version = "0.1.0-alpha.13" dependencies = [ "confy", "reth-discv4", @@ -5730,7 +5730,7 @@ dependencies = [ [[package]] name = "reth-consensus-common" -version = "0.1.0-alpha.12" +version = "0.1.0-alpha.13" dependencies = [ "assert_matches", "cfg-if", @@ -5742,7 +5742,7 @@ dependencies = [ [[package]] name = "reth-db" -version = "0.1.0-alpha.12" +version = "0.1.0-alpha.13" dependencies = [ "arbitrary", "assert_matches", @@ -5787,7 +5787,7 @@ dependencies = [ [[package]] name = "reth-discv4" -version = "0.1.0-alpha.12" +version = "0.1.0-alpha.13" dependencies = [ "alloy-rlp", "discv5", @@ -5810,7 +5810,7 @@ dependencies = [ [[package]] name = "reth-dns-discovery" -version = "0.1.0-alpha.12" +version = "0.1.0-alpha.13" dependencies = [ "alloy-rlp", "async-trait", @@ -5834,7 +5834,7 @@ dependencies = [ [[package]] name = "reth-downloaders" -version = "0.1.0-alpha.12" +version = "0.1.0-alpha.13" dependencies = [ "alloy-rlp", "assert_matches", @@ -5861,7 +5861,7 @@ dependencies = [ [[package]] name = "reth-ecies" -version = "0.1.0-alpha.12" +version = "0.1.0-alpha.13" dependencies = [ "aes 0.8.3", "alloy-rlp", @@ -5891,7 +5891,7 @@ dependencies = [ [[package]] name = "reth-eth-wire" -version = "0.1.0-alpha.12" +version = "0.1.0-alpha.13" dependencies = [ "alloy-rlp", "arbitrary", @@ -5923,7 +5923,7 @@ dependencies = [ [[package]] name = "reth-ethereum-forks" -version = "0.1.0-alpha.12" +version = "0.1.0-alpha.13" dependencies = [ "alloy-primitives", "alloy-rlp", @@ -5939,7 +5939,7 @@ dependencies = [ [[package]] name = "reth-interfaces" -version = "0.1.0-alpha.12" +version = "0.1.0-alpha.13" dependencies = [ "arbitrary", "async-trait", @@ -5966,7 +5966,7 @@ dependencies = [ [[package]] name = "reth-ipc" -version = "0.1.0-alpha.12" +version = "0.1.0-alpha.13" dependencies = [ "async-trait", "bytes", @@ -5985,7 +5985,7 @@ dependencies = [ [[package]] name = "reth-libmdbx" -version = "0.1.0-alpha.12" +version = "0.1.0-alpha.13" dependencies = [ "bitflags 2.4.1", "byteorder", @@ -6004,7 +6004,7 @@ dependencies = [ [[package]] name = "reth-mdbx-sys" -version = "0.1.0-alpha.12" +version = "0.1.0-alpha.13" dependencies = [ "bindgen 0.68.1", "cc", @@ -6013,7 +6013,7 @@ dependencies = [ [[package]] name = "reth-metrics" -version = "0.1.0-alpha.12" +version = "0.1.0-alpha.13" dependencies = [ "futures", "metrics", @@ -6024,7 +6024,7 @@ dependencies = [ [[package]] name = "reth-metrics-derive" -version = "0.1.0-alpha.12" +version = "0.1.0-alpha.13" dependencies = [ "metrics", "once_cell", @@ -6038,7 +6038,7 @@ dependencies = [ [[package]] name = "reth-net-common" -version = "0.1.0-alpha.12" +version = "0.1.0-alpha.13" dependencies = [ "pin-project", "reth-primitives", @@ -6047,7 +6047,7 @@ dependencies = [ [[package]] name = "reth-net-nat" -version = "0.1.0-alpha.12" +version = "0.1.0-alpha.13" dependencies = [ "igd", "pin-project-lite", @@ -6061,7 +6061,7 @@ dependencies = [ [[package]] name = "reth-network" -version = "0.1.0-alpha.12" +version = "0.1.0-alpha.13" dependencies = [ "alloy-rlp", "aquamarine", @@ -6111,7 +6111,7 @@ dependencies = [ [[package]] name = "reth-network-api" -version = "0.1.0-alpha.12" +version = "0.1.0-alpha.13" dependencies = [ "async-trait", "reth-discv4", @@ -6125,7 +6125,7 @@ dependencies = [ [[package]] name = "reth-nippy-jar" -version = "0.1.0-alpha.12" +version = "0.1.0-alpha.13" dependencies = [ "anyhow", "bincode", @@ -6145,7 +6145,7 @@ dependencies = [ [[package]] name = "reth-payload-builder" -version = "0.1.0-alpha.12" +version = "0.1.0-alpha.13" dependencies = [ "alloy-rlp", "futures-util", @@ -6167,7 +6167,7 @@ dependencies = [ [[package]] name = "reth-primitives" -version = "0.1.0-alpha.12" +version = "0.1.0-alpha.13" dependencies = [ "alloy-primitives", "alloy-rlp", @@ -6214,7 +6214,7 @@ dependencies = [ [[package]] name = "reth-provider" -version = "0.1.0-alpha.12" +version = "0.1.0-alpha.13" dependencies = [ "alloy-rlp", "assert_matches", @@ -6241,7 +6241,7 @@ dependencies = [ [[package]] name = "reth-prune" -version = "0.1.0-alpha.12" +version = "0.1.0-alpha.13" dependencies = [ "assert_matches", "itertools 0.11.0", @@ -6263,7 +6263,7 @@ dependencies = [ [[package]] name = "reth-revm" -version = "0.1.0-alpha.12" +version = "0.1.0-alpha.13" dependencies = [ "reth-consensus-common", "reth-interfaces", @@ -6277,7 +6277,7 @@ dependencies = [ [[package]] name = "reth-revm-inspectors" -version = "0.1.0-alpha.12" +version = "0.1.0-alpha.13" dependencies = [ "alloy-primitives", "alloy-sol-types", @@ -6293,7 +6293,7 @@ dependencies = [ [[package]] name = "reth-rpc" -version = "0.1.0-alpha.12" +version = "0.1.0-alpha.13" dependencies = [ "alloy-dyn-abi", "alloy-primitives", @@ -6346,7 +6346,7 @@ dependencies = [ [[package]] name = "reth-rpc-api" -version = "0.1.0-alpha.12" +version = "0.1.0-alpha.13" dependencies = [ "jsonrpsee", "reth-primitives", @@ -6356,7 +6356,7 @@ dependencies = [ [[package]] name = "reth-rpc-api-testing-util" -version = "0.1.0-alpha.12" +version = "0.1.0-alpha.13" dependencies = [ "async-trait", "futures", @@ -6371,7 +6371,7 @@ dependencies = [ [[package]] name = "reth-rpc-builder" -version = "0.1.0-alpha.12" +version = "0.1.0-alpha.13" dependencies = [ "hyper", "jsonrpsee", @@ -6404,7 +6404,7 @@ dependencies = [ [[package]] name = "reth-rpc-engine-api" -version = "0.1.0-alpha.12" +version = "0.1.0-alpha.13" dependencies = [ "alloy-rlp", "assert_matches", @@ -6430,7 +6430,7 @@ dependencies = [ [[package]] name = "reth-rpc-types" -version = "0.1.0-alpha.12" +version = "0.1.0-alpha.13" dependencies = [ "alloy-primitives", "alloy-rlp", @@ -6454,7 +6454,7 @@ dependencies = [ [[package]] name = "reth-rpc-types-compat" -version = "0.1.0-alpha.12" +version = "0.1.0-alpha.13" dependencies = [ "alloy-rlp", "reth-primitives", @@ -6463,7 +6463,7 @@ dependencies = [ [[package]] name = "reth-snapshot" -version = "0.1.0-alpha.12" +version = "0.1.0-alpha.13" dependencies = [ "assert_matches", "clap", @@ -6481,7 +6481,7 @@ dependencies = [ [[package]] name = "reth-stages" -version = "0.1.0-alpha.12" +version = "0.1.0-alpha.13" dependencies = [ "alloy-rlp", "aquamarine", @@ -6521,7 +6521,7 @@ dependencies = [ [[package]] name = "reth-tasks" -version = "0.1.0-alpha.12" +version = "0.1.0-alpha.13" dependencies = [ "dyn-clone", "futures-util", @@ -6535,7 +6535,7 @@ dependencies = [ [[package]] name = "reth-tokio-util" -version = "0.1.0-alpha.12" +version = "0.1.0-alpha.13" dependencies = [ "tokio", "tokio-stream", @@ -6543,7 +6543,7 @@ dependencies = [ [[package]] name = "reth-tracing" -version = "0.1.0-alpha.12" +version = "0.1.0-alpha.13" dependencies = [ "rolling-file", "tracing", @@ -6554,7 +6554,7 @@ dependencies = [ [[package]] name = "reth-transaction-pool" -version = "0.1.0-alpha.12" +version = "0.1.0-alpha.13" dependencies = [ "alloy-rlp", "aquamarine", @@ -6588,7 +6588,7 @@ dependencies = [ [[package]] name = "reth-trie" -version = "0.1.0-alpha.12" +version = "0.1.0-alpha.13" dependencies = [ "alloy-rlp", "auto_impl", diff --git a/Cargo.toml b/Cargo.toml index f895fd82f46c..00b8726f5639 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -63,7 +63,7 @@ default-members = ["bin/reth"] resolver = "2" [workspace.package] -version = "0.1.0-alpha.12" +version = "0.1.0-alpha.13" edition = "2021" rust-version = "1.70" license = "MIT OR Apache-2.0" From dffa46200343f10ed04977343e8c51ae9ee525fc Mon Sep 17 00:00:00 2001 From: Bjerg Date: Mon, 4 Dec 2023 15:35:15 +0100 Subject: [PATCH 088/277] chore: use `human_bytes` for snapshot sizes (#5675) Co-authored-by: Matthias Seitz --- bin/reth/src/db/snapshots/mod.rs | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/bin/reth/src/db/snapshots/mod.rs b/bin/reth/src/db/snapshots/mod.rs index b3909c29d194..7e6980bf3ba9 100644 --- a/bin/reth/src/db/snapshots/mod.rs +++ b/bin/reth/src/db/snapshots/mod.rs @@ -1,4 +1,5 @@ use clap::{builder::RangedU64ValueParser, Parser}; +use human_bytes::human_bytes; use itertools::Itertools; use rayon::iter::{IntoParallelIterator, ParallelIterator}; use reth_db::{database::Database, open_db_read_only, DatabaseEnv}; @@ -204,7 +205,6 @@ impl Command { /// statistics about various aspects of each snapshot, such as filters size, /// offset index size, offset list size, and loading time. fn stats(&self, snapshots: Vec>) -> eyre::Result<()> { - let mb = 1024.0 * 1024.0; let mut total_filters_size = 0; let mut total_index_size = 0; let mut total_offsets_size = 0; @@ -224,10 +224,10 @@ impl Command { total_file_size += file_size; println!("Snapshot: {:?}", snap.as_ref().file_name()); - println!(" File Size: {:>7.2} MB", file_size as f64 / mb); - println!(" Filters Size: {:>7.2} MB", jar.filter_size() as f64 / mb); - println!(" Offset Index Size: {:>7.2} MB", jar.offsets_index_size() as f64 / mb); - println!(" Offset List Size: {:>7.2} MB", jar.offsets_size() as f64 / mb); + println!(" File Size: {:>7}", human_bytes(file_size as f64)); + println!(" Filters Size: {:>7}", human_bytes(jar.filter_size() as f64)); + println!(" Offset Index Size: {:>7}", human_bytes(jar.offsets_index_size() as f64)); + println!(" Offset List Size: {:>7}", human_bytes(jar.offsets_size() as f64)); println!( " Loading Time: {:>7.2} ms | {:>7.2} µs", duration.as_millis() as f64, @@ -237,10 +237,10 @@ impl Command { let avg_duration = total_duration / snapshots.len() as u32; - println!("Total Filters Size: {:>7.2} MB", total_filters_size as f64 / mb); - println!("Total Offset Index Size: {:>7.2} MB", total_index_size as f64 / mb); - println!("Total Offset List Size: {:>7.2} MB", total_offsets_size as f64 / mb); - println!("Total File Size: {:>7.2} GB", total_file_size as f64 / (mb * 1024.0)); + println!("Total Filters Size: {:>7}", human_bytes(total_filters_size as f64)); + println!("Total Offset Index Size: {:>7}", human_bytes(total_index_size as f64)); + println!("Total Offset List Size: {:>7}", human_bytes(total_offsets_size as f64)); + println!("Total File Size: {:>7}", human_bytes(total_file_size as f64)); println!( "Average Loading Time: {:>7.2} ms | {:>7.2} µs", avg_duration.as_millis() as f64, From 05558008187740ae1a4aced6996aad25d5a72df6 Mon Sep 17 00:00:00 2001 From: chipmonkclarity <149391454+chipmonkclarity@users.noreply.github.com> Date: Mon, 4 Dec 2023 17:07:09 +0200 Subject: [PATCH 089/277] add troubleshooting section on concurrency with containers (#5678) --- book/run/troubleshooting.md | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/book/run/troubleshooting.md b/book/run/troubleshooting.md index 41dcdcae3603..f9a6bd25c8a3 100644 --- a/book/run/troubleshooting.md +++ b/book/run/troubleshooting.md @@ -93,4 +93,21 @@ Caused by: make db-tools ./db-tools/mdbx_chk $(reth db path)/mdbx.dat | tee mdbx_chk.log ``` - If `mdbx_chk` has detected any errors, please [open an issue](https://github.com/paradigmxyz/reth/issues) and post the output from the `mdbx_chk.log` file. \ No newline at end of file + If `mdbx_chk` has detected any errors, please [open an issue](https://github.com/paradigmxyz/reth/issues) and post the output from the `mdbx_chk.log` file. + +### Concurrent database access error (using containers/Docker) + +If you encounter an error while accessing the database from multiple processes and you are using multiple containers or a mix of host and container(s), it is possible the error is related to `PID` namespaces. You might see one of the following error messages. + +```console +mdbx:0: panic: Assertion `osal_rdt_unlock() failed: err 1' failed. +``` +or + +```console +pthread_mutex_lock.c:438: __pthread_mutex_lock_full: Assertion `e != ESRCH || !robust' failed +``` + +If you are using Docker, a possible solution is to run all database-accessing containers with `--pid=host` flag. + +For more information, check out the `Containers` section in the [libmdbx README](https://github.com/erthink/libmdbx#containers). \ No newline at end of file From f15e87825061a7bf11c1eb685a5cf7815652a3ec Mon Sep 17 00:00:00 2001 From: Emilia Hane Date: Mon, 4 Dec 2023 17:01:40 +0100 Subject: [PATCH 090/277] Fix eth message ID length bug (#5666) --- crates/net/eth-wire/src/capability.rs | 12 +++++++++--- crates/net/eth-wire/src/protocol.rs | 12 ++++++++++-- crates/net/eth-wire/src/types/message.rs | 7 +++++++ 3 files changed, 26 insertions(+), 5 deletions(-) diff --git a/crates/net/eth-wire/src/capability.rs b/crates/net/eth-wire/src/capability.rs index 8f090c72a2e4..c1e6d84ab616 100644 --- a/crates/net/eth-wire/src/capability.rs +++ b/crates/net/eth-wire/src/capability.rs @@ -5,7 +5,7 @@ use crate::{ p2pstream::MAX_RESERVED_MESSAGE_ID, protocol::{ProtoVersion, Protocol}, version::ParseVersionError, - EthMessage, EthVersion, + EthMessage, EthMessageID, EthVersion, }; use alloy_rlp::{Decodable, Encodable, RlpDecodable, RlpEncodable}; use reth_codecs::add_arbitrary_tests; @@ -98,6 +98,12 @@ impl Capability { pub fn is_eth_v68(&self) -> bool { self.name == "eth" && self.version == 68 } + + /// Whether this is any eth version. + #[inline] + pub fn is_eth(&self) -> bool { + self.is_eth_v66() || self.is_eth_v67() || self.is_eth_v68() + } } impl fmt::Display for Capability { @@ -320,7 +326,7 @@ impl SharedCapability { /// Returns the number of protocol messages supported by this capability. pub fn num_messages(&self) -> Result { match self { - SharedCapability::Eth { version, .. } => Ok(version.total_messages()), + SharedCapability::Eth { version: _version, .. } => Ok(EthMessageID::max() + 1), _ => Err(SharedCapabilityError::UnknownCapability), } } @@ -676,7 +682,7 @@ mod tests { assert_eq!(shared_eth.name(), proto.cap.name); // the 6th shared message is the first message of the eth capability - let shared_eth = shared.find_by_relative_offset(1 + proto.messages).unwrap(); + let shared_eth = shared.find_by_relative_offset(1 + proto.messages()).unwrap(); assert_eq!(shared_eth.name(), "eth"); } } diff --git a/crates/net/eth-wire/src/protocol.rs b/crates/net/eth-wire/src/protocol.rs index 17bcc816a94b..5cbfdc22440c 100644 --- a/crates/net/eth-wire/src/protocol.rs +++ b/crates/net/eth-wire/src/protocol.rs @@ -1,6 +1,6 @@ //! A Protocol defines a P2P subprotocol in a RLPx connection -use crate::{capability::Capability, EthVersion}; +use crate::{capability::Capability, EthMessageID, EthVersion}; /// Type that represents a [Capability] and the number of messages it uses. /// @@ -14,7 +14,7 @@ pub struct Protocol { /// The number of messages used/reserved by this protocol /// /// This is used for message ID multiplexing - pub messages: u8, + messages: u8, } impl Protocol { @@ -50,6 +50,14 @@ impl Protocol { pub(crate) fn split(self) -> (Capability, u8) { (self.cap, self.messages) } + + /// The number of values needed to represent all message IDs of capability. + pub fn messages(&self) -> u8 { + if self.cap.is_eth() { + return EthMessageID::max() + 1 + } + self.messages + } } impl From for Protocol { diff --git a/crates/net/eth-wire/src/types/message.rs b/crates/net/eth-wire/src/types/message.rs index e405f727a295..e33f17b5207b 100644 --- a/crates/net/eth-wire/src/types/message.rs +++ b/crates/net/eth-wire/src/types/message.rs @@ -319,6 +319,13 @@ pub enum EthMessageID { Receipts = 0x10, } +impl EthMessageID { + /// Returns the max value. + pub const fn max() -> u8 { + Self::Receipts as u8 + } +} + impl Encodable for EthMessageID { fn encode(&self, out: &mut dyn BufMut) { out.put_u8(*self as u8); From 73a5b6ced9fb71f485fe3ced97101cbb84bcf348 Mon Sep 17 00:00:00 2001 From: Matthias Seitz Date: Mon, 4 Dec 2023 19:27:57 +0100 Subject: [PATCH 091/277] feat: add debug_getrawTransactions (#5682) --- crates/rpc/rpc-api/src/debug.rs | 4 ++++ crates/rpc/rpc/src/debug.rs | 14 ++++++++++++- crates/storage/provider/src/traits/block.rs | 23 ++++++++++++++++++++- 3 files changed, 39 insertions(+), 2 deletions(-) diff --git a/crates/rpc/rpc-api/src/debug.rs b/crates/rpc/rpc-api/src/debug.rs index 1eb498aad109..3304d672b1ac 100644 --- a/crates/rpc/rpc-api/src/debug.rs +++ b/crates/rpc/rpc-api/src/debug.rs @@ -24,6 +24,10 @@ pub trait DebugApi { #[method(name = "getRawTransaction")] async fn raw_transaction(&self, hash: B256) -> RpcResult; + /// Returns an array of EIP-2718 binary-encoded transactions for the given [BlockId]. + #[method(name = "getRawTransactions")] + async fn raw_transactions(&self, block_id: BlockId) -> RpcResult>; + /// Returns an array of EIP-2718 binary-encoded receipts. #[method(name = "getRawReceipts")] async fn raw_receipts(&self, block_id: BlockId) -> RpcResult>; diff --git a/crates/rpc/rpc/src/debug.rs b/crates/rpc/rpc/src/debug.rs index 392c2172161e..5ce8df6cee0c 100644 --- a/crates/rpc/rpc/src/debug.rs +++ b/crates/rpc/rpc/src/debug.rs @@ -21,7 +21,7 @@ use reth_primitives::{ }, Address, Block, BlockId, BlockNumberOrTag, Bytes, TransactionSigned, B256, }; -use reth_provider::{BlockReaderIdExt, HeaderProvider, StateProviderBox}; +use reth_provider::{BlockReaderIdExt, HeaderProvider, StateProviderBox, TransactionVariant}; use reth_revm::{ database::{StateProviderDatabase, SubState}, tracing::{ @@ -895,6 +895,18 @@ where .unwrap_or_default()) } + /// Handler for `debug_getRawTransactions` + /// Returns the bytes of the transaction for the given hash. + async fn raw_transactions(&self, block_id: BlockId) -> RpcResult> { + let block = self + .inner + .provider + .block_with_senders_by_id(block_id, TransactionVariant::NoHash) + .to_rpc_result()? + .unwrap_or_default(); + Ok(block.into_transactions_ecrecovered().map(|tx| tx.envelope_encoded()).collect()) + } + /// Handler for `debug_getRawReceipts` async fn raw_receipts(&self, block_id: BlockId) -> RpcResult> { let receipts = diff --git a/crates/storage/provider/src/traits/block.rs b/crates/storage/provider/src/traits/block.rs index 137d14fbbff7..c128393b9058 100644 --- a/crates/storage/provider/src/traits/block.rs +++ b/crates/storage/provider/src/traits/block.rs @@ -187,11 +187,32 @@ pub trait BlockReaderIdExt: BlockReader + BlockIdReader + ReceiptProviderIdExt { self.sealed_header_by_id(BlockNumberOrTag::Finalized.into()) } - /// Returns the block with the matching `BlockId` from the database. + /// Returns the block with the matching [BlockId] from the database. /// /// Returns `None` if block is not found. fn block_by_id(&self, id: BlockId) -> ProviderResult>; + /// Returns the block with senders with matching [BlockId]. + /// + /// Returns the block's transactions in the requested variant. + /// + /// Returns `None` if block is not found. + fn block_with_senders_by_id( + &self, + id: BlockId, + transaction_kind: TransactionVariant, + ) -> ProviderResult> { + match id { + BlockId::Hash(hash) => { + self.block_with_senders(hash.block_hash.into(), transaction_kind) + } + BlockId::Number(num) => self.convert_block_number(num)?.map_or_else( + || Ok(None), + |num| self.block_with_senders(num.into(), transaction_kind), + ), + } + } + /// Returns the header with matching tag from the database /// /// Returns `None` if header is not found. From f7b08d4ab762fd41db50ab35d4730410c3d3921d Mon Sep 17 00:00:00 2001 From: Federico Gimenez Date: Mon, 4 Dec 2023 23:48:31 +0100 Subject: [PATCH 092/277] (storage): impl remaining todo! in mock module (#5681) --- crates/storage/db/src/abstraction/mock.rs | 34 +++++++++++------------ 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/crates/storage/db/src/abstraction/mock.rs b/crates/storage/db/src/abstraction/mock.rs index d25286c49c71..8c2cfb0fb35c 100644 --- a/crates/storage/db/src/abstraction/mock.rs +++ b/crates/storage/db/src/abstraction/mock.rs @@ -45,7 +45,7 @@ impl DbTx for TxMock { type DupCursor = CursorMock; fn get(&self, _key: T::Key) -> Result, DatabaseError> { - todo!() + Ok(None) } fn commit(self) -> Result { @@ -72,7 +72,7 @@ impl DbTxMut for TxMock { type DupCursorMut = CursorMock; fn put(&self, _key: T::Key, _value: T::Value) -> Result<(), DatabaseError> { - todo!() + Ok(()) } fn delete( @@ -80,19 +80,19 @@ impl DbTxMut for TxMock { _key: T::Key, _value: Option, ) -> Result { - todo!() + Ok(true) } fn clear(&self) -> Result<(), DatabaseError> { - todo!() + Ok(()) } fn cursor_write(&self) -> Result, DatabaseError> { - todo!() + Ok(CursorMock { _cursor: 0 }) } fn cursor_dup_write(&self) -> Result, DatabaseError> { - todo!() + Ok(CursorMock { _cursor: 0 }) } } @@ -174,15 +174,15 @@ impl DbCursorRO for CursorMock { impl DbDupCursorRO for CursorMock { fn next_dup(&mut self) -> PairResult { - todo!() + Ok(None) } fn next_no_dup(&mut self) -> PairResult { - todo!() + Ok(None) } fn next_dup_val(&mut self) -> ValueOnlyResult { - todo!() + Ok(None) } fn seek_by_key_subkey( @@ -190,7 +190,7 @@ impl DbDupCursorRO for CursorMock { _key: ::Key, _subkey: ::SubKey, ) -> ValueOnlyResult { - todo!() + Ok(None) } fn walk_dup( @@ -198,7 +198,7 @@ impl DbDupCursorRO for CursorMock { _key: Option<::Key>, _subkey: Option<::SubKey>, ) -> Result, DatabaseError> { - todo!() + Ok(DupWalker { cursor: self, start: None }) } } @@ -208,7 +208,7 @@ impl DbCursorRW for CursorMock { _key: ::Key, _value: ::Value, ) -> Result<(), DatabaseError> { - todo!() + Ok(()) } fn insert( @@ -216,7 +216,7 @@ impl DbCursorRW for CursorMock { _key: ::Key, _value: ::Value, ) -> Result<(), DatabaseError> { - todo!() + Ok(()) } fn append( @@ -224,20 +224,20 @@ impl DbCursorRW for CursorMock { _key: ::Key, _value: ::Value, ) -> Result<(), DatabaseError> { - todo!() + Ok(()) } fn delete_current(&mut self) -> Result<(), DatabaseError> { - todo!() + Ok(()) } } impl DbDupCursorRW for CursorMock { fn delete_current_duplicates(&mut self) -> Result<(), DatabaseError> { - todo!() + Ok(()) } fn append_dup(&mut self, _key: ::Key, _value: ::Value) -> Result<(), DatabaseError> { - todo!() + Ok(()) } } From 55f4c30210d5f455ddb0f66097f486f441fdb667 Mon Sep 17 00:00:00 2001 From: Matthias Seitz Date: Tue, 5 Dec 2023 00:36:36 +0100 Subject: [PATCH 093/277] fix: invoke enter,exit only for non root calls (#5686) --- .../revm-inspectors/src/tracing/js/mod.rs | 54 +++++++++++++++---- 1 file changed, 45 insertions(+), 9 deletions(-) diff --git a/crates/revm/revm-inspectors/src/tracing/js/mod.rs b/crates/revm/revm-inspectors/src/tracing/js/mod.rs index 792fd363b8c6..fe7bc8337eac 100644 --- a/crates/revm/revm-inspectors/src/tracing/js/mod.rs +++ b/crates/revm/revm-inspectors/src/tracing/js/mod.rs @@ -40,8 +40,16 @@ pub struct JsInspector { result_fn: JsObject, fault_fn: JsObject, - /// EVM inspector hook functions + // EVM inspector hook functions + /// Invoked when the EVM enters a new call that is _NOT_ the top level call. + /// + /// Corresponds to [Inspector::call] and [Inspector::create_end] but is also invoked on + /// [Inspector::selfdestruct]. enter_fn: Option, + /// Invoked when the EVM exits a call that is _NOT_ the top level call. + /// + /// Corresponds to [Inspector::call_end] and [Inspector::create_end] but also invoked after + /// selfdestruct. exit_fn: Option, /// Executed before each instruction is executed. step_fn: Option, @@ -62,6 +70,7 @@ impl JsInspector { /// - `fault`: a function that will be called when the transaction fails. /// /// Optional functions are invoked during inspection: + /// - `setup`: a function that will be called before the inspection starts. /// - `enter`: a function that will be called when the execution enters a new call. /// - `exit`: a function that will be called when the execution exits a call. /// - `step`: a function that will be called when the execution steps to the next instruction. @@ -246,6 +255,29 @@ impl JsInspector { self.call_stack.last().expect("call stack is empty") } + #[inline] + fn pop_call(&mut self) { + self.call_stack.pop(); + } + + /// Returns true whether the active call is the root call. + #[inline] + fn is_root_call_active(&self) -> bool { + self.call_stack.len() == 1 + } + + /// Returns true if there's an enter function and the active call is not the root call. + #[inline] + fn can_call_enter(&self) -> bool { + self.enter_fn.is_some() && !self.is_root_call_active() + } + + /// Returns true if there's an exit function and the active call is not the root call. + #[inline] + fn can_call_exit(&mut self) -> bool { + self.enter_fn.is_some() && !self.is_root_call_active() + } + /// Pushes a new call to the stack fn push_call( &mut self, @@ -265,10 +297,6 @@ impl JsInspector { self.active_call() } - fn pop_call(&mut self) { - self.call_stack.pop(); - } - /// Registers the precompiles in the JS context fn register_precompiles(&mut self, precompiles: &Precompiles) { if !self.precompiles_registered { @@ -376,7 +404,7 @@ where inputs.gas_limit, ); - if self.enter_fn.is_some() { + if self.can_call_enter() { let call = self.active_call(); let frame = CallFrame { contract: call.contract.clone(), @@ -399,7 +427,7 @@ where ret: InstructionResult, out: Bytes, ) -> (InstructionResult, Gas, Bytes) { - if self.exit_fn.is_some() { + if self.can_call_exit() { let frame_result = FrameResult { gas_used: remaining_gas.spend(), output: out.clone(), error: None }; if let Err(err) = self.try_exit(frame_result) { @@ -431,7 +459,7 @@ where inputs.gas_limit, ); - if self.enter_fn.is_some() { + if self.can_call_enter() { let call = self.active_call(); let frame = CallFrame { contract: call.contract.clone(), kind: call.kind, gas: call.gas_limit }; @@ -452,7 +480,7 @@ where remaining_gas: Gas, out: Bytes, ) -> (InstructionResult, Option
, Gas, Bytes) { - if self.exit_fn.is_some() { + if self.can_call_exit() { let frame_result = FrameResult { gas_used: remaining_gas.spend(), output: out.clone(), error: None }; if let Err(err) = self.try_exit(frame_result) { @@ -466,12 +494,20 @@ where } fn selfdestruct(&mut self, _contract: Address, _target: Address, _value: U256) { + // This is exempt from the root call constraint, because selfdestruct is treated as a + // new scope that is entered and immediately exited. if self.enter_fn.is_some() { let call = self.active_call(); let frame = CallFrame { contract: call.contract.clone(), kind: call.kind, gas: call.gas_limit }; let _ = self.try_enter(frame); } + + // exit with empty frame result ref + if self.exit_fn.is_some() { + let frame_result = FrameResult { gas_used: 0, output: Bytes::new(), error: None }; + let _ = self.try_exit(frame_result); + } } } From a3ca95877dd0d98fe69d122756045f0942746020 Mon Sep 17 00:00:00 2001 From: Matthias Seitz Date: Tue, 5 Dec 2023 00:41:02 +0100 Subject: [PATCH 094/277] fix: use correct error if fault fn missing (#5688) --- crates/revm/revm-inspectors/src/tracing/js/mod.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/revm/revm-inspectors/src/tracing/js/mod.rs b/crates/revm/revm-inspectors/src/tracing/js/mod.rs index fe7bc8337eac..3d82365f034f 100644 --- a/crates/revm/revm-inspectors/src/tracing/js/mod.rs +++ b/crates/revm/revm-inspectors/src/tracing/js/mod.rs @@ -108,9 +108,9 @@ impl JsInspector { .get("fault", &mut ctx)? .as_object() .cloned() - .ok_or(JsInspectorError::ResultFunctionMissing)?; + .ok_or(JsInspectorError::FaultFunctionMissing)?; if !result_fn.is_callable() { - return Err(JsInspectorError::ResultFunctionMissing) + return Err(JsInspectorError::FaultFunctionMissing) } let enter_fn = obj.get("enter", &mut ctx)?.as_object().cloned().filter(|o| o.is_callable()); From dc560789197cbb4216722a60fe9c037086c16c1f Mon Sep 17 00:00:00 2001 From: DoTheBestToGetTheBest <146037313+DoTheBestToGetTheBest@users.noreply.github.com> Date: Mon, 4 Dec 2023 20:16:18 -0800 Subject: [PATCH 095/277] feat(ethereum-fork) : enhance Head struct with methods (#5656) --- crates/ethereum-forks/src/head.rs | 44 +++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/crates/ethereum-forks/src/head.rs b/crates/ethereum-forks/src/head.rs index ac526807da08..18bebed5faa5 100644 --- a/crates/ethereum-forks/src/head.rs +++ b/crates/ethereum-forks/src/head.rs @@ -1,5 +1,6 @@ use alloy_primitives::{BlockNumber, B256, U256}; use serde::{Deserialize, Serialize}; +use std::fmt; /// Describes the current head block. /// @@ -22,3 +23,46 @@ pub struct Head { /// The timestamp of the head block. pub timestamp: u64, } +impl Head { + /// Creates a new `Head` instance. + pub fn new( + number: BlockNumber, + hash: B256, + difficulty: U256, + total_difficulty: U256, + timestamp: u64, + ) -> Self { + Self { number, hash, difficulty, total_difficulty, timestamp } + } + + /// Updates the head block with new information. + pub fn update( + &mut self, + number: BlockNumber, + hash: B256, + difficulty: U256, + total_difficulty: U256, + timestamp: u64, + ) { + self.number = number; + self.hash = hash; + self.difficulty = difficulty; + self.total_difficulty = total_difficulty; + self.timestamp = timestamp; + } + + /// Checks if the head block is an empty block (i.e., has default values). + pub fn is_empty(&self) -> bool { + *self == Self::default() + } +} + +impl fmt::Display for Head { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!( + f, + "Head Block:\n Number: {}\n Hash: {:?}\n Difficulty: {:?}\n Total Difficulty: {:?}\n Timestamp: {}", + self.number, self.hash, self.difficulty, self.total_difficulty, self.timestamp + ) + } +} From 074d7c794500cc2d863061d9486e364d278d1c02 Mon Sep 17 00:00:00 2001 From: Roman Krasiuk Date: Tue, 5 Dec 2023 02:30:37 -0800 Subject: [PATCH 096/277] fix(provider): latest provider proof method (#5695) --- .../provider/src/providers/state/latest.rs | 20 ++++++------------- 1 file changed, 6 insertions(+), 14 deletions(-) diff --git a/crates/storage/provider/src/providers/state/latest.rs b/crates/storage/provider/src/providers/state/latest.rs index df515f78e202..a12eef46305b 100644 --- a/crates/storage/provider/src/providers/state/latest.rs +++ b/crates/storage/provider/src/providers/state/latest.rs @@ -9,10 +9,9 @@ use reth_db::{ }; use reth_interfaces::provider::{ProviderError, ProviderResult}; use reth_primitives::{ - keccak256, trie::AccountProof, Account, Address, BlockNumber, Bytecode, StorageKey, - StorageValue, B256, + trie::AccountProof, Account, Address, BlockNumber, Bytecode, StorageKey, StorageValue, B256, }; -use reth_trie::updates::TrieUpdates; +use reth_trie::{proof::Proof, updates::TrieUpdates}; /// State provider over latest state that takes tx reference. #[derive(Debug)] @@ -95,17 +94,10 @@ impl<'b, TX: DbTx> StateProvider for LatestStateProviderRef<'b, TX> { self.db.get::(code_hash).map_err(Into::into) } - fn proof(&self, address: Address, _keys: &[B256]) -> ProviderResult { - let _hashed_address = keccak256(address); - let _root = self - .db - .cursor_read::()? - .last()? - .ok_or_else(|| ProviderError::HeaderNotFound(0.into()))? - .1 - .state_root; - - unimplemented!() + fn proof(&self, address: Address, slots: &[B256]) -> ProviderResult { + Ok(Proof::new(self.db) + .account_proof(address, slots) + .map_err(Into::::into)?) } } From 8d8700a5c08ba422536c27af4b97a982ca7d0eba Mon Sep 17 00:00:00 2001 From: Roman Krasiuk Date: Tue, 5 Dec 2023 02:31:41 -0800 Subject: [PATCH 097/277] feat(primitives): state root methods (#5694) --- crates/primitives/src/chain/spec.rs | 10 +-- crates/primitives/src/genesis.rs | 58 +---------------- crates/primitives/src/proofs.rs | 94 +++++++++++++++++++++------ crates/primitives/src/trie/account.rs | 59 +++++++++++++++++ crates/primitives/src/trie/mod.rs | 2 + crates/trie/src/account.rs | 39 ----------- crates/trie/src/lib.rs | 3 - crates/trie/src/proof.rs | 5 +- crates/trie/src/test_utils.rs | 9 +-- crates/trie/src/trie.rs | 10 +-- 10 files changed, 150 insertions(+), 139 deletions(-) create mode 100644 crates/primitives/src/trie/account.rs delete mode 100644 crates/trie/src/account.rs diff --git a/crates/primitives/src/chain/spec.rs b/crates/primitives/src/chain/spec.rs index 7430ce92a0f4..9da9720d26b0 100644 --- a/crates/primitives/src/chain/spec.rs +++ b/crates/primitives/src/chain/spec.rs @@ -3,7 +3,7 @@ use crate::{ EIP1559_DEFAULT_BASE_FEE_MAX_CHANGE_DENOMINATOR, EIP1559_DEFAULT_ELASTICITY_MULTIPLIER, EIP1559_INITIAL_BASE_FEE, EMPTY_RECEIPTS, EMPTY_TRANSACTIONS, EMPTY_WITHDRAWALS, }, - proofs::genesis_state_root, + proofs::state_root_ref_unhashed, revm_primitives::{address, b256}, Address, BlockNumber, Chain, ForkFilter, ForkFilterKey, ForkHash, ForkId, Genesis, Hardfork, Head, Header, SealedHeader, B256, EMPTY_OMMER_ROOT_HASH, U256, @@ -606,7 +606,7 @@ impl ChainSpec { difficulty: self.genesis.difficulty, nonce: self.genesis.nonce, extra_data: self.genesis.extra_data.clone(), - state_root: genesis_state_root(&self.genesis.alloc), + state_root: state_root_ref_unhashed(&self.genesis.alloc), timestamp: self.genesis.timestamp, mix_hash: self.genesis.mix_hash, beneficiary: self.genesis.coinbase, @@ -1517,8 +1517,8 @@ impl DepositContract { mod tests { use super::*; use crate::{ - b256, hex, ChainConfig, GenesisAccount, NamedChain, B256, DEV, GOERLI, HOLESKY, MAINNET, - SEPOLIA, U256, + b256, hex, trie::TrieAccount, ChainConfig, GenesisAccount, NamedChain, B256, DEV, GOERLI, + HOLESKY, MAINNET, SEPOLIA, U256, }; use alloy_rlp::Encodable; use bytes::BytesMut; @@ -2520,7 +2520,7 @@ Post-merge hard forks (timestamp based): for (key, expected_rlp) in key_rlp { let account = chainspec.genesis.alloc.get(&key).expect("account should exist"); let mut account_rlp = BytesMut::new(); - account.encode(&mut account_rlp); + TrieAccount::from(account.clone()).encode(&mut account_rlp); assert_eq!(account_rlp, expected_rlp) } diff --git a/crates/primitives/src/genesis.rs b/crates/primitives/src/genesis.rs index 02b4700fe8d9..e1a1ecd53a4a 100644 --- a/crates/primitives/src/genesis.rs +++ b/crates/primitives/src/genesis.rs @@ -1,16 +1,12 @@ use crate::{ - constants::EMPTY_ROOT_HASH, keccak256, serde_helper::{ json_u256::{deserialize_json_ttd_opt, deserialize_json_u256}, num::{u64_hex_or_decimal, u64_hex_or_decimal_opt}, storage::deserialize_storage_map, }, - trie::{HashBuilder, Nibbles}, - Account, Address, Bytes, B256, KECCAK_EMPTY, U256, + Account, Address, Bytes, B256, U256, }; -use alloy_rlp::{encode_fixed_size, length_of_length, Encodable, Header as RlpHeader}; -use itertools::Itertools; use serde::{Deserialize, Serialize}; use std::collections::HashMap; @@ -153,19 +149,6 @@ pub struct GenesisAccount { } impl GenesisAccount { - /// Determines the RLP payload length, without the RLP header. - fn payload_len(&self) -> usize { - let mut len = 0; - len += self.nonce.unwrap_or_default().length(); - len += self.balance.length(); - // rather than rlp-encoding the storage, we just return the length of a single hash - // hashes are a fixed size, so it is safe to use the empty root for this - len += EMPTY_ROOT_HASH.length(); - // we are encoding a hash, so let's just use the length of the empty hash for the code hash - len += KECCAK_EMPTY.length(); - len - } - /// Set the nonce. pub fn with_nonce(mut self, nonce: Option) -> Self { self.nonce = nonce; @@ -191,45 +174,6 @@ impl GenesisAccount { } } -impl Encodable for GenesisAccount { - fn encode(&self, out: &mut dyn bytes::BufMut) { - let header = RlpHeader { list: true, payload_length: self.payload_len() }; - header.encode(out); - - self.nonce.unwrap_or_default().encode(out); - self.balance.encode(out); - self.storage - .as_ref() - .map_or(EMPTY_ROOT_HASH, |storage| { - if storage.is_empty() { - return EMPTY_ROOT_HASH - } - - let storage_with_sorted_hashed_keys = storage - .iter() - .filter(|(_k, &v)| v != B256::ZERO) - .map(|(slot, value)| (keccak256(slot), value)) - .sorted_by_key(|(key, _)| *key); - - let mut hb = HashBuilder::default(); - for (hashed_slot, value) in storage_with_sorted_hashed_keys { - let encoded_value = encode_fixed_size(&U256::from_be_bytes(**value)); - hb.add_leaf(Nibbles::unpack(hashed_slot), &encoded_value); - } - - hb.root() - }) - .encode(out); - self.code.as_ref().map_or(KECCAK_EMPTY, keccak256).encode(out); - } - - fn length(&self) -> usize { - let len = self.payload_len(); - // RLP header length + payload length - len + length_of_length(len) - } -} - impl From for Account { fn from(value: GenesisAccount) -> Self { Account { diff --git a/crates/primitives/src/proofs.rs b/crates/primitives/src/proofs.rs index f4851744fbf1..e2c74b3fb40e 100644 --- a/crates/primitives/src/proofs.rs +++ b/crates/primitives/src/proofs.rs @@ -3,14 +3,14 @@ use crate::{ constants::EMPTY_OMMER_ROOT_HASH, keccak256, - trie::{HashBuilder, Nibbles}, - Address, GenesisAccount, Header, Receipt, ReceiptWithBloom, ReceiptWithBloomRef, - TransactionSigned, Withdrawal, B256, + trie::{HashBuilder, Nibbles, TrieAccount}, + Address, Header, Receipt, ReceiptWithBloom, ReceiptWithBloomRef, TransactionSigned, Withdrawal, + B256, }; +use alloy_primitives::U256; use alloy_rlp::Encodable; use bytes::{BufMut, BytesMut}; use itertools::Itertools; -use std::collections::HashMap; /// Adjust the index of an item for rlp encoding. pub const fn adjust_index_for_rlp(i: usize, len: usize) -> usize { @@ -164,22 +164,73 @@ pub fn calculate_ommers_root(ommers: &[Header]) -> B256 { keccak256(ommers_rlp) } -/// Calculates the root hash for the state, this corresponds to [geth's -/// `deriveHash`](https://github.com/ethereum/go-ethereum/blob/6c149fd4ad063f7c24d726a73bc0546badd1bc73/core/genesis.go#L119). -pub fn genesis_state_root(genesis_alloc: &HashMap) -> B256 { - let accounts_with_sorted_hashed_keys = genesis_alloc - .iter() - .map(|(address, account)| (keccak256(address), account)) - .sorted_by_key(|(key, _)| *key); +/// Hashes and sorts account keys, then proceeds to calculating the root hash of the state +/// represented as MPT. +/// See [state_root_unsorted] for more info. +pub fn state_root_ref_unhashed<'a, A: Into + Clone + 'a>( + state: impl IntoIterator, +) -> B256 { + state_root_unsorted( + state.into_iter().map(|(address, account)| (keccak256(address), account.clone())), + ) +} + +/// Hashes and sorts account keys, then proceeds to calculating the root hash of the state +/// represented as MPT. +/// See [state_root_unsorted] for more info. +pub fn state_root_unhashed>( + state: impl IntoIterator, +) -> B256 { + state_root_unsorted(state.into_iter().map(|(address, account)| (keccak256(address), account))) +} +/// Sorts the hashed account keys and calculates the root hash of the state represented as MPT. +/// See [state_root] for more info. +pub fn state_root_unsorted>( + state: impl IntoIterator, +) -> B256 { + state_root(state.into_iter().sorted_by_key(|(key, _)| *key)) +} + +/// Calculates the root hash of the state represented as MPT. +/// Corresponds to [geth's `deriveHash`](https://github.com/ethereum/go-ethereum/blob/6c149fd4ad063f7c24d726a73bc0546badd1bc73/core/genesis.go#L119). +/// +/// # Panics +/// +/// If the items are not in sorted order. +pub fn state_root>(state: impl IntoIterator) -> B256 { let mut hb = HashBuilder::default(); let mut account_rlp_buf = Vec::new(); - for (hashed_key, account) in accounts_with_sorted_hashed_keys { + for (hashed_key, account) in state { account_rlp_buf.clear(); - account.encode(&mut account_rlp_buf); + account.into().encode(&mut account_rlp_buf); hb.add_leaf(Nibbles::unpack(hashed_key), &account_rlp_buf); } + hb.root() +} + +/// Hashes storage keys, sorts them and them calculates the root hash of the storage trie. +/// See [storage_root_unsorted] for more info. +pub fn storage_root_unhashed(storage: impl IntoIterator) -> B256 { + storage_root_unsorted(storage.into_iter().map(|(slot, value)| (keccak256(slot), value))) +} + +/// Sorts and calculates the root hash of account storage trie. +/// See [storage_root] for more info. +pub fn storage_root_unsorted(storage: impl IntoIterator) -> B256 { + storage_root(storage.into_iter().sorted_by_key(|(key, _)| *key)) +} +/// Calculates the root hash of account storage trie. +/// +/// # Panics +/// +/// If the items are not in sorted order. +pub fn storage_root(storage: impl IntoIterator) -> B256 { + let mut hb = HashBuilder::default(); + for (hashed_slot, value) in storage { + hb.add_leaf(Nibbles::unpack(hashed_slot), alloy_rlp::encode_fixed_size(&value).as_ref()); + } hb.root() } @@ -216,12 +267,13 @@ mod tests { bloom, constants::EMPTY_ROOT_HASH, hex_literal::hex, - proofs::{calculate_receipt_root, calculate_transaction_root, genesis_state_root}, + proofs::{calculate_receipt_root, calculate_transaction_root}, Address, Block, GenesisAccount, Log, Receipt, ReceiptWithBloom, TxType, B256, GOERLI, HOLESKY, MAINNET, SEPOLIA, U256, }; use alloy_primitives::b256; use alloy_rlp::Decodable; + use std::collections::HashMap; #[test] fn check_transaction_root() { @@ -549,8 +601,8 @@ mod tests { #[test] fn check_empty_state_root() { - let genesis_alloc = HashMap::new(); - let root = genesis_state_root(&genesis_alloc); + let genesis_alloc = HashMap::::new(); + let root = state_root_unhashed(genesis_alloc); assert_eq!(root, EMPTY_ROOT_HASH); } @@ -577,7 +629,7 @@ mod tests { test_addr, GenesisAccount { nonce: None, balance: U256::MAX, code: None, storage: None }, ); - let root = genesis_state_root(&genesis_alloc); + let root = state_root_unhashed(genesis_alloc); assert_eq!(root, expected_root); } @@ -587,7 +639,7 @@ mod tests { fn test_chain_state_roots() { let expected_mainnet_state_root = b256!("d7f8974fb5ac78d9ac099b9ad5018bedc2ce0a72dad1827a1709da30580f0544"); - let calculated_mainnet_state_root = genesis_state_root(&MAINNET.genesis.alloc); + let calculated_mainnet_state_root = state_root_ref_unhashed(&MAINNET.genesis.alloc); assert_eq!( expected_mainnet_state_root, calculated_mainnet_state_root, "mainnet state root mismatch" @@ -595,7 +647,7 @@ mod tests { let expected_goerli_state_root = b256!("5d6cded585e73c4e322c30c2f782a336316f17dd85a4863b9d838d2d4b8b3008"); - let calculated_goerli_state_root = genesis_state_root(&GOERLI.genesis.alloc); + let calculated_goerli_state_root = state_root_ref_unhashed(&GOERLI.genesis.alloc); assert_eq!( expected_goerli_state_root, calculated_goerli_state_root, "goerli state root mismatch" @@ -603,7 +655,7 @@ mod tests { let expected_sepolia_state_root = b256!("5eb6e371a698b8d68f665192350ffcecbbbf322916f4b51bd79bb6887da3f494"); - let calculated_sepolia_state_root = genesis_state_root(&SEPOLIA.genesis.alloc); + let calculated_sepolia_state_root = state_root_ref_unhashed(&SEPOLIA.genesis.alloc); assert_eq!( expected_sepolia_state_root, calculated_sepolia_state_root, "sepolia state root mismatch" @@ -611,7 +663,7 @@ mod tests { let expected_holesky_state_root = b256!("69d8c9d72f6fa4ad42d4702b433707212f90db395eb54dc20bc85de253788783"); - let calculated_holesky_state_root = genesis_state_root(&HOLESKY.genesis.alloc); + let calculated_holesky_state_root = state_root_ref_unhashed(&HOLESKY.genesis.alloc); assert_eq!( expected_holesky_state_root, calculated_holesky_state_root, "holesky state root mismatch" diff --git a/crates/primitives/src/trie/account.rs b/crates/primitives/src/trie/account.rs new file mode 100644 index 000000000000..41e8e8723508 --- /dev/null +++ b/crates/primitives/src/trie/account.rs @@ -0,0 +1,59 @@ +use crate::{ + constants::EMPTY_ROOT_HASH, proofs, Account, GenesisAccount, B256, KECCAK_EMPTY, U256, +}; +use alloy_primitives::keccak256; +use alloy_rlp::{RlpDecodable, RlpEncodable}; + +/// An Ethereum account as represented in the trie. +#[derive(Clone, Copy, Debug, PartialEq, Eq, Default, RlpEncodable, RlpDecodable)] +pub struct TrieAccount { + /// Account nonce. + nonce: u64, + /// Account balance. + balance: U256, + /// Account's storage root. + storage_root: B256, + /// Hash of the account's bytecode. + code_hash: B256, +} + +impl From<(Account, B256)> for TrieAccount { + fn from((account, storage_root): (Account, B256)) -> Self { + Self { + nonce: account.nonce, + balance: account.balance, + storage_root, + code_hash: account.bytecode_hash.unwrap_or(KECCAK_EMPTY), + } + } +} + +impl From for TrieAccount { + fn from(account: GenesisAccount) -> Self { + let storage_root = account + .storage + .map(|storage| { + proofs::storage_root_unhashed( + storage + .into_iter() + .filter(|(_, value)| *value != B256::ZERO) + .map(|(slot, value)| (slot, U256::from_be_bytes(*value))), + ) + }) + .unwrap_or(EMPTY_ROOT_HASH); + + Self { + nonce: account.nonce.unwrap_or_default(), + balance: account.balance, + storage_root, + code_hash: account.code.map_or(KECCAK_EMPTY, keccak256), + } + } +} + +impl TrieAccount { + /// Get account's storage root. + pub fn storage_root(&self) -> B256 { + self.storage_root + } +} diff --git a/crates/primitives/src/trie/mod.rs b/crates/primitives/src/trie/mod.rs index b531d8fdac36..632ce3f4cdb7 100644 --- a/crates/primitives/src/trie/mod.rs +++ b/crates/primitives/src/trie/mod.rs @@ -12,12 +12,14 @@ pub use hash_builder::HashBuilder; mod proofs; pub use proofs::{AccountProof, StorageProof}; +mod account; mod mask; mod nibbles; mod storage; mod subnode; pub use self::{ + account::TrieAccount, mask::TrieMask, nibbles::{Nibbles, StoredNibblesSubKey}, storage::StorageTrieEntry, diff --git a/crates/trie/src/account.rs b/crates/trie/src/account.rs deleted file mode 100644 index 0feeff8bada0..000000000000 --- a/crates/trie/src/account.rs +++ /dev/null @@ -1,39 +0,0 @@ -use alloy_rlp::{RlpDecodable, RlpEncodable}; -use reth_primitives::{constants::EMPTY_ROOT_HASH, Account, B256, KECCAK_EMPTY, U256}; - -/// An Ethereum account as represented in the trie. -#[derive(Clone, Copy, Debug, PartialEq, Eq, Default, RlpEncodable, RlpDecodable)] -pub struct EthAccount { - /// Account nonce. - nonce: u64, - /// Account balance. - balance: U256, - /// Account's storage root. - storage_root: B256, - /// Hash of the account's bytecode. - code_hash: B256, -} - -impl From for EthAccount { - fn from(acc: Account) -> Self { - EthAccount { - nonce: acc.nonce, - balance: acc.balance, - storage_root: EMPTY_ROOT_HASH, - code_hash: acc.bytecode_hash.unwrap_or(KECCAK_EMPTY), - } - } -} - -impl EthAccount { - /// Set storage root on account. - pub fn with_storage_root(mut self, storage_root: B256) -> Self { - self.storage_root = storage_root; - self - } - - /// Get account's storage root. - pub fn storage_root(&self) -> B256 { - self.storage_root - } -} diff --git a/crates/trie/src/lib.rs b/crates/trie/src/lib.rs index 31183f5420e8..9e842a7bfa97 100644 --- a/crates/trie/src/lib.rs +++ b/crates/trie/src/lib.rs @@ -15,9 +15,6 @@ #![deny(unused_must_use, rust_2018_idioms)] #![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))] -/// The Ethereum account as represented in the trie. -pub mod account; - /// The implementation of a container for storing intermediate changes to a trie. /// The container indicates when the trie has been modified. pub mod prefix_set; diff --git a/crates/trie/src/proof.rs b/crates/trie/src/proof.rs index eb7c438c4b55..874c241e29cd 100644 --- a/crates/trie/src/proof.rs +++ b/crates/trie/src/proof.rs @@ -1,5 +1,4 @@ use crate::{ - account::EthAccount, hashed_cursor::{HashedCursorFactory, HashedStorageCursor}, node_iter::{AccountNode, AccountNodeIter, StorageNode, StorageNodeIter}, prefix_set::PrefixSetMut, @@ -12,7 +11,7 @@ use reth_db::{tables, transaction::DbTx}; use reth_primitives::{ constants::EMPTY_ROOT_HASH, keccak256, - trie::{AccountProof, HashBuilder, Nibbles, StorageProof}, + trie::{AccountProof, HashBuilder, Nibbles, StorageProof, TrieAccount}, Address, B256, }; @@ -81,7 +80,7 @@ where }; account_rlp.clear(); - let account = EthAccount::from(account).with_storage_root(storage_root); + let account = TrieAccount::from((account, storage_root)); account.encode(&mut account_rlp as &mut dyn BufMut); hash_builder.add_leaf(Nibbles::unpack(hashed_address), &account_rlp); diff --git a/crates/trie/src/test_utils.rs b/crates/trie/src/test_utils.rs index bd16f04cd3ca..2d0eb25e0598 100644 --- a/crates/trie/src/test_utils.rs +++ b/crates/trie/src/test_utils.rs @@ -1,6 +1,7 @@ -use crate::account::EthAccount; use alloy_rlp::{encode_fixed_size, Encodable}; -use reth_primitives::{proofs::triehash::KeccakHasher, Account, Address, B256, U256}; +use reth_primitives::{ + proofs::triehash::KeccakHasher, trie::TrieAccount, Account, Address, B256, U256, +}; /// Re-export of [triehash]. pub use triehash; @@ -14,7 +15,7 @@ where let encoded_accounts = accounts.map(|(address, (account, storage))| { let storage_root = storage_root(storage.into_iter()); let mut out = Vec::new(); - EthAccount::from(account).with_storage_root(storage_root).encode(&mut out); + TrieAccount::from((account, storage_root)).encode(&mut out); (address, out) }); @@ -37,7 +38,7 @@ where let encoded_accounts = accounts.map(|(address, (account, storage))| { let storage_root = storage_root_prehashed(storage.into_iter()); let mut out = Vec::new(); - EthAccount::from(account).with_storage_root(storage_root).encode(&mut out); + TrieAccount::from((account, storage_root)).encode(&mut out); (address, out) }); diff --git a/crates/trie/src/trie.rs b/crates/trie/src/trie.rs index d145c66348f8..ad385a65c7aa 100644 --- a/crates/trie/src/trie.rs +++ b/crates/trie/src/trie.rs @@ -1,5 +1,4 @@ use crate::{ - account::EthAccount, hashed_cursor::{HashedCursorFactory, HashedStorageCursor}, node_iter::{AccountNode, AccountNodeIter, StorageNode, StorageNodeIter}, prefix_set::{PrefixSet, PrefixSetLoader, PrefixSetMut}, @@ -14,7 +13,7 @@ use reth_db::{tables, transaction::DbTx}; use reth_primitives::{ constants::EMPTY_ROOT_HASH, keccak256, - trie::{HashBuilder, Nibbles}, + trie::{HashBuilder, Nibbles, TrieAccount}, Address, BlockNumber, B256, }; use std::{ @@ -287,7 +286,7 @@ where storage_root_calculator.root()? }; - let account = EthAccount::from(account).with_storage_root(storage_root); + let account = TrieAccount::from((account, storage_root)); account_rlp.clear(); account.encode(&mut account_rlp as &mut dyn BufMut); @@ -769,10 +768,7 @@ mod tests { } fn encode_account(account: Account, storage_root: Option) -> Vec { - let mut account = EthAccount::from(account); - if let Some(storage_root) = storage_root { - account = account.with_storage_root(storage_root); - } + let account = TrieAccount::from((account, storage_root.unwrap_or(EMPTY_ROOT_HASH))); let mut account_rlp = Vec::with_capacity(account.length()); account.encode(&mut account_rlp); account_rlp From 11fd7a2844c08cdefb8ccaa249af512dacf3c729 Mon Sep 17 00:00:00 2001 From: rakita Date: Tue, 5 Dec 2023 16:50:34 +0300 Subject: [PATCH 098/277] chore: remove trailing semicolon (#5699) --- bin/reth/src/args/network_args.rs | 2 +- bin/reth/src/args/payload_builder_args.rs | 2 +- crates/interfaces/src/consensus.rs | 4 +--- crates/primitives/src/transaction/signature.rs | 6 +++--- crates/primitives/src/trie/nibbles.rs | 2 +- crates/transaction-pool/src/pool/parked.rs | 6 +++--- crates/transaction-pool/src/pool/pending.rs | 10 +++++----- crates/transaction-pool/src/test_utils/mock.rs | 4 ++-- 8 files changed, 17 insertions(+), 19 deletions(-) diff --git a/bin/reth/src/args/network_args.rs b/bin/reth/src/args/network_args.rs index 2014468d6134..fd9c88d6c14f 100644 --- a/bin/reth/src/args/network_args.rs +++ b/bin/reth/src/args/network_args.rs @@ -118,7 +118,7 @@ impl NetworkArgs { /// If `no_persist_peers` is true then this returns the path to the persistent peers file path. pub fn persistent_peers_file(&self, peers_file: PathBuf) -> Option { if self.no_persist_peers { - return None; + return None } Some(peers_file) diff --git a/bin/reth/src/args/payload_builder_args.rs b/bin/reth/src/args/payload_builder_args.rs index 1619e7bd979a..d7453372b1df 100644 --- a/bin/reth/src/args/payload_builder_args.rs +++ b/bin/reth/src/args/payload_builder_args.rs @@ -110,7 +110,7 @@ impl TypedValueParser for ExtradataValueParser { format!( "Payload builder extradata size exceeds {MAXIMUM_EXTRA_DATA_SIZE}bytes limit" ), - )); + )) } Ok(val.to_string()) } diff --git a/crates/interfaces/src/consensus.rs b/crates/interfaces/src/consensus.rs index a1412fa44499..166111cb722c 100644 --- a/crates/interfaces/src/consensus.rs +++ b/crates/interfaces/src/consensus.rs @@ -38,9 +38,7 @@ pub trait Consensus: Debug + Send + Sync { /// Note: this expects that the headers are in natural order (ascending block number) fn validate_header_range(&self, headers: &[SealedHeader]) -> Result<(), ConsensusError> { let mut headers = headers.iter(); - let Some(mut parent) = headers.next() else { - return Ok(()); - }; + let Some(mut parent) = headers.next() else { return Ok(()) }; self.validate_header(parent)?; for child in headers { self.validate_header(child)?; diff --git a/crates/primitives/src/transaction/signature.rs b/crates/primitives/src/transaction/signature.rs index 209e4aaa6844..6d051633b55c 100644 --- a/crates/primitives/src/transaction/signature.rs +++ b/crates/primitives/src/transaction/signature.rs @@ -80,7 +80,7 @@ impl Signature { pub fn v(&self, chain_id: Option) -> u64 { #[cfg(feature = "optimism")] if self.r.is_zero() && self.s.is_zero() { - return 0; + return 0 } if let Some(chain_id) = chain_id { @@ -107,7 +107,7 @@ impl Signature { } else { // non-EIP-155 legacy scheme, v = 27 for even y-parity, v = 28 for odd y-parity if v != 27 && v != 28 { - return Err(RlpError::Custom("invalid Ethereum signature (V is not 27 or 28)")); + return Err(RlpError::Custom("invalid Ethereum signature (V is not 27 or 28)")) } let odd_y_parity = v == 28; Ok((Signature { r, s, odd_y_parity }, None)) @@ -160,7 +160,7 @@ impl Signature { /// If the S value is too large, then this will return `None` pub fn recover_signer(&self, hash: B256) -> Option
{ if self.s > SECP256K1N_HALF { - return None; + return None } self.recover_signer_unchecked(hash) diff --git a/crates/primitives/src/trie/nibbles.rs b/crates/primitives/src/trie/nibbles.rs index 7c6d92a2bfc8..f813f930980f 100644 --- a/crates/primitives/src/trie/nibbles.rs +++ b/crates/primitives/src/trie/nibbles.rs @@ -424,7 +424,7 @@ impl Nibbles { debug_assert!(*nibble <= 0xf); if *nibble < 0xf { *nibble += 1; - return Some(incremented); + return Some(incremented) } else { *nibble = 0; } diff --git a/crates/transaction-pool/src/pool/parked.rs b/crates/transaction-pool/src/pool/parked.rs index d985ea4cf8ee..13887ac3f3b3 100644 --- a/crates/transaction-pool/src/pool/parked.rs +++ b/crates/transaction-pool/src/pool/parked.rs @@ -152,7 +152,7 @@ impl ParkedPool { ) -> Vec>> { if self.len() <= limit.max_txs { // if we are below the limits, we don't need to drop anything - return Vec::new(); + return Vec::new() } let mut removed = Vec::new(); @@ -173,7 +173,7 @@ impl ParkedPool { } } drop -= list.len(); - continue; + continue } // Otherwise drop only last few transactions @@ -252,7 +252,7 @@ impl ParkedPool> { // still parked -> skip descendant transactions 'this: while let Some((peek, _)) = iter.peek() { if peek.sender != id.sender { - break 'this; + break 'this } iter.next(); } diff --git a/crates/transaction-pool/src/pool/pending.rs b/crates/transaction-pool/src/pool/pending.rs index 44409c68543f..3ae5569a14f2 100644 --- a/crates/transaction-pool/src/pool/pending.rs +++ b/crates/transaction-pool/src/pool/pending.rs @@ -185,7 +185,7 @@ impl PendingPool { // Remove all dependent transactions. 'this: while let Some((next_id, next_tx)) = transactions_iter.peek() { if next_id.sender != id.sender { - break 'this; + break 'this } removed.push(Arc::clone(&next_tx.transaction)); transactions_iter.next(); @@ -228,7 +228,7 @@ impl PendingPool { // Remove all dependent transactions. 'this: while let Some((next_id, next_tx)) = transactions_iter.peek() { if next_id.sender != id.sender { - break 'this; + break 'this } removed.push(Arc::clone(&next_tx.transaction)); transactions_iter.next(); @@ -420,12 +420,12 @@ impl PendingPool { } } - return; + return } if !remove_locals && tx.transaction.is_local() { non_local_senders -= 1; - continue; + continue } total_size += tx.transaction.size(); @@ -460,7 +460,7 @@ impl PendingPool { self.remove_to_limit(&limit, false, &mut removed); if self.size() <= limit.max_size && self.len() <= limit.max_txs { - return removed; + return removed } // now repeat for local transactions diff --git a/crates/transaction-pool/src/test_utils/mock.rs b/crates/transaction-pool/src/test_utils/mock.rs index a1c778af8857..200a7c092bec 100644 --- a/crates/transaction-pool/src/test_utils/mock.rs +++ b/crates/transaction-pool/src/test_utils/mock.rs @@ -637,12 +637,12 @@ impl PoolTransaction for MockTransaction { let base_fee = base_fee as u128; let max_fee_per_gas = self.max_fee_per_gas(); if max_fee_per_gas < base_fee { - return None; + return None } let fee = max_fee_per_gas - base_fee; if let Some(priority_fee) = self.max_priority_fee_per_gas() { - return Some(fee.min(priority_fee)); + return Some(fee.min(priority_fee)) } Some(fee) From 7f3bbf3459bdfec0c5e633d0ef8909eec8ca2b45 Mon Sep 17 00:00:00 2001 From: rakita Date: Tue, 5 Dec 2023 19:36:15 +0300 Subject: [PATCH 099/277] fix(tree): reinsert unwound state to dependent chains (#5683) Co-authored-by: Roman Krasiuk --- Cargo.lock | 1 + bin/reth/Cargo.toml | 1 + crates/blockchain-tree/Cargo.toml | 2 + crates/blockchain-tree/src/blockchain_tree.rs | 275 +++++++++++++++++- crates/consensus/beacon/Cargo.toml | 1 + crates/primitives/src/trie/account.rs | 12 + .../bundle_state_with_receipts.rs | 54 ++++ crates/storage/provider/src/chain.rs | 8 +- 8 files changed, 348 insertions(+), 6 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index c072cbf05681..eb7fc739da22 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5691,6 +5691,7 @@ dependencies = [ "reth-metrics", "reth-primitives", "reth-provider", + "reth-revm", "reth-stages", "tokio", "tracing", diff --git a/bin/reth/Cargo.toml b/bin/reth/Cargo.toml index aad259ed067e..1a0fbdb17338 100644 --- a/bin/reth/Cargo.toml +++ b/bin/reth/Cargo.toml @@ -131,6 +131,7 @@ optimism = [ "reth-basic-payload-builder/optimism", "reth-network/optimism", "reth-network-api/optimism", + "reth-blockchain-tree/optimism", ] # no-op feature flag for switching between the `optimism` and default functionality in CI matrices ethereum = [] diff --git a/crates/blockchain-tree/Cargo.toml b/crates/blockchain-tree/Cargo.toml index b3c554cdc913..c5f0c3a0c79b 100644 --- a/crates/blockchain-tree/Cargo.toml +++ b/crates/blockchain-tree/Cargo.toml @@ -40,8 +40,10 @@ reth-db = { workspace = true, features = ["test-utils"] } reth-interfaces = { workspace = true, features = ["test-utils"] } reth-primitives = { workspace = true , features = ["test-utils"] } reth-provider = { workspace = true, features = ["test-utils"] } +reth-revm.workspace = true parking_lot.workspace = true assert_matches.workspace = true [features] test-utils = [] +optimism = ["reth-primitives/optimism", "reth-interfaces/optimism", "reth-provider/optimism", "reth-revm/optimism"] diff --git a/crates/blockchain-tree/src/blockchain_tree.rs b/crates/blockchain-tree/src/blockchain_tree.rs index b510dc9008da..1e5e81c90e85 100644 --- a/crates/blockchain-tree/src/blockchain_tree.rs +++ b/crates/blockchain-tree/src/blockchain_tree.rs @@ -28,7 +28,10 @@ use reth_provider::{ ChainSpecProvider, DisplayBlocksChain, ExecutorFactory, HeaderProvider, ProviderError, }; use reth_stages::{MetricEvent, MetricEventsSender}; -use std::{collections::BTreeMap, sync::Arc}; +use std::{ + collections::{BTreeMap, HashSet}, + sync::Arc, +}; use tracing::{debug, error, info, instrument, trace, warn}; #[cfg_attr(doc, aquamarine::aquamarine)] @@ -607,6 +610,70 @@ impl BlockchainTree { self.state.insert_chain(chain) } + /// Iterate over all child chains that depend on this block and return + /// their ids. + fn find_all_dependent_chains(&self, block: &BlockHash) -> HashSet { + // Find all forks of given block. + let mut dependent_block = + self.block_indices().fork_to_child().get(block).cloned().unwrap_or_default(); + let mut dependent_chains = HashSet::new(); + + while let Some(block) = dependent_block.pop_back() { + // Get chain of dependent block. + let chain_id = + self.block_indices().get_blocks_chain_id(&block).expect("Block should be in tree"); + + // Find all blocks that fork from this chain. + for chain_block in + self.state.chains.get(&chain_id).expect("Chain should be in tree").blocks().values() + { + if let Some(forks) = self.block_indices().fork_to_child().get(&chain_block.hash()) { + // If there are sub forks append them for processing. + dependent_block.extend(forks); + } + } + // Insert dependent chain id. + dependent_chains.insert(chain_id); + } + dependent_chains + } + + /// Inserts unwound chain back into the tree and updates any dependent chains. + /// + /// This method searches for any chain that depended on this block being part of the canonical + /// chain. Each dependent chain's state is then updated with state entries removed from the + /// plain state during the unwind. + fn insert_unwound_chain(&mut self, chain: AppendableChain) -> Option { + // iterate over all blocks in chain and find any fork blocks that are in tree. + for (number, block) in chain.blocks().iter() { + let hash = block.hash(); + + // find all chains that fork from this block. + let chains_to_bump = self.find_all_dependent_chains(&hash); + if !chains_to_bump.is_empty() { + // if there is such chain, revert state to this block. + let mut cloned_state = chain.state().clone(); + cloned_state.revert_to(*number); + + // prepend state to all chains that fork from this block. + for chain_id in chains_to_bump { + let chain = + self.state.chains.get_mut(&chain_id).expect("Chain should be in tree"); + + debug!(target: "blockchain_tree", + unwound_block= ?block.num_hash(), + chain_id = ?chain_id, + chain_tip = ?chain.tip().num_hash(), + "Prepend unwound block state to blockchain tree chain"); + + chain.prepend_state(cloned_state.state().clone()) + } + } + } + // Insert unwound chain to the tree. + self.insert_chain(chain) + } + /// Checks the block buffer for the given block. pub fn get_buffered_block(&self, hash: &BlockHash) -> Option<&SealedBlockWithSenders> { self.state.get_buffered_block(hash) @@ -1064,7 +1131,7 @@ impl BlockchainTree { let reorg_depth = old_canon_chain.len(); // insert old canon chain - self.insert_chain(AppendableChain::new(old_canon_chain)); + self.insert_unwound_chain(AppendableChain::new(old_canon_chain)); durations_recorder.record_relative(MakeCanonicalAction::InsertOldCanonicalChain); self.update_reorg_metrics(reorg_depth as f64); @@ -1156,7 +1223,7 @@ impl BlockchainTree { if let Some(old_canon_chain) = old_canon_chain { self.block_indices_mut().unwind_canonical_chain(unwind_to); // insert old canonical chain to BlockchainTree. - self.insert_chain(AppendableChain::new(old_canon_chain)); + self.insert_unwound_chain(AppendableChain::new(old_canon_chain)); } Ok(()) @@ -1229,7 +1296,14 @@ mod tests { use reth_db::{tables, test_utils::TempDatabase, transaction::DbTxMut, DatabaseEnv}; use reth_interfaces::test_utils::TestConsensus; use reth_primitives::{ - constants::EMPTY_ROOT_HASH, stage::StageCheckpoint, ChainSpecBuilder, B256, MAINNET, + constants::{EIP1559_INITIAL_BASE_FEE, EMPTY_ROOT_HASH, ETHEREUM_BLOCK_GAS_LIMIT}, + keccak256, + proofs::{calculate_receipt_root, calculate_transaction_root, state_root_unhashed}, + revm_primitives::AccountInfo, + stage::StageCheckpoint, + Account, Address, ChainSpecBuilder, Genesis, GenesisAccount, Header, Signature, + Transaction, TransactionKind, TransactionSigned, TransactionSignedEcRecovered, TxEip1559, + B256, MAINNET, }; use reth_provider::{ test_utils::{ @@ -1238,6 +1312,7 @@ mod tests { }, BlockWriter, BundleStateWithReceipts, ProviderFactory, }; + use reth_revm::EvmProcessorFactory; use std::{ collections::{HashMap, HashSet}, sync::Arc, @@ -1354,6 +1429,196 @@ mod tests { } } + #[test] + fn consecutive_reorgs() { + let signer = Address::random(); + let initial_signer_balance = U256::from(10).pow(U256::from(18)); + let chain_spec = Arc::new( + ChainSpecBuilder::default() + .chain(MAINNET.chain) + .genesis(Genesis { + alloc: HashMap::from([( + signer, + GenesisAccount { balance: initial_signer_balance, ..Default::default() }, + )]), + ..MAINNET.genesis.clone() + }) + .shanghai_activated() + .build(), + ); + let provider_factory = create_test_provider_factory_with_chain_spec(chain_spec.clone()); + let consensus = Arc::new(TestConsensus::default()); + let executor_factory = EvmProcessorFactory::new(chain_spec.clone()); + + { + let provider_rw = provider_factory.provider_rw().unwrap(); + provider_rw + .insert_block( + SealedBlock::new(chain_spec.sealed_genesis_header(), Default::default()), + Some(Vec::new()), + None, + ) + .unwrap(); + let account = Account { balance: initial_signer_balance, ..Default::default() }; + provider_rw.tx_ref().put::(signer, account).unwrap(); + provider_rw.tx_ref().put::(keccak256(signer), account).unwrap(); + provider_rw.commit().unwrap(); + } + + let single_tx_cost = U256::from(EIP1559_INITIAL_BASE_FEE * 21_000); + let mock_tx = |nonce: u64| -> TransactionSignedEcRecovered { + TransactionSigned::from_transaction_and_signature( + Transaction::Eip1559(TxEip1559 { + chain_id: chain_spec.chain.id(), + nonce, + gas_limit: 21_000, + to: TransactionKind::Call(Address::ZERO), + max_fee_per_gas: EIP1559_INITIAL_BASE_FEE as u128, + ..Default::default() + }), + Signature::default(), + ) + .with_signer(signer) + }; + + let mock_block = |number: u64, + parent: Option, + body: Vec, + num_of_signer_txs: u64| + -> SealedBlockWithSenders { + let transactions_root = calculate_transaction_root(&body); + let receipts = body + .iter() + .enumerate() + .map(|(idx, tx)| { + Receipt { + tx_type: tx.tx_type(), + success: true, + cumulative_gas_used: (idx as u64 + 1) * 21_000, + ..Default::default() + } + .with_bloom() + }) + .collect::>(); + + #[cfg(not(feature = "optimism"))] + let receipts_root = calculate_receipt_root(&receipts); + + #[cfg(feature = "optimism")] + let receipts_root = calculate_receipt_root(&receipts, &chain_spec, 0); + + SealedBlockWithSenders::new( + SealedBlock { + header: Header { + number, + parent_hash: parent.unwrap_or_default(), + gas_used: body.len() as u64 * 21_000, + gas_limit: ETHEREUM_BLOCK_GAS_LIMIT, + mix_hash: B256::random(), + base_fee_per_gas: Some(EIP1559_INITIAL_BASE_FEE), + transactions_root, + receipts_root, + state_root: state_root_unhashed(HashMap::from([( + signer, + ( + AccountInfo { + balance: initial_signer_balance - + (single_tx_cost * U256::from(num_of_signer_txs)), + nonce: num_of_signer_txs, + ..Default::default() + }, + EMPTY_ROOT_HASH, + ), + )])), + ..Default::default() + } + .seal_slow(), + body: body.clone().into_iter().map(|tx| tx.into_signed()).collect(), + ommers: Vec::new(), + withdrawals: Some(Vec::new()), + }, + body.iter().map(|tx| tx.signer()).collect(), + ) + .unwrap() + }; + + let fork_block = mock_block(1, Some(chain_spec.genesis_hash()), Vec::from([mock_tx(0)]), 1); + + let canonical_block_1 = + mock_block(2, Some(fork_block.hash), Vec::from([mock_tx(1), mock_tx(2)]), 3); + let canonical_block_2 = mock_block(3, Some(canonical_block_1.hash), Vec::new(), 3); + let canonical_block_3 = + mock_block(4, Some(canonical_block_2.hash), Vec::from([mock_tx(3)]), 4); + + let sidechain_block_1 = mock_block(2, Some(fork_block.hash), Vec::from([mock_tx(1)]), 2); + let sidechain_block_2 = + mock_block(3, Some(sidechain_block_1.hash), Vec::from([mock_tx(2)]), 3); + + let mut tree = BlockchainTree::new( + TreeExternals::new(provider_factory.clone(), consensus, executor_factory.clone()), + BlockchainTreeConfig::default(), + None, + ) + .expect("failed to create tree"); + + tree.insert_block(fork_block.clone(), BlockValidationKind::Exhaustive).unwrap(); + + assert_eq!( + tree.make_canonical(&fork_block.hash).unwrap(), + CanonicalOutcome::Committed { head: fork_block.header.clone() } + ); + + assert_eq!( + tree.insert_block(canonical_block_1.clone(), BlockValidationKind::Exhaustive).unwrap(), + InsertPayloadOk::Inserted(BlockStatus::Valid) + ); + + assert_eq!( + tree.make_canonical(&canonical_block_1.hash).unwrap(), + CanonicalOutcome::Committed { head: canonical_block_1.header.clone() } + ); + + assert_eq!( + tree.insert_block(canonical_block_2.clone(), BlockValidationKind::Exhaustive).unwrap(), + InsertPayloadOk::Inserted(BlockStatus::Valid) + ); + + assert_eq!( + tree.insert_block(sidechain_block_1.clone(), BlockValidationKind::Exhaustive).unwrap(), + InsertPayloadOk::Inserted(BlockStatus::Accepted) + ); + + assert_eq!( + tree.make_canonical(&sidechain_block_1.hash).unwrap(), + CanonicalOutcome::Committed { head: sidechain_block_1.header.clone() } + ); + + assert_eq!( + tree.make_canonical(&canonical_block_1.hash).unwrap(), + CanonicalOutcome::Committed { head: canonical_block_1.header.clone() } + ); + + assert_eq!( + tree.insert_block(sidechain_block_2.clone(), BlockValidationKind::Exhaustive).unwrap(), + InsertPayloadOk::Inserted(BlockStatus::Accepted) + ); + + assert_eq!( + tree.make_canonical(&sidechain_block_2.hash).unwrap(), + CanonicalOutcome::Committed { head: sidechain_block_2.header.clone() } + ); + + assert_eq!( + tree.insert_block(canonical_block_3.clone(), BlockValidationKind::Exhaustive).unwrap(), + InsertPayloadOk::Inserted(BlockStatus::Accepted) + ); + + assert_eq!( + tree.make_canonical(&canonical_block_3.hash).unwrap(), + CanonicalOutcome::Committed { head: canonical_block_3.header.clone() } + ); + } + #[tokio::test] async fn sanity_path() { let data = BlockChainTestData::default_with_numbers(11, 12); @@ -1680,7 +1945,7 @@ mod tests { // check notification. assert_matches!(canon_notif.try_recv(), - Ok(CanonStateNotification::Commit{ new}) + Ok(CanonStateNotification::Commit{ new }) if *new.blocks() == BTreeMap::from([(block2.number,block2.clone())])); // insert unconnected block2b diff --git a/crates/consensus/beacon/Cargo.toml b/crates/consensus/beacon/Cargo.toml index 0feec340020a..3f892778ba0f 100644 --- a/crates/consensus/beacon/Cargo.toml +++ b/crates/consensus/beacon/Cargo.toml @@ -60,4 +60,5 @@ optimism = [ "reth-rpc-types/optimism", "reth-rpc-types-compat/optimism", "reth-payload-builder/optimism", + "reth-blockchain-tree/optimism", ] diff --git a/crates/primitives/src/trie/account.rs b/crates/primitives/src/trie/account.rs index 41e8e8723508..b7bfa092366d 100644 --- a/crates/primitives/src/trie/account.rs +++ b/crates/primitives/src/trie/account.rs @@ -3,6 +3,7 @@ use crate::{ }; use alloy_primitives::keccak256; use alloy_rlp::{RlpDecodable, RlpEncodable}; +use revm_primitives::AccountInfo; /// An Ethereum account as represented in the trie. #[derive(Clone, Copy, Debug, PartialEq, Eq, Default, RlpEncodable, RlpDecodable)] @@ -28,6 +29,17 @@ impl From<(Account, B256)> for TrieAccount { } } +impl From<(AccountInfo, B256)> for TrieAccount { + fn from((account, storage_root): (AccountInfo, B256)) -> Self { + Self { + nonce: account.nonce, + balance: account.balance, + storage_root, + code_hash: account.code_hash, + } + } +} + impl From for TrieAccount { fn from(account: GenesisAccount) -> Self { let storage_root = account diff --git a/crates/storage/provider/src/bundle_state/bundle_state_with_receipts.rs b/crates/storage/provider/src/bundle_state/bundle_state_with_receipts.rs index 33dd0590e38e..d1966428940a 100644 --- a/crates/storage/provider/src/bundle_state/bundle_state_with_receipts.rs +++ b/crates/storage/provider/src/bundle_state/bundle_state_with_receipts.rs @@ -351,6 +351,22 @@ impl BundleStateWithReceipts { self.receipts.extend(other.receipts.receipt_vec); } + /// Prepends present the state with the given BundleState. + /// It adds changes from the given state but does not override any existing changes. + /// + /// Reverts and receipts are not updated. + pub fn prepend_state(&mut self, mut other: BundleState) { + let other_len = other.reverts.len(); + // take this bundle + let this_bundle = std::mem::take(&mut self.bundle); + // extend other bundle with this + other.extend(this_bundle); + // discard other reverts + other.take_n_reverts(other_len); + // swap bundles + std::mem::swap(&mut self.bundle, &mut other) + } + /// Write bundle state to database. /// /// `omit_changed_check` should be set to true of bundle has some of it data @@ -1355,4 +1371,42 @@ mod tests { state.merge_transitions(BundleRetention::PlainState); assert_state_root(&state, &prestate, "recreated changed storage"); } + + #[test] + fn prepend_state() { + let address1 = Address::random(); + let address2 = Address::random(); + + let account1 = RevmAccountInfo { nonce: 1, ..Default::default() }; + let account1_changed = RevmAccountInfo { nonce: 1, ..Default::default() }; + let account2 = RevmAccountInfo { nonce: 1, ..Default::default() }; + + let present_state = BundleState::builder(2..=2) + .state_present_account_info(address1, account1_changed.clone()) + .build(); + assert_eq!(present_state.reverts.len(), 1); + let previous_state = BundleState::builder(1..=1) + .state_present_account_info(address1, account1) + .state_present_account_info(address2, account2.clone()) + .build(); + assert_eq!(previous_state.reverts.len(), 1); + + let mut test = BundleStateWithReceipts { + bundle: present_state, + receipts: Receipts::from_vec(vec![vec![Some(Receipt::default()); 2]; 1]), + first_block: 2, + }; + + test.prepend_state(previous_state); + + assert_eq!(test.receipts.len(), 1); + let end_state = test.state(); + assert_eq!(end_state.state.len(), 2); + // reverts num should stay the same. + assert_eq!(end_state.reverts.len(), 1); + // account1 is not overwritten. + assert_eq!(end_state.state.get(&address1).unwrap().info, Some(account1_changed)); + // account2 got inserted + assert_eq!(end_state.state.get(&address2).unwrap().info, Some(account2)); + } } diff --git a/crates/storage/provider/src/chain.rs b/crates/storage/provider/src/chain.rs index c970f8f35fcf..fd055c97bb7e 100644 --- a/crates/storage/provider/src/chain.rs +++ b/crates/storage/provider/src/chain.rs @@ -6,6 +6,7 @@ use reth_primitives::{ Address, BlockHash, BlockNumHash, BlockNumber, ForkBlock, Receipt, SealedBlock, SealedBlockWithSenders, SealedHeader, TransactionSigned, TransactionSignedEcRecovered, TxHash, }; +use revm::db::BundleState; use std::{borrow::Cow, collections::BTreeMap, fmt}; /// A chain of blocks and their final state. @@ -59,6 +60,11 @@ impl Chain { &self.state } + /// Prepends the given state to the current state. + pub fn prepend_state(&mut self, state: BundleState) { + self.state.prepend_state(state); + } + /// Return true if chain is empty and has no blocks. pub fn is_empty(&self) -> bool { self.blocks.is_empty() @@ -426,7 +432,7 @@ mod tests { #[test] fn chain_append() { - let block = SealedBlockWithSenders::default(); + let block: SealedBlockWithSenders = SealedBlockWithSenders::default(); let block1_hash = B256::new([0x01; 32]); let block2_hash = B256::new([0x02; 32]); let block3_hash = B256::new([0x03; 32]); From b0c4d99cac0e877d54455b09b62b270a378938af Mon Sep 17 00:00:00 2001 From: Federico Gimenez Date: Tue, 5 Dec 2023 19:39:46 +0100 Subject: [PATCH 100/277] test(storage): implement `DbCursorRO::walk_back` method for `CursorMock` (#5670) Co-authored-by: Roman Krasiuk --- crates/storage/db/src/abstraction/mock.rs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/crates/storage/db/src/abstraction/mock.rs b/crates/storage/db/src/abstraction/mock.rs index 8c2cfb0fb35c..54d766d11c83 100644 --- a/crates/storage/db/src/abstraction/mock.rs +++ b/crates/storage/db/src/abstraction/mock.rs @@ -166,9 +166,13 @@ impl DbCursorRO for CursorMock { fn walk_back( &mut self, - _start_key: Option, + start_key: Option, ) -> Result, DatabaseError> { - todo!() + let start: IterPairResult = match start_key { + Some(key) => >::seek(self, key).transpose(), + None => >::last(self).transpose(), + }; + Ok(ReverseWalker::new(self, start)) } } From 926766d482d6f02c57db6fda44f5b9cb45806334 Mon Sep 17 00:00:00 2001 From: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Date: Tue, 5 Dec 2023 20:13:49 +0100 Subject: [PATCH 101/277] perf: misc changes (#5701) --- .../primitives/src/trie/hash_builder/mod.rs | 28 ++--- crates/primitives/src/trie/nibbles.rs | 14 ++- crates/primitives/src/trie/nodes/mod.rs | 6 +- crates/storage/codecs/src/lib.rs | 100 +++++++++++------- 4 files changed, 97 insertions(+), 51 deletions(-) diff --git a/crates/primitives/src/trie/hash_builder/mod.rs b/crates/primitives/src/trie/hash_builder/mod.rs index ed2c86de77ed..6e32d566d8f2 100644 --- a/crates/primitives/src/trie/hash_builder/mod.rs +++ b/crates/primitives/src/trie/hash_builder/mod.rs @@ -203,20 +203,24 @@ impl HashBuilder { trace!(target: "trie::hash_builder", ?current, ?succeeding, "updating merkle tree"); - let mut i = 0; + let mut i = 0usize; + let span = tracing::trace_span!( + target: "trie::hash_builder", + "loop", + i = tracing::field::Empty, + current = tracing::field::Empty, + build_extensions = tracing::field::Empty, + ) + .entered(); loop { - let span = tracing::span!( - target: "trie::hash_builder", - tracing::Level::TRACE, - "loop", - i, - ?current, - ?build_extensions - ); - let _enter = span.enter(); + if !span.is_disabled() { + span.record("i", i); + span.record("current", &format!("{current:?}")); + span.record("build_extensions", build_extensions); + } let preceding_exists = !self.groups.is_empty(); - let preceding_len: usize = self.groups.len().saturating_sub(1); + let preceding_len = self.groups.len().saturating_sub(1); let common_prefix_len = succeeding.common_prefix_length(current.as_slice()); let len = std::cmp::max(preceding_len, common_prefix_len); @@ -254,7 +258,7 @@ impl HashBuilder { if !succeeding.is_empty() || preceding_exists { len_from += 1; } - trace!(target: "trie::hash_builder", "skipping {} nibbles", len_from); + trace!(target: "trie::hash_builder", "skipping {len_from} nibbles"); // The key without the common prefix let short_node_key = current.slice(len_from..); diff --git a/crates/primitives/src/trie/nibbles.rs b/crates/primitives/src/trie/nibbles.rs index f813f930980f..fe60d18bd38f 100644 --- a/crates/primitives/src/trie/nibbles.rs +++ b/crates/primitives/src/trie/nibbles.rs @@ -67,7 +67,6 @@ impl Compact for StoredNibblesSubKey { /// The internal representation is a [`SmallVec`] that stores one nibble per byte. This means that /// each byte has its upper 4 bits set to zero and the lower 4 bits representing the nibble value. #[derive( - Clone, Default, PartialEq, Eq, @@ -83,6 +82,19 @@ impl Compact for StoredNibblesSubKey { #[cfg_attr(any(test, feature = "arbitrary"), derive(arbitrary::Arbitrary))] pub struct Nibbles(SmallVec<[u8; 64]>); +// Override `SmallVec::from` since it's not specialized for `Copy` types. +impl Clone for Nibbles { + #[inline] + fn clone(&self) -> Self { + Self(SmallVec::from_slice(&self.0)) + } + + #[inline] + fn clone_from(&mut self, source: &Self) { + self.0.clone_from(&source.0); + } +} + impl alloy_rlp::Encodable for Nibbles { #[inline] fn length(&self) -> usize { diff --git a/crates/primitives/src/trie/nodes/mod.rs b/crates/primitives/src/trie/nodes/mod.rs index 98e7d494af7e..c0c661795d19 100644 --- a/crates/primitives/src/trie/nodes/mod.rs +++ b/crates/primitives/src/trie/nodes/mod.rs @@ -28,5 +28,9 @@ fn rlp_node(rlp: &[u8]) -> Vec { // TODO: this could return [u8; 33] but Vec is needed everywhere this function is used #[inline] pub fn word_rlp(word: &B256) -> Vec { - [&[EMPTY_STRING_CODE + B256::len_bytes() as u8][..], &word[..]].concat() + // Gets optimized to alloc + write directly into it: https://godbolt.org/z/rfWGG6ebq + let mut arr = [0; 33]; + arr[0] = EMPTY_STRING_CODE + 32; + arr[1..].copy_from_slice(word.as_slice()); + arr.to_vec() } diff --git a/crates/storage/codecs/src/lib.rs b/crates/storage/codecs/src/lib.rs index 2e9a8d3cb522..93d5e4ec22b7 100644 --- a/crates/storage/codecs/src/lib.rs +++ b/crates/storage/codecs/src/lib.rs @@ -53,6 +53,7 @@ pub trait Compact: Sized { fn from_compact(buf: &[u8], len: usize) -> (Self, &[u8]); /// "Optional": If there's no good reason to use it, don't. + #[inline] fn specialized_to_compact(self, buf: &mut B) -> usize where B: bytes::BufMut + AsMut<[u8]>, @@ -61,6 +62,7 @@ pub trait Compact: Sized { } /// "Optional": If there's no good reason to use it, don't. + #[inline] fn specialized_from_compact(buf: &[u8], len: usize) -> (Self, &[u8]) { Self::from_compact(buf, len) } @@ -70,6 +72,7 @@ macro_rules! impl_uint_compact { ($($name:tt),+) => { $( impl Compact for $name { + #[inline] fn to_compact(self, buf: &mut B) -> usize where B: bytes::BufMut + AsMut<[u8]> { @@ -78,16 +81,16 @@ macro_rules! impl_uint_compact { std::mem::size_of::<$name>() - leading } + #[inline] fn from_compact(mut buf: &[u8], len: usize) -> (Self, &[u8]) { - if len > 0 { - let mut arr = [0; std::mem::size_of::<$name>()]; - arr[std::mem::size_of::<$name>() - len..].copy_from_slice(&buf[..len]); - - buf.advance(len); - - return ($name::from_be_bytes(arr), buf) + if len == 0 { + return (0, buf); } - (0, buf) + + let mut arr = [0; std::mem::size_of::<$name>()]; + arr[std::mem::size_of::<$name>() - len..].copy_from_slice(&buf[..len]); + buf.advance(len); + ($name::from_be_bytes(arr), buf) } } )+ @@ -101,6 +104,7 @@ where T: Compact, { /// Returns 0 since we won't include it in the `StructFlags`. + #[inline] fn to_compact(self, buf: &mut B) -> usize where B: bytes::BufMut + AsMut<[u8]>, @@ -122,6 +126,7 @@ where 0 } + #[inline] fn from_compact(buf: &[u8], _: usize) -> (Self, &[u8]) { let (length, mut buf) = decode_varuint(buf); let mut list = Vec::with_capacity(length); @@ -139,6 +144,7 @@ where } /// To be used by fixed sized types like `Vec`. + #[inline] fn specialized_to_compact(self, buf: &mut B) -> usize where B: bytes::BufMut + AsMut<[u8]>, @@ -151,6 +157,7 @@ where } /// To be used by fixed sized types like `Vec`. + #[inline] fn specialized_from_compact(buf: &[u8], len: usize) -> (Self, &[u8]) { let (length, mut buf) = decode_varuint(buf); let mut list = Vec::with_capacity(length); @@ -170,25 +177,27 @@ where T: Compact, { /// Returns 0 for `None` and 1 for `Some(_)`. + #[inline] fn to_compact(self, buf: &mut B) -> usize where B: bytes::BufMut + AsMut<[u8]>, { - let mut tmp = Vec::with_capacity(64); + let Some(element) = self else { + return 0; + }; - if let Some(element) = self { - // We don't know the length until we compact it - let length = element.to_compact(&mut tmp); + // We don't know the length of the element until we compact it. + let mut tmp = Vec::with_capacity(64); + let length = element.to_compact(&mut tmp); - encode_varuint(length, buf); + encode_varuint(length, buf); - buf.put_slice(&tmp); + buf.put_slice(&tmp); - return 1 - } - 0 + 1 } + #[inline] fn from_compact(buf: &[u8], len: usize) -> (Self, &[u8]) { if len == 0 { return (None, buf) @@ -203,53 +212,58 @@ where } /// To be used by fixed sized types like `Option`. + #[inline] fn specialized_to_compact(self, buf: &mut B) -> usize where B: bytes::BufMut + AsMut<[u8]>, { if let Some(element) = self { element.to_compact(buf); - return 1 + 1 + } else { + 0 } - 0 } /// To be used by fixed sized types like `Option`. + #[inline] fn specialized_from_compact(buf: &[u8], len: usize) -> (Self, &[u8]) { if len == 0 { return (None, buf) } let (element, buf) = T::from_compact(buf, len); - (Some(element), buf) } } impl Compact for U256 { + #[inline] fn to_compact(self, buf: &mut B) -> usize where B: bytes::BufMut + AsMut<[u8]>, { - let inner: [u8; 32] = self.to_be_bytes(); + let inner = self.to_be_bytes::<32>(); let size = 32 - (self.leading_zeros() / 8); buf.put_slice(&inner[32 - size..]); size } + #[inline] fn from_compact(mut buf: &[u8], len: usize) -> (Self, &[u8]) { - if len > 0 { - let mut arr = [0; 32]; - arr[(32 - len)..].copy_from_slice(&buf[..len]); - buf.advance(len); - return (U256::from_be_bytes(arr), buf) + if len == 0 { + return (U256::ZERO, buf); } - (U256::ZERO, buf) + let mut arr = [0; 32]; + arr[(32 - len)..].copy_from_slice(&buf[..len]); + buf.advance(len); + (U256::from_be_bytes(arr), buf) } } impl Compact for Bytes { + #[inline] fn to_compact(self, buf: &mut B) -> usize where B: bytes::BufMut + AsMut<[u8]>, @@ -259,12 +273,14 @@ impl Compact for Bytes { len } + #[inline] fn from_compact(mut buf: &[u8], len: usize) -> (Self, &[u8]) { (buf.copy_to_bytes(len).into(), buf) } } impl Compact for [u8; N] { + #[inline] fn to_compact(self, buf: &mut B) -> usize where B: bytes::BufMut + AsMut<[u8]>, @@ -273,6 +289,7 @@ impl Compact for [u8; N] { N } + #[inline] fn from_compact(mut buf: &[u8], len: usize) -> (Self, &[u8]) { if len == 0 { return ([0; N], buf) @@ -290,6 +307,7 @@ macro_rules! impl_compact_for_bytes { ($($name:tt),+) => { $( impl Compact for $name { + #[inline] fn to_compact(self, buf: &mut B) -> usize where B: bytes::BufMut + AsMut<[u8]> @@ -297,6 +315,7 @@ macro_rules! impl_compact_for_bytes { self.0.to_compact(buf) } + #[inline] fn from_compact(buf: &[u8], len: usize) -> (Self, &[u8]) { let (v, buf) = <[u8; std::mem::size_of::<$name>()]>::from_compact(buf, len); (Self::from(v), buf) @@ -310,6 +329,7 @@ impl_compact_for_bytes!(Address, B256, B512, Bloom); impl Compact for bool { /// `bool` vars go directly to the `StructFlags` and are not written to the buffer. + #[inline] fn to_compact(self, _: &mut B) -> usize where B: bytes::BufMut + AsMut<[u8]>, @@ -318,6 +338,7 @@ impl Compact for bool { } /// `bool` expects the real value to come in `len`, and does not advance the cursor. + #[inline] fn from_compact(buf: &[u8], len: usize) -> (Self, &[u8]) { (len != 0, buf) } @@ -334,19 +355,24 @@ where buf.put_u8(n as u8); } -fn decode_varuint(mut buf: &[u8]) -> (usize, &[u8]) { - let mut value: usize = 0; +fn decode_varuint(buf: &[u8]) -> (usize, &[u8]) { + let mut value = 0; - for i in 0usize..33 { - let byte = buf.get_u8(); - if byte < 128 { - value |= usize::from(byte) << (i * 7); - return (value, buf) - } else { - value |= usize::from(byte & 0x7F) << (i * 7); + for i in 0..33 { + let byte = buf[i]; + value |= usize::from(byte & 0x7F) << (i * 7); + if byte < 0x80 { + return (value, &buf[i + 1..]); } } - panic!("Could not correctly decode value."); + + decode_varuint_panic(); +} + +#[inline(never)] +#[cold] +const fn decode_varuint_panic() -> ! { + panic!("could not decode varuint"); } #[cfg(test)] From 227e1b7ad513977f4f48b18041df02686fca5f94 Mon Sep 17 00:00:00 2001 From: Dan Cline <6798349+Rjected@users.noreply.github.com> Date: Tue, 5 Dec 2023 15:48:09 -0500 Subject: [PATCH 102/277] feat: make load_network_config and lookup_head generic over DB (#5692) Co-authored-by: Matthias Seitz --- bin/reth/src/node/mod.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/bin/reth/src/node/mod.rs b/bin/reth/src/node/mod.rs index 1b4eaeb20f59..37773a87b750 100644 --- a/bin/reth/src/node/mod.rs +++ b/bin/reth/src/node/mod.rs @@ -744,7 +744,7 @@ impl NodeCommand { /// Fetches the head block from the database. /// /// If the database is empty, returns the genesis block. - fn lookup_head(&self, db: Arc) -> RethResult { + fn lookup_head(&self, db: DB) -> RethResult { let factory = ProviderFactory::new(db, self.chain.clone()); let provider = factory.provider()?; @@ -826,15 +826,15 @@ impl NodeCommand { } } - fn load_network_config( + fn load_network_config( &self, config: &Config, - db: Arc, + db: DB, executor: TaskExecutor, head: Head, secret_key: SecretKey, default_peers_path: PathBuf, - ) -> NetworkConfig>> { + ) -> NetworkConfig> { let cfg_builder = self .network .network_config(config, self.chain.clone(), secret_key, default_peers_path) From 7ee3c7062b5dd78220fd1271844e75687509e4b9 Mon Sep 17 00:00:00 2001 From: Roman Krasiuk Date: Wed, 6 Dec 2023 04:29:05 -0800 Subject: [PATCH 103/277] chore: temporarily disable doc related CI (#5704) --- .github/workflows/book.yml | 5 ++++- .github/workflows/lint.yml | 6 +++++- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/.github/workflows/book.yml b/.github/workflows/book.yml index d38b41fcf1c1..6d675df170b5 100644 --- a/.github/workflows/book.yml +++ b/.github/workflows/book.yml @@ -53,6 +53,8 @@ jobs: run: mdbook-linkcheck --standalone build: + # TODO: reenable when is resolved. + if: false runs-on: ubuntu-latest timeout-minutes: 60 steps: @@ -114,7 +116,8 @@ jobs: deploy: # Only deploy if a push to main - if: github.ref_name == 'main' && github.event_name == 'push' + # TODO: reenable when is resolved. + if: false && github.ref_name == 'main' && github.event_name == 'push' runs-on: ubuntu-latest needs: [test, lint, build] diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index 293831dcc2d5..2724a4c41090 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -57,6 +57,8 @@ jobs: docs: name: docs + # TODO: reenable when is resolved. + if: false runs-on: ubuntu-latest timeout-minutes: 30 steps: @@ -98,7 +100,9 @@ jobs: name: lint success runs-on: ubuntu-latest if: always() - needs: [clippy, docs, fmt, grafana] + # TODO: reenable when is resolved. + # needs: [clippy, docs, fmt, grafana] + needs: [clippy, fmt, grafana] timeout-minutes: 30 steps: - name: Decide whether the needed jobs succeeded or failed From 857f90722e2002810b2607c18a28253cf4f3ad03 Mon Sep 17 00:00:00 2001 From: Roman Krasiuk Date: Wed, 6 Dec 2023 06:51:23 -0800 Subject: [PATCH 104/277] chore(provider): better observability on append (#5703) --- crates/storage/provider/src/providers/database/provider.rs | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/crates/storage/provider/src/providers/database/provider.rs b/crates/storage/provider/src/providers/database/provider.rs index 7ff06228acc6..46884e9c5319 100644 --- a/crates/storage/provider/src/providers/database/provider.rs +++ b/crates/storage/provider/src/providers/database/provider.rs @@ -2304,10 +2304,9 @@ impl BlockWriter for DatabaseProvider { prune_modes: Option<&PruneModes>, ) -> ProviderResult<()> { if blocks.is_empty() { + debug!(target: "providers::db", "Attempted to append empty block range"); return Ok(()) } - let new_tip = blocks.last().unwrap(); - let new_tip_number = new_tip.number; let first_number = blocks.first().unwrap().number; @@ -2339,10 +2338,10 @@ impl BlockWriter for DatabaseProvider { durations_recorder.record_relative(metrics::Action::InsertHistoryIndices); // Update pipeline progress - self.update_pipeline_stages(new_tip_number, false)?; + self.update_pipeline_stages(last_block_number, false)?; durations_recorder.record_relative(metrics::Action::UpdatePipelineStages); - debug!(target: "providers::db", actions = ?durations_recorder.actions, "Appended blocks"); + debug!(target: "providers::db", range = ?first_number..=last_block_number, actions = ?durations_recorder.actions, "Appended blocks"); Ok(()) } From 3947b91b72731900ecfa74a81c22115325d7a869 Mon Sep 17 00:00:00 2001 From: Matthias Seitz Date: Wed, 6 Dec 2023 17:31:50 +0100 Subject: [PATCH 105/277] chore: add fs read (#5706) --- crates/primitives/src/fs.rs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/crates/primitives/src/fs.rs b/crates/primitives/src/fs.rs index 8e4e50acd6e8..60f46e4da21b 100644 --- a/crates/primitives/src/fs.rs +++ b/crates/primitives/src/fs.rs @@ -108,6 +108,14 @@ pub fn read_to_string(path: impl AsRef) -> Result { fs::read_to_string(path).map_err(|err| FsPathError::read(err, path)) } +/// Read the entire contents of a file into a bytes vector. +/// +/// Wrapper for `std::fs::read` +pub fn read(path: impl AsRef) -> Result> { + let path = path.as_ref(); + fs::read(path).map_err(|err| FsPathError::read(err, path)) +} + /// Wrapper for `std::fs::write` pub fn write(path: impl AsRef, contents: impl AsRef<[u8]>) -> Result<()> { let path = path.as_ref(); From 8fa48c4f899b391882ba4b9052d1ada5b446df0f Mon Sep 17 00:00:00 2001 From: lmittmann <3458786+lmittmann@users.noreply.github.com> Date: Thu, 7 Dec 2023 08:40:33 +0100 Subject: [PATCH 106/277] feat(primitives/net): make `parse_nodes` public (#5708) Co-authored-by: lmittmann --- crates/primitives/src/lib.rs | 4 ++-- crates/primitives/src/net.rs | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/crates/primitives/src/lib.rs b/crates/primitives/src/lib.rs index a8f3705dbcc1..5f271a96c0a7 100644 --- a/crates/primitives/src/lib.rs +++ b/crates/primitives/src/lib.rs @@ -68,8 +68,8 @@ pub use header::{Header, HeadersDirection, SealedHeader}; pub use integer_list::IntegerList; pub use log::{logs_bloom, Log}; pub use net::{ - goerli_nodes, holesky_nodes, mainnet_nodes, sepolia_nodes, NodeRecord, GOERLI_BOOTNODES, - HOLESKY_BOOTNODES, MAINNET_BOOTNODES, SEPOLIA_BOOTNODES, + goerli_nodes, holesky_nodes, mainnet_nodes, parse_nodes, sepolia_nodes, NodeRecord, + GOERLI_BOOTNODES, HOLESKY_BOOTNODES, MAINNET_BOOTNODES, SEPOLIA_BOOTNODES, }; pub use peer::{PeerId, WithPeerId}; pub use prune::{ diff --git a/crates/primitives/src/net.rs b/crates/primitives/src/net.rs index afb43c871e82..d3818b4d8590 100644 --- a/crates/primitives/src/net.rs +++ b/crates/primitives/src/net.rs @@ -62,7 +62,7 @@ pub fn holesky_nodes() -> Vec { } /// Parses all the nodes -fn parse_nodes(nodes: impl IntoIterator>) -> Vec { +pub fn parse_nodes(nodes: impl IntoIterator>) -> Vec { nodes.into_iter().map(|s| s.as_ref().parse().unwrap()).collect() } From 27da72cd576f80f8c1dfe1fd7aad089ce4299206 Mon Sep 17 00:00:00 2001 From: lmittmann <3458786+lmittmann@users.noreply.github.com> Date: Fri, 8 Dec 2023 08:51:22 +0100 Subject: [PATCH 107/277] feat(primitives/net): export `ConnectionInfo` (#5709) Co-authored-by: lmittmann --- crates/net/network/src/peers/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/net/network/src/peers/mod.rs b/crates/net/network/src/peers/mod.rs index 0261753ca7bf..84f941cc59da 100644 --- a/crates/net/network/src/peers/mod.rs +++ b/crates/net/network/src/peers/mod.rs @@ -4,7 +4,7 @@ mod manager; mod reputation; pub(crate) use manager::InboundConnectionError; -pub use manager::{Peer, PeerAction, PeersConfig, PeersHandle, PeersManager}; +pub use manager::{ConnectionInfo, Peer, PeerAction, PeersConfig, PeersHandle, PeersManager}; pub use reputation::ReputationChangeWeights; pub use reth_network_api::PeerKind; From cd4d6c52b0d9c501dcf340052623d29592df168c Mon Sep 17 00:00:00 2001 From: Emilia Hane Date: Fri, 8 Dec 2023 09:21:01 +0100 Subject: [PATCH 108/277] Cap mux simple (#5577) Signed-off-by: Emilia Hane --- Cargo.lock | 2 + crates/net/eth-wire/Cargo.toml | 3 + crates/net/eth-wire/src/capability.rs | 41 +- crates/net/eth-wire/src/disconnect.rs | 2 +- crates/net/eth-wire/src/errors/eth.rs | 9 +- crates/net/eth-wire/src/errors/mod.rs | 2 + crates/net/eth-wire/src/errors/muxdemux.rs | 47 ++ crates/net/eth-wire/src/ethstream.rs | 2 +- crates/net/eth-wire/src/lib.rs | 6 +- crates/net/eth-wire/src/muxdemux.rs | 592 +++++++++++++++++++++ crates/net/eth-wire/src/p2pstream.rs | 20 +- crates/net/network/src/session/active.rs | 2 +- 12 files changed, 703 insertions(+), 25 deletions(-) create mode 100644 crates/net/eth-wire/src/errors/muxdemux.rs create mode 100644 crates/net/eth-wire/src/muxdemux.rs diff --git a/Cargo.lock b/Cargo.lock index eb7fc739da22..1a19bc7ab78c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5898,6 +5898,7 @@ dependencies = [ "arbitrary", "async-trait", "bytes", + "derive_more", "ethers-core", "futures", "metrics", @@ -5909,6 +5910,7 @@ dependencies = [ "reth-discv4", "reth-ecies", "reth-metrics", + "reth-net-common", "reth-primitives", "reth-tracing", "secp256k1 0.27.0", diff --git a/crates/net/eth-wire/Cargo.toml b/crates/net/eth-wire/Cargo.toml index a5f691ef383f..88af65762bf2 100644 --- a/crates/net/eth-wire/Cargo.toml +++ b/crates/net/eth-wire/Cargo.toml @@ -21,6 +21,7 @@ reth-metrics.workspace = true metrics.workspace = true bytes.workspace = true +derive_more = "0.99.17" thiserror.workspace = true serde = { workspace = true, optional = true } tokio = { workspace = true, features = ["full"] } @@ -38,10 +39,12 @@ proptest = { workspace = true, optional = true } proptest-derive = { workspace = true, optional = true } [dev-dependencies] +reth-net-common.workspace = true reth-primitives = { workspace = true, features = ["arbitrary"] } reth-tracing.workspace = true ethers-core = { workspace = true, default-features = false } + test-fuzz = "4" tokio-util = { workspace = true, features = ["io", "codec"] } rand.workspace = true diff --git a/crates/net/eth-wire/src/capability.rs b/crates/net/eth-wire/src/capability.rs index c1e6d84ab616..8696109c68bb 100644 --- a/crates/net/eth-wire/src/capability.rs +++ b/crates/net/eth-wire/src/capability.rs @@ -8,6 +8,7 @@ use crate::{ EthMessage, EthMessageID, EthVersion, }; use alloy_rlp::{Decodable, Encodable, RlpDecodable, RlpEncodable}; +use derive_more::{Deref, DerefMut}; use reth_codecs::add_arbitrary_tests; use reth_primitives::bytes::{BufMut, Bytes}; #[cfg(feature = "serde")] @@ -249,14 +250,23 @@ pub enum SharedCapability { /// This represents the message ID offset for the first message of the eth capability in /// the message id space. offset: u8, + /// The number of messages of this capability. Needed to calculate range of message IDs in + /// demuxing. + messages: u8, }, } impl SharedCapability { - /// Creates a new [`SharedCapability`] based on the given name, offset, and version. + /// Creates a new [`SharedCapability`] based on the given name, offset, version (and messages + /// if the capability is custom). /// /// Returns an error if the offset is equal or less than [`MAX_RESERVED_MESSAGE_ID`]. - pub(crate) fn new(name: &str, version: u8, offset: u8) -> Result { + pub(crate) fn new( + name: &str, + version: u8, + offset: u8, + messages: u8, + ) -> Result { if offset <= MAX_RESERVED_MESSAGE_ID { return Err(SharedCapabilityError::ReservedMessageIdOffset(offset)) } @@ -266,6 +276,7 @@ impl SharedCapability { _ => Ok(Self::UnknownCapability { cap: Capability::new(name.to_string(), version as usize), offset, + messages, }), } } @@ -324,10 +335,10 @@ impl SharedCapability { } /// Returns the number of protocol messages supported by this capability. - pub fn num_messages(&self) -> Result { + pub fn num_messages(&self) -> u8 { match self { - SharedCapability::Eth { version: _version, .. } => Ok(EthMessageID::max() + 1), - _ => Err(SharedCapabilityError::UnknownCapability), + SharedCapability::Eth { version: _version, .. } => EthMessageID::max() + 1, + SharedCapability::UnknownCapability { messages, .. } => *messages, } } } @@ -335,7 +346,7 @@ impl SharedCapability { /// Non-empty,ordered list of recognized shared capabilities. /// /// Shared capabilities are ordered alphabetically by case sensitive name. -#[derive(Debug)] +#[derive(Debug, Clone, Deref, DerefMut, PartialEq, Eq)] pub struct SharedCapabilities(Vec); impl SharedCapabilities { @@ -500,9 +511,14 @@ pub fn shared_capability_offsets( for name in shared_capability_names { let proto_version = shared_capabilities.get(&name).expect("shared; qed"); - let shared_capability = SharedCapability::new(&name, proto_version.version as u8, offset)?; + let shared_capability = SharedCapability::new( + &name, + proto_version.version as u8, + offset, + proto_version.messages, + )?; - offset += proto_version.messages; + offset += shared_capability.num_messages(); shared_with_offsets.push(shared_capability); } @@ -519,9 +535,6 @@ pub enum SharedCapabilityError { /// Unsupported `eth` version. #[error(transparent)] UnsupportedVersion(#[from] ParseVersionError), - /// Cannot determine the number of messages for unknown capabilities. - #[error("cannot determine the number of messages for unknown capabilities")] - UnknownCapability, /// Thrown when the message id for a [SharedCapability] overlaps with the reserved p2p message /// id space [`MAX_RESERVED_MESSAGE_ID`]. #[error("message id offset `{0}` is reserved")] @@ -541,7 +554,7 @@ mod tests { #[test] fn from_eth_68() { - let capability = SharedCapability::new("eth", 68, MAX_RESERVED_MESSAGE_ID + 1).unwrap(); + let capability = SharedCapability::new("eth", 68, MAX_RESERVED_MESSAGE_ID + 1, 13).unwrap(); assert_eq!(capability.name(), "eth"); assert_eq!(capability.version(), 68); @@ -556,7 +569,7 @@ mod tests { #[test] fn from_eth_67() { - let capability = SharedCapability::new("eth", 67, MAX_RESERVED_MESSAGE_ID + 1).unwrap(); + let capability = SharedCapability::new("eth", 67, MAX_RESERVED_MESSAGE_ID + 1, 13).unwrap(); assert_eq!(capability.name(), "eth"); assert_eq!(capability.version(), 67); @@ -571,7 +584,7 @@ mod tests { #[test] fn from_eth_66() { - let capability = SharedCapability::new("eth", 66, MAX_RESERVED_MESSAGE_ID + 1).unwrap(); + let capability = SharedCapability::new("eth", 66, MAX_RESERVED_MESSAGE_ID + 1, 15).unwrap(); assert_eq!(capability.name(), "eth"); assert_eq!(capability.version(), 66); diff --git a/crates/net/eth-wire/src/disconnect.rs b/crates/net/eth-wire/src/disconnect.rs index aa3c6d220ee1..e03f99f07f9f 100644 --- a/crates/net/eth-wire/src/disconnect.rs +++ b/crates/net/eth-wire/src/disconnect.rs @@ -150,7 +150,7 @@ impl Decodable for DisconnectReason { /// lower-level disconnect functions (such as those that exist in the `p2p` protocol) if the /// underlying stream supports it. #[async_trait::async_trait] -pub trait CanDisconnect: Sink + Unpin + Sized { +pub trait CanDisconnect: Sink + Unpin { /// Disconnects from the underlying stream, using a [`DisconnectReason`] as disconnect /// information if the stream implements a protocol that can carry the additional disconnect /// metadata. diff --git a/crates/net/eth-wire/src/errors/eth.rs b/crates/net/eth-wire/src/errors/eth.rs index 21645def4a62..e05fa4e52b3d 100644 --- a/crates/net/eth-wire/src/errors/eth.rs +++ b/crates/net/eth-wire/src/errors/eth.rs @@ -1,6 +1,8 @@ //! Error handling for (`EthStream`)[crate::EthStream] use crate::{ - errors::P2PStreamError, version::ParseVersionError, DisconnectReason, EthMessageID, EthVersion, + errors::{MuxDemuxError, P2PStreamError}, + version::ParseVersionError, + DisconnectReason, EthMessageID, EthVersion, }; use reth_primitives::{Chain, GotExpected, GotExpectedBoxed, ValidationError, B256}; use std::io; @@ -13,6 +15,9 @@ pub enum EthStreamError { /// Error of the underlying P2P connection. P2PStreamError(#[from] P2PStreamError), #[error(transparent)] + /// Error of the underlying de-/muxed P2P connection. + MuxDemuxError(#[from] MuxDemuxError), + #[error(transparent)] /// Failed to parse peer's version. ParseVersionError(#[from] ParseVersionError), #[error(transparent)] @@ -43,6 +48,8 @@ impl EthStreamError { pub fn as_disconnected(&self) -> Option { if let EthStreamError::P2PStreamError(err) = self { err.as_disconnected() + } else if let EthStreamError::MuxDemuxError(MuxDemuxError::P2PStreamError(err)) = self { + err.as_disconnected() } else { None } diff --git a/crates/net/eth-wire/src/errors/mod.rs b/crates/net/eth-wire/src/errors/mod.rs index be3f8ced7f4f..c231e48608e6 100644 --- a/crates/net/eth-wire/src/errors/mod.rs +++ b/crates/net/eth-wire/src/errors/mod.rs @@ -1,7 +1,9 @@ //! Error types for stream variants mod eth; +mod muxdemux; mod p2p; pub use eth::*; +pub use muxdemux::*; pub use p2p::*; diff --git a/crates/net/eth-wire/src/errors/muxdemux.rs b/crates/net/eth-wire/src/errors/muxdemux.rs new file mode 100644 index 000000000000..74ca6e2fcf4c --- /dev/null +++ b/crates/net/eth-wire/src/errors/muxdemux.rs @@ -0,0 +1,47 @@ +use thiserror::Error; + +use crate::capability::{SharedCapabilityError, UnsupportedCapabilityError}; + +use super::P2PStreamError; + +/// Errors thrown by de-/muxing. +#[derive(Error, Debug)] +pub enum MuxDemuxError { + /// Error of the underlying P2P connection. + #[error(transparent)] + P2PStreamError(#[from] P2PStreamError), + /// Stream is in use by secondary stream impeding disconnect. + #[error("secondary streams are still running")] + StreamInUse, + /// Stream has already been set up for this capability stream type. + #[error("stream already init for stream type")] + StreamAlreadyExists, + /// Capability stream type is not shared with peer on underlying p2p connection. + #[error("stream type is not shared on this p2p connection")] + CapabilityNotShared, + /// Capability stream type has not been configured in [`crate::muxdemux::MuxDemuxer`]. + #[error("stream type is not configured")] + CapabilityNotConfigured, + /// Capability stream type has not been configured for + /// [`crate::capability::SharedCapabilities`] type. + #[error("stream type is not recognized")] + CapabilityNotRecognized, + /// Message ID is out of range. + #[error("message id out of range, {0}")] + MessageIdOutOfRange(u8), + /// Demux channel failed. + #[error("sending demuxed bytes to secondary stream failed")] + SendIngressBytesFailed, + /// Mux channel failed. + #[error("sending bytes from secondary stream to mux failed")] + SendEgressBytesFailed, + /// Attempt to disconnect the p2p stream via a stream clone. + #[error("secondary stream cannot disconnect p2p stream")] + CannotDisconnectP2PStream, + /// Shared capability error. + #[error(transparent)] + SharedCapabilityError(#[from] SharedCapabilityError), + /// Capability not supported on the p2p connection. + #[error(transparent)] + UnsupportedCapabilityError(#[from] UnsupportedCapabilityError), +} diff --git a/crates/net/eth-wire/src/ethstream.rs b/crates/net/eth-wire/src/ethstream.rs index 23f64040a438..f1162f7ee464 100644 --- a/crates/net/eth-wire/src/ethstream.rs +++ b/crates/net/eth-wire/src/ethstream.rs @@ -283,7 +283,7 @@ where // start_disconnect, which would ideally be a part of the CanDisconnect trait, or at // least similar. // - // Other parts of reth do not need traits like CanDisconnect because they work + // Other parts of reth do not yet need traits like CanDisconnect because atm they work // exclusively with EthStream>, where the inner P2PStream is accessible, // allowing for its start_disconnect method to be called. // diff --git a/crates/net/eth-wire/src/lib.rs b/crates/net/eth-wire/src/lib.rs index a1c7c70bdf0f..c090deb5f6bd 100644 --- a/crates/net/eth-wire/src/lib.rs +++ b/crates/net/eth-wire/src/lib.rs @@ -21,6 +21,7 @@ pub mod errors; mod ethstream; mod hello; pub mod multiplex; +pub mod muxdemux; mod p2pstream; mod pinger; pub mod protocol; @@ -37,11 +38,14 @@ pub use tokio_util::codec::{ }; pub use crate::{ + capability::Capability, disconnect::{CanDisconnect, DisconnectReason}, ethstream::{EthStream, UnauthedEthStream, MAX_MESSAGE_SIZE}, hello::{HelloMessage, HelloMessageBuilder, HelloMessageWithProtocols}, + muxdemux::{MuxDemuxStream, StreamClone}, p2pstream::{ - P2PMessage, P2PMessageID, P2PStream, ProtocolVersion, UnauthedP2PStream, + DisconnectP2P, P2PMessage, P2PMessageID, P2PStream, ProtocolVersion, UnauthedP2PStream, MAX_RESERVED_MESSAGE_ID, }, + types::EthVersion, }; diff --git a/crates/net/eth-wire/src/muxdemux.rs b/crates/net/eth-wire/src/muxdemux.rs new file mode 100644 index 000000000000..621eb64d9b26 --- /dev/null +++ b/crates/net/eth-wire/src/muxdemux.rs @@ -0,0 +1,592 @@ +//! [`MuxDemuxer`] allows for multiple capability streams to share the same p2p connection. De-/ +//! muxing the connection offers two stream types [`MuxDemuxStream`] and [`StreamClone`]. +//! [`MuxDemuxStream`] is the main stream that wraps the p2p connection, only this stream can +//! advance transfer across the network. One [`MuxDemuxStream`] can have many [`StreamClone`]s, +//! these are weak clones of the stream and depend on advancing the [`MuxDemuxStream`] to make +//! progress. +//! +//! [`MuxDemuxer`] filters bytes according to message ID offset. The message ID offset is +//! negotiated upon start of the p2p connection. Bytes received by polling the [`MuxDemuxStream`] +//! or a [`StreamClone`] are specific to the capability stream wrapping it. When received the +//! message IDs are unmasked so that all message IDs start at 0x0. [`MuxDemuxStream`] and +//! [`StreamClone`] mask message IDs before sinking bytes to the [`MuxDemuxer`]. +//! +//! For example, `EthStream>>` is the main capability stream. +//! Subsequent capability streams clone the p2p connection via EthStream. +//! +//! When [`MuxDemuxStream`] is polled, [`MuxDemuxer`] receives bytes from the network. If these +//! bytes belong to the capability stream wrapping the [`MuxDemuxStream`] then they are passed up +//! directly. If these bytes however belong to another capability stream, then they are buffered +//! on a channel. When [`StreamClone`] is polled, bytes are read from this buffer. Similarly +//! [`StreamClone`] buffers egress bytes for [`MuxDemuxer`] that are read and sent to the network +//! when [`MuxDemuxStream`] is polled. + +use std::{ + collections::HashMap, + pin::Pin, + task::{ready, Context, Poll}, +}; + +use derive_more::{Deref, DerefMut}; +use futures::{Sink, SinkExt, StreamExt}; +use reth_primitives::bytes::{Bytes, BytesMut}; +use tokio::sync::mpsc; +use tokio_stream::Stream; + +use crate::{ + capability::{Capability, SharedCapabilities, SharedCapability}, + errors::MuxDemuxError, + CanDisconnect, DisconnectP2P, DisconnectReason, +}; + +use MuxDemuxError::*; + +/// Stream MUX/DEMUX acts like a regular stream and sink for the owning stream, and handles bytes +/// belonging to other streams over their respective channels. +#[derive(Debug)] +pub struct MuxDemuxer { + // receive and send muxed p2p outputs + inner: S, + // owner of the stream. stores message id offset for this capability. + owner: SharedCapability, + // receive muxed p2p inputs from stream clones + mux: mpsc::UnboundedReceiver, + // send demuxed p2p outputs to app + demux: HashMap>, + // sender to mux stored to make new stream clones + mux_tx: mpsc::UnboundedSender, + // capabilities supported by underlying p2p stream (makes testing easier to store here too). + shared_capabilities: SharedCapabilities, +} + +/// The main stream on top of the p2p stream. Wraps [`MuxDemuxer`] and enforces it can't be dropped +/// before all secondary streams are dropped (stream clones). +#[derive(Debug, Deref, DerefMut)] +pub struct MuxDemuxStream(MuxDemuxer); + +impl MuxDemuxStream { + /// Creates a new [`MuxDemuxer`]. + pub fn try_new( + inner: S, + cap: Capability, + shared_capabilities: SharedCapabilities, + ) -> Result { + let owner = Self::shared_cap(&cap, &shared_capabilities)?.clone(); + + let demux = HashMap::new(); + let (mux_tx, mux) = mpsc::unbounded_channel(); + + Ok(Self(MuxDemuxer { inner, owner, mux, demux, mux_tx, shared_capabilities })) + } + + /// Clones the stream if the given capability stream type is shared on the underlying p2p + /// connection. + pub fn try_clone_stream(&mut self, cap: &Capability) -> Result { + let cap = self.shared_capabilities.ensure_matching_capability(cap)?.clone(); + let ingress = self.reg_new_ingress_buffer(&cap)?; + let mux_tx = self.mux_tx.clone(); + + Ok(StreamClone { stream: ingress, sink: mux_tx, cap }) + } + + /// Starts a graceful disconnect. + pub fn start_disconnect(&mut self, reason: DisconnectReason) -> Result<(), MuxDemuxError> + where + S: DisconnectP2P, + { + if !self.can_drop() { + return Err(StreamInUse) + } + + self.inner.start_disconnect(reason).map_err(|e| e.into()) + } + + /// Returns `true` if the connection is about to disconnect. + pub fn is_disconnecting(&self) -> bool + where + S: DisconnectP2P, + { + self.inner.is_disconnecting() + } + + /// Shared capabilities of underlying p2p connection as negotiated by peers at connection + /// open. + pub fn shared_capabilities(&self) -> &SharedCapabilities { + &self.shared_capabilities + } + + fn shared_cap<'a>( + cap: &Capability, + shared_capabilities: &'a SharedCapabilities, + ) -> Result<&'a SharedCapability, MuxDemuxError> { + for shared_cap in shared_capabilities.iter_caps() { + match shared_cap { + SharedCapability::Eth { .. } if cap.is_eth() => return Ok(shared_cap), + SharedCapability::UnknownCapability { cap: unknown_cap, .. } + if cap == unknown_cap => + { + return Ok(shared_cap) + } + _ => continue, + } + } + + Err(CapabilityNotShared) + } + + fn reg_new_ingress_buffer( + &mut self, + cap: &SharedCapability, + ) -> Result, MuxDemuxError> { + if let Some(tx) = self.demux.get(cap) { + if !tx.is_closed() { + return Err(StreamAlreadyExists) + } + } + let (ingress_tx, ingress) = mpsc::unbounded_channel(); + self.demux.insert(cap.clone(), ingress_tx); + + Ok(ingress) + } + + fn unmask_msg_id(&self, id: &mut u8) -> Result<&SharedCapability, MuxDemuxError> { + for cap in self.shared_capabilities.iter_caps() { + let offset = cap.relative_message_id_offset(); + let next_offset = offset + cap.num_messages(); + if *id < next_offset { + *id -= offset; + return Ok(cap) + } + } + + Err(MessageIdOutOfRange(*id)) + } + + /// Masks message id with offset relative to the message id suffix reserved for capability + /// message ids. The p2p stream further masks the message id (todo: mask whole message id at + /// once to avoid copying message to mutate id byte or sink BytesMut). + fn mask_msg_id(&self, msg: Bytes) -> Bytes { + let mut masked_bytes = BytesMut::zeroed(msg.len()); + masked_bytes[0] = msg[0] + self.owner.relative_message_id_offset(); + masked_bytes[1..].copy_from_slice(&msg[1..]); + + masked_bytes.freeze() + } + + /// Checks if all clones of this shared stream have been dropped, if true then returns // + /// function to drop the stream. + fn can_drop(&mut self) -> bool { + for tx in self.demux.values() { + if !tx.is_closed() { + return false + } + } + + true + } +} + +impl Stream for MuxDemuxStream +where + S: Stream> + CanDisconnect + Unpin, + MuxDemuxError: From + From<>::Error>, +{ + type Item = Result; + + fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + let mut send_count = 0; + let mut mux_exhausted = false; + + loop { + // send buffered bytes from `StreamClone`s. try send at least as many messages as + // there are stream clones. + if self.inner.poll_ready_unpin(cx).is_ready() { + if let Poll::Ready(Some(item)) = self.mux.poll_recv(cx) { + self.inner.start_send_unpin(item)?; + if send_count < self.demux.len() { + send_count += 1; + continue + } + } else { + mux_exhausted = true; + } + } + + // advances the wire and either yields message for the owner or delegates message to a + // stream clone + let res = self.inner.poll_next_unpin(cx); + if res.is_pending() { + // no message is received. continue to send messages from stream clones as long as + // there are messages left to send. + if !mux_exhausted && self.inner.poll_ready_unpin(cx).is_ready() { + continue + } + // flush before returning pending + _ = self.inner.poll_flush_unpin(cx)?; + } + let mut bytes = match ready!(res) { + Some(Ok(bytes)) => bytes, + Some(Err(err)) => { + _ = self.inner.poll_flush_unpin(cx)?; + return Poll::Ready(Some(Err(err.into()))) + } + None => { + _ = self.inner.poll_flush_unpin(cx)?; + return Poll::Ready(None) + } + }; + + // normalize message id suffix for capability + let cap = self.unmask_msg_id(&mut bytes[0])?; + + // yield message for main stream + if *cap == self.owner { + _ = self.inner.poll_flush_unpin(cx)?; + return Poll::Ready(Some(Ok(bytes))) + } + + // delegate message for stream clone + let tx = self.demux.get(cap).ok_or(CapabilityNotConfigured)?; + tx.send(bytes).map_err(|_| SendIngressBytesFailed)?; + } + } +} + +impl Sink for MuxDemuxStream +where + S: Sink + CanDisconnect + Unpin, + MuxDemuxError: From, +{ + type Error = MuxDemuxError; + + fn poll_ready(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + self.inner.poll_ready_unpin(cx).map_err(Into::into) + } + + fn start_send(mut self: Pin<&mut Self>, item: Bytes) -> Result<(), Self::Error> { + let item = self.mask_msg_id(item); + self.inner.start_send_unpin(item).map_err(|e| e.into()) + } + + fn poll_flush(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + self.inner.poll_flush_unpin(cx).map_err(Into::into) + } + + fn poll_close(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + while let Ok(item) = self.mux.try_recv() { + self.inner.start_send_unpin(item)?; + } + _ = self.inner.poll_flush_unpin(cx)?; + + self.inner.poll_close_unpin(cx).map_err(Into::into) + } +} + +#[async_trait::async_trait] +impl CanDisconnect for MuxDemuxStream +where + S: Sink + CanDisconnect + Unpin + Send + Sync, + MuxDemuxError: From, +{ + async fn disconnect(&mut self, reason: DisconnectReason) -> Result<(), MuxDemuxError> { + if self.can_drop() { + return self.inner.disconnect(reason).await.map_err(Into::into) + } + Err(StreamInUse) + } +} + +/// More or less a weak clone of the stream wrapped in [`MuxDemuxer`] but the bytes belonging to +/// other capabilities have been filtered out. +#[derive(Debug)] +pub struct StreamClone { + // receive bytes from de-/muxer + stream: mpsc::UnboundedReceiver, + // send bytes to de-/muxer + sink: mpsc::UnboundedSender, + // message id offset for capability holding this clone + cap: SharedCapability, +} + +impl StreamClone { + fn mask_msg_id(&self, msg: Bytes) -> Bytes { + let mut masked_bytes = BytesMut::zeroed(msg.len()); + masked_bytes[0] = msg[0] + self.cap.relative_message_id_offset(); + masked_bytes[1..].copy_from_slice(&msg[1..]); + + masked_bytes.freeze() + } +} + +impl Stream for StreamClone { + type Item = BytesMut; + + fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + self.stream.poll_recv(cx) + } +} + +impl Sink for StreamClone { + type Error = MuxDemuxError; + + fn poll_ready(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll> { + Poll::Ready(Ok(())) + } + + fn start_send(self: Pin<&mut Self>, item: Bytes) -> Result<(), Self::Error> { + let item = self.mask_msg_id(item); + self.sink.send(item).map_err(|_| SendEgressBytesFailed) + } + + fn poll_flush(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll> { + Poll::Ready(Ok(())) + } + + fn poll_close(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll> { + Poll::Ready(Ok(())) + } +} + +#[async_trait::async_trait] +impl CanDisconnect for StreamClone { + async fn disconnect(&mut self, _reason: DisconnectReason) -> Result<(), MuxDemuxError> { + Err(CannotDisconnectP2PStream) + } +} + +#[cfg(test)] +mod test { + use std::{net::SocketAddr, pin::Pin}; + + use futures::{Future, SinkExt, StreamExt}; + use reth_ecies::util::pk2id; + use reth_primitives::{ + bytes::{BufMut, Bytes, BytesMut}, + ForkFilter, Hardfork, MAINNET, + }; + use secp256k1::{SecretKey, SECP256K1}; + use tokio::{ + net::{TcpListener, TcpStream}, + task::JoinHandle, + }; + use tokio_util::codec::{Decoder, Framed, LengthDelimitedCodec}; + + use crate::{ + capability::{Capability, SharedCapabilities}, + muxdemux::MuxDemuxStream, + protocol::Protocol, + EthVersion, HelloMessageWithProtocols, Status, StatusBuilder, StreamClone, + UnauthedEthStream, UnauthedP2PStream, + }; + + const ETH_68_CAP: Capability = Capability::eth(EthVersion::Eth68); + const ETH_68_PROTOCOL: Protocol = Protocol::new(ETH_68_CAP, 13); + const CUSTOM_CAP: Capability = Capability::new_static("snap", 1); + const CUSTOM_CAP_PROTOCOL: Protocol = Protocol::new(CUSTOM_CAP, 10); + // message IDs `0x00` and `0x01` are normalized for the custom protocol stream + const CUSTOM_REQUEST: [u8; 5] = [0x00, 0x00, 0x01, 0x0, 0xc0]; + const CUSTOM_RESPONSE: [u8; 5] = [0x01, 0x00, 0x01, 0x0, 0xc0]; + + fn shared_caps_eth68() -> SharedCapabilities { + let local_capabilities: Vec = vec![ETH_68_PROTOCOL]; + let peer_capabilities: Vec = vec![ETH_68_CAP]; + SharedCapabilities::try_new(local_capabilities, peer_capabilities).unwrap() + } + + fn shared_caps_eth68_and_custom() -> SharedCapabilities { + let local_capabilities: Vec = vec![ETH_68_PROTOCOL, CUSTOM_CAP_PROTOCOL]; + let peer_capabilities: Vec = vec![ETH_68_CAP, CUSTOM_CAP]; + SharedCapabilities::try_new(local_capabilities, peer_capabilities).unwrap() + } + + struct ConnectionBuilder { + local_addr: SocketAddr, + local_hello: HelloMessageWithProtocols, + status: Status, + fork_filter: ForkFilter, + } + + impl ConnectionBuilder { + fn new() -> Self { + let (_secret_key, pk) = SECP256K1.generate_keypair(&mut rand::thread_rng()); + + let hello = HelloMessageWithProtocols::builder(pk2id(&pk)) + .protocol(ETH_68_PROTOCOL) + .protocol(CUSTOM_CAP_PROTOCOL) + .build(); + + let local_addr = "127.0.0.1:30303".parse().unwrap(); + + Self { + local_hello: hello, + local_addr, + status: StatusBuilder::default().build(), + fork_filter: MAINNET + .hardfork_fork_filter(Hardfork::Frontier) + .expect("The Frontier fork filter should exist on mainnet"), + } + } + + /// Connects a custom sub protocol stream and executes the given closure with that + /// established stream (main stream is eth). + fn with_connect_custom_protocol( + self, + f_local: F, + f_remote: G, + ) -> (JoinHandle, JoinHandle) + where + F: FnOnce(StreamClone) -> Pin + Send)>> + + Send + + Sync + + Send + + 'static, + G: FnOnce(StreamClone) -> Pin + Send)>> + + Send + + Sync + + Send + + 'static, + { + let local_addr = self.local_addr; + + let local_hello = self.local_hello.clone(); + let status = self.status; + let fork_filter = self.fork_filter.clone(); + + let local_handle = tokio::spawn(async move { + let local_listener = TcpListener::bind(local_addr).await.unwrap(); + let (incoming, _) = local_listener.accept().await.unwrap(); + let stream = crate::PassthroughCodec::default().framed(incoming); + + let protocol_proxy = + connect_protocol(stream, local_hello, status, fork_filter).await; + + f_local(protocol_proxy).await + }); + + let remote_key = SecretKey::new(&mut rand::thread_rng()); + let remote_id = pk2id(&remote_key.public_key(SECP256K1)); + let mut remote_hello = self.local_hello.clone(); + remote_hello.id = remote_id; + let fork_filter = self.fork_filter.clone(); + + let remote_handle = tokio::spawn(async move { + let outgoing = TcpStream::connect(local_addr).await.unwrap(); + let stream = crate::PassthroughCodec::default().framed(outgoing); + + let protocol_proxy = + connect_protocol(stream, remote_hello, status, fork_filter).await; + + f_remote(protocol_proxy).await + }); + + (local_handle, remote_handle) + } + } + + async fn connect_protocol( + stream: Framed, + hello: HelloMessageWithProtocols, + status: Status, + fork_filter: ForkFilter, + ) -> StreamClone { + let unauthed_stream = UnauthedP2PStream::new(stream); + let (p2p_stream, _) = unauthed_stream.handshake(hello).await.unwrap(); + + // ensure that the two share capabilities + assert_eq!(*p2p_stream.shared_capabilities(), shared_caps_eth68_and_custom(),); + + let shared_caps = p2p_stream.shared_capabilities().clone(); + let main_cap = shared_caps.eth().unwrap(); + let proxy_server = + MuxDemuxStream::try_new(p2p_stream, main_cap.capability().into_owned(), shared_caps) + .expect("should start mxdmx stream"); + + let (mut main_stream, _) = + UnauthedEthStream::new(proxy_server).handshake(status, fork_filter).await.unwrap(); + + let protocol_proxy = + main_stream.inner_mut().try_clone_stream(&CUSTOM_CAP).expect("should clone stream"); + + tokio::spawn(async move { + loop { + _ = main_stream.next().await.unwrap() + } + }); + + protocol_proxy + } + + #[test] + fn test_unmask_msg_id() { + let mut msg = BytesMut::with_capacity(1); + msg.put_u8(0x07); // eth msg id + + let mxdmx_stream = + MuxDemuxStream::try_new((), Capability::eth(EthVersion::Eth67), shared_caps_eth68()) + .unwrap(); + _ = mxdmx_stream.unmask_msg_id(&mut msg[0]).unwrap(); + + assert_eq!(msg.as_ref(), &[0x07]); + } + + #[test] + fn test_mask_msg_id() { + let mut msg = BytesMut::with_capacity(2); + msg.put_u8(0x10); // eth msg id + msg.put_u8(0x20); // some msg data + + let mxdmx_stream = + MuxDemuxStream::try_new((), Capability::eth(EthVersion::Eth66), shared_caps_eth68()) + .unwrap(); + let egress_bytes = mxdmx_stream.mask_msg_id(msg.freeze()); + + assert_eq!(egress_bytes.as_ref(), &[0x10, 0x20]); + } + + #[test] + fn test_unmask_msg_id_cap_not_in_shared_range() { + let mut msg = BytesMut::with_capacity(1); + msg.put_u8(0x11); + + let mxdmx_stream = + MuxDemuxStream::try_new((), Capability::eth(EthVersion::Eth68), shared_caps_eth68()) + .unwrap(); + + assert!(mxdmx_stream.unmask_msg_id(&mut msg[0]).is_err()); + } + + #[tokio::test(flavor = "multi_thread")] + async fn test_mux_demux() { + let builder = ConnectionBuilder::new(); + + let request = Bytes::from(&CUSTOM_REQUEST[..]); + let response = Bytes::from(&CUSTOM_RESPONSE[..]); + let expected_request = request.clone(); + let expected_response = response.clone(); + + let (local_handle, remote_handle) = builder.with_connect_custom_protocol( + // send request from local addr + |mut protocol_proxy| { + Box::pin(async move { + protocol_proxy.send(request).await.unwrap(); + protocol_proxy.next().await.unwrap() + }) + }, + // respond from remote addr + |mut protocol_proxy| { + Box::pin(async move { + let request = protocol_proxy.next().await.unwrap(); + protocol_proxy.send(response).await.unwrap(); + request + }) + }, + ); + + let (local_res, remote_res) = tokio::join!(local_handle, remote_handle); + + // remote address receives request + assert_eq!(expected_request, remote_res.unwrap().freeze()); + // local address receives response + assert_eq!(expected_response, local_res.unwrap().freeze()); + } +} diff --git a/crates/net/eth-wire/src/p2pstream.rs b/crates/net/eth-wire/src/p2pstream.rs index 1d3f66064350..3ac340add7d6 100644 --- a/crates/net/eth-wire/src/p2pstream.rs +++ b/crates/net/eth-wire/src/p2pstream.rs @@ -301,11 +301,6 @@ impl P2PStream { &self.shared_capabilities } - /// Returns `true` if the connection is about to disconnect. - pub fn is_disconnecting(&self) -> bool { - self.disconnecting - } - /// Returns `true` if the stream has outgoing capacity. fn has_outgoing_capacity(&self) -> bool { self.outgoing_messages.len() < self.outgoing_message_buffer_capacity @@ -326,7 +321,16 @@ impl P2PStream { ping.encode(&mut ping_bytes); self.outgoing_messages.push_back(ping_bytes.freeze()); } +} +pub trait DisconnectP2P { + /// Starts to gracefully disconnect. + fn start_disconnect(&mut self, reason: DisconnectReason) -> Result<(), P2PStreamError>; + /// Returns `true` if the connection is about to disconnect. + fn is_disconnecting(&self) -> bool; +} + +impl DisconnectP2P for P2PStream { /// Starts to gracefully disconnect the connection by sending a Disconnect message and stop /// reading new messages. /// @@ -335,7 +339,7 @@ impl P2PStream { /// # Errors /// /// Returns an error only if the message fails to compress. - pub fn start_disconnect(&mut self, reason: DisconnectReason) -> Result<(), snap::Error> { + fn start_disconnect(&mut self, reason: DisconnectReason) -> Result<(), P2PStreamError> { // clear any buffered messages and queue in self.outgoing_messages.clear(); let disconnect = P2PMessage::Disconnect(reason); @@ -365,6 +369,10 @@ impl P2PStream { self.disconnecting = true; Ok(()) } + + fn is_disconnecting(&self) -> bool { + self.disconnecting + } } impl P2PStream diff --git a/crates/net/network/src/session/active.rs b/crates/net/network/src/session/active.rs index e737d6191f00..25e53a194b7a 100644 --- a/crates/net/network/src/session/active.rs +++ b/crates/net/network/src/session/active.rs @@ -16,7 +16,7 @@ use reth_eth_wire::{ capability::Capabilities, errors::{EthHandshakeError, EthStreamError, P2PStreamError}, message::{EthBroadcastMessage, RequestPair}, - DisconnectReason, EthMessage, EthStream, P2PStream, + DisconnectP2P, DisconnectReason, EthMessage, EthStream, P2PStream, }; use reth_interfaces::p2p::error::RequestError; use reth_metrics::common::mpsc::MeteredPollSender; From 32bf97d7bc7a862e5440f3182428de427585bb0f Mon Sep 17 00:00:00 2001 From: Yash Atreya <44857776+yash-atreya@users.noreply.github.com> Date: Fri, 8 Dec 2023 03:22:42 -0500 Subject: [PATCH 109/277] chore: add default gpo constants (#5662) Co-authored-by: Dan Cline <6798349+Rjected@users.noreply.github.com> Co-authored-by: Matthias Seitz --- bin/reth/src/args/gas_price_oracle_args.rs | 55 +++++++++++++++------- bin/reth/src/args/rpc_server_args.rs | 7 +-- crates/rpc/rpc-builder/src/constants.rs | 6 +++ crates/rpc/rpc/src/eth/gas_oracle.rs | 47 +++++++----------- 4 files changed, 61 insertions(+), 54 deletions(-) diff --git a/bin/reth/src/args/gas_price_oracle_args.rs b/bin/reth/src/args/gas_price_oracle_args.rs index 001ba9017c97..54087693af7a 100644 --- a/bin/reth/src/args/gas_price_oracle_args.rs +++ b/bin/reth/src/args/gas_price_oracle_args.rs @@ -1,33 +1,53 @@ +use crate::primitives::U256; use clap::Args; +use reth_rpc::eth::gas_oracle::GasPriceOracleConfig; +use reth_rpc_builder::constants::{ + DEFAULT_GAS_PRICE_BLOCKS, DEFAULT_GAS_PRICE_PERCENTILE, DEFAULT_IGNORE_GAS_PRICE, + DEFAULT_MAX_GAS_PRICE, +}; /// Parameters to configure Gas Price Oracle -#[derive(Debug, Clone, Args, PartialEq, Eq)] +#[derive(Debug, Clone, Copy, Args, PartialEq, Eq)] #[clap(next_help_heading = "Gas Price Oracle")] pub struct GasPriceOracleArgs { /// Number of recent blocks to check for gas price - #[arg(long = "gpo.blocks", default_value = "20")] - pub blocks: Option, + #[arg(long = "gpo.blocks", default_value_t = DEFAULT_GAS_PRICE_BLOCKS)] + pub blocks: u32, /// Gas Price below which gpo will ignore transactions - #[arg(long = "gpo.ignoreprice", default_value = "2")] - pub ignore_price: Option, + #[arg(long = "gpo.ignoreprice", default_value_t = DEFAULT_IGNORE_GAS_PRICE.to())] + pub ignore_price: u64, /// Maximum transaction priority fee(or gasprice before London Fork) to be recommended by gpo - #[arg(long = "gpo.maxprice", default_value = "500000000000")] - pub max_price: Option, + #[arg(long = "gpo.maxprice", default_value_t = DEFAULT_MAX_GAS_PRICE.to())] + pub max_price: u64, /// The percentile of gas prices to use for the estimate - #[arg(long = "gpo.percentile", default_value = "60")] - pub percentile: Option, + #[arg(long = "gpo.percentile", default_value_t = DEFAULT_GAS_PRICE_PERCENTILE)] + pub percentile: u32, +} + +impl GasPriceOracleArgs { + /// Returns a [GasPriceOracleConfig] from the arguments. + pub fn gas_price_oracle_config(&self) -> GasPriceOracleConfig { + let Self { blocks, ignore_price, max_price, percentile } = self; + GasPriceOracleConfig { + max_price: Some(U256::from(*max_price)), + ignore_price: Some(U256::from(*ignore_price)), + percentile: *percentile, + blocks: *blocks, + ..Default::default() + } + } } impl Default for GasPriceOracleArgs { fn default() -> Self { Self { - blocks: Some(20), - ignore_price: Some(2), - max_price: Some(500000000000), - percentile: Some(60), + blocks: DEFAULT_GAS_PRICE_BLOCKS, + ignore_price: DEFAULT_IGNORE_GAS_PRICE.to(), + max_price: DEFAULT_MAX_GAS_PRICE.to(), + percentile: DEFAULT_GAS_PRICE_PERCENTILE, } } } @@ -36,7 +56,6 @@ impl Default for GasPriceOracleArgs { mod tests { use super::*; use clap::Parser; - /// A helper type to parse Args more easily #[derive(Parser)] struct CommandParser { @@ -50,10 +69,10 @@ mod tests { assert_eq!( args, GasPriceOracleArgs { - blocks: Some(20), - ignore_price: Some(2), - max_price: Some(500000000000), - percentile: Some(60), + blocks: DEFAULT_GAS_PRICE_BLOCKS, + ignore_price: DEFAULT_IGNORE_GAS_PRICE.to(), + max_price: DEFAULT_MAX_GAS_PRICE.to(), + percentile: DEFAULT_GAS_PRICE_PERCENTILE, } ); } diff --git a/bin/reth/src/args/rpc_server_args.rs b/bin/reth/src/args/rpc_server_args.rs index e72807db2f0a..54f0f040fda0 100644 --- a/bin/reth/src/args/rpc_server_args.rs +++ b/bin/reth/src/args/rpc_server_args.rs @@ -358,12 +358,7 @@ impl RethRpcConfig for RpcServerArgs { } fn gas_price_oracle_config(&self) -> GasPriceOracleConfig { - GasPriceOracleConfig::new( - self.gas_price_oracle.blocks, - self.gas_price_oracle.ignore_price, - self.gas_price_oracle.max_price, - self.gas_price_oracle.percentile, - ) + self.gas_price_oracle.gas_price_oracle_config() } fn transport_rpc_module_config(&self) -> TransportRpcModuleConfig { diff --git a/crates/rpc/rpc-builder/src/constants.rs b/crates/rpc/rpc-builder/src/constants.rs index 6659174123fc..f468fb03112a 100644 --- a/crates/rpc/rpc-builder/src/constants.rs +++ b/crates/rpc/rpc-builder/src/constants.rs @@ -1,3 +1,9 @@ +/// GPO reexports +pub use reth_rpc::eth::gas_oracle::{ + DEFAULT_GAS_PRICE_BLOCKS, DEFAULT_GAS_PRICE_PERCENTILE, DEFAULT_IGNORE_GAS_PRICE, + DEFAULT_MAX_GAS_PRICE, +}; + /// The default port for the http server pub const DEFAULT_HTTP_RPC_PORT: u16 = 8545; diff --git a/crates/rpc/rpc/src/eth/gas_oracle.rs b/crates/rpc/rpc/src/eth/gas_oracle.rs index 4eaaa5cdcfc5..8812b19c9eac 100644 --- a/crates/rpc/rpc/src/eth/gas_oracle.rs +++ b/crates/rpc/rpc/src/eth/gas_oracle.rs @@ -19,11 +19,18 @@ pub const SAMPLE_NUMBER: usize = 3_usize; /// The default maximum number of blocks to use for the gas price oracle. pub const MAX_HEADER_HISTORY: u64 = 1024; -/// The default maximum gas price to use for the estimate -pub const DEFAULT_MAX_PRICE: U256 = U256::from_limbs([500_000_000_000u64, 0, 0, 0]); +/// Number of recent blocks to check for gas price +pub const DEFAULT_GAS_PRICE_BLOCKS: u32 = 20; + +/// The percentile of gas prices to use for the estimate +pub const DEFAULT_GAS_PRICE_PERCENTILE: u32 = 60; + +/// Maximum transaction priority fee (or gas price before London Fork) to be recommended by the gas +/// price oracle +pub const DEFAULT_MAX_GAS_PRICE: U256 = U256::from_limbs([500_000_000_000u64, 0, 0, 0]); /// The default minimum gas price, under which the sample will be ignored -pub const DEFAULT_IGNORE_PRICE: U256 = U256::from_limbs([2u64, 0, 0, 0]); +pub const DEFAULT_IGNORE_GAS_PRICE: U256 = U256::from_limbs([2u64, 0, 0, 0]); /// Settings for the [GasPriceOracle] #[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize)] @@ -54,33 +61,13 @@ pub struct GasPriceOracleConfig { impl Default for GasPriceOracleConfig { fn default() -> Self { GasPriceOracleConfig { - blocks: 20, - percentile: 60, - max_header_history: MAX_HEADER_HISTORY, - max_block_history: MAX_HEADER_HISTORY, - default: None, - max_price: Some(DEFAULT_MAX_PRICE), - ignore_price: Some(DEFAULT_IGNORE_PRICE), - } - } -} - -impl GasPriceOracleConfig { - /// Creating a new gpo config with blocks, ignoreprice, maxprice and percentile - pub fn new( - blocks: Option, - ignore_price: Option, - max_price: Option, - percentile: Option, - ) -> Self { - Self { - blocks: blocks.unwrap_or(20), - percentile: percentile.unwrap_or(60), + blocks: DEFAULT_GAS_PRICE_BLOCKS, + percentile: DEFAULT_GAS_PRICE_PERCENTILE, max_header_history: MAX_HEADER_HISTORY, max_block_history: MAX_HEADER_HISTORY, default: None, - max_price: max_price.map(U256::from).or(Some(DEFAULT_MAX_PRICE)), - ignore_price: ignore_price.map(U256::from).or(Some(DEFAULT_IGNORE_PRICE)), + max_price: Some(DEFAULT_MAX_GAS_PRICE), + ignore_price: Some(DEFAULT_IGNORE_GAS_PRICE), } } } @@ -323,12 +310,12 @@ mod tests { #[test] fn max_price_sanity() { - assert_eq!(DEFAULT_MAX_PRICE, U256::from(500_000_000_000u64)); - assert_eq!(DEFAULT_MAX_PRICE, U256::from(500 * GWEI_TO_WEI)) + assert_eq!(DEFAULT_MAX_GAS_PRICE, U256::from(500_000_000_000u64)); + assert_eq!(DEFAULT_MAX_GAS_PRICE, U256::from(500 * GWEI_TO_WEI)) } #[test] fn ignore_price_sanity() { - assert_eq!(DEFAULT_IGNORE_PRICE, U256::from(2u64)); + assert_eq!(DEFAULT_IGNORE_GAS_PRICE, U256::from(2u64)); } } From cf5006108c5ed2b774f09fbf02a694ec67e95ba0 Mon Sep 17 00:00:00 2001 From: Matthias Seitz Date: Fri, 8 Dec 2023 10:08:51 +0100 Subject: [PATCH 110/277] chore: promote trace bad txs to debug (#5714) --- crates/net/network/src/transactions.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/crates/net/network/src/transactions.rs b/crates/net/network/src/transactions.rs index f9ffc138deae..398e0ceeed00 100644 --- a/crates/net/network/src/transactions.rs +++ b/crates/net/network/src/transactions.rs @@ -35,7 +35,7 @@ use std::{ }; use tokio::sync::{mpsc, mpsc::error::TrySendError, oneshot, oneshot::error::RecvError}; use tokio_stream::wrappers::{ReceiverStream, UnboundedReceiverStream}; -use tracing::trace; +use tracing::{debug, trace}; /// Cache limit of transactions to keep track of for a single peer. const PEER_TRANSACTION_CACHE_LIMIT: usize = 1024 * 10; @@ -589,6 +589,7 @@ where self.import_transactions(peer_id, non_blob_txs, TransactionSource::Broadcast); if has_blob_txs { + debug!(target: "net::tx", ?peer_id, "received bad full blob transaction broadcast"); self.report_peer(peer_id, ReputationChangeKind::BadTransactions); } } @@ -856,7 +857,7 @@ where // known that this transaction is bad. (e.g. consensus // rules) if err.is_bad_transaction() && !this.network.is_syncing() { - trace!(target: "net::tx", ?err, "bad pool transaction import"); + debug!(target: "net::tx", ?err, "bad pool transaction import"); this.on_bad_import(err.hash); continue } From c8a8cad9a2bfd833311aea47fbb1bc8ac5a88ad9 Mon Sep 17 00:00:00 2001 From: Siyuan Han <47173566+hsyodyssey@users.noreply.github.com> Date: Sat, 9 Dec 2023 17:56:36 +0800 Subject: [PATCH 111/277] chore(book): update some expired links (#5718) --- book/jsonrpc/txpool.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/book/jsonrpc/txpool.md b/book/jsonrpc/txpool.md index 72fccc15f68c..cb9e9c0e69d2 100644 --- a/book/jsonrpc/txpool.md +++ b/book/jsonrpc/txpool.md @@ -6,7 +6,7 @@ The `txpool` API allows you to inspect the transaction pool. Returns the details of all 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 +See [here](https://geth.ethereum.org/docs/rpc/ns-txpool#txpool-content) for more details | Client | Method invocation | |--------|----------------------------------------------| @@ -16,7 +16,7 @@ See [here](https://geth.ethereum.org/docs/rpc/ns-txpool#txpool_content) for more Retrieves the transactions contained within the txpool, returning pending as well as queued transactions of this address, grouped by nonce. -See [here](https://geth.ethereum.org/docs/rpc/ns-txpool#txpool_contentFrom) for more details +See [here](https://geth.ethereum.org/docs/rpc/ns-txpool#txpool-contentfrom) for more details | Client | Method invocation | |--------|---------------------------------------------------------| @@ -26,7 +26,7 @@ See [here](https://geth.ethereum.org/docs/rpc/ns-txpool#txpool_contentFrom) for Returns a 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. -See [here](https://geth.ethereum.org/docs/rpc/ns-txpool#txpool_inspect) for more details +See [here](https://geth.ethereum.org/docs/rpc/ns-txpool#txpool-inspect) for more details | Client | Method invocation | |--------|----------------------------------------------| @@ -36,7 +36,7 @@ See [here](https://geth.ethereum.org/docs/rpc/ns-txpool#txpool_inspect) for more Returns 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 +See [here](https://geth.ethereum.org/docs/rpc/ns-txpool#txpool-status) for more details | Client | Method invocation | |--------|---------------------------------------------| From 8a670d57ec1c8f3cdf617e09f939d7d2624b6ded Mon Sep 17 00:00:00 2001 From: Matthias Seitz Date: Sat, 9 Dec 2023 15:45:09 +0100 Subject: [PATCH 112/277] fix: use receover_signer_unchecked for tx receipt (#5715) --- crates/primitives/src/transaction/mod.rs | 16 ++++++++++++++++ crates/rpc/rpc/src/eth/api/transactions.rs | 22 +++++++++++++--------- 2 files changed, 29 insertions(+), 9 deletions(-) diff --git a/crates/primitives/src/transaction/mod.rs b/crates/primitives/src/transaction/mod.rs index 9702ae19eb4d..8d86794db3e8 100644 --- a/crates/primitives/src/transaction/mod.rs +++ b/crates/primitives/src/transaction/mod.rs @@ -992,6 +992,22 @@ impl TransactionSigned { self.signature.recover_signer(signature_hash) } + /// Recover signer from signature and hash _without ensuring that the signature has a low `s` + /// value_. + /// + /// Returns `None` if the transaction's signature is invalid, see also + /// [Self::recover_signer_unchecked]. + pub fn recover_signer_unchecked(&self) -> Option
{ + // Optimism's Deposit transaction does not have a signature. Directly return the + // `from` address. + #[cfg(feature = "optimism")] + if let Transaction::Deposit(TxDeposit { from, .. }) = self.transaction { + return Some(from) + } + let signature_hash = self.signature_hash(); + self.signature.recover_signer_unchecked(signature_hash) + } + /// Recovers a list of signers from a transaction list iterator /// /// Returns `None`, if some transaction's signature is invalid, see also diff --git a/crates/rpc/rpc/src/eth/api/transactions.rs b/crates/rpc/rpc/src/eth/api/transactions.rs index af0966162afb..ec98b9cf8c0e 100644 --- a/crates/rpc/rpc/src/eth/api/transactions.rs +++ b/crates/rpc/rpc/src/eth/api/transactions.rs @@ -1146,15 +1146,20 @@ impl From for Transaction { } /// Helper function to construct a transaction receipt +/// +/// Note: This requires _all_ block receipts because we need to calculate the gas used by the +/// transaction. pub(crate) fn build_transaction_receipt_with_block_receipts( - tx: TransactionSigned, + transaction: TransactionSigned, meta: TransactionMeta, receipt: Receipt, all_receipts: &[Receipt], #[cfg(feature = "optimism")] optimism_tx_meta: OptimismTxMeta, ) -> EthResult { - let transaction = - tx.clone().into_ecrecovered().ok_or(EthApiError::InvalidTransactionSignature)?; + // Note: we assume this transaction is valid, because it's mined (or part of pending block) and + // we don't need to check for pre EIP-2 + let from = + transaction.recover_signer_unchecked().ok_or(EthApiError::InvalidTransactionSignature)?; // get the previous transaction cumulative gas used let gas_used = if meta.index == 0 { @@ -1173,14 +1178,14 @@ pub(crate) fn build_transaction_receipt_with_block_receipts( transaction_index: U64::from(meta.index), block_hash: Some(meta.block_hash), block_number: Some(U256::from(meta.block_number)), - from: transaction.signer(), + from, to: None, cumulative_gas_used: U256::from(receipt.cumulative_gas_used), gas_used: Some(U256::from(gas_used)), contract_address: None, logs: Vec::with_capacity(receipt.logs.len()), effective_gas_price: U128::from(transaction.effective_gas_price(meta.base_fee)), - transaction_type: tx.transaction.tx_type().into(), + transaction_type: transaction.transaction.tx_type().into(), // TODO pre-byzantium receipts have a post-transaction state root state_root: None, logs_bloom: receipt.bloom_slow(), @@ -1196,7 +1201,7 @@ pub(crate) fn build_transaction_receipt_with_block_receipts( #[cfg(feature = "optimism")] if let Some(l1_block_info) = optimism_tx_meta.l1_block_info { - if !tx.is_deposit() { + 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); @@ -1206,10 +1211,9 @@ pub(crate) fn build_transaction_receipt_with_block_receipts( } } - match tx.transaction.kind() { + match transaction.transaction.kind() { Create => { - res_receipt.contract_address = - Some(transaction.signer().create(tx.transaction.nonce())); + res_receipt.contract_address = Some(from.create(transaction.transaction.nonce())); } Call(addr) => { res_receipt.to = Some(*addr); From 982346becb6c082029ca24cb40d74086a79dfe59 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Sun, 10 Dec 2023 11:08:54 +0100 Subject: [PATCH 113/277] chore(deps): weekly `cargo update` (#5719) Co-authored-by: github-merge-queue --- Cargo.lock | 177 ++++++++++++++++++++++++----------------------------- 1 file changed, 79 insertions(+), 98 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 1a19bc7ab78c..826d1c2d6e9d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -278,9 +278,9 @@ checksum = "4b46cbb362ab8752921c97e041f5e366ee6297bd428a31275b9fcf1e380f7299" [[package]] name = "anstream" -version = "0.6.4" +version = "0.6.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2ab91ebe16eb252986481c5b62f6098f3b698a45e34b5b98200cf20dd2484a44" +checksum = "d664a92ecae85fd0a7392615844904654d1d5f5514837f471ddef4a057aba1b6" dependencies = [ "anstyle", "anstyle-parse", @@ -298,30 +298,30 @@ checksum = "7079075b41f533b8c61d2a4d073c4676e1f8b249ff94a393b0595db304e0dd87" [[package]] name = "anstyle-parse" -version = "0.2.2" +version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "317b9a89c1868f5ea6ff1d9539a69f45dffc21ce321ac1fd1160dfa48c8e2140" +checksum = "c75ac65da39e5fe5ab759307499ddad880d724eed2f6ce5b5e8a26f4f387928c" dependencies = [ "utf8parse", ] [[package]] name = "anstyle-query" -version = "1.0.0" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ca11d4be1bab0c8bc8734a9aa7bf4ee8316d462a08c6ac5052f888fef5b494b" +checksum = "e28923312444cdd728e4738b3f9c9cac739500909bb3d3c94b43551b16517648" dependencies = [ - "windows-sys 0.48.0", + "windows-sys 0.52.0", ] [[package]] name = "anstyle-wincon" -version = "3.0.1" +version = "3.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0699d10d2f4d628a98ee7b57b289abbc98ff3bad977cb3152709d4bf2330628" +checksum = "1cd54b81ec8d6180e24654d0b371ad22fc3dd083b6ff8ba325b72e00c87660a7" dependencies = [ "anstyle", - "windows-sys 0.48.0", + "windows-sys 0.52.0", ] [[package]] @@ -561,15 +561,6 @@ dependencies = [ "rustc_version 0.4.0", ] -[[package]] -name = "atomic-polyfill" -version = "0.1.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3ff7eb3f316534d83a8a2c3d1674ace8a5a71198eba31e2e2b597833f699b28" -dependencies = [ - "critical-section", -] - [[package]] name = "atomic-polyfill" version = "1.0.3" @@ -999,9 +990,9 @@ checksum = "3190f92dfe48224adc92881c620f08ccf37ff62b91a094bb357fe53bd5e84647" [[package]] name = "boyer-moore-magiclen" -version = "0.2.19" +version = "0.2.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "854f4a08ebbcc4d3d2b8b3863fdffdc9f2046bb1c43610ff9841a51dd5b70f55" +checksum = "95e6233f2d926b5b123caf9d58e3885885255567fbe7776a7fdcae2a4d7241c4" dependencies = [ "debug-helper", ] @@ -1239,9 +1230,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.4.10" +version = "4.4.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41fffed7514f420abec6d183b1d3acfd9099c79c3a10a06ade4f8203f1411272" +checksum = "bfaff671f6b22ca62406885ece523383b9b64022e341e53e009a62ebc47a45f2" dependencies = [ "clap_builder", "clap_derive", @@ -1249,9 +1240,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.4.9" +version = "4.4.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "63361bae7eef3771745f02d8d892bec2fee5f6e34af316ba556e7f97a7069ff1" +checksum = "a216b506622bb1d316cd51328dce24e07bdff4a6128a47c7e7fad11878d5adbb" dependencies = [ "anstream", "anstyle", @@ -2466,7 +2457,7 @@ dependencies = [ "serde", "serde_json", "syn 2.0.39", - "toml 0.8.8", + "toml 0.8.2", "walkdir", ] @@ -2630,9 +2621,9 @@ dependencies = [ [[package]] name = "eyre" -version = "0.6.9" +version = "0.6.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "80f656be11ddf91bd709454d15d5bd896fbaf4cc3314e69349e4d1569f5b46cd" +checksum = "8bbb8258be8305fb0237d7b295f47bb24ff1b136a535f473baf40e70468515aa" dependencies = [ "indenter", "once_cell", @@ -3107,11 +3098,11 @@ dependencies = [ [[package]] name = "heapless" -version = "0.7.16" +version = "0.7.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db04bc24a18b9ea980628ecf00e6c0264f3c1426dac36c00cb49b6fbad8b0743" +checksum = "cdc6457c0eb62c71aac4bc17216026d8410337c4126773b9c5daba343f17964f" dependencies = [ - "atomic-polyfill 0.1.11", + "atomic-polyfill", "hash32", "rustc_version 0.4.0", "serde", @@ -3197,9 +3188,9 @@ dependencies = [ [[package]] name = "http-body" -version = "0.4.5" +version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d5f38f16d184e36f2408a55281cd658ecbd3ca05cce6d6510a176eca393e26d1" +checksum = "7ceab25649e9960c0311ea418d17bee82c0dcec1bd053b5f9a66e265a693bed2" dependencies = [ "bytes", "http", @@ -3712,9 +3703,9 @@ dependencies = [ [[package]] name = "itoa" -version = "1.0.9" +version = "1.0.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af150ab688ff2122fcef229be89cb50dd66af9e01a4ff320cc137eecc9bacc38" +checksum = "b1a46d1a171d865aa5f83f92695765caa047a9b4cbae2cbf37dbd613a793fd4c" [[package]] name = "jemalloc-ctl" @@ -4304,9 +4295,9 @@ dependencies = [ [[package]] name = "mio" -version = "0.8.9" +version = "0.8.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3dce281c5e46beae905d4de1870d8b1509a9142b62eedf18b443b011ca8343d0" +checksum = "8f3d0b296e374a4e6f3c7b0a1f5a51d748a0d34c85e7dc48fc3fa9a87657fe09" dependencies = [ "libc", "log", @@ -4548,7 +4539,7 @@ version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6c11e44798ad209ccdd91fc192f0526a369a01234f7373e1b141c96d7cee4f0e" dependencies = [ - "proc-macro-crate 2.0.0", + "proc-macro-crate 2.0.1", "proc-macro2", "quote", "syn 2.0.39", @@ -4574,12 +4565,12 @@ dependencies = [ [[package]] name = "once_cell" -version = "1.18.0" +version = "1.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d" +checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" dependencies = [ - "atomic-polyfill 1.0.3", "critical-section", + "portable-atomic", ] [[package]] @@ -4677,7 +4668,7 @@ version = "3.6.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "be30eaf4b0a9fba5336683b38de57bb86d179a35862ba6bfcf57625d006bde5b" dependencies = [ - "proc-macro-crate 2.0.0", + "proc-macro-crate 2.0.1", "proc-macro2", "quote", "syn 1.0.109", @@ -4984,9 +4975,9 @@ dependencies = [ [[package]] name = "portable-atomic" -version = "1.5.1" +version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3bccab0e7fd7cc19f820a1c8c91720af652d0c88dc9664dd72aef2614f04af3b" +checksum = "7170ef9988bc169ba16dd36a7fa041e5c4cbeb6a35b76d4c03daded371eae7c0" [[package]] name = "postcard" @@ -5110,11 +5101,12 @@ dependencies = [ [[package]] name = "proc-macro-crate" -version = "2.0.0" +version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e8366a6159044a37876a2b9817124296703c586a5c92e2c53751fa06d8d43e8" +checksum = "97dc5fea232fc28d2f597b37c4876b348a40e33f3b02cc975c8d006d78d94b1a" dependencies = [ - "toml_edit 0.20.7", + "toml_datetime", + "toml_edit 0.20.2", ] [[package]] @@ -5593,7 +5585,7 @@ dependencies = [ "tempfile", "thiserror", "tokio", - "toml 0.8.8", + "toml 0.8.2", "tracing", "tui", "vergen", @@ -5726,7 +5718,7 @@ dependencies = [ "serde", "serde_json", "tempfile", - "toml 0.8.8", + "toml 0.8.2", ] [[package]] @@ -6209,7 +6201,7 @@ dependencies = [ "tempfile", "test-fuzz", "thiserror", - "toml 0.8.8", + "toml 0.8.2", "tracing", "triehash", "zstd 0.12.4", @@ -6701,9 +6693,9 @@ dependencies = [ [[package]] name = "ring" -version = "0.17.6" +version = "0.17.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "684d5e6e18f669ccebf64a92236bb7db9a34f07be010e3627368182027180866" +checksum = "688c63d65483050968b2a8937f7995f443e27041a0f7700aa59b0822aedebb74" dependencies = [ "cc", "getrandom 0.2.11", @@ -6849,9 +6841,9 @@ dependencies = [ [[package]] name = "rustix" -version = "0.38.26" +version = "0.38.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9470c4bf8246c8daf25f9598dca807fb6510347b1e1cfa55749113850c79d88a" +checksum = "72e572a5e8ca657d7366229cdde4bd14c4eb5499a9573d4d366fe1b599daa316" dependencies = [ "bitflags 2.4.1", "errno", @@ -6862,12 +6854,12 @@ dependencies = [ [[package]] name = "rustls" -version = "0.21.9" +version = "0.21.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "629648aced5775d558af50b2b4c7b02983a04b312126d45eeead26e7caa498b9" +checksum = "f9d5a6813c0759e4609cd494e8e725babae6a2ca7b62a5536a13daaec6fcb7ba" dependencies = [ "log", - "ring 0.17.6", + "ring 0.17.7", "rustls-webpki", "sct", ] @@ -6899,7 +6891,7 @@ version = "0.101.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8b6275d1ee7a1cd780b64aca7726599a1dbc893b1e64144529e55c3c2f745765" dependencies = [ - "ring 0.17.6", + "ring 0.17.7", "untrusted 0.9.0", ] @@ -6923,9 +6915,9 @@ dependencies = [ [[package]] name = "ryu" -version = "1.0.15" +version = "1.0.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ad4cc8da4ef723ed60bced201181d83791ad433213d8c24efffda1eec85d741" +checksum = "f98d2aa92eebf49b69786be48e4477826b256916e84a57ff2a4f21923b48eb4c" [[package]] name = "ryu-js" @@ -7019,7 +7011,7 @@ version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "da046153aa2352493d6cb7da4b6e5c0c057d8a1d0a9aa8560baffdd945acd414" dependencies = [ - "ring 0.17.6", + "ring 0.17.7", "untrusted 0.9.0", ] @@ -7054,7 +7046,7 @@ version = "0.28.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2acea373acb8c21ecb5a23741452acd2593ed44ee3d343e72baaa143bc89d0d5" dependencies = [ - "secp256k1-sys 0.9.0", + "secp256k1-sys 0.9.1", ] [[package]] @@ -7068,9 +7060,9 @@ dependencies = [ [[package]] name = "secp256k1-sys" -version = "0.9.0" +version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09e67c467c38fd24bd5499dc9a18183b31575c12ee549197e3e20d57aa4fe3b7" +checksum = "4dd97a086ec737e30053fd5c46f097465d25bb81dd3608825f65298c4c98be83" dependencies = [ "cc", ] @@ -7457,9 +7449,9 @@ dependencies = [ [[package]] name = "snap" -version = "1.1.0" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e9f0ab6ef7eb7353d9119c170a436d1bf248eea575ac42d19d12f4e34130831" +checksum = "1b6b67fb9a61334225b5b790716f609cd58395f895b3fe8b328786812a40bc3b" [[package]] name = "socket2" @@ -7807,9 +7799,9 @@ dependencies = [ [[package]] name = "thin-vec" -version = "0.2.12" +version = "0.2.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aac81b6fd6beb5884b0cf3321b8117e6e5d47ecb6fc89f414cfdcca8b2fe2dd8" +checksum = "a38c90d48152c236a3ab59271da4f4ae63d678c5d7ad6b7714d7cb9760be5e4b" [[package]] name = "thiserror" @@ -7928,9 +7920,9 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.34.0" +version = "1.35.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d0c014766411e834f7af5b8f4cf46257aab4036ca95e9d2c144a10f59ad6f5b9" +checksum = "841d45b238a16291a4e1584e61820b8ae57d696cc5015c459c229ccc6990cc1c" dependencies = [ "backtrace", "bytes", @@ -8017,21 +8009,21 @@ dependencies = [ [[package]] name = "toml" -version = "0.8.8" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1a195ec8c9da26928f773888e0742ca3ca1040c6cd859c919c9f59c1954ab35" +checksum = "185d8ab0dfbb35cf1399a6344d8484209c088f75f8f68230da55d48d95d43e3d" dependencies = [ "serde", "serde_spanned", "toml_datetime", - "toml_edit 0.21.0", + "toml_edit 0.20.2", ] [[package]] name = "toml_datetime" -version = "0.6.5" +version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3550f4e9685620ac18a50ed434eb3aec30db8ba93b0287467bca5826ea25baf1" +checksum = "7cda73e2f1397b1262d6dfdcef8aafae14d1de7748d66822d3bfeeb6d03e5e4b" dependencies = [ "serde", ] @@ -8049,20 +8041,9 @@ dependencies = [ [[package]] name = "toml_edit" -version = "0.20.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70f427fce4d84c72b5b732388bf4a9f4531b53f74e2887e3ecb2481f68f66d81" -dependencies = [ - "indexmap 2.1.0", - "toml_datetime", - "winnow", -] - -[[package]] -name = "toml_edit" -version = "0.21.0" +version = "0.20.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d34d383cd00a163b4a5b85053df514d45bc330f6de7737edfe0a93311d1eaa03" +checksum = "396e4d48bbb2b7554c944bde63101b5ae446cff6ec4a24227428f15eb72ef338" dependencies = [ "indexmap 2.1.0", "serde", @@ -8359,9 +8340,9 @@ dependencies = [ [[package]] name = "try-lock" -version = "0.2.4" +version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3528ecfd12c466c6f163363caf2d02a71161dd5e1cc6ae7b34207ea2d42d81ed" +checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" [[package]] name = "trybuild" @@ -8452,9 +8433,9 @@ dependencies = [ [[package]] name = "unicode-bidi" -version = "0.3.13" +version = "0.3.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92888ba5573ff080736b3648696b70cafad7d250551175acbaa4e0385b3e1460" +checksum = "6f2528f27a9eb2b21e69c95319b30bd0efd85d09c379741b0f78ea1d86be2416" [[package]] name = "unicode-ident" @@ -9004,9 +8985,9 @@ checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04" [[package]] name = "winnow" -version = "0.5.19" +version = "0.5.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "829846f3e3db426d4cee4510841b71a8e58aa2a76b1132579487ae430ccd9c7b" +checksum = "b67b5f0a4e7a27a64c651977932b9dc5667ca7fc31ac44b03ed37a0cf42fdfff" dependencies = [ "memchr", ] @@ -9117,18 +9098,18 @@ dependencies = [ [[package]] name = "zerocopy" -version = "0.7.28" +version = "0.7.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d6f15f7ade05d2a4935e34a457b936c23dc70a05cc1d97133dc99e7a3fe0f0e" +checksum = "306dca4455518f1f31635ec308b6b3e4eb1b11758cefafc782827d0aa7acb5c7" dependencies = [ "zerocopy-derive", ] [[package]] name = "zerocopy-derive" -version = "0.7.28" +version = "0.7.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dbbad221e3f78500350ecbd7dfa4e63ef945c05f4c61cb7f4d3f84cd0bba649b" +checksum = "be912bf68235a88fbefd1b73415cb218405958d1655b2ece9035a19920bdf6ba" dependencies = [ "proc-macro2", "quote", From c58cdf945966942e6ee7367b3848870f26cc54f3 Mon Sep 17 00:00:00 2001 From: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Date: Sun, 10 Dec 2023 11:09:16 +0100 Subject: [PATCH 114/277] Revert "chore: temporarily disable doc related CI" (#5716) --- .github/workflows/book.yml | 5 +---- .github/workflows/lint.yml | 6 +----- 2 files changed, 2 insertions(+), 9 deletions(-) diff --git a/.github/workflows/book.yml b/.github/workflows/book.yml index 6d675df170b5..d38b41fcf1c1 100644 --- a/.github/workflows/book.yml +++ b/.github/workflows/book.yml @@ -53,8 +53,6 @@ jobs: run: mdbook-linkcheck --standalone build: - # TODO: reenable when is resolved. - if: false runs-on: ubuntu-latest timeout-minutes: 60 steps: @@ -116,8 +114,7 @@ jobs: deploy: # Only deploy if a push to main - # TODO: reenable when is resolved. - if: false && github.ref_name == 'main' && github.event_name == 'push' + if: github.ref_name == 'main' && github.event_name == 'push' runs-on: ubuntu-latest needs: [test, lint, build] diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index 2724a4c41090..293831dcc2d5 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -57,8 +57,6 @@ jobs: docs: name: docs - # TODO: reenable when is resolved. - if: false runs-on: ubuntu-latest timeout-minutes: 30 steps: @@ -100,9 +98,7 @@ jobs: name: lint success runs-on: ubuntu-latest if: always() - # TODO: reenable when is resolved. - # needs: [clippy, docs, fmt, grafana] - needs: [clippy, fmt, grafana] + needs: [clippy, docs, fmt, grafana] timeout-minutes: 30 steps: - name: Decide whether the needed jobs succeeded or failed From ae8ad6f26ae5acd9f5029e0c02754f09c0ce20a3 Mon Sep 17 00:00:00 2001 From: Thomas Coratger <60488569+tcoratger@users.noreply.github.com> Date: Mon, 11 Dec 2023 12:34:32 +0100 Subject: [PATCH 115/277] refactor(net): Improved Masking Function for Message IDs (#5724) --- crates/net/eth-wire/src/muxdemux.rs | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/crates/net/eth-wire/src/muxdemux.rs b/crates/net/eth-wire/src/muxdemux.rs index 621eb64d9b26..6c4aaaf1aedc 100644 --- a/crates/net/eth-wire/src/muxdemux.rs +++ b/crates/net/eth-wire/src/muxdemux.rs @@ -163,13 +163,11 @@ impl MuxDemuxStream { } /// Masks message id with offset relative to the message id suffix reserved for capability - /// message ids. The p2p stream further masks the message id (todo: mask whole message id at - /// once to avoid copying message to mutate id byte or sink BytesMut). + /// message ids. The p2p stream further masks the message id fn mask_msg_id(&self, msg: Bytes) -> Bytes { - let mut masked_bytes = BytesMut::zeroed(msg.len()); - masked_bytes[0] = msg[0] + self.owner.relative_message_id_offset(); - masked_bytes[1..].copy_from_slice(&msg[1..]); - + let mut masked_bytes = BytesMut::with_capacity(msg.len()); + masked_bytes.extend_from_slice(&msg); + masked_bytes[0] += self.owner.relative_message_id_offset(); masked_bytes.freeze() } @@ -310,10 +308,9 @@ pub struct StreamClone { impl StreamClone { fn mask_msg_id(&self, msg: Bytes) -> Bytes { - let mut masked_bytes = BytesMut::zeroed(msg.len()); - masked_bytes[0] = msg[0] + self.cap.relative_message_id_offset(); - masked_bytes[1..].copy_from_slice(&msg[1..]); - + let mut masked_bytes = BytesMut::with_capacity(msg.len()); + masked_bytes.extend_from_slice(&msg); + masked_bytes[0] += self.cap.relative_message_id_offset(); masked_bytes.freeze() } } From b83afd4b760e4c5674192a045700ff3b51ebe0cf Mon Sep 17 00:00:00 2001 From: Matthias Seitz Date: Mon, 11 Dec 2023 15:13:47 +0100 Subject: [PATCH 116/277] fix: use unchecked sender recovery in rpc (#5721) --- crates/primitives/src/chain/spec.rs | 6 + crates/primitives/src/transaction/mod.rs | 22 ++ crates/rpc/rpc/src/debug.rs | 318 +++++++++++---------- crates/rpc/rpc/src/eth/api/transactions.rs | 22 +- 4 files changed, 221 insertions(+), 147 deletions(-) diff --git a/crates/primitives/src/chain/spec.rs b/crates/primitives/src/chain/spec.rs index 9da9720d26b0..5d6efecbf733 100644 --- a/crates/primitives/src/chain/spec.rs +++ b/crates/primitives/src/chain/spec.rs @@ -749,6 +749,12 @@ impl ChainSpec { .unwrap_or_else(|| self.is_fork_active_at_timestamp(Hardfork::Cancun, timestamp)) } + /// Convenience method to check if [Hardfork::Homestead] is active at a given block number. + #[inline] + pub fn is_homestead_active_at_block(&self, block_number: u64) -> bool { + self.fork(Hardfork::Homestead).active_at_block(block_number) + } + /// Creates a [`ForkFilter`] for the block described by [Head]. pub fn fork_filter(&self, head: Head) -> ForkFilter { let forks = self.forks_iter().filter_map(|(_, condition)| { diff --git a/crates/primitives/src/transaction/mod.rs b/crates/primitives/src/transaction/mod.rs index 8d86794db3e8..fce4629aaf1b 100644 --- a/crates/primitives/src/transaction/mod.rs +++ b/crates/primitives/src/transaction/mod.rs @@ -1037,6 +1037,16 @@ impl TransactionSigned { Some(TransactionSignedEcRecovered { signed_transaction: self, signer }) } + /// Consumes the type, recover signer and return [`TransactionSignedEcRecovered`] _without + /// ensuring that the signature has a low `s` value_ (EIP-2). + /// + /// Returns `None` if the transaction's signature is invalid, see also + /// [Self::recover_signer_unchecked]. + pub fn into_ecrecovered_unchecked(self) -> Option { + let signer = self.recover_signer_unchecked()?; + Some(TransactionSignedEcRecovered { signed_transaction: self, signer }) + } + /// Tries to recover signer and return [`TransactionSignedEcRecovered`] by cloning the type. pub fn try_ecrecovered(&self) -> Option { let signer = self.recover_signer()?; @@ -1054,6 +1064,18 @@ impl TransactionSigned { } } + /// Tries to recover signer and return [`TransactionSignedEcRecovered`]. _without ensuring that + /// the signature has a low `s` value_ (EIP-2). + /// + /// Returns `Err(Self)` if the transaction's signature is invalid, see also + /// [Self::recover_signer_unchecked]. + pub fn try_into_ecrecovered_unchecked(self) -> Result { + match self.recover_signer_unchecked() { + None => Err(self), + Some(signer) => Ok(TransactionSignedEcRecovered { signed_transaction: self, signer }), + } + } + /// Returns the enveloped encoded transactions. /// /// See also [TransactionSigned::encode_enveloped] diff --git a/crates/rpc/rpc/src/debug.rs b/crates/rpc/rpc/src/debug.rs index 5ce8df6cee0c..f520f42e78a9 100644 --- a/crates/rpc/rpc/src/debug.rs +++ b/crates/rpc/rpc/src/debug.rs @@ -19,9 +19,11 @@ use reth_primitives::{ db::{DatabaseCommit, DatabaseRef}, BlockEnv, CfgEnv, }, - Address, Block, BlockId, BlockNumberOrTag, Bytes, TransactionSigned, B256, + Address, Block, BlockId, BlockNumberOrTag, Bytes, TransactionSignedEcRecovered, B256, +}; +use reth_provider::{ + BlockReaderIdExt, ChainSpecProvider, HeaderProvider, StateProviderBox, TransactionVariant, }; -use reth_provider::{BlockReaderIdExt, HeaderProvider, StateProviderBox, TransactionVariant}; use reth_revm::{ database::{StateProviderDatabase, SubState}, tracing::{ @@ -73,7 +75,7 @@ impl DebugApi { impl DebugApi where - Provider: BlockReaderIdExt + HeaderProvider + 'static, + Provider: BlockReaderIdExt + HeaderProvider + ChainSpecProvider + 'static, Eth: EthTransactions + 'static, { /// Acquires a permit to execute a tracing call. @@ -85,7 +87,7 @@ where async fn trace_block_with( &self, at: BlockId, - transactions: Vec, + transactions: Vec, cfg: CfgEnv, block_env: BlockEnv, opts: GethDebugTracingOptions, @@ -100,7 +102,6 @@ where let mut transactions = transactions.into_iter().peekable(); while let Some(tx) = transactions.next() { - let tx = tx.into_ecrecovered().ok_or(BlockError::InvalidSignature)?; let tx = tx_env_with_recovered(&tx); let env = Env { cfg: cfg.clone(), block: block_env.clone(), tx }; let (result, state_changes) = @@ -133,10 +134,32 @@ where Block::decode(&mut rlp_block.as_ref()).map_err(BlockError::RlpDecodeRawBlock)?; let (cfg, block_env) = self.inner.eth_api.evm_env_for_raw_block(&block.header).await?; - // we trace on top the block's parent block let parent = block.parent_hash; - self.trace_block_with(parent.into(), block.body, cfg, block_env, opts).await + + // Depending on EIP-2 we need to recover the transactions differently + let transactions = + if self.inner.provider.chain_spec().is_homestead_active_at_block(block.number) { + block + .body + .into_iter() + .map(|tx| { + tx.into_ecrecovered() + .ok_or_else(|| EthApiError::InvalidTransactionSignature) + }) + .collect::>>()? + } else { + block + .body + .into_iter() + .map(|tx| { + tx.into_ecrecovered_unchecked() + .ok_or_else(|| EthApiError::InvalidTransactionSignature) + }) + .collect::>>()? + }; + + self.trace_block_with(parent.into(), transactions, cfg, block_env, opts).await } /// Replays a block and returns the trace of each transaction. @@ -153,7 +176,7 @@ where let ((cfg, block_env, _), block) = futures::try_join!( self.inner.eth_api.evm_env_at(block_hash.into()), - self.inner.eth_api.block_by_id(block_id), + self.inner.eth_api.block_by_id_with_senders(block_id), )?; let block = block.ok_or_else(|| EthApiError::UnknownBlockNumber)?; @@ -161,7 +184,14 @@ where // its parent block's state let state_at = block.parent_hash; - self.trace_block_with(state_at.into(), block.body, cfg, block_env, opts).await + self.trace_block_with( + state_at.into(), + block.into_transactions_ecrecovered().collect(), + cfg, + block_env, + opts, + ) + .await } /// Trace the transaction according to the provided options. @@ -354,11 +384,10 @@ where let StateContext { transaction_index, block_number } = state_context.unwrap_or_default(); let transaction_index = transaction_index.unwrap_or_default(); - let target_block = block_number - .unwrap_or(reth_rpc_types::BlockId::Number(reth_rpc_types::BlockNumberOrTag::Latest)); + let target_block = block_number.unwrap_or(BlockId::Number(BlockNumberOrTag::Latest)); let ((cfg, block_env, _), block) = futures::try_join!( self.inner.eth_api.evm_env_at(target_block), - self.inner.eth_api.block_by_id(target_block), + self.inner.eth_api.block_by_id_with_senders(target_block), )?; let opts = opts.unwrap_or_default(); @@ -389,11 +418,10 @@ where if replay_block_txs { // only need to replay the transactions in the block if not all transactions are // to be replayed - let transactions = block.body.into_iter().take(num_txs); + let transactions = block.into_transactions_ecrecovered().take(num_txs); // Execute all transactions until index for tx in transactions { - let tx = tx.into_ecrecovered().ok_or(BlockError::InvalidSignature)?; let tx = tx_env_with_recovered(&tx); let env = Env { cfg: cfg.clone(), block: block_env.clone(), tx }; let (res, _) = transact(&mut db, env)?; @@ -632,7 +660,7 @@ where #[async_trait] impl DebugApiServer for DebugApi where - Provider: BlockReaderIdExt + HeaderProvider + 'static, + Provider: BlockReaderIdExt + HeaderProvider + ChainSpecProvider + 'static, Eth: EthApiSpec + 'static, { /// Handler for `debug_getRawHeader` @@ -658,6 +686,136 @@ where Ok(res.into()) } + /// Handler for `debug_getRawBlock` + async fn raw_block(&self, block_id: BlockId) -> RpcResult { + let block = self.inner.provider.block_by_id(block_id).to_rpc_result()?; + + let mut res = Vec::new(); + if let Some(mut block) = block { + // In RPC withdrawals are always present + if block.withdrawals.is_none() { + block.withdrawals = Some(vec![]); + } + block.encode(&mut res); + } + + Ok(res.into()) + } + + /// Handler for `debug_getRawTransaction` + /// Returns the bytes of the transaction for the given hash. + async fn raw_transaction(&self, hash: B256) -> RpcResult { + let tx = self.inner.eth_api.transaction_by_hash(hash).await?; + Ok(tx + .map(TransactionSource::into_recovered) + .map(|tx| tx.envelope_encoded()) + .unwrap_or_default()) + } + + /// Handler for `debug_getRawTransactions` + /// Returns the bytes of the transaction for the given hash. + async fn raw_transactions(&self, block_id: BlockId) -> RpcResult> { + let block = self + .inner + .provider + .block_with_senders_by_id(block_id, TransactionVariant::NoHash) + .to_rpc_result()? + .unwrap_or_default(); + Ok(block.into_transactions_ecrecovered().map(|tx| tx.envelope_encoded()).collect()) + } + + /// Handler for `debug_getRawReceipts` + async fn raw_receipts(&self, block_id: BlockId) -> RpcResult> { + let receipts = + self.inner.provider.receipts_by_block_id(block_id).to_rpc_result()?.unwrap_or_default(); + let mut all_receipts = Vec::with_capacity(receipts.len()); + + for receipt in receipts { + let mut buf = Vec::new(); + let receipt = receipt.with_bloom(); + receipt.encode(&mut buf); + all_receipts.push(buf.into()); + } + + Ok(all_receipts) + } + + /// Handler for `debug_getBadBlocks` + async fn bad_blocks(&self) -> RpcResult> { + Err(internal_rpc_err("unimplemented")) + } + + /// Handler for `debug_traceChain` + async fn debug_trace_chain( + &self, + _start_exclusive: BlockNumberOrTag, + _end_inclusive: BlockNumberOrTag, + ) -> RpcResult> { + Err(internal_rpc_err("unimplemented")) + } + + /// Handler for `debug_traceBlock` + async fn debug_trace_block( + &self, + rlp_block: Bytes, + opts: Option, + ) -> RpcResult> { + let _permit = self.acquire_trace_permit().await; + Ok(DebugApi::debug_trace_raw_block(self, rlp_block, opts.unwrap_or_default()).await?) + } + + /// Handler for `debug_traceBlockByHash` + async fn debug_trace_block_by_hash( + &self, + block: B256, + opts: Option, + ) -> RpcResult> { + let _permit = self.acquire_trace_permit().await; + Ok(DebugApi::debug_trace_block(self, block.into(), opts.unwrap_or_default()).await?) + } + + /// Handler for `debug_traceBlockByNumber` + async fn debug_trace_block_by_number( + &self, + block: BlockNumberOrTag, + opts: Option, + ) -> RpcResult> { + let _permit = self.acquire_trace_permit().await; + Ok(DebugApi::debug_trace_block(self, block.into(), opts.unwrap_or_default()).await?) + } + + /// Handler for `debug_traceTransaction` + async fn debug_trace_transaction( + &self, + tx_hash: B256, + opts: Option, + ) -> RpcResult { + let _permit = self.acquire_trace_permit().await; + Ok(DebugApi::debug_trace_transaction(self, tx_hash, opts.unwrap_or_default()).await?) + } + + /// Handler for `debug_traceCall` + async fn debug_trace_call( + &self, + request: CallRequest, + block_number: Option, + opts: Option, + ) -> RpcResult { + let _permit = self.acquire_trace_permit().await; + Ok(DebugApi::debug_trace_call(self, request, block_number, opts.unwrap_or_default()) + .await?) + } + + async fn debug_trace_call_many( + &self, + bundles: Vec, + state_context: Option, + opts: Option, + ) -> RpcResult>> { + let _permit = self.acquire_trace_permit().await; + Ok(DebugApi::debug_trace_call_many(self, bundles, state_context, opts).await?) + } + async fn debug_backtrace_at(&self, _location: &str) -> RpcResult<()> { Ok(()) } @@ -868,136 +1026,6 @@ where async fn debug_write_mutex_profile(&self, _file: String) -> RpcResult<()> { Ok(()) } - - /// Handler for `debug_getRawBlock` - async fn raw_block(&self, block_id: BlockId) -> RpcResult { - let block = self.inner.provider.block_by_id(block_id).to_rpc_result()?; - - let mut res = Vec::new(); - if let Some(mut block) = block { - // In RPC withdrawals are always present - if block.withdrawals.is_none() { - block.withdrawals = Some(vec![]); - } - block.encode(&mut res); - } - - Ok(res.into()) - } - - /// Handler for `debug_getRawTransaction` - /// Returns the bytes of the transaction for the given hash. - async fn raw_transaction(&self, hash: B256) -> RpcResult { - let tx = self.inner.eth_api.transaction_by_hash(hash).await?; - Ok(tx - .map(TransactionSource::into_recovered) - .map(|tx| tx.envelope_encoded()) - .unwrap_or_default()) - } - - /// Handler for `debug_getRawTransactions` - /// Returns the bytes of the transaction for the given hash. - async fn raw_transactions(&self, block_id: BlockId) -> RpcResult> { - let block = self - .inner - .provider - .block_with_senders_by_id(block_id, TransactionVariant::NoHash) - .to_rpc_result()? - .unwrap_or_default(); - Ok(block.into_transactions_ecrecovered().map(|tx| tx.envelope_encoded()).collect()) - } - - /// Handler for `debug_getRawReceipts` - async fn raw_receipts(&self, block_id: BlockId) -> RpcResult> { - let receipts = - self.inner.provider.receipts_by_block_id(block_id).to_rpc_result()?.unwrap_or_default(); - let mut all_receipts = Vec::with_capacity(receipts.len()); - - for receipt in receipts { - let mut buf = Vec::new(); - let receipt = receipt.with_bloom(); - receipt.encode(&mut buf); - all_receipts.push(buf.into()); - } - - Ok(all_receipts) - } - - /// Handler for `debug_getBadBlocks` - async fn bad_blocks(&self) -> RpcResult> { - Err(internal_rpc_err("unimplemented")) - } - - /// Handler for `debug_traceChain` - async fn debug_trace_chain( - &self, - _start_exclusive: BlockNumberOrTag, - _end_inclusive: BlockNumberOrTag, - ) -> RpcResult> { - Err(internal_rpc_err("unimplemented")) - } - - /// Handler for `debug_traceBlock` - async fn debug_trace_block( - &self, - rlp_block: Bytes, - opts: Option, - ) -> RpcResult> { - let _permit = self.acquire_trace_permit().await; - Ok(DebugApi::debug_trace_raw_block(self, rlp_block, opts.unwrap_or_default()).await?) - } - - /// Handler for `debug_traceBlockByHash` - async fn debug_trace_block_by_hash( - &self, - block: B256, - opts: Option, - ) -> RpcResult> { - let _permit = self.acquire_trace_permit().await; - Ok(DebugApi::debug_trace_block(self, block.into(), opts.unwrap_or_default()).await?) - } - - /// Handler for `debug_traceBlockByNumber` - async fn debug_trace_block_by_number( - &self, - block: BlockNumberOrTag, - opts: Option, - ) -> RpcResult> { - let _permit = self.acquire_trace_permit().await; - Ok(DebugApi::debug_trace_block(self, block.into(), opts.unwrap_or_default()).await?) - } - - /// Handler for `debug_traceTransaction` - async fn debug_trace_transaction( - &self, - tx_hash: B256, - opts: Option, - ) -> RpcResult { - let _permit = self.acquire_trace_permit().await; - Ok(DebugApi::debug_trace_transaction(self, tx_hash, opts.unwrap_or_default()).await?) - } - - /// Handler for `debug_traceCall` - async fn debug_trace_call( - &self, - request: CallRequest, - block_number: Option, - opts: Option, - ) -> RpcResult { - let _permit = self.acquire_trace_permit().await; - Ok(DebugApi::debug_trace_call(self, request, block_number, opts.unwrap_or_default()) - .await?) - } - - async fn debug_trace_call_many( - &self, - bundles: Vec, - state_context: Option, - opts: Option, - ) -> RpcResult>> { - let _permit = self.acquire_trace_permit().await; - Ok(DebugApi::debug_trace_call_many(self, bundles, state_context, opts).await?) - } } impl std::fmt::Debug for DebugApi { diff --git a/crates/rpc/rpc/src/eth/api/transactions.rs b/crates/rpc/rpc/src/eth/api/transactions.rs index ec98b9cf8c0e..cbff163e5247 100644 --- a/crates/rpc/rpc/src/eth/api/transactions.rs +++ b/crates/rpc/rpc/src/eth/api/transactions.rs @@ -18,7 +18,7 @@ use reth_primitives::{ revm::env::{fill_block_env_with_coinbase, tx_env_with_recovered}, revm_primitives::{db::DatabaseCommit, Env, ExecutionResult, ResultAndState, SpecId, State}, Address, BlockId, BlockNumberOrTag, Bytes, FromRecoveredPooledTransaction, Header, - IntoRecoveredTransaction, Receipt, SealedBlock, + IntoRecoveredTransaction, Receipt, SealedBlock, SealedBlockWithSenders, TransactionKind::{Call, Create}, TransactionMeta, TransactionSigned, TransactionSignedEcRecovered, B256, U128, U256, U64, }; @@ -100,6 +100,14 @@ pub trait EthTransactions: Send + Sync { /// Returns `None` if block does not exist. async fn block_by_id(&self, id: BlockId) -> EthResult>; + /// Get the entire block for the given id. + /// + /// Returns `None` if block does not exist. + async fn block_by_id_with_senders( + &self, + id: BlockId, + ) -> EthResult>; + /// Get all transactions in the block with the given hash. /// /// Returns `None` if block does not exist. @@ -365,6 +373,13 @@ where self.block(id).await } + async fn block_by_id_with_senders( + &self, + id: BlockId, + ) -> EthResult> { + self.block_with_senders(id).await + } + async fn transactions_by_block_id( &self, block: BlockId, @@ -379,8 +394,11 @@ where match this.provider().transaction_by_hash_with_meta(hash)? { None => Ok(None), Some((tx, meta)) => { + // Note: we assume this transaction is valid, because it's mined (or part of + // pending block) and already. We don't need to + // check for pre EIP-2 because this transaction could be pre-EIP-2. let transaction = tx - .into_ecrecovered() + .into_ecrecovered_unchecked() .ok_or(EthApiError::InvalidTransactionSignature)?; let tx = TransactionSource::Block { From c4c07b86bb08451ab21a881971d3a5759cc32648 Mon Sep 17 00:00:00 2001 From: Matthias Seitz Date: Mon, 11 Dec 2023 15:20:45 +0100 Subject: [PATCH 117/277] feat: add network bench (#5728) --- Cargo.lock | 3 + crates/net/network/Cargo.toml | 9 ++ crates/net/network/benches/bench.rs | 89 ++++++++++++++++++++ crates/net/network/src/test_utils/testnet.rs | 18 ++++ 4 files changed, 119 insertions(+) create mode 100644 crates/net/network/benches/bench.rs diff --git a/Cargo.lock b/Cargo.lock index 826d1c2d6e9d..831dcf51bcdd 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1521,6 +1521,7 @@ dependencies = [ "serde_derive", "serde_json", "tinytemplate", + "tokio", "walkdir", ] @@ -6062,6 +6063,7 @@ dependencies = [ "aquamarine", "async-trait", "auto_impl", + "criterion", "enr", "ethers-core", "ethers-middleware", @@ -6075,6 +6077,7 @@ dependencies = [ "metrics", "parking_lot 0.12.1", "pin-project", + "pprof", "rand 0.8.5", "reth-discv4", "reth-dns-discovery", diff --git a/crates/net/network/Cargo.toml b/crates/net/network/Cargo.toml index b63b6637f718..d861b2cfe836 100644 --- a/crates/net/network/Cargo.toml +++ b/crates/net/network/Cargo.toml @@ -89,6 +89,10 @@ enr = { workspace = true, features = ["serde", "rust-secp256k1"] } serial_test.workspace = true tempfile.workspace = true +## Benchmarks +pprof = { workspace = true, features = ["criterion", "flamegraph"] } +criterion = { workspace = true, features = ["async_tokio", "html_reports"] } + [features] default = ["serde"] serde = ["dep:serde", "dep:humantime-serde", "secp256k1/serde", "enr?/serde", "dep:serde_json"] @@ -101,3 +105,8 @@ optimism = [ "reth-network-api/optimism", "reth-rpc-types/optimism", ] + +[[bench]] +name = "bench" +required-features = ["test-utils"] +harness = false diff --git a/crates/net/network/benches/bench.rs b/crates/net/network/benches/bench.rs new file mode 100644 index 000000000000..9f4fb5665997 --- /dev/null +++ b/crates/net/network/benches/bench.rs @@ -0,0 +1,89 @@ +use criterion::*; +use futures::StreamExt; +use pprof::criterion::{Output, PProfProfiler}; +use rand::thread_rng; +use reth_network::{test_utils::Testnet, NetworkEvents}; +use reth_network_api::Peers; +use reth_primitives::U256; +use reth_provider::test_utils::{ExtendedAccount, MockEthProvider}; +use reth_transaction_pool::{test_utils::TransactionGenerator, PoolTransaction}; +use std::sync::Arc; +use tokio::{runtime::Runtime as TokioRuntime, sync::mpsc::unbounded_channel}; + +criterion_group!( + name = brodcast_benches; + config = Criterion::default().with_profiler(PProfProfiler::new(100, Output::Flamegraph(None))); + targets = broadcast_ingress_bench +); + +pub fn broadcast_ingress_bench(c: &mut Criterion) { + let rt = TokioRuntime::new().unwrap(); + + let mut group = c.benchmark_group("Broadcast Ingress"); + group.sample_size(10); + group.bench_function("receive_broadcasts", move |b| { + b.to_async(&rt).iter_with_setup( + || { + // `b.to_async(rt)` automatically enters the + // runtime context and simply calling `block_on` here will cause the code to panic. + tokio::task::block_in_place(|| { + tokio::runtime::Handle::current().block_on(async { + let provider = MockEthProvider::default(); + let mut net = Testnet::create_with(2, provider.clone()).await; + + let mut peer0 = net.remove_peer(0); + let (tx, transactions_rx) = unbounded_channel(); + peer0.network_mut().set_transactions(tx); + let mut events0 = peer0.handle().event_listener(); + let net = net.with_eth_pool(); + let handle = net.spawn(); + let peer1 = handle.peers()[0].network().clone(); + let peer0_id = peer0.peer_id(); + peer1.add_peer(peer0_id, peer0.local_addr()); + + // await connection + tokio::select! { + _ = events0.next() => {} + _ = &mut peer0 => {} + } + + // prepare some transactions + let mut gen = TransactionGenerator::new(thread_rng()); + let num_broadcasts = 10; + for _ in 0..num_broadcasts { + for _ in 0..2 { + let mut txs = Vec::new(); + let tx = gen.gen_eip1559_pooled(); + // ensure the sender has balance + provider.add_account( + tx.sender(), + ExtendedAccount::new(0, U256::from(100_000_000)), + ); + txs.push(Arc::new(tx.transaction().clone().into_signed())); + peer1.send_transactions(peer0_id, txs); + } + } + (num_broadcasts, transactions_rx, peer0, handle) + }) + }) + }, + |(num_txs, mut transactions_rx, mut peer0, _handle)| async move { + let mut count = 0; + loop { + tokio::select! { + _ = transactions_rx.recv() => { + count += 1; + if count == num_txs { + break; + } + }, + _ = &mut peer0 => { + } + } + } + }, + ) + }); +} + +criterion_main!(brodcast_benches); diff --git a/crates/net/network/src/test_utils/testnet.rs b/crates/net/network/src/test_utils/testnet.rs index 7ad4ae867e22..1ac94ae335ad 100644 --- a/crates/net/network/src/test_utils/testnet.rs +++ b/crates/net/network/src/test_utils/testnet.rs @@ -96,6 +96,14 @@ where &self.peers } + /// Remove a peer from the [`Testnet`] and return it. + /// + /// # Panics + /// If the index is out of bounds. + pub fn remove_peer(&mut self, index: usize) -> Peer { + self.peers.remove(index) + } + /// Return a mutable iterator over all peers. pub fn peers_iter_mut(&mut self) -> impl Iterator> + '_ { self.peers.iter_mut() @@ -346,6 +354,16 @@ where self.network.local_addr() } + /// The [PeerId] of this peer. + pub fn peer_id(&self) -> PeerId { + *self.network.peer_id() + } + + /// Returns mutable access to the network. + pub fn network_mut(&mut self) -> &mut NetworkManager { + &mut self.network + } + /// Returns the [`NetworkHandle`] of this peer. pub fn handle(&self) -> NetworkHandle { self.network.handle().clone() From 1e523049ca8d076fa1757b8bb0a9851a61cdefae Mon Sep 17 00:00:00 2001 From: DoTheBestToGetTheBest <146037313+DoTheBestToGetTheBest@users.noreply.github.com> Date: Mon, 11 Dec 2023 06:57:45 -0800 Subject: [PATCH 118/277] feat(bin) enhance logging config to make RUST_LOG for journalId and File writers (#5687) Co-authored-by: Alexey Shekhirin --- bin/reth/src/cli/mod.rs | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/bin/reth/src/cli/mod.rs b/bin/reth/src/cli/mod.rs index 1361b53d2d11..fb01aad765d4 100644 --- a/bin/reth/src/cli/mod.rs +++ b/bin/reth/src/cli/mod.rs @@ -232,20 +232,27 @@ impl Logs { { let mut layers = Vec::new(); + // Function to create a new EnvFilter with environment, default and additional directive + let create_env_filter = |additional_directive: &str| -> eyre::Result { + Ok(EnvFilter::builder() + .from_env_lossy() + .add_directive(DEFAULT_ENV_FILTER_DIRECTIVE.parse()?) + .add_directive(additional_directive.parse()?)) + }; + + // Create and add the journald layer if enabled if self.journald { + let journald_filter = create_env_filter(&self.journald_filter)?; layers.push( - reth_tracing::journald( - EnvFilter::try_new(DEFAULT_ENV_FILTER_DIRECTIVE)? - .add_directive(self.journald_filter.parse()?), - ) - .expect("Could not connect to journald"), + reth_tracing::journald(journald_filter).expect("Could not connect to journald"), ); } + // Create and add the file logging layer if enabled let file_guard = if self.log_file_max_files > 0 { + let file_filter = create_env_filter(&self.log_file_filter)?; let (layer, guard) = reth_tracing::file( - EnvFilter::try_new(DEFAULT_ENV_FILTER_DIRECTIVE)? - .add_directive(self.log_file_filter.parse()?), + file_filter, &self.log_file_directory, "reth.log", self.log_file_max_size * MB_TO_BYTES, From fa1584b22306f6f0573a5d4c85f77a685c3e65d4 Mon Sep 17 00:00:00 2001 From: Matthias Seitz Date: Mon, 11 Dec 2023 15:58:57 +0100 Subject: [PATCH 119/277] feat: add RethTransactionPoolConfig (#5729) --- bin/reth/src/args/txpool_args.rs | 5 +++-- bin/reth/src/cli/config.rs | 10 +++++++++- bin/reth/src/node/mod.rs | 2 +- 3 files changed, 13 insertions(+), 4 deletions(-) diff --git a/bin/reth/src/args/txpool_args.rs b/bin/reth/src/args/txpool_args.rs index 12dea777b25f..ba550f599a87 100644 --- a/bin/reth/src/args/txpool_args.rs +++ b/bin/reth/src/args/txpool_args.rs @@ -1,5 +1,6 @@ //! Transaction pool arguments +use crate::cli::config::RethTransactionPoolConfig; use clap::Args; use reth_transaction_pool::{ LocalTransactionConfig, PoolConfig, PriceBumpConfig, SubPoolLimit, DEFAULT_PRICE_BUMP, @@ -65,9 +66,9 @@ impl Default for TxPoolArgs { } } -impl TxPoolArgs { +impl RethTransactionPoolConfig for TxPoolArgs { /// Returns transaction pool configuration. - pub fn pool_config(&self) -> PoolConfig { + fn pool_config(&self) -> PoolConfig { PoolConfig { local_transactions_config: LocalTransactionConfig { no_exemptions: self.no_locals }, pending_limit: SubPoolLimit { diff --git a/bin/reth/src/cli/config.rs b/bin/reth/src/cli/config.rs index 2d3847bbb59c..a549a31e1f3a 100644 --- a/bin/reth/src/cli/config.rs +++ b/bin/reth/src/cli/config.rs @@ -11,9 +11,10 @@ use reth_rpc_builder::{ auth::AuthServerConfig, error::RpcError, EthConfig, IpcServerBuilder, RpcServerConfig, ServerBuilder, TransportRpcModuleConfig, }; +use reth_transaction_pool::PoolConfig; use std::{borrow::Cow, path::PathBuf, time::Duration}; -/// A trait that provides configured RPC server. +/// A trait that provides a configured RPC server. /// /// This provides all basic config values for the RPC server and is implemented by the /// [RpcServerArgs](crate::args::RpcServerArgs) type. @@ -127,3 +128,10 @@ impl RethNetworkConfig for reth_network::NetworkManager { reth_network::NetworkManager::add_rlpx_sub_protocol(self, protocol); } } + +/// A trait that provides all basic config values for the transaction pool and is implemented by the +/// [TxPoolArgs](crate::args::TxPoolArgs) type. +pub trait RethTransactionPoolConfig { + /// Returns transaction pool configuration. + fn pool_config(&self) -> PoolConfig; +} diff --git a/bin/reth/src/node/mod.rs b/bin/reth/src/node/mod.rs index 37773a87b750..dd67adc5348a 100644 --- a/bin/reth/src/node/mod.rs +++ b/bin/reth/src/node/mod.rs @@ -10,7 +10,7 @@ use crate::{ }, cli::{ components::RethNodeComponentsImpl, - config::RethRpcConfig, + config::{RethRpcConfig, RethTransactionPoolConfig}, ext::{RethCliExt, RethNodeCommandConfig}, }, dirs::{ChainPath, DataDirPath, MaybePlatformPath}, From 4cda40b3cfc13fc655e8476fbba00a66ee41ddad Mon Sep 17 00:00:00 2001 From: Alexey Shekhirin Date: Mon, 11 Dec 2023 19:08:31 +0000 Subject: [PATCH 120/277] Revert 1e523049ca8d076fa1757b8bb0a9851a61cdefae (#5731) --- bin/reth/src/cli/mod.rs | 21 +++++++-------------- 1 file changed, 7 insertions(+), 14 deletions(-) diff --git a/bin/reth/src/cli/mod.rs b/bin/reth/src/cli/mod.rs index fb01aad765d4..1361b53d2d11 100644 --- a/bin/reth/src/cli/mod.rs +++ b/bin/reth/src/cli/mod.rs @@ -232,27 +232,20 @@ impl Logs { { let mut layers = Vec::new(); - // Function to create a new EnvFilter with environment, default and additional directive - let create_env_filter = |additional_directive: &str| -> eyre::Result { - Ok(EnvFilter::builder() - .from_env_lossy() - .add_directive(DEFAULT_ENV_FILTER_DIRECTIVE.parse()?) - .add_directive(additional_directive.parse()?)) - }; - - // Create and add the journald layer if enabled if self.journald { - let journald_filter = create_env_filter(&self.journald_filter)?; layers.push( - reth_tracing::journald(journald_filter).expect("Could not connect to journald"), + reth_tracing::journald( + EnvFilter::try_new(DEFAULT_ENV_FILTER_DIRECTIVE)? + .add_directive(self.journald_filter.parse()?), + ) + .expect("Could not connect to journald"), ); } - // Create and add the file logging layer if enabled let file_guard = if self.log_file_max_files > 0 { - let file_filter = create_env_filter(&self.log_file_filter)?; let (layer, guard) = reth_tracing::file( - file_filter, + EnvFilter::try_new(DEFAULT_ENV_FILTER_DIRECTIVE)? + .add_directive(self.log_file_filter.parse()?), &self.log_file_directory, "reth.log", self.log_file_max_size * MB_TO_BYTES, From 971e8d9b0105e8ce5da007d03fcebeca71347acb Mon Sep 17 00:00:00 2001 From: Thomas Coratger <60488569+tcoratger@users.noreply.github.com> Date: Mon, 11 Dec 2023 21:28:25 +0100 Subject: [PATCH 121/277] refactor(net): Simplified logic for determining shared capability versions (#5725) Co-authored-by: Matthias Seitz --- crates/net/eth-wire/src/capability.rs | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/crates/net/eth-wire/src/capability.rs b/crates/net/eth-wire/src/capability.rs index 8696109c68bb..8b64aa16a468 100644 --- a/crates/net/eth-wire/src/capability.rs +++ b/crates/net/eth-wire/src/capability.rs @@ -479,12 +479,9 @@ pub fn shared_capability_offsets( if let Some(messages) = our_capabilities.get(&peer_capability).copied() { // If multiple versions are shared of the same (equal name) capability, the numerically // highest wins, others are ignored - - let version = shared_capabilities.get(&peer_capability.name).map(|v| v.version); - - // TODO(mattsse): simplify - if version.is_none() || - (version.is_some() && peer_capability.version > version.expect("is some; qed")) + if shared_capabilities + .get(&peer_capability.name) + .map_or(true, |v| peer_capability.version > v.version) { shared_capabilities.insert( peer_capability.name.clone(), @@ -612,6 +609,18 @@ mod tests { assert!(capabilities.supports_eth_v68()); } + #[test] + fn test_peer_capability_version_zero() { + let cap = Capability::new_static("TestName", 0); + let local_capabilities: Vec = + vec![Protocol::new(cap.clone(), 0), EthVersion::Eth67.into(), EthVersion::Eth68.into()]; + let peer_capabilities = vec![cap.clone()]; + + let shared = shared_capability_offsets(local_capabilities, peer_capabilities).unwrap(); + assert_eq!(shared.len(), 1); + assert_eq!(shared[0], SharedCapability::UnknownCapability { cap, offset: 16, messages: 0 }) + } + #[test] fn test_peer_lower_capability_version() { let local_capabilities: Vec = From 64337f31e0f0ec35befaa466c4b25019068e3895 Mon Sep 17 00:00:00 2001 From: Matthias Seitz Date: Mon, 11 Dec 2023 21:47:34 +0100 Subject: [PATCH 122/277] chore: make mixHash optional (#5732) --- crates/rpc/rpc-types-compat/src/block.rs | 2 +- crates/rpc/rpc-types/src/eth/block.rs | 25 +++++++++++++++++------- 2 files changed, 19 insertions(+), 8 deletions(-) diff --git a/crates/rpc/rpc-types-compat/src/block.rs b/crates/rpc/rpc-types-compat/src/block.rs index 578a47b36929..c0cf0bae4391 100644 --- a/crates/rpc/rpc-types-compat/src/block.rs +++ b/crates/rpc/rpc-types-compat/src/block.rs @@ -135,7 +135,7 @@ pub fn from_primitive_with_hash(primitive_header: reth_primitives::SealedHeader) logs_bloom, timestamp: U256::from(timestamp), difficulty, - mix_hash, + mix_hash: Some(mix_hash), nonce: Some(nonce.to_be_bytes().into()), base_fee_per_gas: base_fee_per_gas.map(U256::from), blob_gas_used: blob_gas_used.map(U64::from), diff --git a/crates/rpc/rpc-types/src/eth/block.rs b/crates/rpc/rpc-types/src/eth/block.rs index 17ecf8ce755d..74e69ecccfa3 100644 --- a/crates/rpc/rpc-types/src/eth/block.rs +++ b/crates/rpc/rpc-types/src/eth/block.rs @@ -163,23 +163,34 @@ pub struct Header { /// Extra data pub extra_data: Bytes, /// Mix Hash - pub mix_hash: B256, + /// + /// 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(rename = "baseFeePerGas", skip_serializing_if = "Option::is_none")] + #[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(rename = "blobGasUsed", skip_serializing_if = "Option::is_none")] + #[serde(skip_serializing_if = "Option::is_none")] pub blob_gas_used: Option, /// Excess blob gas - #[serde(rename = "excessBlobGas", skip_serializing_if = "Option::is_none")] + #[serde(skip_serializing_if = "Option::is_none")] pub excess_blob_gas: Option, /// Parent beacon block root - #[serde(rename = "parentBeaconBlockRoot", skip_serializing_if = "Option::is_none")] + #[serde(skip_serializing_if = "Option::is_none")] pub parent_beacon_block_root: Option, } @@ -837,7 +848,7 @@ mod tests { logs_bloom: Bloom::default(), timestamp: U256::from(12), difficulty: U256::from(13), - mix_hash: B256::with_last_byte(14), + 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, @@ -878,7 +889,7 @@ mod tests { logs_bloom: Bloom::default(), timestamp: U256::from(12), difficulty: U256::from(13), - mix_hash: B256::with_last_byte(14), + 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, From c1d7d2bde398bcf410c7e2df13fd7151fc2a58b9 Mon Sep 17 00:00:00 2001 From: Dan Cline <6798349+Rjected@users.noreply.github.com> Date: Mon, 11 Dec 2023 17:28:56 -0500 Subject: [PATCH 123/277] feat: add DatabaseMetrics trait for generic DB in reth node (#5690) --- bin/reth/src/node/mod.rs | 11 ++-- bin/reth/src/prometheus_exporter.rs | 51 ++++--------------- .../db/src/abstraction/database_metrics.rs | 14 +++++ crates/storage/db/src/abstraction/mod.rs | 2 + .../storage/db/src/implementation/mdbx/mod.rs | 42 +++++++++++++++ crates/storage/db/src/lib.rs | 8 ++- 6 files changed, 81 insertions(+), 47 deletions(-) create mode 100644 crates/storage/db/src/abstraction/database_metrics.rs diff --git a/bin/reth/src/node/mod.rs b/bin/reth/src/node/mod.rs index dd67adc5348a..b7b4c520c128 100644 --- a/bin/reth/src/node/mod.rs +++ b/bin/reth/src/node/mod.rs @@ -38,7 +38,7 @@ use reth_config::{ config::{PruneConfig, StageConfig}, Config, }; -use reth_db::{database::Database, init_db, DatabaseEnv}; +use reth_db::{database::Database, database_metrics::DatabaseMetrics, init_db}; use reth_downloaders::{ bodies::bodies::BodiesDownloaderBuilder, headers::reverse_headers::ReverseHeadersDownloaderBuilder, @@ -693,11 +693,14 @@ impl NodeCommand { prometheus_exporter::install_recorder() } - async fn start_metrics_endpoint( + async fn start_metrics_endpoint( &self, prometheus_handle: PrometheusHandle, - db: Arc, - ) -> eyre::Result<()> { + db: Metrics, + ) -> eyre::Result<()> + where + Metrics: DatabaseMetrics + 'static + Send + Sync, + { if let Some(listen_addr) = self.metrics { info!(target: "reth::cli", addr = %listen_addr, "Starting metrics endpoint"); prometheus_exporter::serve( diff --git a/bin/reth/src/prometheus_exporter.rs b/bin/reth/src/prometheus_exporter.rs index 4fa622bffda4..0ec5352c9b33 100644 --- a/bin/reth/src/prometheus_exporter.rs +++ b/bin/reth/src/prometheus_exporter.rs @@ -7,7 +7,7 @@ use hyper::{ use metrics::{describe_gauge, gauge}; use metrics_exporter_prometheus::{PrometheusBuilder, PrometheusHandle}; use metrics_util::layers::{PrefixLayer, Stack}; -use reth_db::{database::Database, tables, DatabaseEnv}; +use reth_db::database_metrics::DatabaseMetrics; use reth_metrics::metrics::Unit; use std::{convert::Infallible, net::SocketAddr, sync::Arc}; use tracing::error; @@ -74,54 +74,21 @@ async fn start_endpoint( } /// Serves Prometheus metrics over HTTP with database and process metrics. -pub(crate) async fn serve( +pub(crate) async fn serve( listen_addr: SocketAddr, handle: PrometheusHandle, - db: Arc, + db: Metrics, process: metrics_process::Collector, -) -> eyre::Result<()> { - let db_stats = move || { - // TODO: A generic stats abstraction for other DB types to deduplicate this and `reth db - // stats` - let _ = db.view(|tx| { - for table in tables::Tables::ALL.iter().map(|table| table.name()) { - let table_db = - tx.inner.open_db(Some(table)).wrap_err("Could not open db.")?; - - let stats = tx - .inner - .db_stat(&table_db) - .wrap_err(format!("Could not find table: {table}"))?; - - let page_size = stats.page_size() as usize; - let leaf_pages = stats.leaf_pages(); - let branch_pages = stats.branch_pages(); - let overflow_pages = stats.overflow_pages(); - let num_pages = leaf_pages + branch_pages + overflow_pages; - let table_size = page_size * num_pages; - let entries = stats.entries(); - - gauge!("db.table_size", table_size as f64, "table" => table); - gauge!("db.table_pages", leaf_pages as f64, "table" => table, "type" => "leaf"); - gauge!("db.table_pages", branch_pages as f64, "table" => table, "type" => "branch"); - gauge!("db.table_pages", overflow_pages as f64, "table" => table, "type" => "overflow"); - gauge!("db.table_entries", entries as f64, "table" => table); - } - - Ok::<(), eyre::Report>(()) - }).map_err(|error| error!(?error, "Failed to read db table stats")); - - if let Ok(freelist) = - db.freelist().map_err(|error| error!(?error, "Failed to read db.freelist")) - { - gauge!("db.freelist", freelist as f64); - } - }; +) -> eyre::Result<()> +where + Metrics: DatabaseMetrics + 'static + Send + Sync, +{ + let db_metrics_hook = move || db.report_metrics(); // Clone `process` to move it into the hook and use the original `process` for describe below. let cloned_process = process.clone(); let hooks: Vec>> = vec![ - Box::new(db_stats), + Box::new(db_metrics_hook), Box::new(move || cloned_process.collect()), Box::new(collect_memory_stats), ]; diff --git a/crates/storage/db/src/abstraction/database_metrics.rs b/crates/storage/db/src/abstraction/database_metrics.rs new file mode 100644 index 000000000000..717e1b342072 --- /dev/null +++ b/crates/storage/db/src/abstraction/database_metrics.rs @@ -0,0 +1,14 @@ +use std::sync::Arc; + +/// Represents a type that can report metrics, used mainly with the database. The `report_metrics` +/// method can be used as a prometheus hook. +pub trait DatabaseMetrics { + /// Reports metrics for the database. + fn report_metrics(&self); +} + +impl DatabaseMetrics for Arc { + fn report_metrics(&self) { + ::report_metrics(self) + } +} diff --git a/crates/storage/db/src/abstraction/mod.rs b/crates/storage/db/src/abstraction/mod.rs index c1cdc3680263..1f2963d178e3 100644 --- a/crates/storage/db/src/abstraction/mod.rs +++ b/crates/storage/db/src/abstraction/mod.rs @@ -4,6 +4,8 @@ pub mod common; pub mod cursor; /// Database traits. pub mod database; +/// Database metrics trait extensions. +pub mod database_metrics; /// mock pub mod mock; /// Table traits diff --git a/crates/storage/db/src/implementation/mdbx/mod.rs b/crates/storage/db/src/implementation/mdbx/mod.rs index 91a56ca44d7d..e4bed305996c 100644 --- a/crates/storage/db/src/implementation/mdbx/mod.rs +++ b/crates/storage/db/src/implementation/mdbx/mod.rs @@ -2,14 +2,18 @@ use crate::{ database::Database, + database_metrics::DatabaseMetrics, tables::{TableType, Tables}, utils::default_page_size, DatabaseError, }; +use eyre::Context; +use metrics::gauge; use reth_interfaces::db::LogLevel; use reth_libmdbx::{ DatabaseFlags, Environment, EnvironmentFlags, Geometry, Mode, PageSize, SyncMode, RO, RW, }; +use reth_tracing::tracing::error; use std::{ops::Deref, path::Path}; use tx::Tx; @@ -59,6 +63,44 @@ impl Database for DatabaseEnv { } } +impl DatabaseMetrics for DatabaseEnv { + fn report_metrics(&self) { + let _ = self.view(|tx| { + for table in Tables::ALL.iter().map(|table| table.name()) { + let table_db = + tx.inner.open_db(Some(table)).wrap_err("Could not open db.")?; + + let stats = tx + .inner + .db_stat(&table_db) + .wrap_err(format!("Could not find table: {table}"))?; + + let page_size = stats.page_size() as usize; + let leaf_pages = stats.leaf_pages(); + let branch_pages = stats.branch_pages(); + let overflow_pages = stats.overflow_pages(); + let num_pages = leaf_pages + branch_pages + overflow_pages; + let table_size = page_size * num_pages; + let entries = stats.entries(); + + gauge!("db.table_size", table_size as f64, "table" => table); + gauge!("db.table_pages", leaf_pages as f64, "table" => table, "type" => "leaf"); + gauge!("db.table_pages", branch_pages as f64, "table" => table, "type" => "branch"); + gauge!("db.table_pages", overflow_pages as f64, "table" => table, "type" => "overflow"); + gauge!("db.table_entries", entries as f64, "table" => table); + } + + Ok::<(), eyre::Report>(()) + }).map_err(|error| error!(?error, "Failed to read db table stats")); + + if let Ok(freelist) = + self.freelist().map_err(|error| error!(?error, "Failed to read db.freelist")) + { + gauge!("db.freelist", freelist as f64); + } + } +} + impl DatabaseEnv { /// Opens the database at the specified path with the given `EnvKind`. /// diff --git a/crates/storage/db/src/lib.rs b/crates/storage/db/src/lib.rs index 0c8078a69b62..a7ff4b365690 100644 --- a/crates/storage/db/src/lib.rs +++ b/crates/storage/db/src/lib.rs @@ -153,7 +153,7 @@ pub fn open_db(path: &Path, log_level: Option) -> eyre::Result DatabaseMetrics for TempDatabase { + fn report_metrics(&self) { + self.db().report_metrics() + } + } + /// Create read/write database for testing pub fn create_test_rw_db() -> Arc> { let path = tempfile::TempDir::new().expect(ERROR_TEMPDIR).into_path(); From 7efd0fd939ab58714032f3e28ff4eccb6856d5d6 Mon Sep 17 00:00:00 2001 From: Alexey Shekhirin Date: Tue, 12 Dec 2023 11:36:59 +0000 Subject: [PATCH 124/277] docs(book): update CLI (#5730) --- book/cli/cli.md | 6 +- book/cli/config.md | 6 +- book/cli/db.md | 895 ++++++++++++++++++++++++++++++---- book/cli/debug.md | 561 ++++++++++++++++++++- book/cli/import.md | 6 +- book/cli/init.md | 6 +- book/cli/node.md | 100 ++-- book/cli/p2p.md | 159 +++++- book/cli/recover.md | 93 +++- book/cli/stage.md | 1003 ++++++++++++++++++++++++++++++++++++-- book/cli/test-vectors.md | 82 +++- 11 files changed, 2710 insertions(+), 207 deletions(-) diff --git a/book/cli/cli.md b/book/cli/cli.md index e695068b230d..394883c5c4b2 100644 --- a/book/cli/cli.md +++ b/book/cli/cli.md @@ -45,14 +45,10 @@ Commands: Options: --chain The chain this node is running. - Possible values are either a built-in chain or the path to a chain specification file. Built-in chains: - - mainnet - - goerli - - sepolia - - holesky + mainnet, sepolia, goerli, holesky, dev [default: mainnet] diff --git a/book/cli/config.md b/book/cli/config.md index d754916d4b3c..78f64a1792ba 100644 --- a/book/cli/config.md +++ b/book/cli/config.md @@ -16,14 +16,10 @@ Options: --chain The chain this node is running. - Possible values are either a built-in chain or the path to a chain specification file. Built-in chains: - - mainnet - - goerli - - sepolia - - holesky + mainnet, sepolia, goerli, holesky, dev [default: mainnet] diff --git a/book/cli/db.md b/book/cli/db.md index 5af1e19a7a64..47f843ad8883 100644 --- a/book/cli/db.md +++ b/book/cli/db.md @@ -8,15 +8,16 @@ $ reth db --help Usage: reth db [OPTIONS] Commands: - stats Lists all the tables, their entry count and their size - list Lists the contents of a table - diff Create a diff between two database tables or two entire databases - get Gets the content of a table for the given key - drop Deletes all database entries - clear Deletes all table entries - version Lists current and local database versions - path Returns the full database path - help Print this message or the help of the given subcommand(s) + stats Lists all the tables, their entry count and their size + list Lists the contents of a table + diff Create a diff between two database tables or two entire databases + get Gets the content of a table for the given key + drop Deletes all database entries + clear Deletes all table entries + snapshot Snapshots tables from database + version Lists current and local database versions + path Returns the full database path + help Print this message or the help of the given subcommand(s) Options: --datadir @@ -32,14 +33,10 @@ Options: --chain The chain this node is running. - Possible values are either a built-in chain or the path to a chain specification file. Built-in chains: - - mainnet - - goerli - - sepolia - - holesky + mainnet, sepolia, goerli, holesky, dev [default: mainnet] @@ -136,120 +133,854 @@ Usage: reth db clear [OPTIONS] Arguments:
Table name -``` -## `reth db diff` +Options: + --datadir + The path to the data dir for all reth files and subdirectories. + + Defaults to the OS-specific data directory: + + - Linux: `$XDG_DATA_HOME/reth/` or `$HOME/.local/share/reth/` + - Windows: `{FOLDERID_RoamingAppData}/reth/` + - macOS: `$HOME/Library/Application Support/reth/` + + [default: default] -Create a diff between two database tables or two entire databases + --chain + The chain this node is running. + Possible values are either a built-in chain or the path to a chain specification file. + + Built-in chains: + mainnet, sepolia, goerli, holesky, dev + + [default: mainnet] -```bash -$ reth db diff --help + --instance + Add a new instance of a node. + + Configures the ports of the node to avoid conflicts with the defaults. This is useful for running multiple nodes on the same machine. + + Max number of instances is 200. It is chosen in a way so that it's not possible to have port numbers that conflict with each other. + + Changes to the following port numbers: - DISCOVERY_PORT: default + `instance` - 1 - AUTH_PORT: default + `instance` * 100 - 100 - HTTP_RPC_PORT: default - `instance` + 1 - WS_RPC_PORT: default + `instance` * 2 - 2 + + [default: 1] -Usage: reth db diff [OPTIONS] --secondary-datadir --output + -h, --help + Print help (see a summary with '-h') -Options: - --secondary-datadir - The path to the data dir for all reth files and subdirectories. -``` +Logging: + --log.file.directory + The path to put log files in + + [default: /reth/logs] -## `reth db drop` + --log.file.max-size + The maximum size (in MB) of one log file + + [default: 200] -Deletes all database entries + --log.file.max-files + The maximum amount of log files that will be stored. If set to 0, background file logging is disabled + + [default: 5] -```bash -$ reth db drop --help + --log.file.filter + The filter to use for logs written to the log file + + [default: debug] -Usage: reth db drop [OPTIONS] + --log.journald + Write logs to journald -Options: - -f, --force - Bypasses the interactive confirmation and drops the database directly + --log.journald.filter + The filter to use for logs written to journald + + [default: error] + + --color + Sets whether or not the formatter emits ANSI terminal escape codes for colors and other text formatting + + [default: always] + + Possible values: + - always: Colors on + - auto: Colors on + - never: Colors off + +Display: + -v, --verbosity... + Set the minimum log level. + + -v Errors + -vv Warnings + -vvv Info + -vvvv Debug + -vvvvv Traces (warning: very verbose!) + + -q, --quiet + Silence all log output ``` -## `reth db get` +## `reth db diff` -Gets the content of a table for the given key +Create a diff between two database tables or two entire databases ```bash -$ reth db get --help +$ reth db diff --help -Usage: reth db get [OPTIONS]
+Usage: reth db diff [OPTIONS] --secondary-datadir --output -Arguments: -
- The table name +Options: + --datadir + The path to the data dir for all reth files and subdirectories. - NOTE: The dupsort tables are not supported now. + Defaults to the OS-specific data directory: + + - Linux: `$XDG_DATA_HOME/reth/` or `$HOME/.local/share/reth/` + - Windows: `{FOLDERID_RoamingAppData}/reth/` + - macOS: `$HOME/Library/Application Support/reth/` + + [default: default] - - The key to get content for -``` + --secondary-datadir + The path to the data dir for all reth files and subdirectories. -## `reth db list` + --chain + The chain this node is running. + Possible values are either a built-in chain or the path to a chain specification file. + + Built-in chains: + mainnet, sepolia, goerli, holesky, dev + + [default: mainnet] -Lists the contents of a table + --instance + Add a new instance of a node. + + Configures the ports of the node to avoid conflicts with the defaults. This is useful for running multiple nodes on the same machine. + + Max number of instances is 200. It is chosen in a way so that it's not possible to have port numbers that conflict with each other. + + Changes to the following port numbers: - DISCOVERY_PORT: default + `instance` - 1 - AUTH_PORT: default + `instance` * 100 - 100 - HTTP_RPC_PORT: default - `instance` + 1 - WS_RPC_PORT: default + `instance` * 2 - 2 + + [default: 1] -```bash -$ reth db list --help + -h, --help + Print help (see a summary with '-h') -Usage: reth db list [OPTIONS]
+Database: + --db.log-level + Database logging level. Levels higher than "notice" require a debug build -Arguments: -
- The table name + Possible values: + - fatal: Enables logging for critical conditions, i.e. assertion failures + - error: Enables logging for error conditions + - warn: Enables logging for warning conditions + - notice: Enables logging for normal but significant condition + - verbose: Enables logging for verbose informational + - debug: Enables logging for debug-level messages + - trace: Enables logging for trace debug-level messages + - extra: Enables logging for extra debug-level messages -Options: - -s, --skip - Skip first N entries + --table
+ The table name to diff. If not specified, all tables are diffed. + + --output + The output directory for the diff report. + +Logging: + --log.file.directory + The path to put log files in - [default: 0] + [default: /reth/logs] - -r, --reverse - Reverse the order of the entries. If enabled last table entries are read + --log.file.max-size + The maximum size (in MB) of one log file + + [default: 200] - -l, --len - How many items to take from the walker + --log.file.max-files + The maximum amount of log files that will be stored. If set to 0, background file logging is disabled [default: 5] - --search - Search parameter for both keys and values. Prefix it with `0x` to search for binary data, and text otherwise. + --log.file.filter + The filter to use for logs written to the log file - ATTENTION! For compressed tables (`Transactions` and `Receipts`), there might be missing results since the search uses the raw uncompressed value from the database. + [default: debug] - -c, --count - Returns the number of rows found + --log.journald + Write logs to journald - -j, --json - Dump as JSON instead of using TUI -``` + --log.journald.filter + The filter to use for logs written to journald + + [default: error] -## `reth db path` + --color + Sets whether or not the formatter emits ANSI terminal escape codes for colors and other text formatting + + [default: always] -Returns the full database path + Possible values: + - always: Colors on + - auto: Colors on + - never: Colors off -```bash -$ reth db path --help +Display: + -v, --verbosity... + Set the minimum log level. + + -v Errors + -vv Warnings + -vvv Info + -vvvv Debug + -vvvvv Traces (warning: very verbose!) -Usage: reth db path [OPTIONS] + -q, --quiet + Silence all log output ``` -## `reth db stats` +## `reth db drop` -Lists all the tables, their entry count and their size +Deletes all database entries ```bash -$ reth db stats --help +$ reth db drop --help -Usage: reth db stats [OPTIONS] -``` +Usage: reth db drop [OPTIONS] -## `reth db version` +Options: + --datadir + The path to the data dir for all reth files and subdirectories. + + Defaults to the OS-specific data directory: + + - Linux: `$XDG_DATA_HOME/reth/` or `$HOME/.local/share/reth/` + - Windows: `{FOLDERID_RoamingAppData}/reth/` + - macOS: `$HOME/Library/Application Support/reth/` + + [default: default] -Lists current and local database versions + -f, --force + Bypasses the interactive confirmation and drops the database directly -```bash -$ reth db version --help + --chain + The chain this node is running. + Possible values are either a built-in chain or the path to a chain specification file. + + Built-in chains: + mainnet, sepolia, goerli, holesky, dev + + [default: mainnet] -Usage: reth db version [OPTIONS] + --instance + Add a new instance of a node. + + Configures the ports of the node to avoid conflicts with the defaults. This is useful for running multiple nodes on the same machine. + + Max number of instances is 200. It is chosen in a way so that it's not possible to have port numbers that conflict with each other. + + Changes to the following port numbers: - DISCOVERY_PORT: default + `instance` - 1 - AUTH_PORT: default + `instance` * 100 - 100 - HTTP_RPC_PORT: default - `instance` + 1 - WS_RPC_PORT: default + `instance` * 2 - 2 + + [default: 1] + + -h, --help + Print help (see a summary with '-h') + +Logging: + --log.file.directory + The path to put log files in + + [default: /reth/logs] + + --log.file.max-size + The maximum size (in MB) of one log file + + [default: 200] + + --log.file.max-files + The maximum amount of log files that will be stored. If set to 0, background file logging is disabled + + [default: 5] + + --log.file.filter + The filter to use for logs written to the log file + + [default: debug] + + --log.journald + Write logs to journald + + --log.journald.filter + The filter to use for logs written to journald + + [default: error] + + --color + Sets whether or not the formatter emits ANSI terminal escape codes for colors and other text formatting + + [default: always] + + Possible values: + - always: Colors on + - auto: Colors on + - never: Colors off + +Display: + -v, --verbosity... + Set the minimum log level. + + -v Errors + -vv Warnings + -vvv Info + -vvvv Debug + -vvvvv Traces (warning: very verbose!) + + -q, --quiet + Silence all log output +``` + +## `reth db get` + +Gets the content of a table for the given key + +```bash +$ reth db get --help + +Usage: reth db get [OPTIONS]
+ +Arguments: +
+ The table name + + NOTE: The dupsort tables are not supported now. + + + The key to get content for + +Options: + --datadir + The path to the data dir for all reth files and subdirectories. + + Defaults to the OS-specific data directory: + + - Linux: `$XDG_DATA_HOME/reth/` or `$HOME/.local/share/reth/` + - Windows: `{FOLDERID_RoamingAppData}/reth/` + - macOS: `$HOME/Library/Application Support/reth/` + + [default: default] + + --raw + Output bytes instead of human-readable decoded value + + --chain + The chain this node is running. + Possible values are either a built-in chain or the path to a chain specification file. + + Built-in chains: + mainnet, sepolia, goerli, holesky, dev + + [default: mainnet] + + --instance + Add a new instance of a node. + + Configures the ports of the node to avoid conflicts with the defaults. This is useful for running multiple nodes on the same machine. + + Max number of instances is 200. It is chosen in a way so that it's not possible to have port numbers that conflict with each other. + + Changes to the following port numbers: - DISCOVERY_PORT: default + `instance` - 1 - AUTH_PORT: default + `instance` * 100 - 100 - HTTP_RPC_PORT: default - `instance` + 1 - WS_RPC_PORT: default + `instance` * 2 - 2 + + [default: 1] + + -h, --help + Print help (see a summary with '-h') + +Logging: + --log.file.directory + The path to put log files in + + [default: /reth/logs] + + --log.file.max-size + The maximum size (in MB) of one log file + + [default: 200] + + --log.file.max-files + The maximum amount of log files that will be stored. If set to 0, background file logging is disabled + + [default: 5] + + --log.file.filter + The filter to use for logs written to the log file + + [default: debug] + + --log.journald + Write logs to journald + + --log.journald.filter + The filter to use for logs written to journald + + [default: error] + + --color + Sets whether or not the formatter emits ANSI terminal escape codes for colors and other text formatting + + [default: always] + + Possible values: + - always: Colors on + - auto: Colors on + - never: Colors off + +Display: + -v, --verbosity... + Set the minimum log level. + + -v Errors + -vv Warnings + -vvv Info + -vvvv Debug + -vvvvv Traces (warning: very verbose!) + + -q, --quiet + Silence all log output +``` + +## `reth db list` + +Lists the contents of a table + +```bash +$ reth db list --help + +Usage: reth db list [OPTIONS]
+ +Arguments: +
+ The table name + +Options: + --datadir + The path to the data dir for all reth files and subdirectories. + + Defaults to the OS-specific data directory: + + - Linux: `$XDG_DATA_HOME/reth/` or `$HOME/.local/share/reth/` + - Windows: `{FOLDERID_RoamingAppData}/reth/` + - macOS: `$HOME/Library/Application Support/reth/` + + [default: default] + + -s, --skip + Skip first N entries + + [default: 0] + + --chain + The chain this node is running. + Possible values are either a built-in chain or the path to a chain specification file. + + Built-in chains: + mainnet, sepolia, goerli, holesky, dev + + [default: mainnet] + + -r, --reverse + Reverse the order of the entries. If enabled last table entries are read + + -l, --len + How many items to take from the walker + + [default: 5] + + --search + Search parameter for both keys and values. Prefix it with `0x` to search for binary data, and text otherwise. + + ATTENTION! For compressed tables (`Transactions` and `Receipts`), there might be missing results since the search uses the raw uncompressed value from the database. + + --min-row-size + Minimum size of row in bytes + + [default: 0] + + --min-key-size + Minimum size of key in bytes + + [default: 0] + + --min-value-size + Minimum size of value in bytes + + [default: 0] + + -c, --count + Returns the number of rows found + + -j, --json + Dump as JSON instead of using TUI + + --raw + Output bytes instead of human-readable decoded value + + --instance + Add a new instance of a node. + + Configures the ports of the node to avoid conflicts with the defaults. This is useful for running multiple nodes on the same machine. + + Max number of instances is 200. It is chosen in a way so that it's not possible to have port numbers that conflict with each other. + + Changes to the following port numbers: - DISCOVERY_PORT: default + `instance` - 1 - AUTH_PORT: default + `instance` * 100 - 100 - HTTP_RPC_PORT: default - `instance` + 1 - WS_RPC_PORT: default + `instance` * 2 - 2 + + [default: 1] + + -h, --help + Print help (see a summary with '-h') + +Logging: + --log.file.directory + The path to put log files in + + [default: /reth/logs] + + --log.file.max-size + The maximum size (in MB) of one log file + + [default: 200] + + --log.file.max-files + The maximum amount of log files that will be stored. If set to 0, background file logging is disabled + + [default: 5] + + --log.file.filter + The filter to use for logs written to the log file + + [default: debug] + + --log.journald + Write logs to journald + + --log.journald.filter + The filter to use for logs written to journald + + [default: error] + + --color + Sets whether or not the formatter emits ANSI terminal escape codes for colors and other text formatting + + [default: always] + + Possible values: + - always: Colors on + - auto: Colors on + - never: Colors off + +Display: + -v, --verbosity... + Set the minimum log level. + + -v Errors + -vv Warnings + -vvv Info + -vvvv Debug + -vvvvv Traces (warning: very verbose!) + + -q, --quiet + Silence all log output +``` + +## `reth db path` + +Returns the full database path + +```bash +$ reth db path --help + +Usage: reth db path [OPTIONS] + +Options: + --datadir + The path to the data dir for all reth files and subdirectories. + + Defaults to the OS-specific data directory: + + - Linux: `$XDG_DATA_HOME/reth/` or `$HOME/.local/share/reth/` + - Windows: `{FOLDERID_RoamingAppData}/reth/` + - macOS: `$HOME/Library/Application Support/reth/` + + [default: default] + + --chain + The chain this node is running. + Possible values are either a built-in chain or the path to a chain specification file. + + Built-in chains: + mainnet, sepolia, goerli, holesky, dev + + [default: mainnet] + + --instance + Add a new instance of a node. + + Configures the ports of the node to avoid conflicts with the defaults. This is useful for running multiple nodes on the same machine. + + Max number of instances is 200. It is chosen in a way so that it's not possible to have port numbers that conflict with each other. + + Changes to the following port numbers: - DISCOVERY_PORT: default + `instance` - 1 - AUTH_PORT: default + `instance` * 100 - 100 - HTTP_RPC_PORT: default - `instance` + 1 - WS_RPC_PORT: default + `instance` * 2 - 2 + + [default: 1] + + -h, --help + Print help (see a summary with '-h') + +Logging: + --log.file.directory + The path to put log files in + + [default: /reth/logs] + + --log.file.max-size + The maximum size (in MB) of one log file + + [default: 200] + + --log.file.max-files + The maximum amount of log files that will be stored. If set to 0, background file logging is disabled + + [default: 5] + + --log.file.filter + The filter to use for logs written to the log file + + [default: debug] + + --log.journald + Write logs to journald + + --log.journald.filter + The filter to use for logs written to journald + + [default: error] + + --color + Sets whether or not the formatter emits ANSI terminal escape codes for colors and other text formatting + + [default: always] + + Possible values: + - always: Colors on + - auto: Colors on + - never: Colors off + +Display: + -v, --verbosity... + Set the minimum log level. + + -v Errors + -vv Warnings + -vvv Info + -vvvv Debug + -vvvvv Traces (warning: very verbose!) + + -q, --quiet + Silence all log output +``` + +## `reth db stats` + +Lists all the tables, their entry count and their size + +```bash +$ reth db stats --help + +Usage: reth db stats [OPTIONS] + +Options: + --datadir + The path to the data dir for all reth files and subdirectories. + + Defaults to the OS-specific data directory: + + - Linux: `$XDG_DATA_HOME/reth/` or `$HOME/.local/share/reth/` + - Windows: `{FOLDERID_RoamingAppData}/reth/` + - macOS: `$HOME/Library/Application Support/reth/` + + [default: default] + + --chain + The chain this node is running. + Possible values are either a built-in chain or the path to a chain specification file. + + Built-in chains: + mainnet, sepolia, goerli, holesky, dev + + [default: mainnet] + + --instance + Add a new instance of a node. + + Configures the ports of the node to avoid conflicts with the defaults. This is useful for running multiple nodes on the same machine. + + Max number of instances is 200. It is chosen in a way so that it's not possible to have port numbers that conflict with each other. + + Changes to the following port numbers: - DISCOVERY_PORT: default + `instance` - 1 - AUTH_PORT: default + `instance` * 100 - 100 - HTTP_RPC_PORT: default - `instance` + 1 - WS_RPC_PORT: default + `instance` * 2 - 2 + + [default: 1] + + -h, --help + Print help (see a summary with '-h') + +Logging: + --log.file.directory + The path to put log files in + + [default: /reth/logs] + + --log.file.max-size + The maximum size (in MB) of one log file + + [default: 200] + + --log.file.max-files + The maximum amount of log files that will be stored. If set to 0, background file logging is disabled + + [default: 5] + + --log.file.filter + The filter to use for logs written to the log file + + [default: debug] + + --log.journald + Write logs to journald + + --log.journald.filter + The filter to use for logs written to journald + + [default: error] + + --color + Sets whether or not the formatter emits ANSI terminal escape codes for colors and other text formatting + + [default: always] + + Possible values: + - always: Colors on + - auto: Colors on + - never: Colors off + +Display: + -v, --verbosity... + Set the minimum log level. + + -v Errors + -vv Warnings + -vvv Info + -vvvv Debug + -vvvvv Traces (warning: very verbose!) + + -q, --quiet + Silence all log output +``` + +## `reth db version` + +Lists current and local database versions + +```bash +$ reth db version --help + +Usage: reth db version [OPTIONS] + +Options: + --datadir + The path to the data dir for all reth files and subdirectories. + + Defaults to the OS-specific data directory: + + - Linux: `$XDG_DATA_HOME/reth/` or `$HOME/.local/share/reth/` + - Windows: `{FOLDERID_RoamingAppData}/reth/` + - macOS: `$HOME/Library/Application Support/reth/` + + [default: default] + + --chain + The chain this node is running. + Possible values are either a built-in chain or the path to a chain specification file. + + Built-in chains: + mainnet, sepolia, goerli, holesky, dev + + [default: mainnet] + + --instance + Add a new instance of a node. + + Configures the ports of the node to avoid conflicts with the defaults. This is useful for running multiple nodes on the same machine. + + Max number of instances is 200. It is chosen in a way so that it's not possible to have port numbers that conflict with each other. + + Changes to the following port numbers: - DISCOVERY_PORT: default + `instance` - 1 - AUTH_PORT: default + `instance` * 100 - 100 - HTTP_RPC_PORT: default - `instance` + 1 - WS_RPC_PORT: default + `instance` * 2 - 2 + + [default: 1] + + -h, --help + Print help (see a summary with '-h') + +Logging: + --log.file.directory + The path to put log files in + + [default: /reth/logs] + + --log.file.max-size + The maximum size (in MB) of one log file + + [default: 200] + + --log.file.max-files + The maximum amount of log files that will be stored. If set to 0, background file logging is disabled + + [default: 5] + + --log.file.filter + The filter to use for logs written to the log file + + [default: debug] + + --log.journald + Write logs to journald + + --log.journald.filter + The filter to use for logs written to journald + + [default: error] + + --color + Sets whether or not the formatter emits ANSI terminal escape codes for colors and other text formatting + + [default: always] + + Possible values: + - always: Colors on + - auto: Colors on + - never: Colors off + +Display: + -v, --verbosity... + Set the minimum log level. + + -v Errors + -vv Warnings + -vvv Info + -vvvv Debug + -vvvvv Traces (warning: very verbose!) + + -q, --quiet + Silence all log output ``` diff --git a/book/cli/debug.md b/book/cli/debug.md index 8de345a662fb..f0498b800979 100644 --- a/book/cli/debug.md +++ b/book/cli/debug.md @@ -11,8 +11,95 @@ Commands: execution Debug the roundtrip execution of blocks as well as the generated data merkle Debug the clean & incremental state root calculations in-memory-merkle Debug in-memory state root calculation + build-block Debug block building help Print this message or the help of the given subcommand(s) +Options: + --chain + The chain this node is running. + Possible values are either a built-in chain or the path to a chain specification file. + + Built-in chains: + mainnet, sepolia, goerli, holesky, dev + + [default: mainnet] + + --instance + Add a new instance of a node. + + Configures the ports of the node to avoid conflicts with the defaults. This is useful for running multiple nodes on the same machine. + + Max number of instances is 200. It is chosen in a way so that it's not possible to have port numbers that conflict with each other. + + Changes to the following port numbers: - DISCOVERY_PORT: default + `instance` - 1 - AUTH_PORT: default + `instance` * 100 - 100 - HTTP_RPC_PORT: default - `instance` + 1 - WS_RPC_PORT: default + `instance` * 2 - 2 + + [default: 1] + + -h, --help + Print help (see a summary with '-h') + +Logging: + --log.file.directory + The path to put log files in + + [default: /reth/logs] + + --log.file.max-size + The maximum size (in MB) of one log file + + [default: 200] + + --log.file.max-files + The maximum amount of log files that will be stored. If set to 0, background file logging is disabled + + [default: 5] + + --log.file.filter + The filter to use for logs written to the log file + + [default: debug] + + --log.journald + Write logs to journald + + --log.journald.filter + The filter to use for logs written to journald + + [default: error] + + --color + Sets whether or not the formatter emits ANSI terminal escape codes for colors and other text formatting + + [default: always] + + Possible values: + - always: Colors on + - auto: Colors on + - never: Colors off + +Display: + -v, --verbosity... + Set the minimum log level. + + -v Errors + -vv Warnings + -vvv Info + -vvvv Debug + -vvvvv Traces (warning: very verbose!) + + -q, --quiet + Silence all log output +``` + +## `reth debug execution` + +Debug the roundtrip execution of blocks as well as the generated data + +```bash +$ reth debug execution --help + +Usage: reth debug execution [OPTIONS] --to + Options: --datadir The path to the data dir for all reth files and subdirectories. @@ -27,14 +114,10 @@ Options: --chain The chain this node is running. - Possible values are either a built-in chain or the path to a chain specification file. Built-in chains: - - mainnet - - goerli - - sepolia - - holesky + mainnet, sepolia, goerli, holesky, dev [default: mainnet] @@ -52,6 +135,99 @@ Options: -h, --help Print help (see a summary with '-h') +Networking: + -d, --disable-discovery + Disable the discovery service + + --disable-dns-discovery + Disable the DNS discovery + + --disable-discv4-discovery + Disable Discv4 discovery + + --discovery.addr + The UDP address to use for P2P discovery/networking + + [default: 0.0.0.0] + + --discovery.port + The UDP port to use for P2P discovery/networking + + [default: 30303] + + --trusted-peers + Comma separated enode URLs of trusted peers for P2P connections. + + --trusted-peers enode://abcd@192.168.0.1:30303 + + --trusted-only + Connect only to trusted peers + + --bootnodes + Comma separated enode URLs for P2P discovery bootstrap. + + Will fall back to a network-specific default if not specified. + + --peers-file + The path to the known peers file. Connected peers are dumped to this file on nodes + shutdown, and read on startup. Cannot be used with `--no-persist-peers`. + + --identity + Custom node identity + + [default: reth/v0.1.0-alpha.13-10a83e594/aarch64-apple-darwin] + + --p2p-secret-key + Secret key to use for this node. + + This will also deterministically set the peer ID. If not specified, it will be set in the data dir for the chain being used. + + --no-persist-peers + Do not persist peers. + + --nat + NAT resolution method (any|none|upnp|publicip|extip:) + + [default: any] + + --addr + Network listening address + + [default: 0.0.0.0] + + --port + Network listening port + + [default: 30303] + + --max-outbound-peers + Maximum number of outbound requests. default: 100 + + --max-inbound-peers + Maximum number of inbound requests. default: 30 + +Database: + --db.log-level + Database logging level. Levels higher than "notice" require a debug build + + Possible values: + - fatal: Enables logging for critical conditions, i.e. assertion failures + - error: Enables logging for error conditions + - warn: Enables logging for warning conditions + - notice: Enables logging for normal but significant condition + - verbose: Enables logging for verbose informational + - debug: Enables logging for debug-level messages + - trace: Enables logging for trace debug-level messages + - extra: Enables logging for extra debug-level messages + + --to + The maximum block height + + --interval + The block interval for sync and unwind. Defaults to `1000` + + [default: 1000] + Logging: --log.file.directory The path to put log files in @@ -105,14 +281,194 @@ Display: Silence all log output ``` -## `reth debug execution` +## `reth debug in-memory-merkle` -Debug the roundtrip execution of blocks as well as the generated data +Debug in-memory state root calculation ```bash -$ reth debug execution --help +$ reth debug in-memory-merkle --help -Usage: reth debug execution [OPTIONS] --to +Usage: reth debug in-memory-merkle [OPTIONS] + +Options: + --datadir + The path to the data dir for all reth files and subdirectories. + + Defaults to the OS-specific data directory: + + - Linux: `$XDG_DATA_HOME/reth/` or `$HOME/.local/share/reth/` + - Windows: `{FOLDERID_RoamingAppData}/reth/` + - macOS: `$HOME/Library/Application Support/reth/` + + [default: default] + + --chain + The chain this node is running. + Possible values are either a built-in chain or the path to a chain specification file. + + Built-in chains: + mainnet, sepolia, goerli, holesky, dev + + [default: mainnet] + + --instance + Add a new instance of a node. + + Configures the ports of the node to avoid conflicts with the defaults. This is useful for running multiple nodes on the same machine. + + Max number of instances is 200. It is chosen in a way so that it's not possible to have port numbers that conflict with each other. + + Changes to the following port numbers: - DISCOVERY_PORT: default + `instance` - 1 - AUTH_PORT: default + `instance` * 100 - 100 - HTTP_RPC_PORT: default - `instance` + 1 - WS_RPC_PORT: default + `instance` * 2 - 2 + + [default: 1] + + -h, --help + Print help (see a summary with '-h') + +Database: + --db.log-level + Database logging level. Levels higher than "notice" require a debug build + + Possible values: + - fatal: Enables logging for critical conditions, i.e. assertion failures + - error: Enables logging for error conditions + - warn: Enables logging for warning conditions + - notice: Enables logging for normal but significant condition + - verbose: Enables logging for verbose informational + - debug: Enables logging for debug-level messages + - trace: Enables logging for trace debug-level messages + - extra: Enables logging for extra debug-level messages + +Networking: + -d, --disable-discovery + Disable the discovery service + + --disable-dns-discovery + Disable the DNS discovery + + --disable-discv4-discovery + Disable Discv4 discovery + + --discovery.addr + The UDP address to use for P2P discovery/networking + + [default: 0.0.0.0] + + --discovery.port + The UDP port to use for P2P discovery/networking + + [default: 30303] + + --trusted-peers + Comma separated enode URLs of trusted peers for P2P connections. + + --trusted-peers enode://abcd@192.168.0.1:30303 + + --trusted-only + Connect only to trusted peers + + --bootnodes + Comma separated enode URLs for P2P discovery bootstrap. + + Will fall back to a network-specific default if not specified. + + --peers-file + The path to the known peers file. Connected peers are dumped to this file on nodes + shutdown, and read on startup. Cannot be used with `--no-persist-peers`. + + --identity + Custom node identity + + [default: reth/v0.1.0-alpha.13-10a83e594/aarch64-apple-darwin] + + --p2p-secret-key + Secret key to use for this node. + + This will also deterministically set the peer ID. If not specified, it will be set in the data dir for the chain being used. + + --no-persist-peers + Do not persist peers. + + --nat + NAT resolution method (any|none|upnp|publicip|extip:) + + [default: any] + + --addr + Network listening address + + [default: 0.0.0.0] + + --port + Network listening port + + [default: 30303] + + --max-outbound-peers + Maximum number of outbound requests. default: 100 + + --max-inbound-peers + Maximum number of inbound requests. default: 30 + + --retries + The number of retries per request + + [default: 5] + + --skip-node-depth + The depth after which we should start comparing branch nodes + +Logging: + --log.file.directory + The path to put log files in + + [default: /reth/logs] + + --log.file.max-size + The maximum size (in MB) of one log file + + [default: 200] + + --log.file.max-files + The maximum amount of log files that will be stored. If set to 0, background file logging is disabled + + [default: 5] + + --log.file.filter + The filter to use for logs written to the log file + + [default: debug] + + --log.journald + Write logs to journald + + --log.journald.filter + The filter to use for logs written to journald + + [default: error] + + --color + Sets whether or not the formatter emits ANSI terminal escape codes for colors and other text formatting + + [default: always] + + Possible values: + - always: Colors on + - auto: Colors on + - never: Colors off + +Display: + -v, --verbosity... + Set the minimum log level. + + -v Errors + -vv Warnings + -vvv Info + -vvvv Debug + -vvvvv Traces (warning: very verbose!) + + -q, --quiet + Silence all log output ``` ## `reth debug merkle` @@ -123,14 +479,187 @@ Debug the clean & incremental state root calculations $ reth debug merkle --help Usage: reth debug merkle [OPTIONS] --to -``` -## `reth debug in-memory-merkle` +Options: + --datadir + The path to the data dir for all reth files and subdirectories. + + Defaults to the OS-specific data directory: + + - Linux: `$XDG_DATA_HOME/reth/` or `$HOME/.local/share/reth/` + - Windows: `{FOLDERID_RoamingAppData}/reth/` + - macOS: `$HOME/Library/Application Support/reth/` + + [default: default] -Debug in-memory state root calculation + --chain + The chain this node is running. + Possible values are either a built-in chain or the path to a chain specification file. + + Built-in chains: + mainnet, sepolia, goerli, holesky, dev + + [default: mainnet] -```bash -$ reth debug in-memory-merkle --help + --instance + Add a new instance of a node. + + Configures the ports of the node to avoid conflicts with the defaults. This is useful for running multiple nodes on the same machine. + + Max number of instances is 200. It is chosen in a way so that it's not possible to have port numbers that conflict with each other. + + Changes to the following port numbers: - DISCOVERY_PORT: default + `instance` - 1 - AUTH_PORT: default + `instance` * 100 - 100 - HTTP_RPC_PORT: default - `instance` + 1 - WS_RPC_PORT: default + `instance` * 2 - 2 + + [default: 1] -Usage: reth debug in-memory-merkle [OPTIONS] -``` \ No newline at end of file + -h, --help + Print help (see a summary with '-h') + +Database: + --db.log-level + Database logging level. Levels higher than "notice" require a debug build + + Possible values: + - fatal: Enables logging for critical conditions, i.e. assertion failures + - error: Enables logging for error conditions + - warn: Enables logging for warning conditions + - notice: Enables logging for normal but significant condition + - verbose: Enables logging for verbose informational + - debug: Enables logging for debug-level messages + - trace: Enables logging for trace debug-level messages + - extra: Enables logging for extra debug-level messages + +Networking: + -d, --disable-discovery + Disable the discovery service + + --disable-dns-discovery + Disable the DNS discovery + + --disable-discv4-discovery + Disable Discv4 discovery + + --discovery.addr + The UDP address to use for P2P discovery/networking + + [default: 0.0.0.0] + + --discovery.port + The UDP port to use for P2P discovery/networking + + [default: 30303] + + --trusted-peers + Comma separated enode URLs of trusted peers for P2P connections. + + --trusted-peers enode://abcd@192.168.0.1:30303 + + --trusted-only + Connect only to trusted peers + + --bootnodes + Comma separated enode URLs for P2P discovery bootstrap. + + Will fall back to a network-specific default if not specified. + + --peers-file + The path to the known peers file. Connected peers are dumped to this file on nodes + shutdown, and read on startup. Cannot be used with `--no-persist-peers`. + + --identity + Custom node identity + + [default: reth/v0.1.0-alpha.13-10a83e594/aarch64-apple-darwin] + + --p2p-secret-key + Secret key to use for this node. + + This will also deterministically set the peer ID. If not specified, it will be set in the data dir for the chain being used. + + --no-persist-peers + Do not persist peers. + + --nat + NAT resolution method (any|none|upnp|publicip|extip:) + + [default: any] + + --addr + Network listening address + + [default: 0.0.0.0] + + --port + Network listening port + + [default: 30303] + + --max-outbound-peers + Maximum number of outbound requests. default: 100 + + --max-inbound-peers + Maximum number of inbound requests. default: 30 + + --retries + The number of retries per request + + [default: 5] + + --to + The height to finish at + + --skip-node-depth + The depth after which we should start comparing branch nodes + +Logging: + --log.file.directory + The path to put log files in + + [default: /reth/logs] + + --log.file.max-size + The maximum size (in MB) of one log file + + [default: 200] + + --log.file.max-files + The maximum amount of log files that will be stored. If set to 0, background file logging is disabled + + [default: 5] + + --log.file.filter + The filter to use for logs written to the log file + + [default: debug] + + --log.journald + Write logs to journald + + --log.journald.filter + The filter to use for logs written to journald + + [default: error] + + --color + Sets whether or not the formatter emits ANSI terminal escape codes for colors and other text formatting + + [default: always] + + Possible values: + - always: Colors on + - auto: Colors on + - never: Colors off + +Display: + -v, --verbosity... + Set the minimum log level. + + -v Errors + -vv Warnings + -vvv Info + -vvvv Debug + -vvvvv Traces (warning: very verbose!) + + -q, --quiet + Silence all log output +``` diff --git a/book/cli/import.md b/book/cli/import.md index 14f6fbcf9b38..08aadbfc7724 100644 --- a/book/cli/import.md +++ b/book/cli/import.md @@ -24,14 +24,10 @@ Options: --chain The chain this node is running. - Possible values are either a built-in chain or the path to a chain specification file. Built-in chains: - - mainnet - - goerli - - sepolia - - holesky + mainnet, sepolia, goerli, holesky, dev [default: mainnet] diff --git a/book/cli/init.md b/book/cli/init.md index c1d974280bcc..e824d8f5ec80 100644 --- a/book/cli/init.md +++ b/book/cli/init.md @@ -21,14 +21,10 @@ Options: --chain The chain this node is running. - Possible values are either a built-in chain or the path to a chain specification file. Built-in chains: - - mainnet - - goerli - - sepolia - - holesky + mainnet, sepolia, goerli, holesky, dev [default: mainnet] diff --git a/book/cli/node.md b/book/cli/node.md index d7b38a8134f7..7cc4dd027532 100644 --- a/book/cli/node.md +++ b/book/cli/node.md @@ -24,15 +24,10 @@ Options: --chain The chain this node is running. - Possible values are either a built-in chain or the path to a chain specification file. Built-in chains: - - mainnet - - goerli - - sepolia - - holesky - - dev + mainnet, sepolia, goerli, holesky, dev [default: mainnet] @@ -69,17 +64,26 @@ Networking: --disable-discv4-discovery Disable Discv4 discovery + --discovery.addr + The UDP address to use for P2P discovery/networking + + [default: 0.0.0.0] + --discovery.port - The UDP port to use for P2P discovery/networking. default: 30303 + The UDP port to use for P2P discovery/networking + + [default: 30303] --trusted-peers - Target trusted peer enodes --trusted-peers enode://abcd@192.168.0.1:30303 + Comma separated enode URLs of trusted peers for P2P connections. + + --trusted-peers enode://abcd@192.168.0.1:30303 --trusted-only Connect only to trusted peers --bootnodes - Bootnodes to connect to initially. + Comma separated enode URLs for P2P discovery bootstrap. Will fall back to a network-specific default if not specified. @@ -90,7 +94,7 @@ Networking: --identity Custom node identity - [default: reth/VERSION/PLATFORM] + [default: reth/v0.1.0-alpha.13-10a83e594/aarch64-apple-darwin] --p2p-secret-key Secret key to use for this node. @@ -105,8 +109,15 @@ Networking: [default: any] + --addr + Network listening address + + [default: 0.0.0.0] + --port - Network listening port. default: 30303 + Network listening port + + [default: 30303] --max-outbound-peers Maximum number of outbound requests. default: 100 @@ -131,7 +142,7 @@ RPC: --http.api Rpc Modules to be configured for the HTTP server - [possible values: admin, debug, eth, net, trace, txpool, web3, rpc, reth, ots] + [possible values: admin, debug, eth, net, trace, txpool, web3, rpc, reth, ots, eth-call-bundle] --http.corsdomain Http Corsdomain to allow request from @@ -155,7 +166,7 @@ RPC: --ws.api Rpc Modules to be configured for the WS server - [possible values: admin, debug, eth, net, trace, txpool, web3, rpc, reth, ots] + [possible values: admin, debug, eth, net, trace, txpool, web3, rpc, reth, ots, eth-call-bundle] --ipcdisable Disable the IPC-RPC server @@ -176,7 +187,16 @@ RPC: [default: 8551] --authrpc.jwtsecret - Path to a JWT secret to use for authenticated RPC endpoints + Path to a JWT secret to use for the authenticated engine-API RPC server. + + This will enforce JWT authentication for all requests coming from the consensus layer. + + If no path is provided, a secret will be generated and stored in the datadir under `//jwt.hex`. For mainnet this would be `~/.reth/mainnet/jwt.hex` by default. + + --rpc.jwtsecret + Hex encoded JWT secret to authenticate the regular RPC server(s), see `--http.api` and `--ws.api`. + + This is __not__ used for the authenticated engine-API RPC server, see `--authrpc.jwtsecret`. --rpc-max-request-size Set the maximum RPC request payload size for both HTTP and WS in megabytes @@ -186,7 +206,7 @@ RPC: --rpc-max-response-size Set the maximum RPC response payload size for both HTTP and WS in megabytes - [default: 115] + [default: 150] [aliases: --rpc.returndata.limit] --rpc-max-subscriptions-per-connection @@ -204,8 +224,13 @@ RPC: [default: 25] + --rpc-max-blocks-per-filter + Maximum number of blocks that could be scanned per filter request. (0 = entire chain) + + [default: 100000] + --rpc-max-logs-per-response - Maximum number of logs that can be returned in a single response + Maximum number of logs that can be returned in a single response. (0 = no limit) [default: 20000] @@ -214,6 +239,27 @@ RPC: [default: 50000000] +RPC State Cache: + --rpc-cache.max-blocks + Max number of blocks in cache + + [default: 5000] + + --rpc-cache.max-receipts + Max number receipts in cache + + [default: 2000] + + --rpc-cache.max-envs + Max number of bytes for cached env data + + [default: 1000] + + --rpc-cache.max-concurrent-db-requests + Max number of concurrent database requests + + [default: 512] + Gas Price Oracle: --gpo.blocks Number of recent blocks to check for gas price @@ -235,21 +281,6 @@ Gas Price Oracle: [default: 60] - --block-cache-len - Maximum number of block cache entries - - [default: 5000] - - --receipt-cache-len - Maximum number of receipt cache entries - - [default: 2000] - - --env-cache-len - Maximum number of env cache entries - - [default: 1000] - TxPool: --txpool.pending_max_count Max number of transaction in the pending sub-pool @@ -296,11 +327,14 @@ TxPool: [default: 100] + --txpool.nolocals + Flag to disable local transaction exemptions + Builder: --builder.extradata Block extra data set by the payload builder - [default: reth/VERSION/OS] + [default: reth/v0.1.0-alpha.13/macos] --builder.gaslimit Target gas ceiling for built blocks @@ -386,7 +420,7 @@ Dev testnet: Pruning: --full - Run full node. Only the most recent 10064 block states are stored. This flag takes priority over pruning configuration in reth.toml + Run full node. Only the most recent [`MINIMUM_PRUNING_DISTANCE`] block states are stored. This flag takes priority over pruning configuration in reth.toml Logging: --log.file.directory diff --git a/book/cli/p2p.md b/book/cli/p2p.md index 4e616976d28d..ba94a774ae2c 100644 --- a/book/cli/p2p.md +++ b/book/cli/p2p.md @@ -18,14 +18,10 @@ Options: --chain The chain this node is running. - Possible values are either a built-in chain or the path to a chain specification file. Built-in chains: - - mainnet - - goerli - - sepolia - - holesky + mainnet, sepolia, goerli, holesky, dev [default: mainnet] @@ -54,8 +50,15 @@ Options: --disable-discv4-discovery Disable Discv4 discovery + --discovery.addr + The UDP address to use for P2P discovery/networking + + [default: 0.0.0.0] + --discovery.port - The UDP port to use for P2P discovery/networking. default: 30303 + The UDP port to use for P2P discovery/networking + + [default: 30303] --trusted-peer Target trusted peer @@ -63,11 +66,6 @@ Options: --trusted-only Connect only to trusted peers - --retries - The number of retries per request - - [default: 5] - --instance Add a new instance of a node. @@ -79,6 +77,11 @@ Options: [default: 1] + --retries + The number of retries per request + + [default: 5] + --nat [default: any] @@ -164,6 +167,73 @@ Usage: reth p2p body [OPTIONS] Arguments: The block number or hash + +Options: + --instance + Add a new instance of a node. + + Configures the ports of the node to avoid conflicts with the defaults. This is useful for running multiple nodes on the same machine. + + Max number of instances is 200. It is chosen in a way so that it's not possible to have port numbers that conflict with each other. + + Changes to the following port numbers: - DISCOVERY_PORT: default + `instance` - 1 - AUTH_PORT: default + `instance` * 100 - 100 - HTTP_RPC_PORT: default - `instance` + 1 - WS_RPC_PORT: default + `instance` * 2 - 2 + + [default: 1] + + -h, --help + Print help (see a summary with '-h') + +Logging: + --log.file.directory + The path to put log files in + + [default: /reth/logs] + + --log.file.max-size + The maximum size (in MB) of one log file + + [default: 200] + + --log.file.max-files + The maximum amount of log files that will be stored. If set to 0, background file logging is disabled + + [default: 5] + + --log.file.filter + The filter to use for logs written to the log file + + [default: debug] + + --log.journald + Write logs to journald + + --log.journald.filter + The filter to use for logs written to journald + + [default: error] + + --color + Sets whether or not the formatter emits ANSI terminal escape codes for colors and other text formatting + + [default: always] + + Possible values: + - always: Colors on + - auto: Colors on + - never: Colors off + +Display: + -v, --verbosity... + Set the minimum log level. + + -v Errors + -vv Warnings + -vvv Info + -vvvv Debug + -vvvvv Traces (warning: very verbose!) + + -q, --quiet + Silence all log output ``` ## `reth p2p header` @@ -178,4 +248,71 @@ Usage: reth p2p header [OPTIONS] Arguments: The header number or hash + +Options: + --instance + Add a new instance of a node. + + Configures the ports of the node to avoid conflicts with the defaults. This is useful for running multiple nodes on the same machine. + + Max number of instances is 200. It is chosen in a way so that it's not possible to have port numbers that conflict with each other. + + Changes to the following port numbers: - DISCOVERY_PORT: default + `instance` - 1 - AUTH_PORT: default + `instance` * 100 - 100 - HTTP_RPC_PORT: default - `instance` + 1 - WS_RPC_PORT: default + `instance` * 2 - 2 + + [default: 1] + + -h, --help + Print help (see a summary with '-h') + +Logging: + --log.file.directory + The path to put log files in + + [default: /reth/logs] + + --log.file.max-size + The maximum size (in MB) of one log file + + [default: 200] + + --log.file.max-files + The maximum amount of log files that will be stored. If set to 0, background file logging is disabled + + [default: 5] + + --log.file.filter + The filter to use for logs written to the log file + + [default: debug] + + --log.journald + Write logs to journald + + --log.journald.filter + The filter to use for logs written to journald + + [default: error] + + --color + Sets whether or not the formatter emits ANSI terminal escape codes for colors and other text formatting + + [default: always] + + Possible values: + - always: Colors on + - auto: Colors on + - never: Colors off + +Display: + -v, --verbosity... + Set the minimum log level. + + -v Errors + -vv Warnings + -vvv Info + -vvvv Debug + -vvvvv Traces (warning: very verbose!) + + -q, --quiet + Silence all log output ``` diff --git a/book/cli/recover.md b/book/cli/recover.md index a18f83d664dc..3f62c15de31f 100644 --- a/book/cli/recover.md +++ b/book/cli/recover.md @@ -14,14 +14,10 @@ Commands: Options: --chain The chain this node is running. - Possible values are either a built-in chain or the path to a chain specification file. Built-in chains: - - mainnet - - goerli - - sepolia - - holesky + mainnet, sepolia, goerli, holesky, dev [default: mainnet] @@ -100,4 +96,91 @@ Recover the node by deleting dangling storage tries $ reth recover storage-tries --help Usage: reth recover storage-tries [OPTIONS] + +Options: + --datadir + The path to the data dir for all reth files and subdirectories. + + Defaults to the OS-specific data directory: + + - Linux: `$XDG_DATA_HOME/reth/` or `$HOME/.local/share/reth/` + - Windows: `{FOLDERID_RoamingAppData}/reth/` + - macOS: `$HOME/Library/Application Support/reth/` + + [default: default] + + --chain + The chain this node is running. + Possible values are either a built-in chain or the path to a chain specification file. + + Built-in chains: + mainnet, sepolia, goerli, holesky, dev + + [default: mainnet] + + --instance + Add a new instance of a node. + + Configures the ports of the node to avoid conflicts with the defaults. This is useful for running multiple nodes on the same machine. + + Max number of instances is 200. It is chosen in a way so that it's not possible to have port numbers that conflict with each other. + + Changes to the following port numbers: - DISCOVERY_PORT: default + `instance` - 1 - AUTH_PORT: default + `instance` * 100 - 100 - HTTP_RPC_PORT: default - `instance` + 1 - WS_RPC_PORT: default + `instance` * 2 - 2 + + [default: 1] + + -h, --help + Print help (see a summary with '-h') + +Logging: + --log.file.directory + The path to put log files in + + [default: /reth/logs] + + --log.file.max-size + The maximum size (in MB) of one log file + + [default: 200] + + --log.file.max-files + The maximum amount of log files that will be stored. If set to 0, background file logging is disabled + + [default: 5] + + --log.file.filter + The filter to use for logs written to the log file + + [default: debug] + + --log.journald + Write logs to journald + + --log.journald.filter + The filter to use for logs written to journald + + [default: error] + + --color + Sets whether or not the formatter emits ANSI terminal escape codes for colors and other text formatting + + [default: always] + + Possible values: + - always: Colors on + - auto: Colors on + - never: Colors off + +Display: + -v, --verbosity... + Set the minimum log level. + + -v Errors + -vv Warnings + -vvv Info + -vvvv Debug + -vvvvv Traces (warning: very verbose!) + + -q, --quiet + Silence all log output ``` diff --git a/book/cli/stage.md b/book/cli/stage.md index 788753ef3f80..057267624a32 100644 --- a/book/cli/stage.md +++ b/book/cli/stage.md @@ -17,14 +17,10 @@ Commands: Options: --chain The chain this node is running. - Possible values are either a built-in chain or the path to a chain specification file. Built-in chains: - - mainnet - - goerli - - sepolia - - holesky + mainnet, sepolia, goerli, holesky, dev [default: mainnet] @@ -103,6 +99,110 @@ Drop a stage's tables from the database $ reth stage drop --help Usage: reth stage drop [OPTIONS] + +Options: + --datadir + The path to the data dir for all reth files and subdirectories. + + Defaults to the OS-specific data directory: + + - Linux: `$XDG_DATA_HOME/reth/` or `$HOME/.local/share/reth/` + - Windows: `{FOLDERID_RoamingAppData}/reth/` + - macOS: `$HOME/Library/Application Support/reth/` + + [default: default] + + --chain + The chain this node is running. + Possible values are either a built-in chain or the path to a chain specification file. + + Built-in chains: + mainnet, sepolia, goerli, holesky, dev + + [default: mainnet] + + --instance + Add a new instance of a node. + + Configures the ports of the node to avoid conflicts with the defaults. This is useful for running multiple nodes on the same machine. + + Max number of instances is 200. It is chosen in a way so that it's not possible to have port numbers that conflict with each other. + + Changes to the following port numbers: - DISCOVERY_PORT: default + `instance` - 1 - AUTH_PORT: default + `instance` * 100 - 100 - HTTP_RPC_PORT: default - `instance` + 1 - WS_RPC_PORT: default + `instance` * 2 - 2 + + [default: 1] + + -h, --help + Print help (see a summary with '-h') + +Database: + --db.log-level + Database logging level. Levels higher than "notice" require a debug build + + Possible values: + - fatal: Enables logging for critical conditions, i.e. assertion failures + - error: Enables logging for error conditions + - warn: Enables logging for warning conditions + - notice: Enables logging for normal but significant condition + - verbose: Enables logging for verbose informational + - debug: Enables logging for debug-level messages + - trace: Enables logging for trace debug-level messages + - extra: Enables logging for extra debug-level messages + + + [possible values: headers, bodies, senders, execution, account-hashing, storage-hashing, hashing, merkle, tx-lookup, account-history, storage-history, total-difficulty] + +Logging: + --log.file.directory + The path to put log files in + + [default: /reth/logs] + + --log.file.max-size + The maximum size (in MB) of one log file + + [default: 200] + + --log.file.max-files + The maximum amount of log files that will be stored. If set to 0, background file logging is disabled + + [default: 5] + + --log.file.filter + The filter to use for logs written to the log file + + [default: debug] + + --log.journald + Write logs to journald + + --log.journald.filter + The filter to use for logs written to journald + + [default: error] + + --color + Sets whether or not the formatter emits ANSI terminal escape codes for colors and other text formatting + + [default: always] + + Possible values: + - always: Colors on + - auto: Colors on + - never: Colors off + +Display: + -v, --verbosity... + Set the minimum log level. + + -v Errors + -vv Warnings + -vvv Info + -vvvv Debug + -vvvvv Traces (warning: very verbose!) + + -q, --quiet + Silence all log output ``` ## `reth stage dump` @@ -120,6 +220,107 @@ Commands: account-hashing AccountHashing stage merkle Merkle stage help Print this message or the help of the given subcommand(s) + +Options: + --datadir + The path to the data dir for all reth files and subdirectories. + + Defaults to the OS-specific data directory: + + - Linux: `$XDG_DATA_HOME/reth/` or `$HOME/.local/share/reth/` + - Windows: `{FOLDERID_RoamingAppData}/reth/` + - macOS: `$HOME/Library/Application Support/reth/` + + [default: default] + + --chain + The chain this node is running. + Possible values are either a built-in chain or the path to a chain specification file. + + Built-in chains: + mainnet, sepolia, goerli, holesky, dev + + [default: mainnet] + + --instance + Add a new instance of a node. + + Configures the ports of the node to avoid conflicts with the defaults. This is useful for running multiple nodes on the same machine. + + Max number of instances is 200. It is chosen in a way so that it's not possible to have port numbers that conflict with each other. + + Changes to the following port numbers: - DISCOVERY_PORT: default + `instance` - 1 - AUTH_PORT: default + `instance` * 100 - 100 - HTTP_RPC_PORT: default - `instance` + 1 - WS_RPC_PORT: default + `instance` * 2 - 2 + + [default: 1] + + -h, --help + Print help (see a summary with '-h') + +Database: + --db.log-level + Database logging level. Levels higher than "notice" require a debug build + + Possible values: + - fatal: Enables logging for critical conditions, i.e. assertion failures + - error: Enables logging for error conditions + - warn: Enables logging for warning conditions + - notice: Enables logging for normal but significant condition + - verbose: Enables logging for verbose informational + - debug: Enables logging for debug-level messages + - trace: Enables logging for trace debug-level messages + - extra: Enables logging for extra debug-level messages + +Logging: + --log.file.directory + The path to put log files in + + [default: /reth/logs] + + --log.file.max-size + The maximum size (in MB) of one log file + + [default: 200] + + --log.file.max-files + The maximum amount of log files that will be stored. If set to 0, background file logging is disabled + + [default: 5] + + --log.file.filter + The filter to use for logs written to the log file + + [default: debug] + + --log.journald + Write logs to journald + + --log.journald.filter + The filter to use for logs written to journald + + [default: error] + + --color + Sets whether or not the formatter emits ANSI terminal escape codes for colors and other text formatting + + [default: always] + + Possible values: + - always: Colors on + - auto: Colors on + - never: Colors off + +Display: + -v, --verbosity... + Set the minimum log level. + + -v Errors + -vv Warnings + -vvv Info + -vvvv Debug + -vvvvv Traces (warning: very verbose!) + + -q, --quiet + Silence all log output ``` ### `reth stage dump execution` @@ -143,39 +344,82 @@ Options: -d, --dry-run If passed, it will dry-run a stage execution from the newly created database right after dumping -``` -### `reth stage dump storage-hashing` + --instance + Add a new instance of a node. + + Configures the ports of the node to avoid conflicts with the defaults. This is useful for running multiple nodes on the same machine. + + Max number of instances is 200. It is chosen in a way so that it's not possible to have port numbers that conflict with each other. + + Changes to the following port numbers: - DISCOVERY_PORT: default + `instance` - 1 - AUTH_PORT: default + `instance` * 100 - 100 - HTTP_RPC_PORT: default - `instance` + 1 - WS_RPC_PORT: default + `instance` * 2 - 2 + + [default: 1] -StorageHashing stage + -h, --help + Print help (see a summary with '-h') -```bash -$ reth stage dump storage-hashing --help +Logging: + --log.file.directory + The path to put log files in + + [default: /reth/logs] -Usage: reth stage dump storage-hashing [OPTIONS] --output-db --from --to + --log.file.max-size + The maximum size (in MB) of one log file + + [default: 200] -Options: - --output-db - The path to the new database folder. + --log.file.max-files + The maximum amount of log files that will be stored. If set to 0, background file logging is disabled + + [default: 5] - -f, --from - From which block + --log.file.filter + The filter to use for logs written to the log file + + [default: debug] - -t, --to - To which block + --log.journald + Write logs to journald - -d, --dry-run - If passed, it will dry-run a stage execution from the newly created database right after dumping + --log.journald.filter + The filter to use for logs written to journald + + [default: error] + + --color + Sets whether or not the formatter emits ANSI terminal escape codes for colors and other text formatting + + [default: always] + + Possible values: + - always: Colors on + - auto: Colors on + - never: Colors off + +Display: + -v, --verbosity... + Set the minimum log level. + + -v Errors + -vv Warnings + -vvv Info + -vvvv Debug + -vvvvv Traces (warning: very verbose!) + + -q, --quiet + Silence all log output ``` -### `reth stage dump account-hashing` +### `reth stage dump storage-hashing` -AccountHashing stage +StorageHashing stage ```bash -$ reth stage dump account-hashing --help +$ reth stage dump storage-hashing --help -Usage: reth stage dump account-hashing [OPTIONS] --output-db --from --to +Usage: reth stage dump storage-hashing [OPTIONS] --output-db --from --to Options: --output-db @@ -189,14 +433,169 @@ Options: -d, --dry-run If passed, it will dry-run a stage execution from the newly created database right after dumping -``` - -### `reth stage dump merkle` -Merkle stage - -```bash -$ reth stage dump merkle --help + --instance + Add a new instance of a node. + + Configures the ports of the node to avoid conflicts with the defaults. This is useful for running multiple nodes on the same machine. + + Max number of instances is 200. It is chosen in a way so that it's not possible to have port numbers that conflict with each other. + + Changes to the following port numbers: - DISCOVERY_PORT: default + `instance` - 1 - AUTH_PORT: default + `instance` * 100 - 100 - HTTP_RPC_PORT: default - `instance` + 1 - WS_RPC_PORT: default + `instance` * 2 - 2 + + [default: 1] + + -h, --help + Print help (see a summary with '-h') + +Logging: + --log.file.directory + The path to put log files in + + [default: /reth/logs] + + --log.file.max-size + The maximum size (in MB) of one log file + + [default: 200] + + --log.file.max-files + The maximum amount of log files that will be stored. If set to 0, background file logging is disabled + + [default: 5] + + --log.file.filter + The filter to use for logs written to the log file + + [default: debug] + + --log.journald + Write logs to journald + + --log.journald.filter + The filter to use for logs written to journald + + [default: error] + + --color + Sets whether or not the formatter emits ANSI terminal escape codes for colors and other text formatting + + [default: always] + + Possible values: + - always: Colors on + - auto: Colors on + - never: Colors off + +Display: + -v, --verbosity... + Set the minimum log level. + + -v Errors + -vv Warnings + -vvv Info + -vvvv Debug + -vvvvv Traces (warning: very verbose!) + + -q, --quiet + Silence all log output +``` + +### `reth stage dump account-hashing` + +AccountHashing stage + +```bash +$ reth stage dump account-hashing --help + +Usage: reth stage dump account-hashing [OPTIONS] --output-db --from --to + +Options: + --output-db + The path to the new database folder. + + -f, --from + From which block + + -t, --to + To which block + + -d, --dry-run + If passed, it will dry-run a stage execution from the newly created database right after dumping + + --instance + Add a new instance of a node. + + Configures the ports of the node to avoid conflicts with the defaults. This is useful for running multiple nodes on the same machine. + + Max number of instances is 200. It is chosen in a way so that it's not possible to have port numbers that conflict with each other. + + Changes to the following port numbers: - DISCOVERY_PORT: default + `instance` - 1 - AUTH_PORT: default + `instance` * 100 - 100 - HTTP_RPC_PORT: default - `instance` + 1 - WS_RPC_PORT: default + `instance` * 2 - 2 + + [default: 1] + + -h, --help + Print help (see a summary with '-h') + +Logging: + --log.file.directory + The path to put log files in + + [default: /reth/logs] + + --log.file.max-size + The maximum size (in MB) of one log file + + [default: 200] + + --log.file.max-files + The maximum amount of log files that will be stored. If set to 0, background file logging is disabled + + [default: 5] + + --log.file.filter + The filter to use for logs written to the log file + + [default: debug] + + --log.journald + Write logs to journald + + --log.journald.filter + The filter to use for logs written to journald + + [default: error] + + --color + Sets whether or not the formatter emits ANSI terminal escape codes for colors and other text formatting + + [default: always] + + Possible values: + - always: Colors on + - auto: Colors on + - never: Colors off + +Display: + -v, --verbosity... + Set the minimum log level. + + -v Errors + -vv Warnings + -vvv Info + -vvvv Debug + -vvvvv Traces (warning: very verbose!) + + -q, --quiet + Silence all log output +``` + +### `reth stage dump merkle` + +Merkle stage + +```bash +$ reth stage dump merkle --help Usage: reth stage dump merkle [OPTIONS] --output-db --from --to @@ -212,6 +611,72 @@ Options: -d, --dry-run If passed, it will dry-run a stage execution from the newly created database right after dumping + + --instance + Add a new instance of a node. + + Configures the ports of the node to avoid conflicts with the defaults. This is useful for running multiple nodes on the same machine. + + Max number of instances is 200. It is chosen in a way so that it's not possible to have port numbers that conflict with each other. + + Changes to the following port numbers: - DISCOVERY_PORT: default + `instance` - 1 - AUTH_PORT: default + `instance` * 100 - 100 - HTTP_RPC_PORT: default - `instance` + 1 - WS_RPC_PORT: default + `instance` * 2 - 2 + + [default: 1] + + -h, --help + Print help (see a summary with '-h') + +Logging: + --log.file.directory + The path to put log files in + + [default: /reth/logs] + + --log.file.max-size + The maximum size (in MB) of one log file + + [default: 200] + + --log.file.max-files + The maximum amount of log files that will be stored. If set to 0, background file logging is disabled + + [default: 5] + + --log.file.filter + The filter to use for logs written to the log file + + [default: debug] + + --log.journald + Write logs to journald + + --log.journald.filter + The filter to use for logs written to journald + + [default: error] + + --color + Sets whether or not the formatter emits ANSI terminal escape codes for colors and other text formatting + + [default: always] + + Possible values: + - always: Colors on + - auto: Colors on + - never: Colors off + +Display: + -v, --verbosity... + Set the minimum log level. + + -v Errors + -vv Warnings + -vvv Info + -vvvv Debug + -vvvvv Traces (warning: very verbose!) + + -q, --quiet + Silence all log output ``` ## `reth stage run` @@ -235,6 +700,26 @@ Options: --config The path to the configuration file to use. + --datadir + The path to the data dir for all reth files and subdirectories. + + Defaults to the OS-specific data directory: + + - Linux: `$XDG_DATA_HOME/reth/` or `$HOME/.local/share/reth/` + - Windows: `{FOLDERID_RoamingAppData}/reth/` + - macOS: `$HOME/Library/Application Support/reth/` + + [default: default] + + --chain + The chain this node is running. + Possible values are either a built-in chain or the path to a chain specification file. + + Built-in chains: + mainnet, sepolia, goerli, holesky, dev + + [default: mainnet] + --metrics Enable Prometheus metrics. @@ -253,19 +738,379 @@ Options: Normally, running the stage requires unwinding for stages that already have been run, in order to not rewrite to the same database slots. You can optionally skip the unwinding phase if you're syncing a block range that has not been synced before. -``` -### `reth stage unwind to-block` + --instance + Add a new instance of a node. + + Configures the ports of the node to avoid conflicts with the defaults. This is useful for running multiple nodes on the same machine. + + Max number of instances is 200. It is chosen in a way so that it's not possible to have port numbers that conflict with each other. + + Changes to the following port numbers: - DISCOVERY_PORT: default + `instance` - 1 - AUTH_PORT: default + `instance` * 100 - 100 - HTTP_RPC_PORT: default - `instance` + 1 - WS_RPC_PORT: default + `instance` * 2 - 2 + + [default: 1] -Unwinds the database until the given block number (range is inclusive) + -h, --help + Print help (see a summary with '-h') -```bash +Networking: + -d, --disable-discovery + Disable the discovery service + + --disable-dns-discovery + Disable the DNS discovery + + --disable-discv4-discovery + Disable Discv4 discovery + + --discovery.addr + The UDP address to use for P2P discovery/networking + + [default: 0.0.0.0] + + --discovery.port + The UDP port to use for P2P discovery/networking + + [default: 30303] + + --trusted-peers + Comma separated enode URLs of trusted peers for P2P connections. + + --trusted-peers enode://abcd@192.168.0.1:30303 + + --trusted-only + Connect only to trusted peers + + --bootnodes + Comma separated enode URLs for P2P discovery bootstrap. + + Will fall back to a network-specific default if not specified. + + --peers-file + The path to the known peers file. Connected peers are dumped to this file on nodes + shutdown, and read on startup. Cannot be used with `--no-persist-peers`. + + --identity + Custom node identity + + [default: reth/v0.1.0-alpha.13-10a83e594/aarch64-apple-darwin] + + --p2p-secret-key + Secret key to use for this node. + + This will also deterministically set the peer ID. If not specified, it will be set in the data dir for the chain being used. + + --no-persist-peers + Do not persist peers. + + --nat + NAT resolution method (any|none|upnp|publicip|extip:) + + [default: any] + + --addr + Network listening address + + [default: 0.0.0.0] + + --port + Network listening port + + [default: 30303] + + --max-outbound-peers + Maximum number of outbound requests. default: 100 + + --max-inbound-peers + Maximum number of inbound requests. default: 30 + +Database: + --db.log-level + Database logging level. Levels higher than "notice" require a debug build + + Possible values: + - fatal: Enables logging for critical conditions, i.e. assertion failures + - error: Enables logging for error conditions + - warn: Enables logging for warning conditions + - notice: Enables logging for normal but significant condition + - verbose: Enables logging for verbose informational + - debug: Enables logging for debug-level messages + - trace: Enables logging for trace debug-level messages + - extra: Enables logging for extra debug-level messages + + -c, --commit + Commits the changes in the database. WARNING: potentially destructive. + + Useful when you want to run diagnostics on the database. + +Logging: + --log.file.directory + The path to put log files in + + [default: /reth/logs] + + --log.file.max-size + The maximum size (in MB) of one log file + + [default: 200] + + --log.file.max-files + The maximum amount of log files that will be stored. If set to 0, background file logging is disabled + + [default: 5] + + --log.file.filter + The filter to use for logs written to the log file + + [default: debug] + + --log.journald + Write logs to journald + + --log.journald.filter + The filter to use for logs written to journald + + [default: error] + + --color + Sets whether or not the formatter emits ANSI terminal escape codes for colors and other text formatting + + [default: always] + + Possible values: + - always: Colors on + - auto: Colors on + - never: Colors off + +Display: + -v, --verbosity... + Set the minimum log level. + + -v Errors + -vv Warnings + -vvv Info + -vvvv Debug + -vvvvv Traces (warning: very verbose!) + + -q, --quiet + Silence all log output +``` + +## `reth stage unwind` + +Unwinds a certain block range, deleting it from the database + +```bash +$ reth stage unwind --help + +Usage: reth stage unwind [OPTIONS] + +Commands: + to-block Unwinds the database until the given block number (range is inclusive) + num-blocks Unwinds the given number of blocks from the database + help Print this message or the help of the given subcommand(s) + +Options: + --datadir + The path to the data dir for all reth files and subdirectories. + + Defaults to the OS-specific data directory: + + - Linux: `$XDG_DATA_HOME/reth/` or `$HOME/.local/share/reth/` + - Windows: `{FOLDERID_RoamingAppData}/reth/` + - macOS: `$HOME/Library/Application Support/reth/` + + [default: default] + + --chain + The chain this node is running. + Possible values are either a built-in chain or the path to a chain specification file. + + Built-in chains: + mainnet, sepolia, goerli, holesky, dev + + [default: mainnet] + + --instance + Add a new instance of a node. + + Configures the ports of the node to avoid conflicts with the defaults. This is useful for running multiple nodes on the same machine. + + Max number of instances is 200. It is chosen in a way so that it's not possible to have port numbers that conflict with each other. + + Changes to the following port numbers: - DISCOVERY_PORT: default + `instance` - 1 - AUTH_PORT: default + `instance` * 100 - 100 - HTTP_RPC_PORT: default - `instance` + 1 - WS_RPC_PORT: default + `instance` * 2 - 2 + + [default: 1] + + -h, --help + Print help (see a summary with '-h') + +Database: + --db.log-level + Database logging level. Levels higher than "notice" require a debug build + + Possible values: + - fatal: Enables logging for critical conditions, i.e. assertion failures + - error: Enables logging for error conditions + - warn: Enables logging for warning conditions + - notice: Enables logging for normal but significant condition + - verbose: Enables logging for verbose informational + - debug: Enables logging for debug-level messages + - trace: Enables logging for trace debug-level messages + - extra: Enables logging for extra debug-level messages + +Logging: + --log.file.directory + The path to put log files in + + [default: /reth/logs] + + --log.file.max-size + The maximum size (in MB) of one log file + + [default: 200] + + --log.file.max-files + The maximum amount of log files that will be stored. If set to 0, background file logging is disabled + + [default: 5] + + --log.file.filter + The filter to use for logs written to the log file + + [default: debug] + + --log.journald + Write logs to journald + + --log.journald.filter + The filter to use for logs written to journald + + [default: error] + + --color + Sets whether or not the formatter emits ANSI terminal escape codes for colors and other text formatting + + [default: always] + + Possible values: + - always: Colors on + - auto: Colors on + - never: Colors off + +Display: + -v, --verbosity... + Set the minimum log level. + + -v Errors + -vv Warnings + -vvv Info + -vvvv Debug + -vvvvv Traces (warning: very verbose!) + + -q, --quiet + Silence all log output +``` + +### `reth stage unwind to-block` + +Unwinds the database until the given block number (range is inclusive) + +```bash $ reth stage unwind to-block --help Usage: reth stage unwind to-block [OPTIONS] Arguments: + + +Options: + --datadir + The path to the data dir for all reth files and subdirectories. + + Defaults to the OS-specific data directory: + + - Linux: `$XDG_DATA_HOME/reth/` or `$HOME/.local/share/reth/` + - Windows: `{FOLDERID_RoamingAppData}/reth/` + - macOS: `$HOME/Library/Application Support/reth/` + + [default: default] + + --chain + The chain this node is running. + Possible values are either a built-in chain or the path to a chain specification file. + + Built-in chains: + mainnet, sepolia, goerli, holesky, dev + + [default: mainnet] + + --instance + Add a new instance of a node. + + Configures the ports of the node to avoid conflicts with the defaults. This is useful for running multiple nodes on the same machine. + + Max number of instances is 200. It is chosen in a way so that it's not possible to have port numbers that conflict with each other. + + Changes to the following port numbers: - DISCOVERY_PORT: default + `instance` - 1 - AUTH_PORT: default + `instance` * 100 - 100 - HTTP_RPC_PORT: default - `instance` + 1 - WS_RPC_PORT: default + `instance` * 2 - 2 + + [default: 1] + + -h, --help + Print help (see a summary with '-h') + +Logging: + --log.file.directory + The path to put log files in + + [default: /reth/logs] + + --log.file.max-size + The maximum size (in MB) of one log file + + [default: 200] + + --log.file.max-files + The maximum amount of log files that will be stored. If set to 0, background file logging is disabled + + [default: 5] + + --log.file.filter + The filter to use for logs written to the log file + + [default: debug] + + --log.journald + Write logs to journald + + --log.journald.filter + The filter to use for logs written to journald + + [default: error] + + --color + Sets whether or not the formatter emits ANSI terminal escape codes for colors and other text formatting + + [default: always] + + Possible values: + - always: Colors on + - auto: Colors on + - never: Colors off + +Display: + -v, --verbosity... + Set the minimum log level. + + -v Errors + -vv Warnings + -vvv Info + -vvvv Debug + -vvvvv Traces (warning: very verbose!) + + -q, --quiet + Silence all log output ``` ### `reth stage unwind num-blocks` @@ -279,4 +1124,92 @@ Usage: reth stage unwind num-blocks [OPTIONS] Arguments: + + +Options: + --datadir + The path to the data dir for all reth files and subdirectories. + + Defaults to the OS-specific data directory: + + - Linux: `$XDG_DATA_HOME/reth/` or `$HOME/.local/share/reth/` + - Windows: `{FOLDERID_RoamingAppData}/reth/` + - macOS: `$HOME/Library/Application Support/reth/` + + [default: default] + + --chain + The chain this node is running. + Possible values are either a built-in chain or the path to a chain specification file. + + Built-in chains: + mainnet, sepolia, goerli, holesky, dev + + [default: mainnet] + + --instance + Add a new instance of a node. + + Configures the ports of the node to avoid conflicts with the defaults. This is useful for running multiple nodes on the same machine. + + Max number of instances is 200. It is chosen in a way so that it's not possible to have port numbers that conflict with each other. + + Changes to the following port numbers: - DISCOVERY_PORT: default + `instance` - 1 - AUTH_PORT: default + `instance` * 100 - 100 - HTTP_RPC_PORT: default - `instance` + 1 - WS_RPC_PORT: default + `instance` * 2 - 2 + + [default: 1] + + -h, --help + Print help (see a summary with '-h') + +Logging: + --log.file.directory + The path to put log files in + + [default: /reth/logs] + + --log.file.max-size + The maximum size (in MB) of one log file + + [default: 200] + + --log.file.max-files + The maximum amount of log files that will be stored. If set to 0, background file logging is disabled + + [default: 5] + + --log.file.filter + The filter to use for logs written to the log file + + [default: debug] + + --log.journald + Write logs to journald + + --log.journald.filter + The filter to use for logs written to journald + + [default: error] + + --color + Sets whether or not the formatter emits ANSI terminal escape codes for colors and other text formatting + + [default: always] + + Possible values: + - always: Colors on + - auto: Colors on + - never: Colors off + +Display: + -v, --verbosity... + Set the minimum log level. + + -v Errors + -vv Warnings + -vvv Info + -vvvv Debug + -vvvvv Traces (warning: very verbose!) + + -q, --quiet + Silence all log output ``` diff --git a/book/cli/test-vectors.md b/book/cli/test-vectors.md index 232a4e397174..13dfd0e79319 100644 --- a/book/cli/test-vectors.md +++ b/book/cli/test-vectors.md @@ -14,14 +14,10 @@ Commands: Options: --chain The chain this node is running. - Possible values are either a built-in chain or the path to a chain specification file. Built-in chains: - - mainnet - - goerli - - sepolia - - holesky + mainnet, sepolia, goerli, holesky, dev [default: mainnet] @@ -104,4 +100,80 @@ Usage: reth test-vectors tables [OPTIONS] [NAMES]... Arguments: [NAMES]... List of table names. Case-sensitive + +Options: + --chain + The chain this node is running. + Possible values are either a built-in chain or the path to a chain specification file. + + Built-in chains: + mainnet, sepolia, goerli, holesky, dev + + [default: mainnet] + + --instance + Add a new instance of a node. + + Configures the ports of the node to avoid conflicts with the defaults. This is useful for running multiple nodes on the same machine. + + Max number of instances is 200. It is chosen in a way so that it's not possible to have port numbers that conflict with each other. + + Changes to the following port numbers: - DISCOVERY_PORT: default + `instance` - 1 - AUTH_PORT: default + `instance` * 100 - 100 - HTTP_RPC_PORT: default - `instance` + 1 - WS_RPC_PORT: default + `instance` * 2 - 2 + + [default: 1] + + -h, --help + Print help (see a summary with '-h') + +Logging: + --log.file.directory + The path to put log files in + + [default: /reth/logs] + + --log.file.max-size + The maximum size (in MB) of one log file + + [default: 200] + + --log.file.max-files + The maximum amount of log files that will be stored. If set to 0, background file logging is disabled + + [default: 5] + + --log.file.filter + The filter to use for logs written to the log file + + [default: debug] + + --log.journald + Write logs to journald + + --log.journald.filter + The filter to use for logs written to journald + + [default: error] + + --color + Sets whether or not the formatter emits ANSI terminal escape codes for colors and other text formatting + + [default: always] + + Possible values: + - always: Colors on + - auto: Colors on + - never: Colors off + +Display: + -v, --verbosity... + Set the minimum log level. + + -v Errors + -vv Warnings + -vvv Info + -vvvv Debug + -vvvvv Traces (warning: very verbose!) + + -q, --quiet + Silence all log output ``` From 829d3ede65a792fb26f2b5d513265bb4fd59425a Mon Sep 17 00:00:00 2001 From: Alexey Shekhirin Date: Tue, 12 Dec 2023 12:07:28 +0000 Subject: [PATCH 125/277] feat(bin): parse `RUST_LOG` in journald and log file filters (#5735) --- bin/reth/src/cli/mod.rs | 51 ++++++++++++++++++++++++++++++++--------- 1 file changed, 40 insertions(+), 11 deletions(-) diff --git a/bin/reth/src/cli/mod.rs b/bin/reth/src/cli/mod.rs index 1361b53d2d11..14f25a279293 100644 --- a/bin/reth/src/cli/mod.rs +++ b/bin/reth/src/cli/mod.rs @@ -23,10 +23,10 @@ pub mod components; pub mod config; pub mod ext; -/// Default [Directive] for [EnvFilter] which disables high-frequency debug logs from `hyper` and -/// `trust-dns` -const DEFAULT_ENV_FILTER_DIRECTIVE: &str = - "hyper::proto::h1=off,trust_dns_proto=off,trust_dns_resolver=off"; +/// Default [directives](Directive) for [EnvFilter] which disables high-frequency debug logs from +/// `hyper` and `trust-dns` +const DEFAULT_ENV_FILTER_DIRECTIVES: [&str; 3] = + ["hyper::proto::h1=off", "trust_dns_proto=off", "atrust_dns_resolver=off"]; /// The main reth cli interface. /// @@ -232,20 +232,32 @@ impl Logs { { let mut layers = Vec::new(); + // Function to create a new EnvFilter with environment (from `RUST_LOG` env var), default + // (from `DEFAULT_DIRECTIVES`) and additional directives. + let create_env_filter = |additional_directives: &str| -> eyre::Result { + let env_filter = EnvFilter::builder().from_env_lossy(); + + DEFAULT_ENV_FILTER_DIRECTIVES + .into_iter() + .chain(additional_directives.split(',')) + .try_fold(env_filter, |env_filter, directive| { + Ok(env_filter.add_directive(directive.parse()?)) + }) + }; + + // Create and add the journald layer if enabled if self.journald { + let journald_filter = create_env_filter(&self.journald_filter)?; layers.push( - reth_tracing::journald( - EnvFilter::try_new(DEFAULT_ENV_FILTER_DIRECTIVE)? - .add_directive(self.journald_filter.parse()?), - ) - .expect("Could not connect to journald"), + reth_tracing::journald(journald_filter).expect("Could not connect to journald"), ); } + // Create and add the file logging layer if enabled let file_guard = if self.log_file_max_files > 0 { + let file_filter = create_env_filter(&self.log_file_filter)?; let (layer, guard) = reth_tracing::file( - EnvFilter::try_new(DEFAULT_ENV_FILTER_DIRECTIVE)? - .add_directive(self.log_file_filter.parse()?), + file_filter, &self.log_file_directory, "reth.log", self.log_file_max_size * MB_TO_BYTES, @@ -383,4 +395,21 @@ mod tests { .unwrap(); assert!(reth.run().is_err()); } + + #[test] + fn parse_env_filter_directives() { + let temp_dir = tempfile::tempdir().unwrap(); + + std::env::set_var("RUST_LOG", "info,evm=debug"); + let reth = Cli::<()>::try_parse_from([ + "reth", + "init", + "--datadir", + temp_dir.path().to_str().unwrap(), + "--log.file.filter", + "debug,net=trace", + ]) + .unwrap(); + assert!(reth.run().is_ok()); + } } From 701e37804eb8552a18312347a355d20ac4c7cdbd Mon Sep 17 00:00:00 2001 From: Matthias Seitz Date: Tue, 12 Dec 2023 17:02:49 +0100 Subject: [PATCH 126/277] fix: dont include empty revert reason (#5736) --- crates/revm/revm-inspectors/src/tracing/types.rs | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/crates/revm/revm-inspectors/src/tracing/types.rs b/crates/revm/revm-inspectors/src/tracing/types.rs index 7f10a204f338..4f559f43dc7c 100644 --- a/crates/revm/revm-inspectors/src/tracing/types.rs +++ b/crates/revm/revm-inspectors/src/tracing/types.rs @@ -347,7 +347,10 @@ impl CallTraceNode { // we need to populate error and revert reason if !self.trace.success { - call_frame.revert_reason = decode_revert_reason(self.trace.output.as_ref()); + // decode the revert reason, but don't include it if it's empty + call_frame.revert_reason = decode_revert_reason(self.trace.output.as_ref()) + .filter(|reason| !reason.is_empty()); + // Note: the call tracer mimics parity's trace transaction and geth maps errors to parity style error messages, call_frame.error = self.trace.as_error_msg(TraceStyle::Parity); } @@ -676,3 +679,14 @@ impl AsRef<[u8]> for RecordedMemory { self.as_bytes() } } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn decode_empty_revert() { + let reason = decode_revert_reason("".as_bytes()); + assert_eq!(reason, Some("".to_string())); + } +} From 5062b7ea866960a99fb3b459550034fb6c37e766 Mon Sep 17 00:00:00 2001 From: Matthias Seitz Date: Tue, 12 Dec 2023 17:34:31 +0100 Subject: [PATCH 127/277] feat: integrate multiplexing (#5559) --- Cargo.lock | 23 + crates/net/eth-wire/Cargo.toml | 1 + crates/net/eth-wire/src/capability.rs | 24 +- crates/net/eth-wire/src/ethstream.rs | 6 + crates/net/eth-wire/src/hello.rs | 20 + crates/net/eth-wire/src/multiplex.rs | 453 +++++++++++++++---- crates/net/eth-wire/src/test_utils.rs | 103 +++++ crates/net/eth-wire/src/types/status.rs | 5 + crates/net/network/src/protocol.rs | 64 ++- crates/net/network/src/session/active.rs | 29 +- crates/net/network/src/session/conn.rs | 156 +++++++ crates/net/network/src/session/handle.rs | 6 +- crates/net/network/src/session/mod.rs | 83 +++- crates/net/network/src/test_utils/testnet.rs | 6 + crates/net/network/tests/it/main.rs | 1 + crates/net/network/tests/it/multiplex.rs | 330 ++++++++++++++ 16 files changed, 1176 insertions(+), 134 deletions(-) create mode 100644 crates/net/network/src/session/conn.rs create mode 100644 crates/net/network/tests/it/multiplex.rs diff --git a/Cargo.lock b/Cargo.lock index 831dcf51bcdd..af3eb7ed77ba 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -539,6 +539,28 @@ dependencies = [ "pin-project-lite", ] +[[package]] +name = "async-stream" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd56dd203fef61ac097dd65721a419ddccb106b2d2b70ba60a6b529f03961a51" +dependencies = [ + "async-stream-impl", + "futures-core", + "pin-project-lite", +] + +[[package]] +name = "async-stream-impl" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16e62a023e7c117e27523144c5d2459f4397fcc3cab0085af8e2224f643a0193" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.39", +] + [[package]] name = "async-trait" version = "0.1.74" @@ -5889,6 +5911,7 @@ version = "0.1.0-alpha.13" dependencies = [ "alloy-rlp", "arbitrary", + "async-stream", "async-trait", "bytes", "derive_more", diff --git a/crates/net/eth-wire/Cargo.toml b/crates/net/eth-wire/Cargo.toml index 88af65762bf2..29e6adcf0ed6 100644 --- a/crates/net/eth-wire/Cargo.toml +++ b/crates/net/eth-wire/Cargo.toml @@ -53,6 +53,7 @@ secp256k1 = { workspace = true, features = ["global-context", "rand-std", "recov arbitrary = { workspace = true, features = ["derive"] } proptest.workspace = true proptest-derive.workspace = true +async-stream = "0.3" [features] default = ["serde"] diff --git a/crates/net/eth-wire/src/capability.rs b/crates/net/eth-wire/src/capability.rs index 8b64aa16a468..5f799b2ac037 100644 --- a/crates/net/eth-wire/src/capability.rs +++ b/crates/net/eth-wire/src/capability.rs @@ -317,6 +317,14 @@ impl SharedCapability { } } + /// Returns the eth version if it's the `eth` capability. + pub fn eth_version(&self) -> Option { + match self { + SharedCapability::Eth { version, .. } => Some(*version), + _ => None, + } + } + /// Returns the message ID offset of the current capability. /// /// This represents the message ID offset for the first message of the eth capability in the @@ -375,8 +383,8 @@ impl SharedCapabilities { /// Returns the negotiated eth version if it is shared. #[inline] - pub fn eth_version(&self) -> Result { - self.eth().map(|cap| cap.version()) + pub fn eth_version(&self) -> Result { + self.eth().map(|cap| cap.eth_version().expect("is eth; qed")) } /// Returns true if the shared capabilities contain the given capability. @@ -438,6 +446,18 @@ impl SharedCapabilities { ) -> Result<&SharedCapability, UnsupportedCapabilityError> { self.find(cap).ok_or_else(|| UnsupportedCapabilityError { capability: cap.clone() }) } + + /// Returns the number of shared capabilities. + #[inline] + pub fn len(&self) -> usize { + self.0.len() + } + + /// Returns true if there are no shared capabilities. + #[inline] + pub fn is_empty(&self) -> bool { + self.0.is_empty() + } } /// Determines the offsets for each shared capability between the input list of peer diff --git a/crates/net/eth-wire/src/ethstream.rs b/crates/net/eth-wire/src/ethstream.rs index f1162f7ee464..35ee8d2762d6 100644 --- a/crates/net/eth-wire/src/ethstream.rs +++ b/crates/net/eth-wire/src/ethstream.rs @@ -166,6 +166,7 @@ where #[pin_project] #[derive(Debug)] pub struct EthStream { + /// Negotiated eth version. version: EthVersion, #[pin] inner: S, @@ -174,26 +175,31 @@ pub struct EthStream { impl EthStream { /// Creates a new unauthed [`EthStream`] from a provided stream. You will need /// to manually handshake a peer. + #[inline] pub fn new(version: EthVersion, inner: S) -> Self { Self { version, inner } } /// Returns the eth version. + #[inline] pub fn version(&self) -> EthVersion { self.version } /// Returns the underlying stream. + #[inline] pub fn inner(&self) -> &S { &self.inner } /// Returns mutable access to the underlying stream. + #[inline] pub fn inner_mut(&mut self) -> &mut S { &mut self.inner } /// Consumes this type and returns the wrapped stream. + #[inline] pub fn into_inner(self) -> S { self.inner } diff --git a/crates/net/eth-wire/src/hello.rs b/crates/net/eth-wire/src/hello.rs index f1e6848655e7..e992021f4be2 100644 --- a/crates/net/eth-wire/src/hello.rs +++ b/crates/net/eth-wire/src/hello.rs @@ -49,6 +49,7 @@ impl HelloMessageWithProtocols { } /// Returns the raw [HelloMessage] without the additional protocol information. + #[inline] pub fn message(&self) -> HelloMessage { HelloMessage { protocol_version: self.protocol_version, @@ -69,6 +70,25 @@ impl HelloMessageWithProtocols { id: self.id, } } + + /// Returns true if the set of protocols contains the given protocol. + #[inline] + pub fn contains_protocol(&self, protocol: &Protocol) -> bool { + self.protocols.iter().any(|p| p.cap == protocol.cap) + } + + /// Adds a new protocol to the set. + /// + /// Returns an error if the protocol already exists. + #[inline] + pub fn try_add_protocol(&mut self, protocol: Protocol) -> Result<(), Protocol> { + if self.contains_protocol(&protocol) { + Err(protocol) + } else { + self.protocols.push(protocol); + Ok(()) + } + } } // TODO: determine if we should allow for the extra fields at the end like EIP-706 suggests diff --git a/crates/net/eth-wire/src/multiplex.rs b/crates/net/eth-wire/src/multiplex.rs index f76be4fbe826..0cb9cd62fc9e 100644 --- a/crates/net/eth-wire/src/multiplex.rs +++ b/crates/net/eth-wire/src/multiplex.rs @@ -16,45 +16,89 @@ use std::{ task::{ready, Context, Poll}, }; +use crate::{ + capability::{Capability, SharedCapabilities, SharedCapability, UnsupportedCapabilityError}, + errors::{EthStreamError, P2PStreamError}, + CanDisconnect, DisconnectReason, EthStream, P2PStream, Status, UnauthedEthStream, +}; use bytes::{Bytes, BytesMut}; use futures::{pin_mut, Sink, SinkExt, Stream, StreamExt, TryStream, TryStreamExt}; +use reth_primitives::ForkFilter; use tokio::sync::{mpsc, mpsc::UnboundedSender}; use tokio_stream::wrappers::UnboundedReceiverStream; -use crate::{ - capability::{Capability, SharedCapabilities, SharedCapability, UnsupportedCapabilityError}, - errors::P2PStreamError, - CanDisconnect, DisconnectReason, P2PStream, -}; - /// A Stream and Sink type that wraps a raw rlpx stream [P2PStream] and handles message ID /// multiplexing. #[derive(Debug)] pub struct RlpxProtocolMultiplexer { - /// The raw p2p stream - conn: P2PStream, - /// All the subprotocols that are multiplexed on top of the raw p2p stream - protocols: Vec, + inner: MultiplexInner, } impl RlpxProtocolMultiplexer { /// Wraps the raw p2p stream pub fn new(conn: P2PStream) -> Self { - Self { conn, protocols: Default::default() } + Self { + inner: MultiplexInner { + conn, + protocols: Default::default(), + out_buffer: Default::default(), + }, + } } - /// Installs a new protocol on top of the raw p2p stream - pub fn install_protocol( + /// Installs a new protocol on top of the raw p2p stream. + /// + /// This accepts a closure that receives a [ProtocolConnection] that will yield messages for the + /// given capability. + pub fn install_protocol( &mut self, - _cap: Capability, - _st: S, - ) -> Result<(), UnsupportedCapabilityError> { - todo!() + cap: &Capability, + f: F, + ) -> Result<(), UnsupportedCapabilityError> + where + F: FnOnce(ProtocolConnection) -> Proto, + Proto: Stream + Send + 'static, + { + self.inner.install_protocol(cap, f) } /// Returns the [SharedCapabilities] of the underlying raw p2p stream pub fn shared_capabilities(&self) -> &SharedCapabilities { - self.conn.shared_capabilities() + self.inner.shared_capabilities() + } + + /// Converts this multiplexer into a [RlpxSatelliteStream] with the given primary protocol. + pub fn into_satellite_stream( + self, + cap: &Capability, + primary: F, + ) -> Result, P2PStreamError> + where + F: FnOnce(ProtocolProxy) -> Primary, + { + let Ok(shared_cap) = self.shared_capabilities().ensure_matching_capability(cap).cloned() + else { + return Err(P2PStreamError::CapabilityNotShared) + }; + + let (to_primary, from_wire) = mpsc::unbounded_channel(); + let (to_wire, from_primary) = mpsc::unbounded_channel(); + let proxy = ProtocolProxy { + shared_cap: shared_cap.clone(), + from_wire: UnboundedReceiverStream::new(from_wire), + to_wire, + }; + + let st = primary(proxy); + Ok(RlpxSatelliteStream { + inner: self.inner, + primary: PrimaryProtocol { + to_primary, + from_primary: UnboundedReceiverStream::new(from_primary), + st, + shared_cap, + }, + }) } /// Converts this multiplexer into a [RlpxSatelliteStream] with the given primary protocol. @@ -62,7 +106,7 @@ impl RlpxProtocolMultiplexer { /// Returns an error if the primary protocol is not supported by the remote or the handshake /// failed. pub async fn into_satellite_stream_with_handshake( - mut self, + self, cap: &Capability, handshake: F, ) -> Result, Err> @@ -71,6 +115,34 @@ impl RlpxProtocolMultiplexer { Fut: Future>, St: Stream> + Sink + Unpin, P2PStreamError: Into, + { + self.into_satellite_stream_with_tuple_handshake(cap, move |proxy| async move { + let st = handshake(proxy).await?; + Ok((st, ())) + }) + .await + .map(|(st, _)| st) + } + + /// Converts this multiplexer into a [RlpxSatelliteStream] with the given primary protocol. + /// + /// Returns an error if the primary protocol is not supported by the remote or the handshake + /// failed. + /// + /// This accepts a closure that does a handshake with the remote peer and returns a tuple of the + /// primary stream and extra data. + /// + /// See also [UnauthedEthStream::handshake] + pub async fn into_satellite_stream_with_tuple_handshake( + mut self, + cap: &Capability, + handshake: F, + ) -> Result<(RlpxSatelliteStream, Extra), Err> + where + F: FnOnce(ProtocolProxy) -> Fut, + Fut: Future>, + St: Stream> + Sink + Unpin, + P2PStreamError: Into, { let Ok(shared_cap) = self.shared_capabilities().ensure_matching_capability(cap).cloned() else { @@ -80,7 +152,7 @@ impl RlpxProtocolMultiplexer { let (to_primary, from_wire) = mpsc::unbounded_channel(); let (to_wire, mut from_primary) = mpsc::unbounded_channel(); let proxy = ProtocolProxy { - cap: shared_cap.clone(), + shared_cap: shared_cap.clone(), from_wire: UnboundedReceiverStream::new(from_wire), to_wire, }; @@ -92,45 +164,118 @@ impl RlpxProtocolMultiplexer { // complete loop { tokio::select! { - Some(Ok(msg)) = self.conn.next() => { + Some(Ok(msg)) = self.inner.conn.next() => { // Ensure the message belongs to the primary protocol let offset = msg[0]; - if let Some(cap) = self.conn.shared_capabilities().find_by_relative_offset(offset) { - if cap == &shared_cap { + if let Some(cap) = self.shared_capabilities().find_by_relative_offset(offset).cloned() { + if cap == shared_cap { // delegate to primary let _ = to_primary.send(msg); } else { // delegate to satellite - for proto in &self.protocols { - if proto.cap == *cap { - // TODO: need some form of backpressure here so buffering can't be abused - proto.send_raw(msg); - break - } - } + self.inner.delegate_message(&cap, msg); } } else { return Err(P2PStreamError::UnknownReservedMessageId(offset).into()) } } Some(msg) = from_primary.recv() => { - self.conn.send(msg).await.map_err(Into::into)?; + self.inner.conn.send(msg).await.map_err(Into::into)?; } res = &mut f => { - let primary = res?; - return Ok(RlpxSatelliteStream { - conn: self.conn, - to_primary, - from_primary: UnboundedReceiverStream::new(from_primary), - primary, - primary_capability: shared_cap, - satellites: self.protocols, - out_buffer: Default::default(), - }) + let (st, extra) = res?; + return Ok((RlpxSatelliteStream { + inner: self.inner, + primary: PrimaryProtocol { + to_primary, + from_primary: UnboundedReceiverStream::new(from_primary), + st, + shared_cap, + } + }, extra)) } } } } + + /// Converts this multiplexer into a [RlpxSatelliteStream] with eth protocol as the given + /// primary protocol. + pub async fn into_eth_satellite_stream( + self, + status: Status, + fork_filter: ForkFilter, + ) -> Result<(RlpxSatelliteStream>, Status), EthStreamError> + where + St: Stream> + Sink + Unpin, + { + let eth_cap = self.inner.conn.shared_capabilities().eth_version()?; + self.into_satellite_stream_with_tuple_handshake( + &Capability::eth(eth_cap), + move |proxy| async move { + UnauthedEthStream::new(proxy).handshake(status, fork_filter).await + }, + ) + .await + } +} + +#[derive(Debug)] +struct MultiplexInner { + /// The raw p2p stream + conn: P2PStream, + /// All the subprotocols that are multiplexed on top of the raw p2p stream + protocols: Vec, + /// Buffer for outgoing messages on the wire. + out_buffer: VecDeque, +} + +impl MultiplexInner { + fn shared_capabilities(&self) -> &SharedCapabilities { + self.conn.shared_capabilities() + } + + /// Delegates a message to the matching protocol. + fn delegate_message(&mut self, cap: &SharedCapability, msg: BytesMut) -> bool { + for proto in &self.protocols { + if proto.shared_cap == *cap { + proto.send_raw(msg); + return true + } + } + false + } + + fn install_protocol( + &mut self, + cap: &Capability, + f: F, + ) -> Result<(), UnsupportedCapabilityError> + where + F: FnOnce(ProtocolConnection) -> Proto, + Proto: Stream + Send + 'static, + { + let shared_cap = + self.conn.shared_capabilities().ensure_matching_capability(cap).cloned()?; + let (to_satellite, rx) = mpsc::unbounded_channel(); + let proto_conn = ProtocolConnection { from_wire: UnboundedReceiverStream::new(rx) }; + let st = f(proto_conn); + let st = ProtocolStream { shared_cap, to_satellite, satellite_st: Box::pin(st) }; + self.protocols.push(st); + Ok(()) + } +} + +/// Represents a protocol in the multiplexer that is used as the primary protocol. +#[derive(Debug)] +struct PrimaryProtocol { + /// Channel to send messages to the primary protocol. + to_primary: UnboundedSender, + /// Receiver for messages from the primary protocol. + from_primary: UnboundedReceiverStream, + /// Shared capability of the primary protocol. + shared_cap: SharedCapability, + /// The primary stream. + st: Primary, } /// A Stream and Sink type that acts as a wrapper around a primary RLPx subprotocol (e.g. "eth") @@ -138,7 +283,7 @@ impl RlpxProtocolMultiplexer { /// Only emits and sends _non-empty_ messages #[derive(Debug)] pub struct ProtocolProxy { - cap: SharedCapability, + shared_cap: SharedCapability, /// Receives _non-empty_ messages from the wire from_wire: UnboundedReceiverStream, /// Sends _non-empty_ messages from the wire @@ -163,7 +308,7 @@ impl ProtocolProxy { #[inline] fn mask_msg_id(&self, msg: Bytes) -> Bytes { let mut masked_bytes = BytesMut::zeroed(msg.len()); - masked_bytes[0] = msg[0] + self.cap.relative_message_id_offset(); + masked_bytes[0] = msg[0] + self.shared_cap.relative_message_id_offset(); masked_bytes[1..].copy_from_slice(&msg[1..]); masked_bytes.freeze() } @@ -175,7 +320,7 @@ impl ProtocolProxy { /// If the message is empty. #[inline] fn unmask_id(&self, mut msg: BytesMut) -> BytesMut { - msg[0] -= self.cap.relative_message_id_offset(); + msg[0] -= self.shared_cap.relative_message_id_offset(); msg } } @@ -237,20 +382,60 @@ impl Stream for ProtocolConnection { } /// A Stream and Sink type that acts as a wrapper around a primary RLPx subprotocol (e.g. "eth") -/// [EthStream](crate::EthStream) and can also handle additional subprotocols. +/// [EthStream] and can also handle additional subprotocols. #[derive(Debug)] pub struct RlpxSatelliteStream { - /// The raw p2p stream - conn: P2PStream, - to_primary: UnboundedSender, - from_primary: UnboundedReceiverStream, - primary: Primary, - primary_capability: SharedCapability, - satellites: Vec, - out_buffer: VecDeque, + inner: MultiplexInner, + primary: PrimaryProtocol, } -impl RlpxSatelliteStream {} +impl RlpxSatelliteStream { + /// Installs a new protocol on top of the raw p2p stream. + /// + /// This accepts a closure that receives a [ProtocolConnection] that will yield messages for the + /// given capability. + pub fn install_protocol( + &mut self, + cap: &Capability, + f: F, + ) -> Result<(), UnsupportedCapabilityError> + where + F: FnOnce(ProtocolConnection) -> Proto, + Proto: Stream + Send + 'static, + { + self.inner.install_protocol(cap, f) + } + + /// Returns the primary protocol. + #[inline] + pub fn primary(&self) -> &Primary { + &self.primary.st + } + + /// Returns mutable access to the primary protocol. + #[inline] + pub fn primary_mut(&mut self) -> &mut Primary { + &mut self.primary.st + } + + /// Returns the underlying [P2PStream]. + #[inline] + pub fn inner(&self) -> &P2PStream { + &self.inner.conn + } + + /// Returns mutable access to the underlying [P2PStream]. + #[inline] + pub fn inner_mut(&mut self) -> &mut P2PStream { + &mut self.inner.conn + } + + /// Consumes this type and returns the wrapped [P2PStream]. + #[inline] + pub fn into_inner(self) -> P2PStream { + self.inner.conn + } +} impl Stream for RlpxSatelliteStream where @@ -265,16 +450,16 @@ where loop { // first drain the primary stream - if let Poll::Ready(Some(msg)) = this.primary.try_poll_next_unpin(cx) { + if let Poll::Ready(Some(msg)) = this.primary.st.try_poll_next_unpin(cx) { return Poll::Ready(Some(msg)) } - let mut out_ready = true; + let mut conn_ready = true; loop { - match this.conn.poll_ready_unpin(cx) { + match this.inner.conn.poll_ready_unpin(cx) { Poll::Ready(_) => { - if let Some(msg) = this.out_buffer.pop_front() { - if let Err(err) = this.conn.start_send_unpin(msg) { + if let Some(msg) = this.inner.out_buffer.pop_front() { + if let Err(err) = this.inner.conn.start_send_unpin(msg) { return Poll::Ready(Some(Err(err.into()))) } } else { @@ -282,7 +467,7 @@ where } } Poll::Pending => { - out_ready = false; + conn_ready = false; break } } @@ -290,9 +475,9 @@ where // advance primary out loop { - match this.from_primary.poll_next_unpin(cx) { + match this.primary.from_primary.poll_next_unpin(cx) { Poll::Ready(Some(msg)) => { - this.out_buffer.push_back(msg); + this.inner.out_buffer.push_back(msg); } Poll::Ready(None) => { // primary closed @@ -303,16 +488,16 @@ where } // advance all satellites - for idx in (0..this.satellites.len()).rev() { - let mut proto = this.satellites.swap_remove(idx); + for idx in (0..this.inner.protocols.len()).rev() { + let mut proto = this.inner.protocols.swap_remove(idx); loop { match proto.poll_next_unpin(cx) { Poll::Ready(Some(msg)) => { - this.out_buffer.push_back(msg); + this.inner.out_buffer.push_back(msg); } Poll::Ready(None) => return Poll::Ready(None), Poll::Pending => { - this.satellites.push(proto); + this.inner.protocols.push(proto); break } } @@ -322,21 +507,21 @@ where let mut delegated = false; loop { // pull messages from connection - match this.conn.poll_next_unpin(cx) { + match this.inner.conn.poll_next_unpin(cx) { Poll::Ready(Some(Ok(msg))) => { delegated = true; let offset = msg[0]; // delegate the multiplexed message to the correct protocol if let Some(cap) = - this.conn.shared_capabilities().find_by_relative_offset(offset) + this.inner.conn.shared_capabilities().find_by_relative_offset(offset) { - if cap == &this.primary_capability { + if cap == &this.primary.shared_cap { // delegate to primary - let _ = this.to_primary.send(msg); + let _ = this.primary.to_primary.send(msg); } else { - // delegate to satellite - for proto in &this.satellites { - if proto.cap == *cap { + // delegate to installed satellite if any + for proto in &this.inner.protocols { + if proto.shared_cap == *cap { proto.send_raw(msg); break } @@ -358,7 +543,7 @@ where } } - if !delegated || !out_ready || this.out_buffer.is_empty() { + if !conn_ready || (!delegated && this.inner.out_buffer.is_empty()) { return Poll::Pending } } @@ -368,41 +553,41 @@ where impl Sink for RlpxSatelliteStream where St: Stream> + Sink + Unpin, - Primary: Sink + Unpin, + Primary: Sink + Unpin, P2PStreamError: Into<>::Error>, { type Error = >::Error; fn poll_ready(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { let this = self.get_mut(); - if let Err(err) = ready!(this.conn.poll_ready_unpin(cx)) { + if let Err(err) = ready!(this.inner.conn.poll_ready_unpin(cx)) { return Poll::Ready(Err(err.into())) } - if let Err(err) = ready!(this.primary.poll_ready_unpin(cx)) { + if let Err(err) = ready!(this.primary.st.poll_ready_unpin(cx)) { return Poll::Ready(Err(err)) } Poll::Ready(Ok(())) } fn start_send(self: Pin<&mut Self>, item: T) -> Result<(), Self::Error> { - self.get_mut().primary.start_send_unpin(item) + self.get_mut().primary.st.start_send_unpin(item) } fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { - self.get_mut().conn.poll_flush_unpin(cx).map_err(Into::into) + self.get_mut().inner.conn.poll_flush_unpin(cx).map_err(Into::into) } fn poll_close(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { - self.get_mut().conn.poll_close_unpin(cx).map_err(Into::into) + self.get_mut().inner.conn.poll_close_unpin(cx).map_err(Into::into) } } /// Wraps a RLPx subprotocol and handles message ID multiplexing. struct ProtocolStream { - cap: SharedCapability, + shared_cap: SharedCapability, /// the channel shared with the satellite stream to_satellite: UnboundedSender, - satellite_st: Pin>>, + satellite_st: Pin + Send>>, } impl ProtocolStream { @@ -413,7 +598,7 @@ impl ProtocolStream { /// If the message is empty. #[inline] fn mask_msg_id(&self, mut msg: BytesMut) -> Bytes { - msg[0] += self.cap.relative_message_id_offset(); + msg[0] += self.shared_cap.relative_message_id_offset(); msg.freeze() } @@ -424,7 +609,7 @@ impl ProtocolStream { /// If the message is empty. #[inline] fn unmask_id(&self, mut msg: BytesMut) -> BytesMut { - msg[0] -= self.cap.relative_message_id_offset(); + msg[0] -= self.shared_cap.relative_message_id_offset(); msg } @@ -446,7 +631,7 @@ impl Stream for ProtocolStream { impl fmt::Debug for ProtocolStream { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("ProtocolStream").field("cap", &self.cap).finish_non_exhaustive() + f.debug_struct("ProtocolStream").field("cap", &self.shared_cap).finish_non_exhaustive() } } @@ -454,10 +639,13 @@ impl fmt::Debug for ProtocolStream { mod tests { use super::*; use crate::{ - test_utils::{connect_passthrough, eth_handshake, eth_hello}, + test_utils::{ + connect_passthrough, eth_handshake, eth_hello, + proto::{test_hello, TestProtoMessage}, + }, UnauthedEthStream, UnauthedP2PStream, }; - use tokio::net::TcpListener; + use tokio::{net::TcpListener, sync::oneshot}; use tokio_util::codec::Decoder; #[tokio::test] @@ -487,7 +675,6 @@ mod tests { let eth = conn.shared_capabilities().eth().unwrap().clone(); let multiplexer = RlpxProtocolMultiplexer::new(conn); - let _satellite = multiplexer .into_satellite_stream_with_handshake( eth.capability().as_ref(), @@ -498,4 +685,94 @@ mod tests { .await .unwrap(); } + + /// A test that install a satellite stream eth+test protocol and sends messages between them. + #[tokio::test(flavor = "multi_thread")] + async fn eth_test_protocol_satellite() { + reth_tracing::init_test_tracing(); + let listener = TcpListener::bind("127.0.0.1:0").await.unwrap(); + let local_addr = listener.local_addr().unwrap(); + let (status, fork_filter) = eth_handshake(); + let other_status = status; + let other_fork_filter = fork_filter.clone(); + let _handle = tokio::spawn(async move { + let (incoming, _) = listener.accept().await.unwrap(); + let stream = crate::PassthroughCodec::default().framed(incoming); + let (server_hello, _) = test_hello(); + let (conn, _) = UnauthedP2PStream::new(stream).handshake(server_hello).await.unwrap(); + + let (mut st, _their_status) = RlpxProtocolMultiplexer::new(conn) + .into_eth_satellite_stream(other_status, other_fork_filter) + .await + .unwrap(); + + st.install_protocol(&TestProtoMessage::capability(), |mut conn| { + async_stream::stream! { + yield TestProtoMessage::ping().encoded(); + let msg = conn.next().await.unwrap(); + let msg = TestProtoMessage::decode_message(&mut &msg[..]).unwrap(); + assert_eq!(msg, TestProtoMessage::pong()); + + yield TestProtoMessage::message("hello").encoded(); + let msg = conn.next().await.unwrap(); + let msg = TestProtoMessage::decode_message(&mut &msg[..]).unwrap(); + assert_eq!(msg, TestProtoMessage::message("good bye!")); + + yield TestProtoMessage::message("good bye!").encoded(); + + futures::future::pending::<()>().await; + unreachable!() + } + }) + .unwrap(); + + loop { + let _ = st.next().await; + } + }); + + let conn = connect_passthrough(local_addr, test_hello().0).await; + let (mut st, _their_status) = RlpxProtocolMultiplexer::new(conn) + .into_eth_satellite_stream(status, fork_filter) + .await + .unwrap(); + + let (tx, mut rx) = oneshot::channel(); + + st.install_protocol(&TestProtoMessage::capability(), |mut conn| { + async_stream::stream! { + let msg = conn.next().await.unwrap(); + let msg = TestProtoMessage::decode_message(&mut &msg[..]).unwrap(); + assert_eq!(msg, TestProtoMessage::ping()); + + yield TestProtoMessage::pong().encoded(); + + let msg = conn.next().await.unwrap(); + let msg = TestProtoMessage::decode_message(&mut &msg[..]).unwrap(); + assert_eq!(msg, TestProtoMessage::message("hello")); + + yield TestProtoMessage::message("good bye!").encoded(); + + let msg = conn.next().await.unwrap(); + let msg = TestProtoMessage::decode_message(&mut &msg[..]).unwrap(); + assert_eq!(msg, TestProtoMessage::message("good bye!")); + + tx.send(()).unwrap(); + + futures::future::pending::<()>().await; + unreachable!() + } + }) + .unwrap(); + + loop { + tokio::select! { + _ = &mut rx => { + break + } + _ = st.next() => { + } + } + } + } } diff --git a/crates/net/eth-wire/src/test_utils.rs b/crates/net/eth-wire/src/test_utils.rs index 01bd9a048dc3..ceaa8206cf97 100644 --- a/crates/net/eth-wire/src/test_utils.rs +++ b/crates/net/eth-wire/src/test_utils.rs @@ -55,3 +55,106 @@ pub async fn connect_passthrough( p2p_stream } + +/// A Rplx subprotocol for testing +pub mod proto { + use super::*; + use crate::{capability::Capability, protocol::Protocol}; + use bytes::{Buf, BufMut, BytesMut}; + + /// Returns a new testing `HelloMessage` with eth and the test protocol + pub fn test_hello() -> (HelloMessageWithProtocols, SecretKey) { + let mut handshake = eth_hello(); + handshake.0.protocols.push(TestProtoMessage::protocol()); + handshake + } + + #[repr(u8)] + #[derive(Clone, Copy, Debug, PartialEq, Eq)] + pub enum TestProtoMessageId { + Ping = 0x00, + Pong = 0x01, + Message = 0x02, + } + + #[derive(Clone, Debug, PartialEq, Eq)] + pub enum TestProtoMessageKind { + Message(String), + Ping, + Pong, + } + + /// An `test` protocol message, containing a message ID and payload. + #[derive(Clone, Debug, PartialEq, Eq)] + pub struct TestProtoMessage { + pub message_type: TestProtoMessageId, + pub message: TestProtoMessageKind, + } + + impl TestProtoMessage { + /// Returns the capability for the `test` protocol. + pub fn capability() -> Capability { + Capability::new_static("test", 1) + } + + /// Returns the protocol for the `test` protocol. + pub fn protocol() -> Protocol { + Protocol::new(Self::capability(), 3) + } + + /// Creates a ping message + pub fn ping() -> Self { + Self { message_type: TestProtoMessageId::Ping, message: TestProtoMessageKind::Ping } + } + + /// Creates a pong message + pub fn pong() -> Self { + Self { message_type: TestProtoMessageId::Pong, message: TestProtoMessageKind::Pong } + } + + /// Creates a message + pub fn message(msg: impl Into) -> Self { + Self { + message_type: TestProtoMessageId::Message, + message: TestProtoMessageKind::Message(msg.into()), + } + } + + /// Creates a new `TestProtoMessage` with the given message ID and payload. + pub fn encoded(&self) -> BytesMut { + let mut buf = BytesMut::new(); + buf.put_u8(self.message_type as u8); + match &self.message { + TestProtoMessageKind::Ping => {} + TestProtoMessageKind::Pong => {} + TestProtoMessageKind::Message(msg) => { + buf.put(msg.as_bytes()); + } + } + buf + } + + /// Decodes a `TestProtoMessage` from the given message buffer. + pub fn decode_message(buf: &mut &[u8]) -> Option { + if buf.is_empty() { + return None; + } + let id = buf[0]; + buf.advance(1); + let message_type = match id { + 0x00 => TestProtoMessageId::Ping, + 0x01 => TestProtoMessageId::Pong, + 0x02 => TestProtoMessageId::Message, + _ => return None, + }; + let message = match message_type { + TestProtoMessageId::Ping => TestProtoMessageKind::Ping, + TestProtoMessageId::Pong => TestProtoMessageKind::Pong, + TestProtoMessageId::Message => { + TestProtoMessageKind::Message(String::from_utf8_lossy(&buf[..]).into_owned()) + } + }; + Some(Self { message_type, message }) + } + } +} diff --git a/crates/net/eth-wire/src/types/status.rs b/crates/net/eth-wire/src/types/status.rs index 5f80449440fc..0925b7d97c22 100644 --- a/crates/net/eth-wire/src/types/status.rs +++ b/crates/net/eth-wire/src/types/status.rs @@ -66,6 +66,11 @@ impl Status { Default::default() } + /// Sets the [EthVersion] for the status. + pub fn set_eth_version(&mut self, version: EthVersion) { + self.version = version as u8; + } + /// Create a [`StatusBuilder`] from the given [`ChainSpec`] and head block. /// /// Sets the `chain` and `genesis`, `blockhash`, and `forkid` fields based on the [`ChainSpec`] diff --git a/crates/net/network/src/protocol.rs b/crates/net/network/src/protocol.rs index adcfb75f22db..cc9ed51c9049 100644 --- a/crates/net/network/src/protocol.rs +++ b/crates/net/network/src/protocol.rs @@ -9,7 +9,12 @@ use reth_eth_wire::{ use reth_network_api::Direction; use reth_primitives::BytesMut; use reth_rpc_types::PeerId; -use std::{fmt, net::SocketAddr, pin::Pin}; +use std::{ + fmt, + net::SocketAddr, + ops::{Deref, DerefMut}, + pin::Pin, +}; /// A trait that allows to offer additional RLPx-based application-level protocols when establishing /// a peer-to-peer connection. @@ -113,6 +118,57 @@ impl RlpxSubProtocols { pub fn push(&mut self, protocol: impl IntoRlpxSubProtocol) { self.protocols.push(protocol.into_rlpx_sub_protocol()); } + + /// Returns all additional protocol handlers that should be announced to the remote during the + /// Rlpx handshake on an incoming connection. + pub(crate) fn on_incoming(&self, socket_addr: SocketAddr) -> RlpxSubProtocolHandlers { + RlpxSubProtocolHandlers( + self.protocols + .iter() + .filter_map(|protocol| protocol.0.on_incoming(socket_addr)) + .collect(), + ) + } + + /// Returns all additional protocol handlers that should be announced to the remote during the + /// Rlpx handshake on an outgoing connection. + pub(crate) fn on_outgoing( + &self, + socket_addr: SocketAddr, + peer_id: PeerId, + ) -> RlpxSubProtocolHandlers { + RlpxSubProtocolHandlers( + self.protocols + .iter() + .filter_map(|protocol| protocol.0.on_outgoing(socket_addr, peer_id)) + .collect(), + ) + } +} + +/// A set of additional RLPx-based sub-protocol connection handlers. +#[derive(Default)] +pub(crate) struct RlpxSubProtocolHandlers(Vec>); + +impl RlpxSubProtocolHandlers { + /// Returns all handlers. + pub(crate) fn into_iter(self) -> impl Iterator> { + self.0.into_iter() + } +} + +impl Deref for RlpxSubProtocolHandlers { + type Target = Vec>; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl DerefMut for RlpxSubProtocolHandlers { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.0 + } } pub(crate) trait DynProtocolHandler: fmt::Debug + Send + Sync + 'static { @@ -156,7 +212,7 @@ pub(crate) trait DynConnectionHandler: Send + Sync + 'static { ) -> OnNotSupported; fn into_connection( - self, + self: Box, direction: Direction, peer_id: PeerId, conn: ProtocolConnection, @@ -181,11 +237,11 @@ where } fn into_connection( - self, + self: Box, direction: Direction, peer_id: PeerId, conn: ProtocolConnection, ) -> Pin + Send + 'static>> { - Box::pin(T::into_connection(self, direction, peer_id, conn)) + Box::pin(T::into_connection(*self, direction, peer_id, conn)) } } diff --git a/crates/net/network/src/session/active.rs b/crates/net/network/src/session/active.rs index 25e53a194b7a..a8424713cd34 100644 --- a/crates/net/network/src/session/active.rs +++ b/crates/net/network/src/session/active.rs @@ -4,6 +4,7 @@ use crate::{ message::{NewBlockMessage, PeerMessage, PeerRequest, PeerResponse, PeerResponseResult}, session::{ config::INITIAL_REQUEST_TIMEOUT, + conn::EthRlpxConnection, handle::{ActiveSessionMessage, SessionCommand}, SessionId, }, @@ -11,16 +12,16 @@ use crate::{ use core::sync::atomic::Ordering; use fnv::FnvHashMap; use futures::{stream::Fuse, SinkExt, StreamExt}; -use reth_ecies::stream::ECIESStream; + use reth_eth_wire::{ capability::Capabilities, errors::{EthHandshakeError, EthStreamError, P2PStreamError}, message::{EthBroadcastMessage, RequestPair}, - DisconnectP2P, DisconnectReason, EthMessage, EthStream, P2PStream, + DisconnectP2P, DisconnectReason, EthMessage, }; use reth_interfaces::p2p::error::RequestError; use reth_metrics::common::mpsc::MeteredPollSender; -use reth_net_common::bandwidth_meter::MeteredStream; + use reth_primitives::PeerId; use std::{ collections::VecDeque, @@ -32,7 +33,6 @@ use std::{ time::{Duration, Instant}, }; use tokio::{ - net::TcpStream, sync::{mpsc::error::TrySendError, oneshot}, time::Interval, }; @@ -51,11 +51,6 @@ const SAMPLE_IMPACT: f64 = 0.1; /// Amount of RTTs before timeout const TIMEOUT_SCALING: u32 = 3; -/// The type of the underlying peer network connection. -// This type is boxed because the underlying stream is ~6KB, -// mostly coming from `P2PStream`'s `snap::Encoder` (2072), and `ECIESStream` (3600). -pub type PeerConnection = Box>>>>; - /// The type that advances an established session by listening for incoming messages (from local /// node or read from connection) and emitting events back to the /// [`SessionManager`](super::SessionManager). @@ -70,7 +65,7 @@ pub(crate) struct ActiveSession { /// Keeps track of request ids. pub(crate) next_id: u64, /// The underlying connection. - pub(crate) conn: PeerConnection, + pub(crate) conn: EthRlpxConnection, /// Identifier of the node we're connected to. pub(crate) remote_peer_id: PeerId, /// The address we're connected to. @@ -771,16 +766,19 @@ mod tests { handle::PendingSessionEvent, start_pending_incoming_session, }; - use reth_ecies::util::pk2id; + use reth_ecies::{stream::ECIESStream, util::pk2id}; use reth_eth_wire::{ - GetBlockBodies, HelloMessageWithProtocols, Status, StatusBuilder, UnauthedEthStream, - UnauthedP2PStream, + EthStream, GetBlockBodies, HelloMessageWithProtocols, P2PStream, Status, StatusBuilder, + UnauthedEthStream, UnauthedP2PStream, }; - use reth_net_common::bandwidth_meter::BandwidthMeter; + use reth_net_common::bandwidth_meter::{BandwidthMeter, MeteredStream}; use reth_primitives::{ForkFilter, Hardfork, MAINNET}; use secp256k1::{SecretKey, SECP256K1}; use std::time::Duration; - use tokio::{net::TcpListener, sync::mpsc}; + use tokio::{ + net::{TcpListener, TcpStream}, + sync::mpsc, + }; /// Returns a testing `HelloMessage` and new secretkey fn eth_hello(server_key: &SecretKey) -> HelloMessageWithProtocols { @@ -856,6 +854,7 @@ mod tests { self.hello.clone(), self.status, self.fork_filter.clone(), + Default::default(), )); let mut stream = ReceiverStream::new(pending_sessions_rx); diff --git a/crates/net/network/src/session/conn.rs b/crates/net/network/src/session/conn.rs new file mode 100644 index 000000000000..a94b3406eb17 --- /dev/null +++ b/crates/net/network/src/session/conn.rs @@ -0,0 +1,156 @@ +//! Connection types for a session + +use futures::{Sink, Stream}; +use reth_ecies::stream::ECIESStream; +use reth_eth_wire::{ + errors::EthStreamError, + message::EthBroadcastMessage, + multiplex::{ProtocolProxy, RlpxSatelliteStream}, + EthMessage, EthStream, EthVersion, P2PStream, +}; +use reth_net_common::bandwidth_meter::MeteredStream; +use std::{ + pin::Pin, + task::{Context, Poll}, +}; +use tokio::net::TcpStream; + +/// The type of the underlying peer network connection. +pub type EthPeerConnection = EthStream>>>; + +/// Various connection types that at least support the ETH protocol. +pub type EthSatelliteConnection = + RlpxSatelliteStream>, EthStream>; + +/// Connection types that support the ETH protocol. +/// +/// Either a [`EthPeerConnection`] or an [`EthSatelliteConnection`]. +// This type is boxed because the underlying stream is ~6KB, +// mostly coming from `P2PStream`'s `snap::Encoder` (2072), and `ECIESStream` (3600). +#[derive(Debug)] +pub enum EthRlpxConnection { + /// A That only supports the ETH protocol. + EthOnly(Box), + /// A connection that supports the ETH protocol and __at least one other__ RLPx protocol. + Satellite(Box), +} + +impl EthRlpxConnection { + /// Returns the negotiated ETH version. + #[inline] + pub(crate) fn version(&self) -> EthVersion { + match self { + Self::EthOnly(conn) => conn.version(), + Self::Satellite(conn) => conn.primary().version(), + } + } + + /// Consumes this type and returns the wrapped [P2PStream]. + #[inline] + pub(crate) fn into_inner(self) -> P2PStream>> { + match self { + Self::EthOnly(conn) => conn.into_inner(), + Self::Satellite(conn) => conn.into_inner(), + } + } + + /// Returns mutable access to the underlying stream. + #[inline] + pub(crate) fn inner_mut(&mut self) -> &mut P2PStream>> { + match self { + Self::EthOnly(conn) => conn.inner_mut(), + Self::Satellite(conn) => conn.inner_mut(), + } + } + + /// Returns access to the underlying stream. + #[inline] + pub(crate) fn inner(&self) -> &P2PStream>> { + match self { + Self::EthOnly(conn) => conn.inner(), + Self::Satellite(conn) => conn.inner(), + } + } + + /// Same as [`Sink::start_send`] but accepts a [`EthBroadcastMessage`] instead. + #[inline] + pub fn start_send_broadcast( + &mut self, + item: EthBroadcastMessage, + ) -> Result<(), EthStreamError> { + match self { + Self::EthOnly(conn) => conn.start_send_broadcast(item), + Self::Satellite(conn) => conn.primary_mut().start_send_broadcast(item), + } + } +} + +impl From for EthRlpxConnection { + #[inline] + fn from(conn: EthPeerConnection) -> Self { + Self::EthOnly(Box::new(conn)) + } +} + +impl From for EthRlpxConnection { + #[inline] + fn from(conn: EthSatelliteConnection) -> Self { + Self::Satellite(Box::new(conn)) + } +} + +macro_rules! delegate_call { + ($self:ident.$method:ident($($args:ident),+)) => { + unsafe { + match $self.get_unchecked_mut() { + Self::EthOnly(l) => Pin::new_unchecked(l).$method($($args),+), + Self::Satellite(r) => Pin::new_unchecked(r).$method($($args),+), + } + } + } +} + +impl Stream for EthRlpxConnection { + type Item = Result; + + fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + delegate_call!(self.poll_next(cx)) + } +} + +impl Sink for EthRlpxConnection { + type Error = EthStreamError; + + fn poll_ready(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + delegate_call!(self.poll_ready(cx)) + } + + fn start_send(self: Pin<&mut Self>, item: EthMessage) -> Result<(), Self::Error> { + delegate_call!(self.start_send(item)) + } + + fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + delegate_call!(self.poll_flush(cx)) + } + + fn poll_close(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + delegate_call!(self.poll_close(cx)) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + fn assert_eth_stream() + where + St: Stream> + Sink, + { + } + + #[test] + fn test_eth_stream_variants() { + assert_eth_stream::(); + assert_eth_stream::(); + } +} diff --git a/crates/net/network/src/session/handle.rs b/crates/net/network/src/session/handle.rs index 44ca0419718e..9e8f4ec2ace9 100644 --- a/crates/net/network/src/session/handle.rs +++ b/crates/net/network/src/session/handle.rs @@ -1,9 +1,7 @@ //! Session handles. - -use super::active::PeerConnection; use crate::{ message::PeerMessage, - session::{Direction, SessionId}, + session::{conn::EthRlpxConnection, Direction, SessionId}, }; use reth_ecies::ECIESError; use reth_eth_wire::{ @@ -174,7 +172,7 @@ pub enum PendingSessionEvent { status: Arc, /// The actual connection stream which can be used to send and receive `eth` protocol /// messages - conn: PeerConnection, + conn: EthRlpxConnection, /// The direction of the session, either `Inbound` or `Outgoing` direction: Direction, /// The remote node's user agent, usually containing the client name and version diff --git a/crates/net/network/src/session/mod.rs b/crates/net/network/src/session/mod.rs index 863964ac97ed..98e5f9e59143 100644 --- a/crates/net/network/src/session/mod.rs +++ b/crates/net/network/src/session/mod.rs @@ -40,15 +40,16 @@ use tracing::{instrument, trace}; mod active; mod config; +mod conn; mod handle; pub use crate::message::PeerRequestSender; +use crate::protocol::{IntoRlpxSubProtocol, RlpxSubProtocolHandlers, RlpxSubProtocols}; pub use config::{SessionLimits, SessionsConfig}; pub use handle::{ ActiveSessionHandle, ActiveSessionMessage, PendingSessionEvent, PendingSessionHandle, SessionCommand, }; - -use crate::protocol::{IntoRlpxSubProtocol, RlpxSubProtocols}; +use reth_eth_wire::multiplex::RlpxProtocolMultiplexer; pub use reth_network_api::{Direction, PeerInfo}; /// Internal identifier for active sessions. @@ -228,6 +229,7 @@ impl SessionManager { let hello_message = self.hello_message.clone(); let status = self.status; let fork_filter = self.fork_filter.clone(); + let extra_handlers = self.extra_protocols.on_incoming(remote_addr); self.spawn(start_pending_incoming_session( disconnect_rx, session_id, @@ -238,6 +240,7 @@ impl SessionManager { hello_message, status, fork_filter, + extra_handlers, )); let handle = PendingSessionHandle { @@ -261,6 +264,7 @@ impl SessionManager { let fork_filter = self.fork_filter.clone(); let status = self.status; let band_with_meter = self.bandwidth_meter.clone(); + let extra_handlers = self.extra_protocols.on_outgoing(remote_addr, remote_peer_id); self.spawn(start_pending_outbound_session( disconnect_rx, pending_events, @@ -272,6 +276,7 @@ impl SessionManager { status, fork_filter, band_with_meter, + extra_handlers, )); let handle = PendingSessionHandle { @@ -757,6 +762,7 @@ pub(crate) async fn start_pending_incoming_session( hello: HelloMessageWithProtocols, status: Status, fork_filter: ForkFilter, + extra_handlers: RlpxSubProtocolHandlers, ) { authenticate( disconnect_rx, @@ -769,6 +775,7 @@ pub(crate) async fn start_pending_incoming_session( hello, status, fork_filter, + extra_handlers, ) .await } @@ -787,6 +794,7 @@ async fn start_pending_outbound_session( status: Status, fork_filter: ForkFilter, bandwidth_meter: BandwidthMeter, + extra_handlers: RlpxSubProtocolHandlers, ) { let stream = match TcpStream::connect(remote_addr).await { Ok(stream) => { @@ -818,6 +826,7 @@ async fn start_pending_outbound_session( hello, status, fork_filter, + extra_handlers, ) .await } @@ -835,6 +844,7 @@ async fn authenticate( hello: HelloMessageWithProtocols, status: Status, fork_filter: ForkFilter, + extra_handlers: RlpxSubProtocolHandlers, ) { let local_addr = stream.inner().local_addr().ok(); let stream = match get_eciess_stream(stream, secret_key, direction).await { @@ -863,6 +873,7 @@ async fn authenticate( hello, status, fork_filter, + extra_handlers, ) .boxed(); @@ -900,7 +911,10 @@ async fn get_eciess_stream( /// Authenticate the stream via handshake /// -/// On Success return the authenticated stream as [`PendingSessionEvent`] +/// On Success return the authenticated stream as [`PendingSessionEvent`]. +/// +/// If additional [RlpxSubProtocolHandlers] are provided, the hello message will be updated to also +/// negotiate the additional protocols. #[allow(clippy::too_many_arguments)] async fn authenticate_stream( stream: UnauthedP2PStream>>, @@ -908,10 +922,14 @@ async fn authenticate_stream( remote_addr: SocketAddr, local_addr: Option, direction: Direction, - hello: HelloMessageWithProtocols, - status: Status, + mut hello: HelloMessageWithProtocols, + mut status: Status, fork_filter: ForkFilter, + mut extra_handlers: RlpxSubProtocolHandlers, ) -> PendingSessionEvent { + // Add extra protocols to the hello message + extra_handlers.retain(|handler| hello.try_add_protocol(handler.protocol()).is_ok()); + // conduct the p2p handshake and return the authenticated stream let (p2p_stream, their_hello) = match stream.handshake(hello).await { Ok(stream_res) => stream_res, @@ -925,8 +943,8 @@ async fn authenticate_stream( } }; - // Ensure we negotiated eth protocol - let version = match p2p_stream.shared_capabilities().eth_version() { + // Ensure we negotiated mandatory eth protocol + let eth_version = match p2p_stream.shared_capabilities().eth_version() { Ok(version) => version, Err(err) => { return PendingSessionEvent::Disconnected { @@ -938,22 +956,45 @@ async fn authenticate_stream( } }; - // if the hello handshake was successful we can try status handshake - // - // Before trying status handshake, set up the version to shared_capability - let status = Status { version, ..status }; - let eth_unauthed = UnauthedEthStream::new(p2p_stream); - let (eth_stream, their_status) = match eth_unauthed.handshake(status, fork_filter).await { - Ok(stream_res) => stream_res, - Err(err) => { - return PendingSessionEvent::Disconnected { - remote_addr, - session_id, - direction, - error: Some(err), + let (conn, their_status) = if p2p_stream.shared_capabilities().len() == 1 { + // if the hello handshake was successful we can try status handshake + // + // Before trying status handshake, set up the version to negotiated shared version + status.set_eth_version(eth_version); + let eth_unauthed = UnauthedEthStream::new(p2p_stream); + let (eth_stream, their_status) = match eth_unauthed.handshake(status, fork_filter).await { + Ok(stream_res) => stream_res, + Err(err) => { + return PendingSessionEvent::Disconnected { + remote_addr, + session_id, + direction, + error: Some(err), + } } + }; + (eth_stream.into(), their_status) + } else { + // Multiplex the stream with the extra protocols + let (mut multiplex_stream, their_status) = RlpxProtocolMultiplexer::new(p2p_stream) + .into_eth_satellite_stream(status, fork_filter) + .await + .unwrap(); + + // install additional handlers + for handler in extra_handlers.into_iter() { + let cap = handler.protocol().cap; + let remote_peer_id = their_hello.id; + multiplex_stream + .install_protocol(&cap, move |conn| { + handler.into_connection(direction, remote_peer_id, conn) + }) + .ok(); } + + (multiplex_stream.into(), their_status) }; + PendingSessionEvent::Established { session_id, remote_addr, @@ -961,7 +1002,7 @@ async fn authenticate_stream( peer_id: their_hello.id, capabilities: Arc::new(Capabilities::from(their_hello.capabilities)), status: Arc::new(their_status), - conn: Box::new(eth_stream), + conn, direction, client_id: their_hello.client_version, } diff --git a/crates/net/network/src/test_utils/testnet.rs b/crates/net/network/src/test_utils/testnet.rs index 1ac94ae335ad..75fc124b7a50 100644 --- a/crates/net/network/src/test_utils/testnet.rs +++ b/crates/net/network/src/test_utils/testnet.rs @@ -4,6 +4,7 @@ use crate::{ builder::ETH_REQUEST_CHANNEL_CAPACITY, error::NetworkError, eth_requests::EthRequestHandler, + protocol::IntoRlpxSubProtocol, transactions::{TransactionsHandle, TransactionsManager}, NetworkConfig, NetworkConfigBuilder, NetworkEvent, NetworkEvents, NetworkHandle, NetworkManager, @@ -340,6 +341,11 @@ where self.network.num_connected_peers() } + /// Adds an additional protocol handler to the peer. + pub fn add_rlpx_sub_protocol(&mut self, protocol: impl IntoRlpxSubProtocol) { + self.network.add_rlpx_sub_protocol(protocol); + } + /// Returns a handle to the peer's network. pub fn peer_handle(&self) -> PeerHandle { PeerHandle { diff --git a/crates/net/network/tests/it/main.rs b/crates/net/network/tests/it/main.rs index 1d65a90cc8a0..a277252dc93f 100644 --- a/crates/net/network/tests/it/main.rs +++ b/crates/net/network/tests/it/main.rs @@ -2,6 +2,7 @@ mod big_pooled_txs_req; mod clique; mod connect; mod geth; +mod multiplex; mod requests; mod session; mod startup; diff --git a/crates/net/network/tests/it/multiplex.rs b/crates/net/network/tests/it/multiplex.rs new file mode 100644 index 000000000000..3026a8efec87 --- /dev/null +++ b/crates/net/network/tests/it/multiplex.rs @@ -0,0 +1,330 @@ +//! Testing gossiping of transactions. + +use crate::multiplex::proto::{PingPongProtoMessage, PingPongProtoMessageKind}; +use futures::{Stream, StreamExt}; +use reth_eth_wire::{ + capability::SharedCapabilities, multiplex::ProtocolConnection, protocol::Protocol, +}; +use reth_network::{ + protocol::{ConnectionHandler, OnNotSupported, ProtocolHandler}, + test_utils::Testnet, +}; +use reth_network_api::Direction; +use reth_primitives::BytesMut; +use reth_provider::test_utils::MockEthProvider; +use reth_rpc_types::PeerId; +use std::{ + net::SocketAddr, + pin::Pin, + task::{ready, Context, Poll}, +}; +use tokio::sync::{mpsc, oneshot}; +use tokio_stream::wrappers::UnboundedReceiverStream; + +/// A simple Rplx subprotocol for +mod proto { + use super::*; + use reth_eth_wire::capability::Capability; + use reth_primitives::{Buf, BufMut}; + + #[repr(u8)] + #[derive(Clone, Copy, Debug, PartialEq, Eq)] + pub enum PingPongProtoMessageId { + Ping = 0x00, + Pong = 0x01, + PingMessage = 0x02, + PongMessage = 0x03, + } + + #[derive(Clone, Debug, PartialEq, Eq)] + pub enum PingPongProtoMessageKind { + Ping, + Pong, + PingMessage(String), + PongMessage(String), + } + + /// An protocol message, containing a message ID and payload. + #[derive(Clone, Debug, PartialEq, Eq)] + pub struct PingPongProtoMessage { + pub message_type: PingPongProtoMessageId, + pub message: PingPongProtoMessageKind, + } + + impl PingPongProtoMessage { + /// Returns the capability for the `ping` protocol. + pub fn capability() -> Capability { + Capability::new_static("ping", 1) + } + + /// Returns the protocol for the `test` protocol. + pub fn protocol() -> Protocol { + Protocol::new(Self::capability(), 4) + } + + /// Creates a ping message + pub fn ping() -> Self { + Self { + message_type: PingPongProtoMessageId::Ping, + message: PingPongProtoMessageKind::Ping, + } + } + + /// Creates a pong message + pub fn pong() -> Self { + Self { + message_type: PingPongProtoMessageId::Pong, + message: PingPongProtoMessageKind::Pong, + } + } + + /// Creates a ping message + pub fn ping_message(msg: impl Into) -> Self { + Self { + message_type: PingPongProtoMessageId::PingMessage, + message: PingPongProtoMessageKind::PingMessage(msg.into()), + } + } + /// Creates a ping message + pub fn pong_message(msg: impl Into) -> Self { + Self { + message_type: PingPongProtoMessageId::PongMessage, + message: PingPongProtoMessageKind::PongMessage(msg.into()), + } + } + + /// Creates a new `TestProtoMessage` with the given message ID and payload. + pub fn encoded(&self) -> BytesMut { + let mut buf = BytesMut::new(); + buf.put_u8(self.message_type as u8); + match &self.message { + PingPongProtoMessageKind::Ping => {} + PingPongProtoMessageKind::Pong => {} + PingPongProtoMessageKind::PingMessage(msg) => { + buf.put(msg.as_bytes()); + } + PingPongProtoMessageKind::PongMessage(msg) => { + buf.put(msg.as_bytes()); + } + } + buf + } + + /// Decodes a `TestProtoMessage` from the given message buffer. + pub fn decode_message(buf: &mut &[u8]) -> Option { + if buf.is_empty() { + return None; + } + let id = buf[0]; + buf.advance(1); + let message_type = match id { + 0x00 => PingPongProtoMessageId::Ping, + 0x01 => PingPongProtoMessageId::Pong, + 0x02 => PingPongProtoMessageId::PingMessage, + 0x03 => PingPongProtoMessageId::PongMessage, + _ => return None, + }; + let message = match message_type { + PingPongProtoMessageId::Ping => PingPongProtoMessageKind::Ping, + PingPongProtoMessageId::Pong => PingPongProtoMessageKind::Pong, + PingPongProtoMessageId::PingMessage => PingPongProtoMessageKind::PingMessage( + String::from_utf8_lossy(&buf[..]).into_owned(), + ), + PingPongProtoMessageId::PongMessage => PingPongProtoMessageKind::PongMessage( + String::from_utf8_lossy(&buf[..]).into_owned(), + ), + }; + Some(Self { message_type, message }) + } + } +} + +#[derive(Debug)] +struct PingPongProtoHandler { + state: ProtocolState, +} + +impl ProtocolHandler for PingPongProtoHandler { + type ConnectionHandler = PingPongConnectionHandler; + + fn on_incoming(&self, _socket_addr: SocketAddr) -> Option { + Some(PingPongConnectionHandler { state: self.state.clone() }) + } + + fn on_outgoing( + &self, + _socket_addr: SocketAddr, + _peer_id: PeerId, + ) -> Option { + Some(PingPongConnectionHandler { state: self.state.clone() }) + } +} + +#[derive(Clone, Debug)] +struct ProtocolState { + events: mpsc::UnboundedSender, +} + +#[derive(Debug)] +#[allow(dead_code)] +enum ProtocolEvent { + Established { + direction: Direction, + peer_id: PeerId, + to_connection: mpsc::UnboundedSender, + }, +} + +enum Command { + /// Send a ping message to the peer. + PingMessage { + msg: String, + /// The response will be sent to this channel. + response: oneshot::Sender, + }, +} + +struct PingPongConnectionHandler { + state: ProtocolState, +} + +impl ConnectionHandler for PingPongConnectionHandler { + type Connection = PingPongProtoConnection; + + fn protocol(&self) -> Protocol { + PingPongProtoMessage::protocol() + } + + fn on_unsupported_by_peer( + self, + _supported: &SharedCapabilities, + _direction: Direction, + _peer_id: PeerId, + ) -> OnNotSupported { + OnNotSupported::KeepAlive + } + + fn into_connection( + self, + direction: Direction, + _peer_id: PeerId, + conn: ProtocolConnection, + ) -> Self::Connection { + let (tx, rx) = mpsc::unbounded_channel(); + self.state + .events + .send(ProtocolEvent::Established { direction, peer_id: _peer_id, to_connection: tx }) + .ok(); + PingPongProtoConnection { + conn, + initial_ping: direction.is_outgoing().then(PingPongProtoMessage::ping), + commands: UnboundedReceiverStream::new(rx), + pending_pong: None, + } + } +} + +struct PingPongProtoConnection { + conn: ProtocolConnection, + initial_ping: Option, + commands: UnboundedReceiverStream, + pending_pong: Option>, +} + +impl Stream for PingPongProtoConnection { + type Item = BytesMut; + + fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + let this = self.get_mut(); + if let Some(initial_ping) = this.initial_ping.take() { + return Poll::Ready(Some(initial_ping.encoded())); + } + + loop { + if let Poll::Ready(Some(cmd)) = this.commands.poll_next_unpin(cx) { + return match cmd { + Command::PingMessage { msg, response } => { + this.pending_pong = Some(response); + Poll::Ready(Some(PingPongProtoMessage::ping_message(msg).encoded())) + } + } + } + let Some(msg) = ready!(this.conn.poll_next_unpin(cx)) else { + return Poll::Ready(None); + }; + + let Some(msg) = PingPongProtoMessage::decode_message(&mut &msg[..]) else { + return Poll::Ready(None); + }; + + match msg.message { + PingPongProtoMessageKind::Ping => { + return Poll::Ready(Some(PingPongProtoMessage::pong().encoded())); + } + PingPongProtoMessageKind::Pong => {} + PingPongProtoMessageKind::PingMessage(msg) => { + return Poll::Ready(Some(PingPongProtoMessage::pong_message(msg).encoded())); + } + PingPongProtoMessageKind::PongMessage(msg) => { + if let Some(sender) = this.pending_pong.take() { + sender.send(msg).ok(); + } + continue + } + } + + return Poll::Pending; + } + } +} + +#[tokio::test(flavor = "multi_thread")] +async fn test_proto_multiplex() { + reth_tracing::init_test_tracing(); + let provider = MockEthProvider::default(); + let mut net = Testnet::create_with(2, provider.clone()).await; + + let (tx, mut from_peer0) = mpsc::unbounded_channel(); + net.peers_mut()[0] + .add_rlpx_sub_protocol(PingPongProtoHandler { state: ProtocolState { events: tx } }); + + let (tx, mut from_peer1) = mpsc::unbounded_channel(); + net.peers_mut()[1] + .add_rlpx_sub_protocol(PingPongProtoHandler { state: ProtocolState { events: tx } }); + + let handle = net.spawn(); + // connect all the peers + handle.connect_peers().await; + + let peer0_to_peer1 = from_peer0.recv().await.unwrap(); + let peer0_conn = match peer0_to_peer1 { + ProtocolEvent::Established { direction: _, peer_id, to_connection } => { + assert_eq!(peer_id, *handle.peers()[1].peer_id()); + to_connection + } + }; + + let peer1_to_peer0 = from_peer1.recv().await.unwrap(); + let peer1_conn = match peer1_to_peer0 { + ProtocolEvent::Established { direction: _, peer_id, to_connection } => { + assert_eq!(peer_id, *handle.peers()[0].peer_id()); + to_connection + } + }; + + let (tx, rx) = oneshot::channel(); + // send a ping message from peer0 to peer1 + peer0_conn.send(Command::PingMessage { msg: "hello!".to_string(), response: tx }).unwrap(); + + let response = rx.await.unwrap(); + assert_eq!(response, "hello!"); + + let (tx, rx) = oneshot::channel(); + // send a ping message from peer1 to peer0 + peer1_conn + .send(Command::PingMessage { msg: "hello from peer1!".to_string(), response: tx }) + .unwrap(); + + let response = rx.await.unwrap(); + assert_eq!(response, "hello from peer1!"); +} From 013b4c93db27e6c1926d3295319f9a4319d4f691 Mon Sep 17 00:00:00 2001 From: Devon Bear Date: Wed, 13 Dec 2023 13:36:24 -0500 Subject: [PATCH 128/277] feat(rpc): Add `tx_hash` to `debug_traceBlockByX` (#5743) Co-authored-by: Matthias Seitz --- crates/rpc/rpc-types/src/eth/trace/common.rs | 30 ++++++++++++++++--- .../rpc/rpc-types/src/eth/trace/geth/mod.rs | 16 ++++++++++ crates/rpc/rpc/src/debug.rs | 13 ++++++-- 3 files changed, 52 insertions(+), 7 deletions(-) diff --git a/crates/rpc/rpc-types/src/eth/trace/common.rs b/crates/rpc/rpc-types/src/eth/trace/common.rs index 78ee088ce282..617b448502d4 100644 --- a/crates/rpc/rpc-types/src/eth/trace/common.rs +++ b/crates/rpc/rpc-types/src/eth/trace/common.rs @@ -1,14 +1,36 @@ //! 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)] -#[allow(missing_docs)] +#[serde(untagged, rename_all = "camelCase")] pub enum TraceResult { /// Untagged success variant - Success { result: Ok }, + 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 { error: Err }, + 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/geth/mod.rs b/crates/rpc/rpc-types/src/eth/trace/geth/mod.rs index a95a3dc2dade..d0f92411e663 100644 --- a/crates/rpc/rpc-types/src/eth/trace/geth/mod.rs +++ b/crates/rpc/rpc-types/src/eth/trace/geth/mod.rs @@ -559,4 +559,20 @@ mod tests { 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/src/debug.rs b/crates/rpc/rpc/src/debug.rs index f520f42e78a9..b4238e1fdace 100644 --- a/crates/rpc/rpc/src/debug.rs +++ b/crates/rpc/rpc/src/debug.rs @@ -102,12 +102,19 @@ where let mut transactions = transactions.into_iter().peekable(); while let Some(tx) = transactions.next() { + let tx_hash = tx.hash; let tx = tx_env_with_recovered(&tx); let env = Env { cfg: cfg.clone(), block: block_env.clone(), tx }; let (result, state_changes) = - this.trace_transaction(opts.clone(), env, at, &mut db)?; - results.push(TraceResult::Success { result }); - + this.trace_transaction(opts.clone(), env, at, &mut db).map_err(|err| { + results.push(TraceResult::Error { + error: err.to_string(), + tx_hash: Some(tx_hash), + }); + err + })?; + + results.push(TraceResult::Success { result, tx_hash: Some(tx_hash) }); if transactions.peek().is_some() { // need to apply the state changes of this transaction before executing the // next transaction From 744930f1de7c8621743c137bf4f95114046bcece Mon Sep 17 00:00:00 2001 From: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Date: Thu, 14 Dec 2023 12:18:18 +0200 Subject: [PATCH 129/277] docs: improve CLI reference generator (#5740) --- .gitattributes | 3 + Makefile | 2 +- book/SUMMARY.md | 106 +- book/cli/SUMMARY.md | 39 + book/cli/cli.md | 120 +- book/cli/config.json | 39 - book/cli/db.md | 986 --------------- book/cli/debug.md | 665 ---------- book/cli/help.py | 264 ++++ book/cli/p2p.md | 318 ----- book/cli/reth.md | 100 ++ book/cli/{ => reth}/config.md | 7 +- book/cli/reth/db.md | 121 ++ book/cli/reth/db/clear.md | 99 ++ book/cli/reth/db/diff.md | 118 ++ book/cli/reth/db/drop.md | 98 ++ book/cli/reth/db/get.md | 107 ++ book/cli/reth/db/list.md | 141 +++ book/cli/reth/db/path.md | 95 ++ book/cli/reth/db/snapshot.md | 144 +++ book/cli/reth/db/stats.md | 95 ++ book/cli/reth/db/version.md | 95 ++ book/cli/reth/debug.md | 91 ++ book/cli/{ => reth}/import.md | 7 +- book/cli/{ => reth}/init.md | 7 +- book/cli/{ => reth}/node.md | 11 +- book/cli/{recover.md => reth/p2p.md} | 151 +-- book/cli/reth/p2p/body.md | 79 ++ book/cli/reth/p2p/header.md | 79 ++ book/cli/reth/recover.md | 88 ++ book/cli/reth/recover/storage-tries.md | 95 ++ book/cli/reth/stage.md | 91 ++ book/cli/reth/stage/drop.md | 112 ++ book/cli/reth/stage/dump.md | 116 ++ book/cli/reth/stage/dump/account-hashing.md | 87 ++ book/cli/reth/stage/dump/execution.md | 87 ++ book/cli/reth/stage/dump/merkle.md | 87 ++ book/cli/reth/stage/dump/storage-hashing.md | 87 ++ book/cli/reth/stage/run.md | 213 ++++ book/cli/reth/stage/unwind.md | 114 ++ book/cli/reth/stage/unwind/num-blocks.md | 99 ++ book/cli/reth/stage/unwind/to-block.md | 99 ++ book/cli/reth/test-vectors.md | 88 ++ book/cli/reth/test-vectors/tables.md | 88 ++ book/cli/stage.md | 1215 ------------------- book/cli/test-vectors.md | 179 --- book/cli/update.sh | 144 +-- book/run/mainnet.md | 2 +- book/run/troubleshooting.md | 4 +- 49 files changed, 3482 insertions(+), 3800 deletions(-) create mode 100644 book/cli/SUMMARY.md delete mode 100644 book/cli/config.json delete mode 100644 book/cli/db.md delete mode 100644 book/cli/debug.md create mode 100755 book/cli/help.py delete mode 100644 book/cli/p2p.md create mode 100644 book/cli/reth.md rename book/cli/{ => reth}/config.md (97%) create mode 100644 book/cli/reth/db.md create mode 100644 book/cli/reth/db/clear.md create mode 100644 book/cli/reth/db/diff.md create mode 100644 book/cli/reth/db/drop.md create mode 100644 book/cli/reth/db/get.md create mode 100644 book/cli/reth/db/list.md create mode 100644 book/cli/reth/db/path.md create mode 100644 book/cli/reth/db/snapshot.md create mode 100644 book/cli/reth/db/stats.md create mode 100644 book/cli/reth/db/version.md create mode 100644 book/cli/reth/debug.md rename book/cli/{ => reth}/import.md (98%) rename book/cli/{ => reth}/init.md (98%) rename book/cli/{ => reth}/node.md (98%) rename book/cli/{recover.md => reth/p2p.md} (51%) create mode 100644 book/cli/reth/p2p/body.md create mode 100644 book/cli/reth/p2p/header.md create mode 100644 book/cli/reth/recover.md create mode 100644 book/cli/reth/recover/storage-tries.md create mode 100644 book/cli/reth/stage.md create mode 100644 book/cli/reth/stage/drop.md create mode 100644 book/cli/reth/stage/dump.md create mode 100644 book/cli/reth/stage/dump/account-hashing.md create mode 100644 book/cli/reth/stage/dump/execution.md create mode 100644 book/cli/reth/stage/dump/merkle.md create mode 100644 book/cli/reth/stage/dump/storage-hashing.md create mode 100644 book/cli/reth/stage/run.md create mode 100644 book/cli/reth/stage/unwind.md create mode 100644 book/cli/reth/stage/unwind/num-blocks.md create mode 100644 book/cli/reth/stage/unwind/to-block.md create mode 100644 book/cli/reth/test-vectors.md create mode 100644 book/cli/reth/test-vectors/tables.md delete mode 100644 book/cli/stage.md delete mode 100644 book/cli/test-vectors.md diff --git a/.gitattributes b/.gitattributes index 72601fc1f8a5..1315d378f8f9 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1 +1,4 @@ +book/cli/**/*.md linguist-vendored +book/cli/cli.md -linguist-vendored + crates/storage/libmdbx-rs/mdbx-sys/** linguist-vendored diff --git a/Makefile b/Makefile index 9ce4eb7d17e8..c3ea8f6db516 100644 --- a/Makefile +++ b/Makefile @@ -227,7 +227,7 @@ db-tools: ## Compile MDBX debugging tools. update-book-cli: ## Update book cli documentation. cargo build --bin reth --features "$(FEATURES)" --profile "$(PROFILE)" @echo "Updating book cli doc..." - @./book/cli/update.sh $(BUILD_PATH) + @./book/cli/update.sh $(BUILD_PATH)/$(PROFILE)/reth .PHONY: maxperf maxperf: diff --git a/book/SUMMARY.md b/book/SUMMARY.md index 1c77704a950b..4ffbf84588c0 100644 --- a/book/SUMMARY.md +++ b/book/SUMMARY.md @@ -1,41 +1,69 @@ # Reth Book -1. [Introduction](./intro.md) -1. [Installation](./installation/installation.md) - 1. [Pre-Built Binaries](./installation/binaries.md) - 1. [Docker](./installation/docker.md) - 1. [Build from Source](./installation/source.md) - 1. [Build for ARM devices](./installation/build-for-arm-devices.md) - 1. [Update Priorities](./installation/priorities.md) -1. [Run a Node](./run/run-a-node.md) - 1. [Mainnet or official testnets](./run/mainnet.md) - 1. [OP Stack](./run/optimism.md) - 1. [Private testnet](./run/private-testnet.md) - 1. [Metrics](./run/observability.md) - 1. [Configuring Reth](./run/config.md) - 1. [Transaction types](./run/transactions.md) - 1. [Pruning & Full Node](./run/pruning.md) - 1. [Ports](./run/ports.md) - 1. [Troubleshooting](./run/troubleshooting.md) -1. [Interacting with Reth over JSON-RPC](./jsonrpc/intro.md) - 1. [eth](./jsonrpc/eth.md) - 1. [web3](./jsonrpc/web3.md) - 1. [net](./jsonrpc/net.md) - 1. [txpool](./jsonrpc/txpool.md) - 1. [debug](./jsonrpc/debug.md) - 1. [trace](./jsonrpc/trace.md) - 1. [admin](./jsonrpc/admin.md) - 1. [rpc](./jsonrpc/rpc.md) -1. [CLI Reference](./cli/cli.md) - 1. [reth node](./cli/node.md) - 1. [reth init](./cli/init.md) - 1. [reth import](./cli/import.md) - 1. [reth db](./cli/db.md) - 1. [reth stage](./cli/stage.md) - 1. [reth p2p](./cli/p2p.md) - 1. [reth test-vectors](./cli/test-vectors.md) - 1. [reth config](./cli/config.md) - 1. [reth debug](./cli/debug.md) - 1. [reth recover](./cli/recover.md) -1. [Developers](./developers/developers.md) - 1. [Contribute](./developers/contribute.md) +- [Introduction](./intro.md) +- [Installation](./installation/installation.md) + - [Pre-Built Binaries](./installation/binaries.md) + - [Docker](./installation/docker.md) + - [Build from Source](./installation/source.md) + - [Build for ARM devices](./installation/build-for-arm-devices.md) + - [Update Priorities](./installation/priorities.md) +- [Run a Node](./run/run-a-node.md) + - [Mainnet or official testnets](./run/mainnet.md) + - [OP Stack](./run/optimism.md) + - [Private testnet](./run/private-testnet.md) + - [Metrics](./run/observability.md) + - [Configuring Reth](./run/config.md) + - [Transaction types](./run/transactions.md) + - [Pruning & Full Node](./run/pruning.md) + - [Ports](./run/ports.md) + - [Troubleshooting](./run/troubleshooting.md) +- [Interacting with Reth over JSON-RPC](./jsonrpc/intro.md) + - [eth](./jsonrpc/eth.md) + - [web3](./jsonrpc/web3.md) + - [net](./jsonrpc/net.md) + - [txpool](./jsonrpc/txpool.md) + - [debug](./jsonrpc/debug.md) + - [trace](./jsonrpc/trace.md) + - [admin](./jsonrpc/admin.md) + - [rpc](./jsonrpc/rpc.md) +- [CLI Reference](./cli/cli.md) + - [`reth`](./cli/reth.md) + - [`reth node`](./cli/reth/node.md) + - [`reth init`](./cli/reth/init.md) + - [`reth import`](./cli/reth/import.md) + - [`reth db`](./cli/reth/db.md) + - [`reth db stats`](./cli/reth/db/stats.md) + - [`reth db list`](./cli/reth/db/list.md) + - [`reth db diff`](./cli/reth/db/diff.md) + - [`reth db get`](./cli/reth/db/get.md) + - [`reth db drop`](./cli/reth/db/drop.md) + - [`reth db clear`](./cli/reth/db/clear.md) + - [`reth db snapshot`](./cli/reth/db/snapshot.md) + - [`reth db version`](./cli/reth/db/version.md) + - [`reth db path`](./cli/reth/db/path.md) + - [`reth stage`](./cli/reth/stage.md) + - [`reth stage run`](./cli/reth/stage/run.md) + - [`reth stage drop`](./cli/reth/stage/drop.md) + - [`reth stage dump`](./cli/reth/stage/dump.md) + - [`reth stage dump execution`](./cli/reth/stage/dump/execution.md) + - [`reth stage dump storage-hashing`](./cli/reth/stage/dump/storage-hashing.md) + - [`reth stage dump account-hashing`](./cli/reth/stage/dump/account-hashing.md) + - [`reth stage dump merkle`](./cli/reth/stage/dump/merkle.md) + - [`reth stage unwind`](./cli/reth/stage/unwind.md) + - [`reth stage unwind to-block`](./cli/reth/stage/unwind/to-block.md) + - [`reth stage unwind num-blocks`](./cli/reth/stage/unwind/num-blocks.md) + - [`reth p2p`](./cli/reth/p2p.md) + - [`reth p2p header`](./cli/reth/p2p/header.md) + - [`reth p2p body`](./cli/reth/p2p/body.md) + - [`reth test-vectors`](./cli/reth/test-vectors.md) + - [`reth test-vectors tables`](./cli/reth/test-vectors/tables.md) + - [`reth config`](./cli/reth/config.md) + - [`reth debug`](./cli/reth/debug.md) + - [`reth debug execution`](./cli/reth/debug/execution.md) + - [`reth debug merkle`](./cli/reth/debug/merkle.md) + - [`reth debug in-memory-merkle`](./cli/reth/debug/in-memory-merkle.md) + - [`reth debug build-block`](./cli/reth/debug/build-block.md) + - [`reth recover`](./cli/reth/recover.md) + - [`reth recover storage-tries`](./cli/reth/recover/storage-tries.md) +- [Developers](./developers/developers.md) + - [Contribute](./developers/contribute.md) diff --git a/book/cli/SUMMARY.md b/book/cli/SUMMARY.md new file mode 100644 index 000000000000..7427e488c7e1 --- /dev/null +++ b/book/cli/SUMMARY.md @@ -0,0 +1,39 @@ +- [`reth`](./reth.md) + - [`reth node`](./reth/node.md) + - [`reth init`](./reth/init.md) + - [`reth import`](./reth/import.md) + - [`reth db`](./reth/db.md) + - [`reth db stats`](./reth/db/stats.md) + - [`reth db list`](./reth/db/list.md) + - [`reth db diff`](./reth/db/diff.md) + - [`reth db get`](./reth/db/get.md) + - [`reth db drop`](./reth/db/drop.md) + - [`reth db clear`](./reth/db/clear.md) + - [`reth db snapshot`](./reth/db/snapshot.md) + - [`reth db version`](./reth/db/version.md) + - [`reth db path`](./reth/db/path.md) + - [`reth stage`](./reth/stage.md) + - [`reth stage run`](./reth/stage/run.md) + - [`reth stage drop`](./reth/stage/drop.md) + - [`reth stage dump`](./reth/stage/dump.md) + - [`reth stage dump execution`](./reth/stage/dump/execution.md) + - [`reth stage dump storage-hashing`](./reth/stage/dump/storage-hashing.md) + - [`reth stage dump account-hashing`](./reth/stage/dump/account-hashing.md) + - [`reth stage dump merkle`](./reth/stage/dump/merkle.md) + - [`reth stage unwind`](./reth/stage/unwind.md) + - [`reth stage unwind to-block`](./reth/stage/unwind/to-block.md) + - [`reth stage unwind num-blocks`](./reth/stage/unwind/num-blocks.md) + - [`reth p2p`](./reth/p2p.md) + - [`reth p2p header`](./reth/p2p/header.md) + - [`reth p2p body`](./reth/p2p/body.md) + - [`reth test-vectors`](./reth/test-vectors.md) + - [`reth test-vectors tables`](./reth/test-vectors/tables.md) + - [`reth config`](./reth/config.md) + - [`reth debug`](./reth/debug.md) + - [`reth debug execution`](./reth/debug/execution.md) + - [`reth debug merkle`](./reth/debug/merkle.md) + - [`reth debug in-memory-merkle`](./reth/debug/in-memory-merkle.md) + - [`reth debug build-block`](./reth/debug/build-block.md) + - [`reth recover`](./reth/recover.md) + - [`reth recover storage-tries`](./reth/recover/storage-tries.md) + diff --git a/book/cli/cli.md b/book/cli/cli.md index 394883c5c4b2..ef1a98af5259 100644 --- a/book/cli/cli.md +++ b/book/cli/cli.md @@ -2,122 +2,6 @@ The Reth node is operated via the CLI by running the `reth node` command. To stop it, press `ctrl-c`. You may need to wait a bit as Reth tears down existing p2p connections or other cleanup tasks. -However, Reth has more commands than that: +However, Reth has more commands: -```bash -reth --help -``` - -Some of the most useful commands as a node developer are: -* [`reth node`](./node.md): Starts the Reth node's components, including the JSON-RPC. -* [`reth init`](./init.md): Initialize the database from a genesis file. -* [`reth import`](./import.md): This syncs RLP encoded blocks from a file. -* [`reth db`](./db.md): Administrative TUI to the key-value store. -* [`reth stage`](./stage.md): Runs a stage in isolation. Useful for testing and benchmarking. -* [`reth p2p`](./p2p.md): P2P-related utilities -* [`reth test-vectors`](./test-vectors.md): Generate Test Vectors -* [`reth config`](./config.md): Write config to stdout -* [`reth debug`](./debug.md): Various debug routines - -See below for the full list of commands. - -## Commands - -```bash -$ reth --help -Reth - -Usage: reth [OPTIONS] - -Commands: - node Start the node - init Initialize the database from a genesis file - import This syncs RLP encoded blocks from a file - db Database debugging utilities - stage Manipulate individual stages - p2p P2P Debugging utilities - test-vectors Generate Test Vectors - config Write config to stdout - debug Various debug routines - recover Scripts for node recovery - help Print this message or the help of the given subcommand(s) - -Options: - --chain - The chain this node is running. - Possible values are either a built-in chain or the path to a chain specification file. - - Built-in chains: - mainnet, sepolia, goerli, holesky, dev - - [default: mainnet] - - --instance - Add a new instance of a node. - - Configures the ports of the node to avoid conflicts with the defaults. This is useful for running multiple nodes on the same machine. - - Max number of instances is 200. It is chosen in a way so that it's not possible to have port numbers that conflict with each other. - - Changes to the following port numbers: - DISCOVERY_PORT: default + `instance` - 1 - AUTH_PORT: default + `instance` * 100 - 100 - HTTP_RPC_PORT: default - `instance` + 1 - WS_RPC_PORT: default + `instance` * 2 - 2 - - [default: 1] - - -h, --help - Print help (see a summary with '-h') - - -V, --version - Print version - -Logging: - --log.file.directory - The path to put log files in - - [default: /reth/logs] - - --log.file.max-size - The maximum size (in MB) of one log file - - [default: 200] - - --log.file.max-files - The maximum amount of log files that will be stored. If set to 0, background file logging is disabled - - [default: 5] - - --log.file.filter - The filter to use for logs written to the log file - - [default: debug] - - --log.journald - Write logs to journald - - --log.journald.filter - The filter to use for logs written to journald - - [default: error] - - --color - Sets whether or not the formatter emits ANSI terminal escape codes for colors and other text formatting - - [default: always] - - Possible values: - - always: Colors on - - auto: Colors on - - never: Colors off - -Display: - -v, --verbosity... - Set the minimum log level. - - -v Errors - -vv Warnings - -vvv Info - -vvvv Debug - -vvvvv Traces (warning: very verbose!) - - -q, --quiet - Silence all log output -``` +{{#include ./SUMMARY.md}} diff --git a/book/cli/config.json b/book/cli/config.json deleted file mode 100644 index 406575eebea4..000000000000 --- a/book/cli/config.json +++ /dev/null @@ -1,39 +0,0 @@ -{ - "commands": { - "config": [], - "db": { - "stats": [], - "list": [], - "diff": [], - "get": [], - "drop": [], - "clear": [], - "version": [], - "path": [] - }, - "debug": { - "execution": [], - "merkle": [], - "in-memory-merkle": [] - }, - "import": [], - "init": [], - "node": [], - "p2p": { - "header": [], - "body": [] - }, - "recover": { - "storage-tries": [] - }, - "stage": { - "run": [], - "drop": [], - "dump": ["execution", "storage-hashing", "account-hashing", "merkle"], - "unwind": ["to-block", "num-blocks"] - }, - "test-vectors": { - "tables": [] - } - } -} diff --git a/book/cli/db.md b/book/cli/db.md deleted file mode 100644 index 47f843ad8883..000000000000 --- a/book/cli/db.md +++ /dev/null @@ -1,986 +0,0 @@ -# `reth db` - -Database debugging utilities - -```bash -$ reth db --help - -Usage: reth db [OPTIONS] - -Commands: - stats Lists all the tables, their entry count and their size - list Lists the contents of a table - diff Create a diff between two database tables or two entire databases - get Gets the content of a table for the given key - drop Deletes all database entries - clear Deletes all table entries - snapshot Snapshots tables from database - version Lists current and local database versions - path Returns the full database path - help Print this message or the help of the given subcommand(s) - -Options: - --datadir - The path to the data dir for all reth files and subdirectories. - - Defaults to the OS-specific data directory: - - - Linux: `$XDG_DATA_HOME/reth/` or `$HOME/.local/share/reth/` - - Windows: `{FOLDERID_RoamingAppData}/reth/` - - macOS: `$HOME/Library/Application Support/reth/` - - [default: default] - - --chain - The chain this node is running. - Possible values are either a built-in chain or the path to a chain specification file. - - Built-in chains: - mainnet, sepolia, goerli, holesky, dev - - [default: mainnet] - - --instance - Add a new instance of a node. - - Configures the ports of the node to avoid conflicts with the defaults. This is useful for running multiple nodes on the same machine. - - Max number of instances is 200. It is chosen in a way so that it's not possible to have port numbers that conflict with each other. - - Changes to the following port numbers: - DISCOVERY_PORT: default + `instance` - 1 - AUTH_PORT: default + `instance` * 100 - 100 - HTTP_RPC_PORT: default - `instance` + 1 - WS_RPC_PORT: default + `instance` * 2 - 2 - - [default: 1] - - -h, --help - Print help (see a summary with '-h') - -Database: - --db.log-level - Database logging level. Levels higher than "notice" require a debug build - - Possible values: - - fatal: Enables logging for critical conditions, i.e. assertion failures - - error: Enables logging for error conditions - - warn: Enables logging for warning conditions - - notice: Enables logging for normal but significant condition - - verbose: Enables logging for verbose informational - - debug: Enables logging for debug-level messages - - trace: Enables logging for trace debug-level messages - - extra: Enables logging for extra debug-level messages - -Logging: - --log.file.directory - The path to put log files in - - [default: /reth/logs] - - --log.file.max-size - The maximum size (in MB) of one log file - - [default: 200] - - --log.file.max-files - The maximum amount of log files that will be stored. If set to 0, background file logging is disabled - - [default: 5] - - --log.file.filter - The filter to use for logs written to the log file - - [default: debug] - - --log.journald - Write logs to journald - - --log.journald.filter - The filter to use for logs written to journald - - [default: error] - - --color - Sets whether or not the formatter emits ANSI terminal escape codes for colors and other text formatting - - [default: always] - - Possible values: - - always: Colors on - - auto: Colors on - - never: Colors off - -Display: - -v, --verbosity... - Set the minimum log level. - - -v Errors - -vv Warnings - -vvv Info - -vvvv Debug - -vvvvv Traces (warning: very verbose!) - - -q, --quiet - Silence all log output -``` - -## `reth db clear` - -Deletes all table entries - -```bash -$ reth db clear --help - -Usage: reth db clear [OPTIONS]
- -Arguments: -
- Table name - -Options: - --datadir - The path to the data dir for all reth files and subdirectories. - - Defaults to the OS-specific data directory: - - - Linux: `$XDG_DATA_HOME/reth/` or `$HOME/.local/share/reth/` - - Windows: `{FOLDERID_RoamingAppData}/reth/` - - macOS: `$HOME/Library/Application Support/reth/` - - [default: default] - - --chain - The chain this node is running. - Possible values are either a built-in chain or the path to a chain specification file. - - Built-in chains: - mainnet, sepolia, goerli, holesky, dev - - [default: mainnet] - - --instance - Add a new instance of a node. - - Configures the ports of the node to avoid conflicts with the defaults. This is useful for running multiple nodes on the same machine. - - Max number of instances is 200. It is chosen in a way so that it's not possible to have port numbers that conflict with each other. - - Changes to the following port numbers: - DISCOVERY_PORT: default + `instance` - 1 - AUTH_PORT: default + `instance` * 100 - 100 - HTTP_RPC_PORT: default - `instance` + 1 - WS_RPC_PORT: default + `instance` * 2 - 2 - - [default: 1] - - -h, --help - Print help (see a summary with '-h') - -Logging: - --log.file.directory - The path to put log files in - - [default: /reth/logs] - - --log.file.max-size - The maximum size (in MB) of one log file - - [default: 200] - - --log.file.max-files - The maximum amount of log files that will be stored. If set to 0, background file logging is disabled - - [default: 5] - - --log.file.filter - The filter to use for logs written to the log file - - [default: debug] - - --log.journald - Write logs to journald - - --log.journald.filter - The filter to use for logs written to journald - - [default: error] - - --color - Sets whether or not the formatter emits ANSI terminal escape codes for colors and other text formatting - - [default: always] - - Possible values: - - always: Colors on - - auto: Colors on - - never: Colors off - -Display: - -v, --verbosity... - Set the minimum log level. - - -v Errors - -vv Warnings - -vvv Info - -vvvv Debug - -vvvvv Traces (warning: very verbose!) - - -q, --quiet - Silence all log output -``` - -## `reth db diff` - -Create a diff between two database tables or two entire databases - -```bash -$ reth db diff --help - -Usage: reth db diff [OPTIONS] --secondary-datadir --output - -Options: - --datadir - The path to the data dir for all reth files and subdirectories. - - Defaults to the OS-specific data directory: - - - Linux: `$XDG_DATA_HOME/reth/` or `$HOME/.local/share/reth/` - - Windows: `{FOLDERID_RoamingAppData}/reth/` - - macOS: `$HOME/Library/Application Support/reth/` - - [default: default] - - --secondary-datadir - The path to the data dir for all reth files and subdirectories. - - --chain - The chain this node is running. - Possible values are either a built-in chain or the path to a chain specification file. - - Built-in chains: - mainnet, sepolia, goerli, holesky, dev - - [default: mainnet] - - --instance - Add a new instance of a node. - - Configures the ports of the node to avoid conflicts with the defaults. This is useful for running multiple nodes on the same machine. - - Max number of instances is 200. It is chosen in a way so that it's not possible to have port numbers that conflict with each other. - - Changes to the following port numbers: - DISCOVERY_PORT: default + `instance` - 1 - AUTH_PORT: default + `instance` * 100 - 100 - HTTP_RPC_PORT: default - `instance` + 1 - WS_RPC_PORT: default + `instance` * 2 - 2 - - [default: 1] - - -h, --help - Print help (see a summary with '-h') - -Database: - --db.log-level - Database logging level. Levels higher than "notice" require a debug build - - Possible values: - - fatal: Enables logging for critical conditions, i.e. assertion failures - - error: Enables logging for error conditions - - warn: Enables logging for warning conditions - - notice: Enables logging for normal but significant condition - - verbose: Enables logging for verbose informational - - debug: Enables logging for debug-level messages - - trace: Enables logging for trace debug-level messages - - extra: Enables logging for extra debug-level messages - - --table
- The table name to diff. If not specified, all tables are diffed. - - --output - The output directory for the diff report. - -Logging: - --log.file.directory - The path to put log files in - - [default: /reth/logs] - - --log.file.max-size - The maximum size (in MB) of one log file - - [default: 200] - - --log.file.max-files - The maximum amount of log files that will be stored. If set to 0, background file logging is disabled - - [default: 5] - - --log.file.filter - The filter to use for logs written to the log file - - [default: debug] - - --log.journald - Write logs to journald - - --log.journald.filter - The filter to use for logs written to journald - - [default: error] - - --color - Sets whether or not the formatter emits ANSI terminal escape codes for colors and other text formatting - - [default: always] - - Possible values: - - always: Colors on - - auto: Colors on - - never: Colors off - -Display: - -v, --verbosity... - Set the minimum log level. - - -v Errors - -vv Warnings - -vvv Info - -vvvv Debug - -vvvvv Traces (warning: very verbose!) - - -q, --quiet - Silence all log output -``` - -## `reth db drop` - -Deletes all database entries - -```bash -$ reth db drop --help - -Usage: reth db drop [OPTIONS] - -Options: - --datadir - The path to the data dir for all reth files and subdirectories. - - Defaults to the OS-specific data directory: - - - Linux: `$XDG_DATA_HOME/reth/` or `$HOME/.local/share/reth/` - - Windows: `{FOLDERID_RoamingAppData}/reth/` - - macOS: `$HOME/Library/Application Support/reth/` - - [default: default] - - -f, --force - Bypasses the interactive confirmation and drops the database directly - - --chain - The chain this node is running. - Possible values are either a built-in chain or the path to a chain specification file. - - Built-in chains: - mainnet, sepolia, goerli, holesky, dev - - [default: mainnet] - - --instance - Add a new instance of a node. - - Configures the ports of the node to avoid conflicts with the defaults. This is useful for running multiple nodes on the same machine. - - Max number of instances is 200. It is chosen in a way so that it's not possible to have port numbers that conflict with each other. - - Changes to the following port numbers: - DISCOVERY_PORT: default + `instance` - 1 - AUTH_PORT: default + `instance` * 100 - 100 - HTTP_RPC_PORT: default - `instance` + 1 - WS_RPC_PORT: default + `instance` * 2 - 2 - - [default: 1] - - -h, --help - Print help (see a summary with '-h') - -Logging: - --log.file.directory - The path to put log files in - - [default: /reth/logs] - - --log.file.max-size - The maximum size (in MB) of one log file - - [default: 200] - - --log.file.max-files - The maximum amount of log files that will be stored. If set to 0, background file logging is disabled - - [default: 5] - - --log.file.filter - The filter to use for logs written to the log file - - [default: debug] - - --log.journald - Write logs to journald - - --log.journald.filter - The filter to use for logs written to journald - - [default: error] - - --color - Sets whether or not the formatter emits ANSI terminal escape codes for colors and other text formatting - - [default: always] - - Possible values: - - always: Colors on - - auto: Colors on - - never: Colors off - -Display: - -v, --verbosity... - Set the minimum log level. - - -v Errors - -vv Warnings - -vvv Info - -vvvv Debug - -vvvvv Traces (warning: very verbose!) - - -q, --quiet - Silence all log output -``` - -## `reth db get` - -Gets the content of a table for the given key - -```bash -$ reth db get --help - -Usage: reth db get [OPTIONS]
- -Arguments: -
- The table name - - NOTE: The dupsort tables are not supported now. - - - The key to get content for - -Options: - --datadir - The path to the data dir for all reth files and subdirectories. - - Defaults to the OS-specific data directory: - - - Linux: `$XDG_DATA_HOME/reth/` or `$HOME/.local/share/reth/` - - Windows: `{FOLDERID_RoamingAppData}/reth/` - - macOS: `$HOME/Library/Application Support/reth/` - - [default: default] - - --raw - Output bytes instead of human-readable decoded value - - --chain - The chain this node is running. - Possible values are either a built-in chain or the path to a chain specification file. - - Built-in chains: - mainnet, sepolia, goerli, holesky, dev - - [default: mainnet] - - --instance - Add a new instance of a node. - - Configures the ports of the node to avoid conflicts with the defaults. This is useful for running multiple nodes on the same machine. - - Max number of instances is 200. It is chosen in a way so that it's not possible to have port numbers that conflict with each other. - - Changes to the following port numbers: - DISCOVERY_PORT: default + `instance` - 1 - AUTH_PORT: default + `instance` * 100 - 100 - HTTP_RPC_PORT: default - `instance` + 1 - WS_RPC_PORT: default + `instance` * 2 - 2 - - [default: 1] - - -h, --help - Print help (see a summary with '-h') - -Logging: - --log.file.directory - The path to put log files in - - [default: /reth/logs] - - --log.file.max-size - The maximum size (in MB) of one log file - - [default: 200] - - --log.file.max-files - The maximum amount of log files that will be stored. If set to 0, background file logging is disabled - - [default: 5] - - --log.file.filter - The filter to use for logs written to the log file - - [default: debug] - - --log.journald - Write logs to journald - - --log.journald.filter - The filter to use for logs written to journald - - [default: error] - - --color - Sets whether or not the formatter emits ANSI terminal escape codes for colors and other text formatting - - [default: always] - - Possible values: - - always: Colors on - - auto: Colors on - - never: Colors off - -Display: - -v, --verbosity... - Set the minimum log level. - - -v Errors - -vv Warnings - -vvv Info - -vvvv Debug - -vvvvv Traces (warning: very verbose!) - - -q, --quiet - Silence all log output -``` - -## `reth db list` - -Lists the contents of a table - -```bash -$ reth db list --help - -Usage: reth db list [OPTIONS]
- -Arguments: -
- The table name - -Options: - --datadir - The path to the data dir for all reth files and subdirectories. - - Defaults to the OS-specific data directory: - - - Linux: `$XDG_DATA_HOME/reth/` or `$HOME/.local/share/reth/` - - Windows: `{FOLDERID_RoamingAppData}/reth/` - - macOS: `$HOME/Library/Application Support/reth/` - - [default: default] - - -s, --skip - Skip first N entries - - [default: 0] - - --chain - The chain this node is running. - Possible values are either a built-in chain or the path to a chain specification file. - - Built-in chains: - mainnet, sepolia, goerli, holesky, dev - - [default: mainnet] - - -r, --reverse - Reverse the order of the entries. If enabled last table entries are read - - -l, --len - How many items to take from the walker - - [default: 5] - - --search - Search parameter for both keys and values. Prefix it with `0x` to search for binary data, and text otherwise. - - ATTENTION! For compressed tables (`Transactions` and `Receipts`), there might be missing results since the search uses the raw uncompressed value from the database. - - --min-row-size - Minimum size of row in bytes - - [default: 0] - - --min-key-size - Minimum size of key in bytes - - [default: 0] - - --min-value-size - Minimum size of value in bytes - - [default: 0] - - -c, --count - Returns the number of rows found - - -j, --json - Dump as JSON instead of using TUI - - --raw - Output bytes instead of human-readable decoded value - - --instance - Add a new instance of a node. - - Configures the ports of the node to avoid conflicts with the defaults. This is useful for running multiple nodes on the same machine. - - Max number of instances is 200. It is chosen in a way so that it's not possible to have port numbers that conflict with each other. - - Changes to the following port numbers: - DISCOVERY_PORT: default + `instance` - 1 - AUTH_PORT: default + `instance` * 100 - 100 - HTTP_RPC_PORT: default - `instance` + 1 - WS_RPC_PORT: default + `instance` * 2 - 2 - - [default: 1] - - -h, --help - Print help (see a summary with '-h') - -Logging: - --log.file.directory - The path to put log files in - - [default: /reth/logs] - - --log.file.max-size - The maximum size (in MB) of one log file - - [default: 200] - - --log.file.max-files - The maximum amount of log files that will be stored. If set to 0, background file logging is disabled - - [default: 5] - - --log.file.filter - The filter to use for logs written to the log file - - [default: debug] - - --log.journald - Write logs to journald - - --log.journald.filter - The filter to use for logs written to journald - - [default: error] - - --color - Sets whether or not the formatter emits ANSI terminal escape codes for colors and other text formatting - - [default: always] - - Possible values: - - always: Colors on - - auto: Colors on - - never: Colors off - -Display: - -v, --verbosity... - Set the minimum log level. - - -v Errors - -vv Warnings - -vvv Info - -vvvv Debug - -vvvvv Traces (warning: very verbose!) - - -q, --quiet - Silence all log output -``` - -## `reth db path` - -Returns the full database path - -```bash -$ reth db path --help - -Usage: reth db path [OPTIONS] - -Options: - --datadir - The path to the data dir for all reth files and subdirectories. - - Defaults to the OS-specific data directory: - - - Linux: `$XDG_DATA_HOME/reth/` or `$HOME/.local/share/reth/` - - Windows: `{FOLDERID_RoamingAppData}/reth/` - - macOS: `$HOME/Library/Application Support/reth/` - - [default: default] - - --chain - The chain this node is running. - Possible values are either a built-in chain or the path to a chain specification file. - - Built-in chains: - mainnet, sepolia, goerli, holesky, dev - - [default: mainnet] - - --instance - Add a new instance of a node. - - Configures the ports of the node to avoid conflicts with the defaults. This is useful for running multiple nodes on the same machine. - - Max number of instances is 200. It is chosen in a way so that it's not possible to have port numbers that conflict with each other. - - Changes to the following port numbers: - DISCOVERY_PORT: default + `instance` - 1 - AUTH_PORT: default + `instance` * 100 - 100 - HTTP_RPC_PORT: default - `instance` + 1 - WS_RPC_PORT: default + `instance` * 2 - 2 - - [default: 1] - - -h, --help - Print help (see a summary with '-h') - -Logging: - --log.file.directory - The path to put log files in - - [default: /reth/logs] - - --log.file.max-size - The maximum size (in MB) of one log file - - [default: 200] - - --log.file.max-files - The maximum amount of log files that will be stored. If set to 0, background file logging is disabled - - [default: 5] - - --log.file.filter - The filter to use for logs written to the log file - - [default: debug] - - --log.journald - Write logs to journald - - --log.journald.filter - The filter to use for logs written to journald - - [default: error] - - --color - Sets whether or not the formatter emits ANSI terminal escape codes for colors and other text formatting - - [default: always] - - Possible values: - - always: Colors on - - auto: Colors on - - never: Colors off - -Display: - -v, --verbosity... - Set the minimum log level. - - -v Errors - -vv Warnings - -vvv Info - -vvvv Debug - -vvvvv Traces (warning: very verbose!) - - -q, --quiet - Silence all log output -``` - -## `reth db stats` - -Lists all the tables, their entry count and their size - -```bash -$ reth db stats --help - -Usage: reth db stats [OPTIONS] - -Options: - --datadir - The path to the data dir for all reth files and subdirectories. - - Defaults to the OS-specific data directory: - - - Linux: `$XDG_DATA_HOME/reth/` or `$HOME/.local/share/reth/` - - Windows: `{FOLDERID_RoamingAppData}/reth/` - - macOS: `$HOME/Library/Application Support/reth/` - - [default: default] - - --chain - The chain this node is running. - Possible values are either a built-in chain or the path to a chain specification file. - - Built-in chains: - mainnet, sepolia, goerli, holesky, dev - - [default: mainnet] - - --instance - Add a new instance of a node. - - Configures the ports of the node to avoid conflicts with the defaults. This is useful for running multiple nodes on the same machine. - - Max number of instances is 200. It is chosen in a way so that it's not possible to have port numbers that conflict with each other. - - Changes to the following port numbers: - DISCOVERY_PORT: default + `instance` - 1 - AUTH_PORT: default + `instance` * 100 - 100 - HTTP_RPC_PORT: default - `instance` + 1 - WS_RPC_PORT: default + `instance` * 2 - 2 - - [default: 1] - - -h, --help - Print help (see a summary with '-h') - -Logging: - --log.file.directory - The path to put log files in - - [default: /reth/logs] - - --log.file.max-size - The maximum size (in MB) of one log file - - [default: 200] - - --log.file.max-files - The maximum amount of log files that will be stored. If set to 0, background file logging is disabled - - [default: 5] - - --log.file.filter - The filter to use for logs written to the log file - - [default: debug] - - --log.journald - Write logs to journald - - --log.journald.filter - The filter to use for logs written to journald - - [default: error] - - --color - Sets whether or not the formatter emits ANSI terminal escape codes for colors and other text formatting - - [default: always] - - Possible values: - - always: Colors on - - auto: Colors on - - never: Colors off - -Display: - -v, --verbosity... - Set the minimum log level. - - -v Errors - -vv Warnings - -vvv Info - -vvvv Debug - -vvvvv Traces (warning: very verbose!) - - -q, --quiet - Silence all log output -``` - -## `reth db version` - -Lists current and local database versions - -```bash -$ reth db version --help - -Usage: reth db version [OPTIONS] - -Options: - --datadir - The path to the data dir for all reth files and subdirectories. - - Defaults to the OS-specific data directory: - - - Linux: `$XDG_DATA_HOME/reth/` or `$HOME/.local/share/reth/` - - Windows: `{FOLDERID_RoamingAppData}/reth/` - - macOS: `$HOME/Library/Application Support/reth/` - - [default: default] - - --chain - The chain this node is running. - Possible values are either a built-in chain or the path to a chain specification file. - - Built-in chains: - mainnet, sepolia, goerli, holesky, dev - - [default: mainnet] - - --instance - Add a new instance of a node. - - Configures the ports of the node to avoid conflicts with the defaults. This is useful for running multiple nodes on the same machine. - - Max number of instances is 200. It is chosen in a way so that it's not possible to have port numbers that conflict with each other. - - Changes to the following port numbers: - DISCOVERY_PORT: default + `instance` - 1 - AUTH_PORT: default + `instance` * 100 - 100 - HTTP_RPC_PORT: default - `instance` + 1 - WS_RPC_PORT: default + `instance` * 2 - 2 - - [default: 1] - - -h, --help - Print help (see a summary with '-h') - -Logging: - --log.file.directory - The path to put log files in - - [default: /reth/logs] - - --log.file.max-size - The maximum size (in MB) of one log file - - [default: 200] - - --log.file.max-files - The maximum amount of log files that will be stored. If set to 0, background file logging is disabled - - [default: 5] - - --log.file.filter - The filter to use for logs written to the log file - - [default: debug] - - --log.journald - Write logs to journald - - --log.journald.filter - The filter to use for logs written to journald - - [default: error] - - --color - Sets whether or not the formatter emits ANSI terminal escape codes for colors and other text formatting - - [default: always] - - Possible values: - - always: Colors on - - auto: Colors on - - never: Colors off - -Display: - -v, --verbosity... - Set the minimum log level. - - -v Errors - -vv Warnings - -vvv Info - -vvvv Debug - -vvvvv Traces (warning: very verbose!) - - -q, --quiet - Silence all log output -``` diff --git a/book/cli/debug.md b/book/cli/debug.md deleted file mode 100644 index f0498b800979..000000000000 --- a/book/cli/debug.md +++ /dev/null @@ -1,665 +0,0 @@ -# `reth debug` - -Various debug routines - -```bash -$ reth debug --help - -Usage: reth debug [OPTIONS] - -Commands: - execution Debug the roundtrip execution of blocks as well as the generated data - merkle Debug the clean & incremental state root calculations - in-memory-merkle Debug in-memory state root calculation - build-block Debug block building - help Print this message or the help of the given subcommand(s) - -Options: - --chain - The chain this node is running. - Possible values are either a built-in chain or the path to a chain specification file. - - Built-in chains: - mainnet, sepolia, goerli, holesky, dev - - [default: mainnet] - - --instance - Add a new instance of a node. - - Configures the ports of the node to avoid conflicts with the defaults. This is useful for running multiple nodes on the same machine. - - Max number of instances is 200. It is chosen in a way so that it's not possible to have port numbers that conflict with each other. - - Changes to the following port numbers: - DISCOVERY_PORT: default + `instance` - 1 - AUTH_PORT: default + `instance` * 100 - 100 - HTTP_RPC_PORT: default - `instance` + 1 - WS_RPC_PORT: default + `instance` * 2 - 2 - - [default: 1] - - -h, --help - Print help (see a summary with '-h') - -Logging: - --log.file.directory - The path to put log files in - - [default: /reth/logs] - - --log.file.max-size - The maximum size (in MB) of one log file - - [default: 200] - - --log.file.max-files - The maximum amount of log files that will be stored. If set to 0, background file logging is disabled - - [default: 5] - - --log.file.filter - The filter to use for logs written to the log file - - [default: debug] - - --log.journald - Write logs to journald - - --log.journald.filter - The filter to use for logs written to journald - - [default: error] - - --color - Sets whether or not the formatter emits ANSI terminal escape codes for colors and other text formatting - - [default: always] - - Possible values: - - always: Colors on - - auto: Colors on - - never: Colors off - -Display: - -v, --verbosity... - Set the minimum log level. - - -v Errors - -vv Warnings - -vvv Info - -vvvv Debug - -vvvvv Traces (warning: very verbose!) - - -q, --quiet - Silence all log output -``` - -## `reth debug execution` - -Debug the roundtrip execution of blocks as well as the generated data - -```bash -$ reth debug execution --help - -Usage: reth debug execution [OPTIONS] --to - -Options: - --datadir - The path to the data dir for all reth files and subdirectories. - - Defaults to the OS-specific data directory: - - - Linux: `$XDG_DATA_HOME/reth/` or `$HOME/.local/share/reth/` - - Windows: `{FOLDERID_RoamingAppData}/reth/` - - macOS: `$HOME/Library/Application Support/reth/` - - [default: default] - - --chain - The chain this node is running. - Possible values are either a built-in chain or the path to a chain specification file. - - Built-in chains: - mainnet, sepolia, goerli, holesky, dev - - [default: mainnet] - - --instance - Add a new instance of a node. - - Configures the ports of the node to avoid conflicts with the defaults. This is useful for running multiple nodes on the same machine. - - Max number of instances is 200. It is chosen in a way so that it's not possible to have port numbers that conflict with each other. - - Changes to the following port numbers: - DISCOVERY_PORT: default + `instance` - 1 - AUTH_PORT: default + `instance` * 100 - 100 - HTTP_RPC_PORT: default - `instance` + 1 - WS_RPC_PORT: default + `instance` * 2 - 2 - - [default: 1] - - -h, --help - Print help (see a summary with '-h') - -Networking: - -d, --disable-discovery - Disable the discovery service - - --disable-dns-discovery - Disable the DNS discovery - - --disable-discv4-discovery - Disable Discv4 discovery - - --discovery.addr - The UDP address to use for P2P discovery/networking - - [default: 0.0.0.0] - - --discovery.port - The UDP port to use for P2P discovery/networking - - [default: 30303] - - --trusted-peers - Comma separated enode URLs of trusted peers for P2P connections. - - --trusted-peers enode://abcd@192.168.0.1:30303 - - --trusted-only - Connect only to trusted peers - - --bootnodes - Comma separated enode URLs for P2P discovery bootstrap. - - Will fall back to a network-specific default if not specified. - - --peers-file - The path to the known peers file. Connected peers are dumped to this file on nodes - shutdown, and read on startup. Cannot be used with `--no-persist-peers`. - - --identity - Custom node identity - - [default: reth/v0.1.0-alpha.13-10a83e594/aarch64-apple-darwin] - - --p2p-secret-key - Secret key to use for this node. - - This will also deterministically set the peer ID. If not specified, it will be set in the data dir for the chain being used. - - --no-persist-peers - Do not persist peers. - - --nat - NAT resolution method (any|none|upnp|publicip|extip:) - - [default: any] - - --addr - Network listening address - - [default: 0.0.0.0] - - --port - Network listening port - - [default: 30303] - - --max-outbound-peers - Maximum number of outbound requests. default: 100 - - --max-inbound-peers - Maximum number of inbound requests. default: 30 - -Database: - --db.log-level - Database logging level. Levels higher than "notice" require a debug build - - Possible values: - - fatal: Enables logging for critical conditions, i.e. assertion failures - - error: Enables logging for error conditions - - warn: Enables logging for warning conditions - - notice: Enables logging for normal but significant condition - - verbose: Enables logging for verbose informational - - debug: Enables logging for debug-level messages - - trace: Enables logging for trace debug-level messages - - extra: Enables logging for extra debug-level messages - - --to - The maximum block height - - --interval - The block interval for sync and unwind. Defaults to `1000` - - [default: 1000] - -Logging: - --log.file.directory - The path to put log files in - - [default: /reth/logs] - - --log.file.max-size - The maximum size (in MB) of one log file - - [default: 200] - - --log.file.max-files - The maximum amount of log files that will be stored. If set to 0, background file logging is disabled - - [default: 5] - - --log.file.filter - The filter to use for logs written to the log file - - [default: debug] - - --log.journald - Write logs to journald - - --log.journald.filter - The filter to use for logs written to journald - - [default: error] - - --color - Sets whether or not the formatter emits ANSI terminal escape codes for colors and other text formatting - - [default: always] - - Possible values: - - always: Colors on - - auto: Colors on - - never: Colors off - -Display: - -v, --verbosity... - Set the minimum log level. - - -v Errors - -vv Warnings - -vvv Info - -vvvv Debug - -vvvvv Traces (warning: very verbose!) - - -q, --quiet - Silence all log output -``` - -## `reth debug in-memory-merkle` - -Debug in-memory state root calculation - -```bash -$ reth debug in-memory-merkle --help - -Usage: reth debug in-memory-merkle [OPTIONS] - -Options: - --datadir - The path to the data dir for all reth files and subdirectories. - - Defaults to the OS-specific data directory: - - - Linux: `$XDG_DATA_HOME/reth/` or `$HOME/.local/share/reth/` - - Windows: `{FOLDERID_RoamingAppData}/reth/` - - macOS: `$HOME/Library/Application Support/reth/` - - [default: default] - - --chain - The chain this node is running. - Possible values are either a built-in chain or the path to a chain specification file. - - Built-in chains: - mainnet, sepolia, goerli, holesky, dev - - [default: mainnet] - - --instance - Add a new instance of a node. - - Configures the ports of the node to avoid conflicts with the defaults. This is useful for running multiple nodes on the same machine. - - Max number of instances is 200. It is chosen in a way so that it's not possible to have port numbers that conflict with each other. - - Changes to the following port numbers: - DISCOVERY_PORT: default + `instance` - 1 - AUTH_PORT: default + `instance` * 100 - 100 - HTTP_RPC_PORT: default - `instance` + 1 - WS_RPC_PORT: default + `instance` * 2 - 2 - - [default: 1] - - -h, --help - Print help (see a summary with '-h') - -Database: - --db.log-level - Database logging level. Levels higher than "notice" require a debug build - - Possible values: - - fatal: Enables logging for critical conditions, i.e. assertion failures - - error: Enables logging for error conditions - - warn: Enables logging for warning conditions - - notice: Enables logging for normal but significant condition - - verbose: Enables logging for verbose informational - - debug: Enables logging for debug-level messages - - trace: Enables logging for trace debug-level messages - - extra: Enables logging for extra debug-level messages - -Networking: - -d, --disable-discovery - Disable the discovery service - - --disable-dns-discovery - Disable the DNS discovery - - --disable-discv4-discovery - Disable Discv4 discovery - - --discovery.addr - The UDP address to use for P2P discovery/networking - - [default: 0.0.0.0] - - --discovery.port - The UDP port to use for P2P discovery/networking - - [default: 30303] - - --trusted-peers - Comma separated enode URLs of trusted peers for P2P connections. - - --trusted-peers enode://abcd@192.168.0.1:30303 - - --trusted-only - Connect only to trusted peers - - --bootnodes - Comma separated enode URLs for P2P discovery bootstrap. - - Will fall back to a network-specific default if not specified. - - --peers-file - The path to the known peers file. Connected peers are dumped to this file on nodes - shutdown, and read on startup. Cannot be used with `--no-persist-peers`. - - --identity - Custom node identity - - [default: reth/v0.1.0-alpha.13-10a83e594/aarch64-apple-darwin] - - --p2p-secret-key - Secret key to use for this node. - - This will also deterministically set the peer ID. If not specified, it will be set in the data dir for the chain being used. - - --no-persist-peers - Do not persist peers. - - --nat - NAT resolution method (any|none|upnp|publicip|extip:) - - [default: any] - - --addr - Network listening address - - [default: 0.0.0.0] - - --port - Network listening port - - [default: 30303] - - --max-outbound-peers - Maximum number of outbound requests. default: 100 - - --max-inbound-peers - Maximum number of inbound requests. default: 30 - - --retries - The number of retries per request - - [default: 5] - - --skip-node-depth - The depth after which we should start comparing branch nodes - -Logging: - --log.file.directory - The path to put log files in - - [default: /reth/logs] - - --log.file.max-size - The maximum size (in MB) of one log file - - [default: 200] - - --log.file.max-files - The maximum amount of log files that will be stored. If set to 0, background file logging is disabled - - [default: 5] - - --log.file.filter - The filter to use for logs written to the log file - - [default: debug] - - --log.journald - Write logs to journald - - --log.journald.filter - The filter to use for logs written to journald - - [default: error] - - --color - Sets whether or not the formatter emits ANSI terminal escape codes for colors and other text formatting - - [default: always] - - Possible values: - - always: Colors on - - auto: Colors on - - never: Colors off - -Display: - -v, --verbosity... - Set the minimum log level. - - -v Errors - -vv Warnings - -vvv Info - -vvvv Debug - -vvvvv Traces (warning: very verbose!) - - -q, --quiet - Silence all log output -``` - -## `reth debug merkle` - -Debug the clean & incremental state root calculations - -```bash -$ reth debug merkle --help - -Usage: reth debug merkle [OPTIONS] --to - -Options: - --datadir - The path to the data dir for all reth files and subdirectories. - - Defaults to the OS-specific data directory: - - - Linux: `$XDG_DATA_HOME/reth/` or `$HOME/.local/share/reth/` - - Windows: `{FOLDERID_RoamingAppData}/reth/` - - macOS: `$HOME/Library/Application Support/reth/` - - [default: default] - - --chain - The chain this node is running. - Possible values are either a built-in chain or the path to a chain specification file. - - Built-in chains: - mainnet, sepolia, goerli, holesky, dev - - [default: mainnet] - - --instance - Add a new instance of a node. - - Configures the ports of the node to avoid conflicts with the defaults. This is useful for running multiple nodes on the same machine. - - Max number of instances is 200. It is chosen in a way so that it's not possible to have port numbers that conflict with each other. - - Changes to the following port numbers: - DISCOVERY_PORT: default + `instance` - 1 - AUTH_PORT: default + `instance` * 100 - 100 - HTTP_RPC_PORT: default - `instance` + 1 - WS_RPC_PORT: default + `instance` * 2 - 2 - - [default: 1] - - -h, --help - Print help (see a summary with '-h') - -Database: - --db.log-level - Database logging level. Levels higher than "notice" require a debug build - - Possible values: - - fatal: Enables logging for critical conditions, i.e. assertion failures - - error: Enables logging for error conditions - - warn: Enables logging for warning conditions - - notice: Enables logging for normal but significant condition - - verbose: Enables logging for verbose informational - - debug: Enables logging for debug-level messages - - trace: Enables logging for trace debug-level messages - - extra: Enables logging for extra debug-level messages - -Networking: - -d, --disable-discovery - Disable the discovery service - - --disable-dns-discovery - Disable the DNS discovery - - --disable-discv4-discovery - Disable Discv4 discovery - - --discovery.addr - The UDP address to use for P2P discovery/networking - - [default: 0.0.0.0] - - --discovery.port - The UDP port to use for P2P discovery/networking - - [default: 30303] - - --trusted-peers - Comma separated enode URLs of trusted peers for P2P connections. - - --trusted-peers enode://abcd@192.168.0.1:30303 - - --trusted-only - Connect only to trusted peers - - --bootnodes - Comma separated enode URLs for P2P discovery bootstrap. - - Will fall back to a network-specific default if not specified. - - --peers-file - The path to the known peers file. Connected peers are dumped to this file on nodes - shutdown, and read on startup. Cannot be used with `--no-persist-peers`. - - --identity - Custom node identity - - [default: reth/v0.1.0-alpha.13-10a83e594/aarch64-apple-darwin] - - --p2p-secret-key - Secret key to use for this node. - - This will also deterministically set the peer ID. If not specified, it will be set in the data dir for the chain being used. - - --no-persist-peers - Do not persist peers. - - --nat - NAT resolution method (any|none|upnp|publicip|extip:) - - [default: any] - - --addr - Network listening address - - [default: 0.0.0.0] - - --port - Network listening port - - [default: 30303] - - --max-outbound-peers - Maximum number of outbound requests. default: 100 - - --max-inbound-peers - Maximum number of inbound requests. default: 30 - - --retries - The number of retries per request - - [default: 5] - - --to - The height to finish at - - --skip-node-depth - The depth after which we should start comparing branch nodes - -Logging: - --log.file.directory - The path to put log files in - - [default: /reth/logs] - - --log.file.max-size - The maximum size (in MB) of one log file - - [default: 200] - - --log.file.max-files - The maximum amount of log files that will be stored. If set to 0, background file logging is disabled - - [default: 5] - - --log.file.filter - The filter to use for logs written to the log file - - [default: debug] - - --log.journald - Write logs to journald - - --log.journald.filter - The filter to use for logs written to journald - - [default: error] - - --color - Sets whether or not the formatter emits ANSI terminal escape codes for colors and other text formatting - - [default: always] - - Possible values: - - always: Colors on - - auto: Colors on - - never: Colors off - -Display: - -v, --verbosity... - Set the minimum log level. - - -v Errors - -vv Warnings - -vvv Info - -vvvv Debug - -vvvvv Traces (warning: very verbose!) - - -q, --quiet - Silence all log output -``` diff --git a/book/cli/help.py b/book/cli/help.py new file mode 100755 index 000000000000..6f86da4a6f3a --- /dev/null +++ b/book/cli/help.py @@ -0,0 +1,264 @@ +#!/usr/bin/env python3 + +import argparse +import os +import re +import subprocess +import sys +from os import makedirs, path + +HELP_KEY = "help" +SECTION_START = "" +SECTION_END = "" +SECTION_RE = rf"\s*{SECTION_START}.*?{SECTION_END}" + +README = """\ +# CLI Reference + + + +Automatically-generated CLI reference from `--help` output. + +{{#include ./SUMMARY.md}} +""" + + +def main(): + args = parse_args(sys.argv[1:]) + for cmd in args.commands: + if cmd.find(" ") >= 0: + raise Exception(f"subcommands are not allowed: {cmd}") + makedirs(args.out_dir, exist_ok=True) + + output = {} + + # Iterate over all commands and their subcommands. + cmd_iter = [[cmd] for cmd in args.commands] + for cmd in cmd_iter: + subcmds, stdout = get_entry(cmd) + if args.verbose and len(subcmds) > 0: + eprint(f"Found subcommands for \"{' '.join(cmd)}\": {subcmds}") + + # Add entry to output map, e.g. `output["cmd"]["subcmd"]["help"] = "..."`. + e = output + for arg in cmd: + tmp = e.get(arg) + if not tmp: + e[arg] = {} + tmp = e[arg] + e = tmp + e[HELP_KEY] = stdout + + # Append subcommands. + for subcmd in subcmds: + cmd_iter.append(cmd + [subcmd]) + + # Generate markdown files. + summary = "" + root_summary = "" + for cmd, obj in output.items(): + cmd_markdown(args.out_dir, cmd, obj) + + root_path = path.relpath(args.out_dir, args.root_dir) + summary += cmd_summary("", cmd, obj, 0) + summary += "\n" + + root_summary += cmd_summary(root_path, cmd, obj, args.root_indentation) + root_summary += "\n" + with open(path.join(args.out_dir, "SUMMARY.md"), "w") as f: + f.write(summary) + + # Generate README.md. + if args.readme: + with open(path.join(args.out_dir, "README.md"), "w") as f: + f.write(README) + + if args.root_summary: + update_root_summary(args.root_dir, root_summary) + + +def parse_args(args: list[str]): + """Parses command line arguments.""" + parser = argparse.ArgumentParser( + description="Generate markdown files from help output of commands" + ) + parser.add_argument("--root-dir", default=".", help="Root directory") + parser.add_argument( + "--root-indentation", + default=0, + type=int, + help="Indentation for the root SUMMARY.md file", + ) + parser.add_argument("--out-dir", help="Output directory") + parser.add_argument( + "--readme", + action="store_true", + help="Whether to add a README.md file", + ) + parser.add_argument( + "--root-summary", + action="store_true", + help="Whether to update the root SUMMARY.md file", + ) + parser.add_argument( + "commands", + nargs="+", + help="Command to generate markdown for. Can be a subcommand.", + ) + parser.add_argument( + "--verbose", "-v", action="store_true", help="Print verbose output" + ) + return parser.parse_known_args(args)[0] + + +def get_entry(cmd: list[str]): + """Returns the subcommands and help output for a command.""" + env = os.environ.copy() + env["NO_COLOR"] = "1" + env["COLUMNS"] = "100" + env["LINES"] = "10000" + output = subprocess.run(cmd + ["--help"], capture_output=True, env=env) + if output.returncode != 0: + stderr = output.stderr.decode("utf-8") + raise Exception(f"Command \"{' '.join(cmd)}\" failed:\n{stderr}") + stdout = output.stdout.decode("utf-8") + subcmds = parse_sub_commands(stdout) + return subcmds, stdout + + +def parse_sub_commands(s: str): + """Returns a list of subcommands from the help output of a command.""" + idx = s.find("Commands:") + if idx < 0: + return [] + s = s[idx:] + + idx = s.find("Options:") + if idx < 0: + return [] + s = s[:idx] + + idx = s.find("Arguments:") + if idx >= 0: + s = s[:idx] + + subcmds = s.splitlines()[1:] + subcmds = filter( + lambda x: x.strip() != "" and x.startswith(" ") and x[2] != " ", subcmds + ) + subcmds = map(lambda x: x.strip().split(" ")[0], subcmds) + subcmds = filter(lambda x: x != "help", subcmds) + return list(subcmds) + + +def cmd_markdown(out_dir: str, cmd: str, obj: object): + """Writes the markdown for a command and its subcommands to out_dir.""" + + def rec(cmd: list[str], obj: object): + out = "" + out += f"# {' '.join(cmd)}\n\n" + out += help_markdown(cmd, obj[HELP_KEY]) + out_path = out_dir + for arg in cmd: + out_path = path.join(out_path, arg) + makedirs(path.dirname(out_path), exist_ok=True) + with open(f"{out_path}.md", "w") as f: + f.write(out) + + for k, v in obj.items(): + if k == HELP_KEY: + continue + rec(cmd + [k], v) + + rec([command_name(cmd)], obj) + + +def help_markdown(cmd: list[str], s: str): + """Returns the markdown for a command's help output.""" + cmd[0] = command_name(cmd[0]) + description, s = parse_description(s) + return f"""\ +{description} + +```bash +$ {' '.join(cmd)} --help +{preprocess_help(s.strip())} +```""" + + +def parse_description(s: str): + """Splits the help output into a description and the rest.""" + idx = s.find("Usage:") + if idx < 0: + return "", s + return s[:idx].strip().splitlines()[0].strip(), s[idx:] + + +def cmd_summary(md_root: str, cmd: str, obj: object, indent: int): + """Returns the summary for a command and its subcommands.""" + + def rec(cmd: list[str], obj: object, indent: int): + nonlocal out + cmd_s = " ".join(cmd) + cmd_path = cmd_s.replace(" ", "/") + if md_root != "": + cmd_path = f"{md_root}/{cmd_path}" + out += f"{' ' * indent}- [`{cmd_s}`](./{cmd_path}.md)\n" + + for k, v in obj.items(): + if k == HELP_KEY: + continue + rec(cmd + [k], v, indent + 2) + + out = "" + rec([command_name(cmd)], obj, indent) + return out + + +def update_root_summary(root_dir: str, root_summary: str): + """Replaces the CLI_REFERENCE section in the root SUMMARY.md file.""" + summary_file = path.join(root_dir, "SUMMARY.md") + + with open(summary_file, "r") as f: + real_root_summary = f.read() + + if not re.search(SECTION_RE, real_root_summary, flags=re.DOTALL): + raise Exception( + f"Could not find CLI_REFERENCE section in {summary_file}. " + "Please add the following section to the file:\n" + f"{SECTION_START}\n{SECTION_END}" + ) + + last_line = re.findall(f".*{SECTION_END}", real_root_summary)[0] + root_summary_s = root_summary.rstrip().replace("\n\n", "\n") + replace_with = f" {SECTION_START}\n{root_summary_s}\n{last_line}" + + real_root_summary = re.sub( + SECTION_RE, replace_with, real_root_summary, flags=re.DOTALL + ) + root_summary_file = path.join(root_dir, "SUMMARY.md") + with open(root_summary_file, "w") as f: + f.write(real_root_summary) + + +def eprint(*args, **kwargs): + """Prints to stderr.""" + print(*args, file=sys.stderr, **kwargs) + + +def command_name(cmd: str): + """Returns the name of a command.""" + return cmd.split("/")[-1] + +def preprocess_help(s: str): + """Preprocesses the help output of a command.""" + # Remove the user-specific paths. + s = re.sub(r"default: /.*/reth", "default: ", s) + # Remove the commit SHA. + s = re.sub(r"-[0-9A-Fa-f]{6,10}/", "-/", s) + + return s + + +if __name__ == "__main__": + main() diff --git a/book/cli/p2p.md b/book/cli/p2p.md deleted file mode 100644 index ba94a774ae2c..000000000000 --- a/book/cli/p2p.md +++ /dev/null @@ -1,318 +0,0 @@ -# `reth p2p` - -P2P Debugging utilities - -```bash -$ reth p2p --help - -Usage: reth p2p [OPTIONS] - -Commands: - header Download block header - body Download block body - help Print this message or the help of the given subcommand(s) - -Options: - --config - The path to the configuration file to use. - - --chain - The chain this node is running. - Possible values are either a built-in chain or the path to a chain specification file. - - Built-in chains: - mainnet, sepolia, goerli, holesky, dev - - [default: mainnet] - - --datadir - The path to the data dir for all reth files and subdirectories. - - Defaults to the OS-specific data directory: - - - Linux: `$XDG_DATA_HOME/reth/` or `$HOME/.local/share/reth/` - - Windows: `{FOLDERID_RoamingAppData}/reth/` - - macOS: `$HOME/Library/Application Support/reth/` - - [default: default] - - --p2p-secret-key - Secret key to use for this node. - - This also will deterministically set the peer ID. - - -d, --disable-discovery - Disable the discovery service - - --disable-dns-discovery - Disable the DNS discovery - - --disable-discv4-discovery - Disable Discv4 discovery - - --discovery.addr - The UDP address to use for P2P discovery/networking - - [default: 0.0.0.0] - - --discovery.port - The UDP port to use for P2P discovery/networking - - [default: 30303] - - --trusted-peer - Target trusted peer - - --trusted-only - Connect only to trusted peers - - --instance - Add a new instance of a node. - - Configures the ports of the node to avoid conflicts with the defaults. This is useful for running multiple nodes on the same machine. - - Max number of instances is 200. It is chosen in a way so that it's not possible to have port numbers that conflict with each other. - - Changes to the following port numbers: - DISCOVERY_PORT: default + `instance` - 1 - AUTH_PORT: default + `instance` * 100 - 100 - HTTP_RPC_PORT: default - `instance` + 1 - WS_RPC_PORT: default + `instance` * 2 - 2 - - [default: 1] - - --retries - The number of retries per request - - [default: 5] - - --nat - [default: any] - - -h, --help - Print help (see a summary with '-h') - -Database: - --db.log-level - Database logging level. Levels higher than "notice" require a debug build - - Possible values: - - fatal: Enables logging for critical conditions, i.e. assertion failures - - error: Enables logging for error conditions - - warn: Enables logging for warning conditions - - notice: Enables logging for normal but significant condition - - verbose: Enables logging for verbose informational - - debug: Enables logging for debug-level messages - - trace: Enables logging for trace debug-level messages - - extra: Enables logging for extra debug-level messages - -Logging: - --log.file.directory - The path to put log files in - - [default: /reth/logs] - - --log.file.max-size - The maximum size (in MB) of one log file - - [default: 200] - - --log.file.max-files - The maximum amount of log files that will be stored. If set to 0, background file logging is disabled - - [default: 5] - - --log.file.filter - The filter to use for logs written to the log file - - [default: debug] - - --log.journald - Write logs to journald - - --log.journald.filter - The filter to use for logs written to journald - - [default: error] - - --color - Sets whether or not the formatter emits ANSI terminal escape codes for colors and other text formatting - - [default: always] - - Possible values: - - always: Colors on - - auto: Colors on - - never: Colors off - -Display: - -v, --verbosity... - Set the minimum log level. - - -v Errors - -vv Warnings - -vvv Info - -vvvv Debug - -vvvvv Traces (warning: very verbose!) - - -q, --quiet - Silence all log output -``` - -## `reth p2p body` - -Download block body - -```bash -$ reth p2p body --help - -Usage: reth p2p body [OPTIONS] - -Arguments: - - The block number or hash - -Options: - --instance - Add a new instance of a node. - - Configures the ports of the node to avoid conflicts with the defaults. This is useful for running multiple nodes on the same machine. - - Max number of instances is 200. It is chosen in a way so that it's not possible to have port numbers that conflict with each other. - - Changes to the following port numbers: - DISCOVERY_PORT: default + `instance` - 1 - AUTH_PORT: default + `instance` * 100 - 100 - HTTP_RPC_PORT: default - `instance` + 1 - WS_RPC_PORT: default + `instance` * 2 - 2 - - [default: 1] - - -h, --help - Print help (see a summary with '-h') - -Logging: - --log.file.directory - The path to put log files in - - [default: /reth/logs] - - --log.file.max-size - The maximum size (in MB) of one log file - - [default: 200] - - --log.file.max-files - The maximum amount of log files that will be stored. If set to 0, background file logging is disabled - - [default: 5] - - --log.file.filter - The filter to use for logs written to the log file - - [default: debug] - - --log.journald - Write logs to journald - - --log.journald.filter - The filter to use for logs written to journald - - [default: error] - - --color - Sets whether or not the formatter emits ANSI terminal escape codes for colors and other text formatting - - [default: always] - - Possible values: - - always: Colors on - - auto: Colors on - - never: Colors off - -Display: - -v, --verbosity... - Set the minimum log level. - - -v Errors - -vv Warnings - -vvv Info - -vvvv Debug - -vvvvv Traces (warning: very verbose!) - - -q, --quiet - Silence all log output -``` - -## `reth p2p header` - -Download block header - -```bash -$ reth p2p header --help - -Usage: reth p2p header [OPTIONS] - -Arguments: - - The header number or hash - -Options: - --instance - Add a new instance of a node. - - Configures the ports of the node to avoid conflicts with the defaults. This is useful for running multiple nodes on the same machine. - - Max number of instances is 200. It is chosen in a way so that it's not possible to have port numbers that conflict with each other. - - Changes to the following port numbers: - DISCOVERY_PORT: default + `instance` - 1 - AUTH_PORT: default + `instance` * 100 - 100 - HTTP_RPC_PORT: default - `instance` + 1 - WS_RPC_PORT: default + `instance` * 2 - 2 - - [default: 1] - - -h, --help - Print help (see a summary with '-h') - -Logging: - --log.file.directory - The path to put log files in - - [default: /reth/logs] - - --log.file.max-size - The maximum size (in MB) of one log file - - [default: 200] - - --log.file.max-files - The maximum amount of log files that will be stored. If set to 0, background file logging is disabled - - [default: 5] - - --log.file.filter - The filter to use for logs written to the log file - - [default: debug] - - --log.journald - Write logs to journald - - --log.journald.filter - The filter to use for logs written to journald - - [default: error] - - --color - Sets whether or not the formatter emits ANSI terminal escape codes for colors and other text formatting - - [default: always] - - Possible values: - - always: Colors on - - auto: Colors on - - never: Colors off - -Display: - -v, --verbosity... - Set the minimum log level. - - -v Errors - -vv Warnings - -vvv Info - -vvvv Debug - -vvvvv Traces (warning: very verbose!) - - -q, --quiet - Silence all log output -``` diff --git a/book/cli/reth.md b/book/cli/reth.md new file mode 100644 index 000000000000..08772686347f --- /dev/null +++ b/book/cli/reth.md @@ -0,0 +1,100 @@ +# reth + +Reth + +```bash +$ reth --help +Usage: reth [OPTIONS] + +Commands: + node Start the node + init Initialize the database from a genesis file + import This syncs RLP encoded blocks from a file + db Database debugging utilities + stage Manipulate individual stages + p2p P2P Debugging utilities + test-vectors Generate Test Vectors + config Write config to stdout + debug Various debug routines + recover Scripts for node recovery + help Print this message or the help of the given subcommand(s) + +Options: + --chain + The chain this node is running. + Possible values are either a built-in chain or the path to a chain specification file. + + Built-in chains: + mainnet, sepolia, goerli, holesky, dev + + [default: mainnet] + + --instance + Add a new instance of a node. + + Configures the ports of the node to avoid conflicts with the defaults. This is useful for running multiple nodes on the same machine. + + Max number of instances is 200. It is chosen in a way so that it's not possible to have port numbers that conflict with each other. + + Changes to the following port numbers: - DISCOVERY_PORT: default + `instance` - 1 - AUTH_PORT: default + `instance` * 100 - 100 - HTTP_RPC_PORT: default - `instance` + 1 - WS_RPC_PORT: default + `instance` * 2 - 2 + + [default: 1] + + -h, --help + Print help (see a summary with '-h') + + -V, --version + Print version + +Logging: + --log.file.directory + The path to put log files in + + [default: /logs] + + --log.file.max-size + The maximum size (in MB) of one log file + + [default: 200] + + --log.file.max-files + The maximum amount of log files that will be stored. If set to 0, background file logging is disabled + + [default: 5] + + --log.file.filter + The filter to use for logs written to the log file + + [default: debug] + + --log.journald + Write logs to journald + + --log.journald.filter + The filter to use for logs written to journald + + [default: error] + + --color + Sets whether or not the formatter emits ANSI terminal escape codes for colors and other text formatting + + [default: always] + + Possible values: + - always: Colors on + - auto: Colors on + - never: Colors off + +Display: + -v, --verbosity... + Set the minimum log level. + + -v Errors + -vv Warnings + -vvv Info + -vvvv Debug + -vvvvv Traces (warning: very verbose!) + + -q, --quiet + Silence all log output +``` \ No newline at end of file diff --git a/book/cli/config.md b/book/cli/reth/config.md similarity index 97% rename from book/cli/config.md rename to book/cli/reth/config.md index 78f64a1792ba..bd1abe4fe9d6 100644 --- a/book/cli/config.md +++ b/book/cli/reth/config.md @@ -1,10 +1,9 @@ -# `reth config` +# reth config Write config to stdout ```bash $ reth config --help - Usage: reth config [OPTIONS] Options: @@ -41,7 +40,7 @@ Logging: --log.file.directory The path to put log files in - [default: /reth/logs] + [default: /logs] --log.file.max-size The maximum size (in MB) of one log file @@ -88,4 +87,4 @@ Display: -q, --quiet Silence all log output -``` +``` \ No newline at end of file diff --git a/book/cli/reth/db.md b/book/cli/reth/db.md new file mode 100644 index 000000000000..0ef1ac526d11 --- /dev/null +++ b/book/cli/reth/db.md @@ -0,0 +1,121 @@ +# reth db + +Database debugging utilities + +```bash +$ reth db --help +Usage: reth db [OPTIONS] + +Commands: + stats Lists all the tables, their entry count and their size + list Lists the contents of a table + diff Create a diff between two database tables or two entire databases + get Gets the content of a table for the given key + drop Deletes all database entries + clear Deletes all table entries + snapshot Snapshots tables from database + version Lists current and local database versions + path Returns the full database path + help Print this message or the help of the given subcommand(s) + +Options: + --datadir + The path to the data dir for all reth files and subdirectories. + + Defaults to the OS-specific data directory: + + - Linux: `$XDG_DATA_HOME/reth/` or `$HOME/.local/share/reth/` + - Windows: `{FOLDERID_RoamingAppData}/reth/` + - macOS: `$HOME/Library/Application Support/reth/` + + [default: default] + + --chain + The chain this node is running. + Possible values are either a built-in chain or the path to a chain specification file. + + Built-in chains: + mainnet, sepolia, goerli, holesky, dev + + [default: mainnet] + + --instance + Add a new instance of a node. + + Configures the ports of the node to avoid conflicts with the defaults. This is useful for running multiple nodes on the same machine. + + Max number of instances is 200. It is chosen in a way so that it's not possible to have port numbers that conflict with each other. + + Changes to the following port numbers: - DISCOVERY_PORT: default + `instance` - 1 - AUTH_PORT: default + `instance` * 100 - 100 - HTTP_RPC_PORT: default - `instance` + 1 - WS_RPC_PORT: default + `instance` * 2 - 2 + + [default: 1] + + -h, --help + Print help (see a summary with '-h') + +Database: + --db.log-level + Database logging level. Levels higher than "notice" require a debug build + + Possible values: + - fatal: Enables logging for critical conditions, i.e. assertion failures + - error: Enables logging for error conditions + - warn: Enables logging for warning conditions + - notice: Enables logging for normal but significant condition + - verbose: Enables logging for verbose informational + - debug: Enables logging for debug-level messages + - trace: Enables logging for trace debug-level messages + - extra: Enables logging for extra debug-level messages + +Logging: + --log.file.directory + The path to put log files in + + [default: /logs] + + --log.file.max-size + The maximum size (in MB) of one log file + + [default: 200] + + --log.file.max-files + The maximum amount of log files that will be stored. If set to 0, background file logging is disabled + + [default: 5] + + --log.file.filter + The filter to use for logs written to the log file + + [default: debug] + + --log.journald + Write logs to journald + + --log.journald.filter + The filter to use for logs written to journald + + [default: error] + + --color + Sets whether or not the formatter emits ANSI terminal escape codes for colors and other text formatting + + [default: always] + + Possible values: + - always: Colors on + - auto: Colors on + - never: Colors off + +Display: + -v, --verbosity... + Set the minimum log level. + + -v Errors + -vv Warnings + -vvv Info + -vvvv Debug + -vvvvv Traces (warning: very verbose!) + + -q, --quiet + Silence all log output +``` \ No newline at end of file diff --git a/book/cli/reth/db/clear.md b/book/cli/reth/db/clear.md new file mode 100644 index 000000000000..6015dc75173e --- /dev/null +++ b/book/cli/reth/db/clear.md @@ -0,0 +1,99 @@ +# reth db clear + +Deletes all table entries + +```bash +$ reth db clear --help +Usage: reth db clear [OPTIONS]
+ +Arguments: +
+ Table name + +Options: + --datadir + The path to the data dir for all reth files and subdirectories. + + Defaults to the OS-specific data directory: + + - Linux: `$XDG_DATA_HOME/reth/` or `$HOME/.local/share/reth/` + - Windows: `{FOLDERID_RoamingAppData}/reth/` + - macOS: `$HOME/Library/Application Support/reth/` + + [default: default] + + --chain + The chain this node is running. + Possible values are either a built-in chain or the path to a chain specification file. + + Built-in chains: + mainnet, sepolia, goerli, holesky, dev + + [default: mainnet] + + --instance + Add a new instance of a node. + + Configures the ports of the node to avoid conflicts with the defaults. This is useful for running multiple nodes on the same machine. + + Max number of instances is 200. It is chosen in a way so that it's not possible to have port numbers that conflict with each other. + + Changes to the following port numbers: - DISCOVERY_PORT: default + `instance` - 1 - AUTH_PORT: default + `instance` * 100 - 100 - HTTP_RPC_PORT: default - `instance` + 1 - WS_RPC_PORT: default + `instance` * 2 - 2 + + [default: 1] + + -h, --help + Print help (see a summary with '-h') + +Logging: + --log.file.directory + The path to put log files in + + [default: /logs] + + --log.file.max-size + The maximum size (in MB) of one log file + + [default: 200] + + --log.file.max-files + The maximum amount of log files that will be stored. If set to 0, background file logging is disabled + + [default: 5] + + --log.file.filter + The filter to use for logs written to the log file + + [default: debug] + + --log.journald + Write logs to journald + + --log.journald.filter + The filter to use for logs written to journald + + [default: error] + + --color + Sets whether or not the formatter emits ANSI terminal escape codes for colors and other text formatting + + [default: always] + + Possible values: + - always: Colors on + - auto: Colors on + - never: Colors off + +Display: + -v, --verbosity... + Set the minimum log level. + + -v Errors + -vv Warnings + -vvv Info + -vvvv Debug + -vvvvv Traces (warning: very verbose!) + + -q, --quiet + Silence all log output +``` \ No newline at end of file diff --git a/book/cli/reth/db/diff.md b/book/cli/reth/db/diff.md new file mode 100644 index 000000000000..6c03b8a9b70b --- /dev/null +++ b/book/cli/reth/db/diff.md @@ -0,0 +1,118 @@ +# reth db diff + +Create a diff between two database tables or two entire databases + +```bash +$ reth db diff --help +Usage: reth db diff [OPTIONS] --secondary-datadir --output + +Options: + --datadir + The path to the data dir for all reth files and subdirectories. + + Defaults to the OS-specific data directory: + + - Linux: `$XDG_DATA_HOME/reth/` or `$HOME/.local/share/reth/` + - Windows: `{FOLDERID_RoamingAppData}/reth/` + - macOS: `$HOME/Library/Application Support/reth/` + + [default: default] + + --secondary-datadir + The path to the data dir for all reth files and subdirectories. + + --chain + The chain this node is running. + Possible values are either a built-in chain or the path to a chain specification file. + + Built-in chains: + mainnet, sepolia, goerli, holesky, dev + + [default: mainnet] + + --instance + Add a new instance of a node. + + Configures the ports of the node to avoid conflicts with the defaults. This is useful for running multiple nodes on the same machine. + + Max number of instances is 200. It is chosen in a way so that it's not possible to have port numbers that conflict with each other. + + Changes to the following port numbers: - DISCOVERY_PORT: default + `instance` - 1 - AUTH_PORT: default + `instance` * 100 - 100 - HTTP_RPC_PORT: default - `instance` + 1 - WS_RPC_PORT: default + `instance` * 2 - 2 + + [default: 1] + + -h, --help + Print help (see a summary with '-h') + +Database: + --db.log-level + Database logging level. Levels higher than "notice" require a debug build + + Possible values: + - fatal: Enables logging for critical conditions, i.e. assertion failures + - error: Enables logging for error conditions + - warn: Enables logging for warning conditions + - notice: Enables logging for normal but significant condition + - verbose: Enables logging for verbose informational + - debug: Enables logging for debug-level messages + - trace: Enables logging for trace debug-level messages + - extra: Enables logging for extra debug-level messages + + --table
+ The table name to diff. If not specified, all tables are diffed. + + --output + The output directory for the diff report. + +Logging: + --log.file.directory + The path to put log files in + + [default: /logs] + + --log.file.max-size + The maximum size (in MB) of one log file + + [default: 200] + + --log.file.max-files + The maximum amount of log files that will be stored. If set to 0, background file logging is disabled + + [default: 5] + + --log.file.filter + The filter to use for logs written to the log file + + [default: debug] + + --log.journald + Write logs to journald + + --log.journald.filter + The filter to use for logs written to journald + + [default: error] + + --color + Sets whether or not the formatter emits ANSI terminal escape codes for colors and other text formatting + + [default: always] + + Possible values: + - always: Colors on + - auto: Colors on + - never: Colors off + +Display: + -v, --verbosity... + Set the minimum log level. + + -v Errors + -vv Warnings + -vvv Info + -vvvv Debug + -vvvvv Traces (warning: very verbose!) + + -q, --quiet + Silence all log output +``` \ No newline at end of file diff --git a/book/cli/reth/db/drop.md b/book/cli/reth/db/drop.md new file mode 100644 index 000000000000..ab87c4d746b1 --- /dev/null +++ b/book/cli/reth/db/drop.md @@ -0,0 +1,98 @@ +# reth db drop + +Deletes all database entries + +```bash +$ reth db drop --help +Usage: reth db drop [OPTIONS] + +Options: + --datadir + The path to the data dir for all reth files and subdirectories. + + Defaults to the OS-specific data directory: + + - Linux: `$XDG_DATA_HOME/reth/` or `$HOME/.local/share/reth/` + - Windows: `{FOLDERID_RoamingAppData}/reth/` + - macOS: `$HOME/Library/Application Support/reth/` + + [default: default] + + -f, --force + Bypasses the interactive confirmation and drops the database directly + + --chain + The chain this node is running. + Possible values are either a built-in chain or the path to a chain specification file. + + Built-in chains: + mainnet, sepolia, goerli, holesky, dev + + [default: mainnet] + + --instance + Add a new instance of a node. + + Configures the ports of the node to avoid conflicts with the defaults. This is useful for running multiple nodes on the same machine. + + Max number of instances is 200. It is chosen in a way so that it's not possible to have port numbers that conflict with each other. + + Changes to the following port numbers: - DISCOVERY_PORT: default + `instance` - 1 - AUTH_PORT: default + `instance` * 100 - 100 - HTTP_RPC_PORT: default - `instance` + 1 - WS_RPC_PORT: default + `instance` * 2 - 2 + + [default: 1] + + -h, --help + Print help (see a summary with '-h') + +Logging: + --log.file.directory + The path to put log files in + + [default: /logs] + + --log.file.max-size + The maximum size (in MB) of one log file + + [default: 200] + + --log.file.max-files + The maximum amount of log files that will be stored. If set to 0, background file logging is disabled + + [default: 5] + + --log.file.filter + The filter to use for logs written to the log file + + [default: debug] + + --log.journald + Write logs to journald + + --log.journald.filter + The filter to use for logs written to journald + + [default: error] + + --color + Sets whether or not the formatter emits ANSI terminal escape codes for colors and other text formatting + + [default: always] + + Possible values: + - always: Colors on + - auto: Colors on + - never: Colors off + +Display: + -v, --verbosity... + Set the minimum log level. + + -v Errors + -vv Warnings + -vvv Info + -vvvv Debug + -vvvvv Traces (warning: very verbose!) + + -q, --quiet + Silence all log output +``` \ No newline at end of file diff --git a/book/cli/reth/db/get.md b/book/cli/reth/db/get.md new file mode 100644 index 000000000000..e35b60345c01 --- /dev/null +++ b/book/cli/reth/db/get.md @@ -0,0 +1,107 @@ +# reth db get + +Gets the content of a table for the given key + +```bash +$ reth db get --help +Usage: reth db get [OPTIONS]
+ +Arguments: +
+ The table name + + NOTE: The dupsort tables are not supported now. + + + The key to get content for + +Options: + --datadir + The path to the data dir for all reth files and subdirectories. + + Defaults to the OS-specific data directory: + + - Linux: `$XDG_DATA_HOME/reth/` or `$HOME/.local/share/reth/` + - Windows: `{FOLDERID_RoamingAppData}/reth/` + - macOS: `$HOME/Library/Application Support/reth/` + + [default: default] + + --raw + Output bytes instead of human-readable decoded value + + --chain + The chain this node is running. + Possible values are either a built-in chain or the path to a chain specification file. + + Built-in chains: + mainnet, sepolia, goerli, holesky, dev + + [default: mainnet] + + --instance + Add a new instance of a node. + + Configures the ports of the node to avoid conflicts with the defaults. This is useful for running multiple nodes on the same machine. + + Max number of instances is 200. It is chosen in a way so that it's not possible to have port numbers that conflict with each other. + + Changes to the following port numbers: - DISCOVERY_PORT: default + `instance` - 1 - AUTH_PORT: default + `instance` * 100 - 100 - HTTP_RPC_PORT: default - `instance` + 1 - WS_RPC_PORT: default + `instance` * 2 - 2 + + [default: 1] + + -h, --help + Print help (see a summary with '-h') + +Logging: + --log.file.directory + The path to put log files in + + [default: /logs] + + --log.file.max-size + The maximum size (in MB) of one log file + + [default: 200] + + --log.file.max-files + The maximum amount of log files that will be stored. If set to 0, background file logging is disabled + + [default: 5] + + --log.file.filter + The filter to use for logs written to the log file + + [default: debug] + + --log.journald + Write logs to journald + + --log.journald.filter + The filter to use for logs written to journald + + [default: error] + + --color + Sets whether or not the formatter emits ANSI terminal escape codes for colors and other text formatting + + [default: always] + + Possible values: + - always: Colors on + - auto: Colors on + - never: Colors off + +Display: + -v, --verbosity... + Set the minimum log level. + + -v Errors + -vv Warnings + -vvv Info + -vvvv Debug + -vvvvv Traces (warning: very verbose!) + + -q, --quiet + Silence all log output +``` \ No newline at end of file diff --git a/book/cli/reth/db/list.md b/book/cli/reth/db/list.md new file mode 100644 index 000000000000..3f9ed77e0ac4 --- /dev/null +++ b/book/cli/reth/db/list.md @@ -0,0 +1,141 @@ +# reth db list + +Lists the contents of a table + +```bash +$ reth db list --help +Usage: reth db list [OPTIONS]
+ +Arguments: +
+ The table name + +Options: + --datadir + The path to the data dir for all reth files and subdirectories. + + Defaults to the OS-specific data directory: + + - Linux: `$XDG_DATA_HOME/reth/` or `$HOME/.local/share/reth/` + - Windows: `{FOLDERID_RoamingAppData}/reth/` + - macOS: `$HOME/Library/Application Support/reth/` + + [default: default] + + -s, --skip + Skip first N entries + + [default: 0] + + --chain + The chain this node is running. + Possible values are either a built-in chain or the path to a chain specification file. + + Built-in chains: + mainnet, sepolia, goerli, holesky, dev + + [default: mainnet] + + -r, --reverse + Reverse the order of the entries. If enabled last table entries are read + + -l, --len + How many items to take from the walker + + [default: 5] + + --search + Search parameter for both keys and values. Prefix it with `0x` to search for binary data, and text otherwise. + + ATTENTION! For compressed tables (`Transactions` and `Receipts`), there might be missing results since the search uses the raw uncompressed value from the database. + + --min-row-size + Minimum size of row in bytes + + [default: 0] + + --min-key-size + Minimum size of key in bytes + + [default: 0] + + --min-value-size + Minimum size of value in bytes + + [default: 0] + + -c, --count + Returns the number of rows found + + -j, --json + Dump as JSON instead of using TUI + + --raw + Output bytes instead of human-readable decoded value + + --instance + Add a new instance of a node. + + Configures the ports of the node to avoid conflicts with the defaults. This is useful for running multiple nodes on the same machine. + + Max number of instances is 200. It is chosen in a way so that it's not possible to have port numbers that conflict with each other. + + Changes to the following port numbers: - DISCOVERY_PORT: default + `instance` - 1 - AUTH_PORT: default + `instance` * 100 - 100 - HTTP_RPC_PORT: default - `instance` + 1 - WS_RPC_PORT: default + `instance` * 2 - 2 + + [default: 1] + + -h, --help + Print help (see a summary with '-h') + +Logging: + --log.file.directory + The path to put log files in + + [default: /logs] + + --log.file.max-size + The maximum size (in MB) of one log file + + [default: 200] + + --log.file.max-files + The maximum amount of log files that will be stored. If set to 0, background file logging is disabled + + [default: 5] + + --log.file.filter + The filter to use for logs written to the log file + + [default: debug] + + --log.journald + Write logs to journald + + --log.journald.filter + The filter to use for logs written to journald + + [default: error] + + --color + Sets whether or not the formatter emits ANSI terminal escape codes for colors and other text formatting + + [default: always] + + Possible values: + - always: Colors on + - auto: Colors on + - never: Colors off + +Display: + -v, --verbosity... + Set the minimum log level. + + -v Errors + -vv Warnings + -vvv Info + -vvvv Debug + -vvvvv Traces (warning: very verbose!) + + -q, --quiet + Silence all log output +``` \ No newline at end of file diff --git a/book/cli/reth/db/path.md b/book/cli/reth/db/path.md new file mode 100644 index 000000000000..6180bab9203d --- /dev/null +++ b/book/cli/reth/db/path.md @@ -0,0 +1,95 @@ +# reth db path + +Returns the full database path + +```bash +$ reth db path --help +Usage: reth db path [OPTIONS] + +Options: + --datadir + The path to the data dir for all reth files and subdirectories. + + Defaults to the OS-specific data directory: + + - Linux: `$XDG_DATA_HOME/reth/` or `$HOME/.local/share/reth/` + - Windows: `{FOLDERID_RoamingAppData}/reth/` + - macOS: `$HOME/Library/Application Support/reth/` + + [default: default] + + --chain + The chain this node is running. + Possible values are either a built-in chain or the path to a chain specification file. + + Built-in chains: + mainnet, sepolia, goerli, holesky, dev + + [default: mainnet] + + --instance + Add a new instance of a node. + + Configures the ports of the node to avoid conflicts with the defaults. This is useful for running multiple nodes on the same machine. + + Max number of instances is 200. It is chosen in a way so that it's not possible to have port numbers that conflict with each other. + + Changes to the following port numbers: - DISCOVERY_PORT: default + `instance` - 1 - AUTH_PORT: default + `instance` * 100 - 100 - HTTP_RPC_PORT: default - `instance` + 1 - WS_RPC_PORT: default + `instance` * 2 - 2 + + [default: 1] + + -h, --help + Print help (see a summary with '-h') + +Logging: + --log.file.directory + The path to put log files in + + [default: /logs] + + --log.file.max-size + The maximum size (in MB) of one log file + + [default: 200] + + --log.file.max-files + The maximum amount of log files that will be stored. If set to 0, background file logging is disabled + + [default: 5] + + --log.file.filter + The filter to use for logs written to the log file + + [default: debug] + + --log.journald + Write logs to journald + + --log.journald.filter + The filter to use for logs written to journald + + [default: error] + + --color + Sets whether or not the formatter emits ANSI terminal escape codes for colors and other text formatting + + [default: always] + + Possible values: + - always: Colors on + - auto: Colors on + - never: Colors off + +Display: + -v, --verbosity... + Set the minimum log level. + + -v Errors + -vv Warnings + -vvv Info + -vvvv Debug + -vvvvv Traces (warning: very verbose!) + + -q, --quiet + Silence all log output +``` \ No newline at end of file diff --git a/book/cli/reth/db/snapshot.md b/book/cli/reth/db/snapshot.md new file mode 100644 index 000000000000..2be9ce0f7adc --- /dev/null +++ b/book/cli/reth/db/snapshot.md @@ -0,0 +1,144 @@ +# reth db snapshot + +Snapshots tables from database + +```bash +$ reth db snapshot --help +Usage: reth db snapshot [OPTIONS] [SEGMENTS]... + +Arguments: + [SEGMENTS]... + Snapshot segments to generate + + Possible values: + - headers: Snapshot segment responsible for the `CanonicalHeaders`, `Headers`, `HeaderTD` tables + - transactions: Snapshot segment responsible for the `Transactions` table + - receipts: Snapshot segment responsible for the `Receipts` table + +Options: + --datadir + The path to the data dir for all reth files and subdirectories. + + Defaults to the OS-specific data directory: + + - Linux: `$XDG_DATA_HOME/reth/` or `$HOME/.local/share/reth/` + - Windows: `{FOLDERID_RoamingAppData}/reth/` + - macOS: `$HOME/Library/Application Support/reth/` + + [default: default] + + -f, --from + Starting block for the snapshot + + [default: 0] + + -b, --block-interval + Number of blocks in the snapshot + + [default: 500000] + + --chain + The chain this node is running. + Possible values are either a built-in chain or the path to a chain specification file. + + Built-in chains: + mainnet, sepolia, goerli, holesky, dev + + [default: mainnet] + + -p, --parallel + Sets the number of snapshots built in parallel. Note: Each parallel build is memory-intensive + + [default: 1] + + --only-stats + Flag to skip snapshot creation and print snapshot files stats + + --bench + Flag to enable database-to-snapshot benchmarking + + --only-bench + Flag to skip snapshot creation and only run benchmarks on existing snapshots + + -c, --compression + Compression algorithms to use + + [default: lz4] + [possible values: lz4, zstd, zstd-with-dictionary, uncompressed] + + --with-filters + Flag to enable inclusion list filters and PHFs + + --phf + Specifies the perfect hashing function to use + + Possible values: + - fmph: Fingerprint-Based Minimal Perfect Hash Function + - go-fmph: Fingerprint-Based Minimal Perfect Hash Function with Group Optimization + + --instance + Add a new instance of a node. + + Configures the ports of the node to avoid conflicts with the defaults. This is useful for running multiple nodes on the same machine. + + Max number of instances is 200. It is chosen in a way so that it's not possible to have port numbers that conflict with each other. + + Changes to the following port numbers: - DISCOVERY_PORT: default + `instance` - 1 - AUTH_PORT: default + `instance` * 100 - 100 - HTTP_RPC_PORT: default - `instance` + 1 - WS_RPC_PORT: default + `instance` * 2 - 2 + + [default: 1] + + -h, --help + Print help (see a summary with '-h') + +Logging: + --log.file.directory + The path to put log files in + + [default: /logs] + + --log.file.max-size + The maximum size (in MB) of one log file + + [default: 200] + + --log.file.max-files + The maximum amount of log files that will be stored. If set to 0, background file logging is disabled + + [default: 5] + + --log.file.filter + The filter to use for logs written to the log file + + [default: debug] + + --log.journald + Write logs to journald + + --log.journald.filter + The filter to use for logs written to journald + + [default: error] + + --color + Sets whether or not the formatter emits ANSI terminal escape codes for colors and other text formatting + + [default: always] + + Possible values: + - always: Colors on + - auto: Colors on + - never: Colors off + +Display: + -v, --verbosity... + Set the minimum log level. + + -v Errors + -vv Warnings + -vvv Info + -vvvv Debug + -vvvvv Traces (warning: very verbose!) + + -q, --quiet + Silence all log output +``` \ No newline at end of file diff --git a/book/cli/reth/db/stats.md b/book/cli/reth/db/stats.md new file mode 100644 index 000000000000..385935827869 --- /dev/null +++ b/book/cli/reth/db/stats.md @@ -0,0 +1,95 @@ +# reth db stats + +Lists all the tables, their entry count and their size + +```bash +$ reth db stats --help +Usage: reth db stats [OPTIONS] + +Options: + --datadir + The path to the data dir for all reth files and subdirectories. + + Defaults to the OS-specific data directory: + + - Linux: `$XDG_DATA_HOME/reth/` or `$HOME/.local/share/reth/` + - Windows: `{FOLDERID_RoamingAppData}/reth/` + - macOS: `$HOME/Library/Application Support/reth/` + + [default: default] + + --chain + The chain this node is running. + Possible values are either a built-in chain or the path to a chain specification file. + + Built-in chains: + mainnet, sepolia, goerli, holesky, dev + + [default: mainnet] + + --instance + Add a new instance of a node. + + Configures the ports of the node to avoid conflicts with the defaults. This is useful for running multiple nodes on the same machine. + + Max number of instances is 200. It is chosen in a way so that it's not possible to have port numbers that conflict with each other. + + Changes to the following port numbers: - DISCOVERY_PORT: default + `instance` - 1 - AUTH_PORT: default + `instance` * 100 - 100 - HTTP_RPC_PORT: default - `instance` + 1 - WS_RPC_PORT: default + `instance` * 2 - 2 + + [default: 1] + + -h, --help + Print help (see a summary with '-h') + +Logging: + --log.file.directory + The path to put log files in + + [default: /logs] + + --log.file.max-size + The maximum size (in MB) of one log file + + [default: 200] + + --log.file.max-files + The maximum amount of log files that will be stored. If set to 0, background file logging is disabled + + [default: 5] + + --log.file.filter + The filter to use for logs written to the log file + + [default: debug] + + --log.journald + Write logs to journald + + --log.journald.filter + The filter to use for logs written to journald + + [default: error] + + --color + Sets whether or not the formatter emits ANSI terminal escape codes for colors and other text formatting + + [default: always] + + Possible values: + - always: Colors on + - auto: Colors on + - never: Colors off + +Display: + -v, --verbosity... + Set the minimum log level. + + -v Errors + -vv Warnings + -vvv Info + -vvvv Debug + -vvvvv Traces (warning: very verbose!) + + -q, --quiet + Silence all log output +``` \ No newline at end of file diff --git a/book/cli/reth/db/version.md b/book/cli/reth/db/version.md new file mode 100644 index 000000000000..847dc0cb897c --- /dev/null +++ b/book/cli/reth/db/version.md @@ -0,0 +1,95 @@ +# reth db version + +Lists current and local database versions + +```bash +$ reth db version --help +Usage: reth db version [OPTIONS] + +Options: + --datadir + The path to the data dir for all reth files and subdirectories. + + Defaults to the OS-specific data directory: + + - Linux: `$XDG_DATA_HOME/reth/` or `$HOME/.local/share/reth/` + - Windows: `{FOLDERID_RoamingAppData}/reth/` + - macOS: `$HOME/Library/Application Support/reth/` + + [default: default] + + --chain + The chain this node is running. + Possible values are either a built-in chain or the path to a chain specification file. + + Built-in chains: + mainnet, sepolia, goerli, holesky, dev + + [default: mainnet] + + --instance + Add a new instance of a node. + + Configures the ports of the node to avoid conflicts with the defaults. This is useful for running multiple nodes on the same machine. + + Max number of instances is 200. It is chosen in a way so that it's not possible to have port numbers that conflict with each other. + + Changes to the following port numbers: - DISCOVERY_PORT: default + `instance` - 1 - AUTH_PORT: default + `instance` * 100 - 100 - HTTP_RPC_PORT: default - `instance` + 1 - WS_RPC_PORT: default + `instance` * 2 - 2 + + [default: 1] + + -h, --help + Print help (see a summary with '-h') + +Logging: + --log.file.directory + The path to put log files in + + [default: /logs] + + --log.file.max-size + The maximum size (in MB) of one log file + + [default: 200] + + --log.file.max-files + The maximum amount of log files that will be stored. If set to 0, background file logging is disabled + + [default: 5] + + --log.file.filter + The filter to use for logs written to the log file + + [default: debug] + + --log.journald + Write logs to journald + + --log.journald.filter + The filter to use for logs written to journald + + [default: error] + + --color + Sets whether or not the formatter emits ANSI terminal escape codes for colors and other text formatting + + [default: always] + + Possible values: + - always: Colors on + - auto: Colors on + - never: Colors off + +Display: + -v, --verbosity... + Set the minimum log level. + + -v Errors + -vv Warnings + -vvv Info + -vvvv Debug + -vvvvv Traces (warning: very verbose!) + + -q, --quiet + Silence all log output +``` \ No newline at end of file diff --git a/book/cli/reth/debug.md b/book/cli/reth/debug.md new file mode 100644 index 000000000000..f41966bebee6 --- /dev/null +++ b/book/cli/reth/debug.md @@ -0,0 +1,91 @@ +# reth debug + +Various debug routines + +```bash +$ reth debug --help +Usage: reth debug [OPTIONS] + +Commands: + execution Debug the roundtrip execution of blocks as well as the generated data + merkle Debug the clean & incremental state root calculations + in-memory-merkle Debug in-memory state root calculation + build-block Debug block building + help Print this message or the help of the given subcommand(s) + +Options: + --chain + The chain this node is running. + Possible values are either a built-in chain or the path to a chain specification file. + + Built-in chains: + mainnet, sepolia, goerli, holesky, dev + + [default: mainnet] + + --instance + Add a new instance of a node. + + Configures the ports of the node to avoid conflicts with the defaults. This is useful for running multiple nodes on the same machine. + + Max number of instances is 200. It is chosen in a way so that it's not possible to have port numbers that conflict with each other. + + Changes to the following port numbers: - DISCOVERY_PORT: default + `instance` - 1 - AUTH_PORT: default + `instance` * 100 - 100 - HTTP_RPC_PORT: default - `instance` + 1 - WS_RPC_PORT: default + `instance` * 2 - 2 + + [default: 1] + + -h, --help + Print help (see a summary with '-h') + +Logging: + --log.file.directory + The path to put log files in + + [default: /logs] + + --log.file.max-size + The maximum size (in MB) of one log file + + [default: 200] + + --log.file.max-files + The maximum amount of log files that will be stored. If set to 0, background file logging is disabled + + [default: 5] + + --log.file.filter + The filter to use for logs written to the log file + + [default: debug] + + --log.journald + Write logs to journald + + --log.journald.filter + The filter to use for logs written to journald + + [default: error] + + --color + Sets whether or not the formatter emits ANSI terminal escape codes for colors and other text formatting + + [default: always] + + Possible values: + - always: Colors on + - auto: Colors on + - never: Colors off + +Display: + -v, --verbosity... + Set the minimum log level. + + -v Errors + -vv Warnings + -vvv Info + -vvvv Debug + -vvvvv Traces (warning: very verbose!) + + -q, --quiet + Silence all log output +``` \ No newline at end of file diff --git a/book/cli/import.md b/book/cli/reth/import.md similarity index 98% rename from book/cli/import.md rename to book/cli/reth/import.md index 08aadbfc7724..b92a0c9a18a3 100644 --- a/book/cli/import.md +++ b/book/cli/reth/import.md @@ -1,10 +1,9 @@ -# `reth import` +# reth import This syncs RLP encoded blocks from a file ```bash $ reth import --help - Usage: reth import [OPTIONS] Options: @@ -69,7 +68,7 @@ Logging: --log.file.directory The path to put log files in - [default: /reth/logs] + [default: /logs] --log.file.max-size The maximum size (in MB) of one log file @@ -116,4 +115,4 @@ Display: -q, --quiet Silence all log output -``` +``` \ No newline at end of file diff --git a/book/cli/init.md b/book/cli/reth/init.md similarity index 98% rename from book/cli/init.md rename to book/cli/reth/init.md index e824d8f5ec80..3f5353176002 100644 --- a/book/cli/init.md +++ b/book/cli/reth/init.md @@ -1,10 +1,9 @@ -# `reth init` +# reth init Initialize the database from a genesis file ```bash $ reth init --help - Usage: reth init [OPTIONS] Options: @@ -60,7 +59,7 @@ Logging: --log.file.directory The path to put log files in - [default: /reth/logs] + [default: /logs] --log.file.max-size The maximum size (in MB) of one log file @@ -107,4 +106,4 @@ Display: -q, --quiet Silence all log output -``` +``` \ No newline at end of file diff --git a/book/cli/node.md b/book/cli/reth/node.md similarity index 98% rename from book/cli/node.md rename to book/cli/reth/node.md index 7cc4dd027532..a3a990250ef7 100644 --- a/book/cli/node.md +++ b/book/cli/reth/node.md @@ -1,10 +1,9 @@ -# `reth node` +# reth node Start the node ```bash $ reth node --help - Usage: reth node [OPTIONS] Options: @@ -94,7 +93,7 @@ Networking: --identity Custom node identity - [default: reth/v0.1.0-alpha.13-10a83e594/aarch64-apple-darwin] + [default: reth/v0.1.0-alpha.13-/aarch64-apple-darwin] --p2p-secret-key Secret key to use for this node. @@ -174,7 +173,7 @@ RPC: --ipcpath Filename for IPC socket/pipe within the datadir - [default: /tmp/reth.ipc] + [default: .ipc] --authrpc.addr Auth server address to listen on @@ -426,7 +425,7 @@ Logging: --log.file.directory The path to put log files in - [default: /reth/logs] + [default: /logs] --log.file.max-size The maximum size (in MB) of one log file @@ -473,4 +472,4 @@ Display: -q, --quiet Silence all log output -``` +``` \ No newline at end of file diff --git a/book/cli/recover.md b/book/cli/reth/p2p.md similarity index 51% rename from book/cli/recover.md rename to book/cli/reth/p2p.md index 3f62c15de31f..835ac0a8d600 100644 --- a/book/cli/recover.md +++ b/book/cli/reth/p2p.md @@ -1,17 +1,20 @@ -# `reth recover` +# reth p2p -Scripts for node recovery +P2P Debugging utilities ```bash -$ reth recover --help - -Usage: reth recover [OPTIONS] +$ reth p2p --help +Usage: reth p2p [OPTIONS] Commands: - storage-tries Recover the node by deleting dangling storage tries - help Print this message or the help of the given subcommand(s) + header Download block header + body Download block body + help Print this message or the help of the given subcommand(s) Options: + --config + The path to the configuration file to use. + --chain The chain this node is running. Possible values are either a built-in chain or the path to a chain specification file. @@ -21,102 +24,46 @@ Options: [default: mainnet] - --instance - Add a new instance of a node. - - Configures the ports of the node to avoid conflicts with the defaults. This is useful for running multiple nodes on the same machine. - - Max number of instances is 200. It is chosen in a way so that it's not possible to have port numbers that conflict with each other. - - Changes to the following port numbers: - DISCOVERY_PORT: default + `instance` - 1 - AUTH_PORT: default + `instance` * 100 - 100 - HTTP_RPC_PORT: default - `instance` + 1 - WS_RPC_PORT: default + `instance` * 2 - 2 + --datadir + The path to the data dir for all reth files and subdirectories. - [default: 1] - - -h, --help - Print help (see a summary with '-h') - -Logging: - --log.file.directory - The path to put log files in + Defaults to the OS-specific data directory: - [default: /reth/logs] - - --log.file.max-size - The maximum size (in MB) of one log file + - Linux: `$XDG_DATA_HOME/reth/` or `$HOME/.local/share/reth/` + - Windows: `{FOLDERID_RoamingAppData}/reth/` + - macOS: `$HOME/Library/Application Support/reth/` - [default: 200] + [default: default] - --log.file.max-files - The maximum amount of log files that will be stored. If set to 0, background file logging is disabled + --p2p-secret-key + Secret key to use for this node. - [default: 5] + This also will deterministically set the peer ID. - --log.file.filter - The filter to use for logs written to the log file - - [default: debug] + -d, --disable-discovery + Disable the discovery service - --log.journald - Write logs to journald + --disable-dns-discovery + Disable the DNS discovery - --log.journald.filter - The filter to use for logs written to journald - - [default: error] + --disable-discv4-discovery + Disable Discv4 discovery - --color - Sets whether or not the formatter emits ANSI terminal escape codes for colors and other text formatting + --discovery.addr + The UDP address to use for P2P discovery/networking - [default: always] - - Possible values: - - always: Colors on - - auto: Colors on - - never: Colors off + [default: 0.0.0.0] -Display: - -v, --verbosity... - Set the minimum log level. + --discovery.port + The UDP port to use for P2P discovery/networking - -v Errors - -vv Warnings - -vvv Info - -vvvv Debug - -vvvvv Traces (warning: very verbose!) + [default: 30303] - -q, --quiet - Silence all log output -``` - -## `reth recover storage-tries` - -Recover the node by deleting dangling storage tries - -```bash -$ reth recover storage-tries --help - -Usage: reth recover storage-tries [OPTIONS] - -Options: - --datadir - The path to the data dir for all reth files and subdirectories. - - Defaults to the OS-specific data directory: - - - Linux: `$XDG_DATA_HOME/reth/` or `$HOME/.local/share/reth/` - - Windows: `{FOLDERID_RoamingAppData}/reth/` - - macOS: `$HOME/Library/Application Support/reth/` - - [default: default] + --trusted-peer + Target trusted peer - --chain - The chain this node is running. - Possible values are either a built-in chain or the path to a chain specification file. - - Built-in chains: - mainnet, sepolia, goerli, holesky, dev - - [default: mainnet] + --trusted-only + Connect only to trusted peers --instance Add a new instance of a node. @@ -129,14 +76,36 @@ Options: [default: 1] + --retries + The number of retries per request + + [default: 5] + + --nat + [default: any] + -h, --help Print help (see a summary with '-h') +Database: + --db.log-level + Database logging level. Levels higher than "notice" require a debug build + + Possible values: + - fatal: Enables logging for critical conditions, i.e. assertion failures + - error: Enables logging for error conditions + - warn: Enables logging for warning conditions + - notice: Enables logging for normal but significant condition + - verbose: Enables logging for verbose informational + - debug: Enables logging for debug-level messages + - trace: Enables logging for trace debug-level messages + - extra: Enables logging for extra debug-level messages + Logging: --log.file.directory The path to put log files in - [default: /reth/logs] + [default: /logs] --log.file.max-size The maximum size (in MB) of one log file @@ -183,4 +152,4 @@ Display: -q, --quiet Silence all log output -``` +``` \ No newline at end of file diff --git a/book/cli/reth/p2p/body.md b/book/cli/reth/p2p/body.md new file mode 100644 index 000000000000..a5c3189affb7 --- /dev/null +++ b/book/cli/reth/p2p/body.md @@ -0,0 +1,79 @@ +# reth p2p body + +Download block body + +```bash +$ reth p2p body --help +Usage: reth p2p body [OPTIONS] + +Arguments: + + The block number or hash + +Options: + --instance + Add a new instance of a node. + + Configures the ports of the node to avoid conflicts with the defaults. This is useful for running multiple nodes on the same machine. + + Max number of instances is 200. It is chosen in a way so that it's not possible to have port numbers that conflict with each other. + + Changes to the following port numbers: - DISCOVERY_PORT: default + `instance` - 1 - AUTH_PORT: default + `instance` * 100 - 100 - HTTP_RPC_PORT: default - `instance` + 1 - WS_RPC_PORT: default + `instance` * 2 - 2 + + [default: 1] + + -h, --help + Print help (see a summary with '-h') + +Logging: + --log.file.directory + The path to put log files in + + [default: /logs] + + --log.file.max-size + The maximum size (in MB) of one log file + + [default: 200] + + --log.file.max-files + The maximum amount of log files that will be stored. If set to 0, background file logging is disabled + + [default: 5] + + --log.file.filter + The filter to use for logs written to the log file + + [default: debug] + + --log.journald + Write logs to journald + + --log.journald.filter + The filter to use for logs written to journald + + [default: error] + + --color + Sets whether or not the formatter emits ANSI terminal escape codes for colors and other text formatting + + [default: always] + + Possible values: + - always: Colors on + - auto: Colors on + - never: Colors off + +Display: + -v, --verbosity... + Set the minimum log level. + + -v Errors + -vv Warnings + -vvv Info + -vvvv Debug + -vvvvv Traces (warning: very verbose!) + + -q, --quiet + Silence all log output +``` \ No newline at end of file diff --git a/book/cli/reth/p2p/header.md b/book/cli/reth/p2p/header.md new file mode 100644 index 000000000000..52d0630e6546 --- /dev/null +++ b/book/cli/reth/p2p/header.md @@ -0,0 +1,79 @@ +# reth p2p header + +Download block header + +```bash +$ reth p2p header --help +Usage: reth p2p header [OPTIONS] + +Arguments: + + The header number or hash + +Options: + --instance + Add a new instance of a node. + + Configures the ports of the node to avoid conflicts with the defaults. This is useful for running multiple nodes on the same machine. + + Max number of instances is 200. It is chosen in a way so that it's not possible to have port numbers that conflict with each other. + + Changes to the following port numbers: - DISCOVERY_PORT: default + `instance` - 1 - AUTH_PORT: default + `instance` * 100 - 100 - HTTP_RPC_PORT: default - `instance` + 1 - WS_RPC_PORT: default + `instance` * 2 - 2 + + [default: 1] + + -h, --help + Print help (see a summary with '-h') + +Logging: + --log.file.directory + The path to put log files in + + [default: /logs] + + --log.file.max-size + The maximum size (in MB) of one log file + + [default: 200] + + --log.file.max-files + The maximum amount of log files that will be stored. If set to 0, background file logging is disabled + + [default: 5] + + --log.file.filter + The filter to use for logs written to the log file + + [default: debug] + + --log.journald + Write logs to journald + + --log.journald.filter + The filter to use for logs written to journald + + [default: error] + + --color + Sets whether or not the formatter emits ANSI terminal escape codes for colors and other text formatting + + [default: always] + + Possible values: + - always: Colors on + - auto: Colors on + - never: Colors off + +Display: + -v, --verbosity... + Set the minimum log level. + + -v Errors + -vv Warnings + -vvv Info + -vvvv Debug + -vvvvv Traces (warning: very verbose!) + + -q, --quiet + Silence all log output +``` \ No newline at end of file diff --git a/book/cli/reth/recover.md b/book/cli/reth/recover.md new file mode 100644 index 000000000000..5f2b79d6dd2f --- /dev/null +++ b/book/cli/reth/recover.md @@ -0,0 +1,88 @@ +# reth recover + +Scripts for node recovery + +```bash +$ reth recover --help +Usage: reth recover [OPTIONS] + +Commands: + storage-tries Recover the node by deleting dangling storage tries + help Print this message or the help of the given subcommand(s) + +Options: + --chain + The chain this node is running. + Possible values are either a built-in chain or the path to a chain specification file. + + Built-in chains: + mainnet, sepolia, goerli, holesky, dev + + [default: mainnet] + + --instance + Add a new instance of a node. + + Configures the ports of the node to avoid conflicts with the defaults. This is useful for running multiple nodes on the same machine. + + Max number of instances is 200. It is chosen in a way so that it's not possible to have port numbers that conflict with each other. + + Changes to the following port numbers: - DISCOVERY_PORT: default + `instance` - 1 - AUTH_PORT: default + `instance` * 100 - 100 - HTTP_RPC_PORT: default - `instance` + 1 - WS_RPC_PORT: default + `instance` * 2 - 2 + + [default: 1] + + -h, --help + Print help (see a summary with '-h') + +Logging: + --log.file.directory + The path to put log files in + + [default: /logs] + + --log.file.max-size + The maximum size (in MB) of one log file + + [default: 200] + + --log.file.max-files + The maximum amount of log files that will be stored. If set to 0, background file logging is disabled + + [default: 5] + + --log.file.filter + The filter to use for logs written to the log file + + [default: debug] + + --log.journald + Write logs to journald + + --log.journald.filter + The filter to use for logs written to journald + + [default: error] + + --color + Sets whether or not the formatter emits ANSI terminal escape codes for colors and other text formatting + + [default: always] + + Possible values: + - always: Colors on + - auto: Colors on + - never: Colors off + +Display: + -v, --verbosity... + Set the minimum log level. + + -v Errors + -vv Warnings + -vvv Info + -vvvv Debug + -vvvvv Traces (warning: very verbose!) + + -q, --quiet + Silence all log output +``` \ No newline at end of file diff --git a/book/cli/reth/recover/storage-tries.md b/book/cli/reth/recover/storage-tries.md new file mode 100644 index 000000000000..f13cc9e31f18 --- /dev/null +++ b/book/cli/reth/recover/storage-tries.md @@ -0,0 +1,95 @@ +# reth recover storage-tries + +Recover the node by deleting dangling storage tries + +```bash +$ reth recover storage-tries --help +Usage: reth recover storage-tries [OPTIONS] + +Options: + --datadir + The path to the data dir for all reth files and subdirectories. + + Defaults to the OS-specific data directory: + + - Linux: `$XDG_DATA_HOME/reth/` or `$HOME/.local/share/reth/` + - Windows: `{FOLDERID_RoamingAppData}/reth/` + - macOS: `$HOME/Library/Application Support/reth/` + + [default: default] + + --chain + The chain this node is running. + Possible values are either a built-in chain or the path to a chain specification file. + + Built-in chains: + mainnet, sepolia, goerli, holesky, dev + + [default: mainnet] + + --instance + Add a new instance of a node. + + Configures the ports of the node to avoid conflicts with the defaults. This is useful for running multiple nodes on the same machine. + + Max number of instances is 200. It is chosen in a way so that it's not possible to have port numbers that conflict with each other. + + Changes to the following port numbers: - DISCOVERY_PORT: default + `instance` - 1 - AUTH_PORT: default + `instance` * 100 - 100 - HTTP_RPC_PORT: default - `instance` + 1 - WS_RPC_PORT: default + `instance` * 2 - 2 + + [default: 1] + + -h, --help + Print help (see a summary with '-h') + +Logging: + --log.file.directory + The path to put log files in + + [default: /logs] + + --log.file.max-size + The maximum size (in MB) of one log file + + [default: 200] + + --log.file.max-files + The maximum amount of log files that will be stored. If set to 0, background file logging is disabled + + [default: 5] + + --log.file.filter + The filter to use for logs written to the log file + + [default: debug] + + --log.journald + Write logs to journald + + --log.journald.filter + The filter to use for logs written to journald + + [default: error] + + --color + Sets whether or not the formatter emits ANSI terminal escape codes for colors and other text formatting + + [default: always] + + Possible values: + - always: Colors on + - auto: Colors on + - never: Colors off + +Display: + -v, --verbosity... + Set the minimum log level. + + -v Errors + -vv Warnings + -vvv Info + -vvvv Debug + -vvvvv Traces (warning: very verbose!) + + -q, --quiet + Silence all log output +``` \ No newline at end of file diff --git a/book/cli/reth/stage.md b/book/cli/reth/stage.md new file mode 100644 index 000000000000..a0365e98f2f7 --- /dev/null +++ b/book/cli/reth/stage.md @@ -0,0 +1,91 @@ +# reth stage + +Manipulate individual stages + +```bash +$ reth stage --help +Usage: reth stage [OPTIONS] + +Commands: + run Run a single stage + drop Drop a stage's tables from the database + dump Dumps a stage from a range into a new database + unwind Unwinds a certain block range, deleting it from the database + help Print this message or the help of the given subcommand(s) + +Options: + --chain + The chain this node is running. + Possible values are either a built-in chain or the path to a chain specification file. + + Built-in chains: + mainnet, sepolia, goerli, holesky, dev + + [default: mainnet] + + --instance + Add a new instance of a node. + + Configures the ports of the node to avoid conflicts with the defaults. This is useful for running multiple nodes on the same machine. + + Max number of instances is 200. It is chosen in a way so that it's not possible to have port numbers that conflict with each other. + + Changes to the following port numbers: - DISCOVERY_PORT: default + `instance` - 1 - AUTH_PORT: default + `instance` * 100 - 100 - HTTP_RPC_PORT: default - `instance` + 1 - WS_RPC_PORT: default + `instance` * 2 - 2 + + [default: 1] + + -h, --help + Print help (see a summary with '-h') + +Logging: + --log.file.directory + The path to put log files in + + [default: /logs] + + --log.file.max-size + The maximum size (in MB) of one log file + + [default: 200] + + --log.file.max-files + The maximum amount of log files that will be stored. If set to 0, background file logging is disabled + + [default: 5] + + --log.file.filter + The filter to use for logs written to the log file + + [default: debug] + + --log.journald + Write logs to journald + + --log.journald.filter + The filter to use for logs written to journald + + [default: error] + + --color + Sets whether or not the formatter emits ANSI terminal escape codes for colors and other text formatting + + [default: always] + + Possible values: + - always: Colors on + - auto: Colors on + - never: Colors off + +Display: + -v, --verbosity... + Set the minimum log level. + + -v Errors + -vv Warnings + -vvv Info + -vvvv Debug + -vvvvv Traces (warning: very verbose!) + + -q, --quiet + Silence all log output +``` \ No newline at end of file diff --git a/book/cli/reth/stage/drop.md b/book/cli/reth/stage/drop.md new file mode 100644 index 000000000000..97e437d87891 --- /dev/null +++ b/book/cli/reth/stage/drop.md @@ -0,0 +1,112 @@ +# reth stage drop + +Drop a stage's tables from the database + +```bash +$ reth stage drop --help +Usage: reth stage drop [OPTIONS] + +Options: + --datadir + The path to the data dir for all reth files and subdirectories. + + Defaults to the OS-specific data directory: + + - Linux: `$XDG_DATA_HOME/reth/` or `$HOME/.local/share/reth/` + - Windows: `{FOLDERID_RoamingAppData}/reth/` + - macOS: `$HOME/Library/Application Support/reth/` + + [default: default] + + --chain + The chain this node is running. + Possible values are either a built-in chain or the path to a chain specification file. + + Built-in chains: + mainnet, sepolia, goerli, holesky, dev + + [default: mainnet] + + --instance + Add a new instance of a node. + + Configures the ports of the node to avoid conflicts with the defaults. This is useful for running multiple nodes on the same machine. + + Max number of instances is 200. It is chosen in a way so that it's not possible to have port numbers that conflict with each other. + + Changes to the following port numbers: - DISCOVERY_PORT: default + `instance` - 1 - AUTH_PORT: default + `instance` * 100 - 100 - HTTP_RPC_PORT: default - `instance` + 1 - WS_RPC_PORT: default + `instance` * 2 - 2 + + [default: 1] + + -h, --help + Print help (see a summary with '-h') + +Database: + --db.log-level + Database logging level. Levels higher than "notice" require a debug build + + Possible values: + - fatal: Enables logging for critical conditions, i.e. assertion failures + - error: Enables logging for error conditions + - warn: Enables logging for warning conditions + - notice: Enables logging for normal but significant condition + - verbose: Enables logging for verbose informational + - debug: Enables logging for debug-level messages + - trace: Enables logging for trace debug-level messages + - extra: Enables logging for extra debug-level messages + + + [possible values: headers, bodies, senders, execution, account-hashing, storage-hashing, hashing, merkle, tx-lookup, account-history, storage-history, total-difficulty] + +Logging: + --log.file.directory + The path to put log files in + + [default: /logs] + + --log.file.max-size + The maximum size (in MB) of one log file + + [default: 200] + + --log.file.max-files + The maximum amount of log files that will be stored. If set to 0, background file logging is disabled + + [default: 5] + + --log.file.filter + The filter to use for logs written to the log file + + [default: debug] + + --log.journald + Write logs to journald + + --log.journald.filter + The filter to use for logs written to journald + + [default: error] + + --color + Sets whether or not the formatter emits ANSI terminal escape codes for colors and other text formatting + + [default: always] + + Possible values: + - always: Colors on + - auto: Colors on + - never: Colors off + +Display: + -v, --verbosity... + Set the minimum log level. + + -v Errors + -vv Warnings + -vvv Info + -vvvv Debug + -vvvvv Traces (warning: very verbose!) + + -q, --quiet + Silence all log output +``` \ No newline at end of file diff --git a/book/cli/reth/stage/dump.md b/book/cli/reth/stage/dump.md new file mode 100644 index 000000000000..c5f08bb2f5a3 --- /dev/null +++ b/book/cli/reth/stage/dump.md @@ -0,0 +1,116 @@ +# reth stage dump + +Dumps a stage from a range into a new database + +```bash +$ reth stage dump --help +Usage: reth stage dump [OPTIONS] + +Commands: + execution Execution stage + storage-hashing StorageHashing stage + account-hashing AccountHashing stage + merkle Merkle stage + help Print this message or the help of the given subcommand(s) + +Options: + --datadir + The path to the data dir for all reth files and subdirectories. + + Defaults to the OS-specific data directory: + + - Linux: `$XDG_DATA_HOME/reth/` or `$HOME/.local/share/reth/` + - Windows: `{FOLDERID_RoamingAppData}/reth/` + - macOS: `$HOME/Library/Application Support/reth/` + + [default: default] + + --chain + The chain this node is running. + Possible values are either a built-in chain or the path to a chain specification file. + + Built-in chains: + mainnet, sepolia, goerli, holesky, dev + + [default: mainnet] + + --instance + Add a new instance of a node. + + Configures the ports of the node to avoid conflicts with the defaults. This is useful for running multiple nodes on the same machine. + + Max number of instances is 200. It is chosen in a way so that it's not possible to have port numbers that conflict with each other. + + Changes to the following port numbers: - DISCOVERY_PORT: default + `instance` - 1 - AUTH_PORT: default + `instance` * 100 - 100 - HTTP_RPC_PORT: default - `instance` + 1 - WS_RPC_PORT: default + `instance` * 2 - 2 + + [default: 1] + + -h, --help + Print help (see a summary with '-h') + +Database: + --db.log-level + Database logging level. Levels higher than "notice" require a debug build + + Possible values: + - fatal: Enables logging for critical conditions, i.e. assertion failures + - error: Enables logging for error conditions + - warn: Enables logging for warning conditions + - notice: Enables logging for normal but significant condition + - verbose: Enables logging for verbose informational + - debug: Enables logging for debug-level messages + - trace: Enables logging for trace debug-level messages + - extra: Enables logging for extra debug-level messages + +Logging: + --log.file.directory + The path to put log files in + + [default: /logs] + + --log.file.max-size + The maximum size (in MB) of one log file + + [default: 200] + + --log.file.max-files + The maximum amount of log files that will be stored. If set to 0, background file logging is disabled + + [default: 5] + + --log.file.filter + The filter to use for logs written to the log file + + [default: debug] + + --log.journald + Write logs to journald + + --log.journald.filter + The filter to use for logs written to journald + + [default: error] + + --color + Sets whether or not the formatter emits ANSI terminal escape codes for colors and other text formatting + + [default: always] + + Possible values: + - always: Colors on + - auto: Colors on + - never: Colors off + +Display: + -v, --verbosity... + Set the minimum log level. + + -v Errors + -vv Warnings + -vvv Info + -vvvv Debug + -vvvvv Traces (warning: very verbose!) + + -q, --quiet + Silence all log output +``` \ No newline at end of file diff --git a/book/cli/reth/stage/dump/account-hashing.md b/book/cli/reth/stage/dump/account-hashing.md new file mode 100644 index 000000000000..7524e4ac7360 --- /dev/null +++ b/book/cli/reth/stage/dump/account-hashing.md @@ -0,0 +1,87 @@ +# reth stage dump account-hashing + +AccountHashing stage + +```bash +$ reth stage dump account-hashing --help +Usage: reth stage dump account-hashing [OPTIONS] --output-db --from --to + +Options: + --output-db + The path to the new database folder. + + -f, --from + From which block + + -t, --to + To which block + + -d, --dry-run + If passed, it will dry-run a stage execution from the newly created database right after dumping + + --instance + Add a new instance of a node. + + Configures the ports of the node to avoid conflicts with the defaults. This is useful for running multiple nodes on the same machine. + + Max number of instances is 200. It is chosen in a way so that it's not possible to have port numbers that conflict with each other. + + Changes to the following port numbers: - DISCOVERY_PORT: default + `instance` - 1 - AUTH_PORT: default + `instance` * 100 - 100 - HTTP_RPC_PORT: default - `instance` + 1 - WS_RPC_PORT: default + `instance` * 2 - 2 + + [default: 1] + + -h, --help + Print help (see a summary with '-h') + +Logging: + --log.file.directory + The path to put log files in + + [default: /logs] + + --log.file.max-size + The maximum size (in MB) of one log file + + [default: 200] + + --log.file.max-files + The maximum amount of log files that will be stored. If set to 0, background file logging is disabled + + [default: 5] + + --log.file.filter + The filter to use for logs written to the log file + + [default: debug] + + --log.journald + Write logs to journald + + --log.journald.filter + The filter to use for logs written to journald + + [default: error] + + --color + Sets whether or not the formatter emits ANSI terminal escape codes for colors and other text formatting + + [default: always] + + Possible values: + - always: Colors on + - auto: Colors on + - never: Colors off + +Display: + -v, --verbosity... + Set the minimum log level. + + -v Errors + -vv Warnings + -vvv Info + -vvvv Debug + -vvvvv Traces (warning: very verbose!) + + -q, --quiet + Silence all log output +``` \ No newline at end of file diff --git a/book/cli/reth/stage/dump/execution.md b/book/cli/reth/stage/dump/execution.md new file mode 100644 index 000000000000..2025012ad3f3 --- /dev/null +++ b/book/cli/reth/stage/dump/execution.md @@ -0,0 +1,87 @@ +# reth stage dump execution + +Execution stage + +```bash +$ reth stage dump execution --help +Usage: reth stage dump execution [OPTIONS] --output-db --from --to + +Options: + --output-db + The path to the new database folder. + + -f, --from + From which block + + -t, --to + To which block + + -d, --dry-run + If passed, it will dry-run a stage execution from the newly created database right after dumping + + --instance + Add a new instance of a node. + + Configures the ports of the node to avoid conflicts with the defaults. This is useful for running multiple nodes on the same machine. + + Max number of instances is 200. It is chosen in a way so that it's not possible to have port numbers that conflict with each other. + + Changes to the following port numbers: - DISCOVERY_PORT: default + `instance` - 1 - AUTH_PORT: default + `instance` * 100 - 100 - HTTP_RPC_PORT: default - `instance` + 1 - WS_RPC_PORT: default + `instance` * 2 - 2 + + [default: 1] + + -h, --help + Print help (see a summary with '-h') + +Logging: + --log.file.directory + The path to put log files in + + [default: /logs] + + --log.file.max-size + The maximum size (in MB) of one log file + + [default: 200] + + --log.file.max-files + The maximum amount of log files that will be stored. If set to 0, background file logging is disabled + + [default: 5] + + --log.file.filter + The filter to use for logs written to the log file + + [default: debug] + + --log.journald + Write logs to journald + + --log.journald.filter + The filter to use for logs written to journald + + [default: error] + + --color + Sets whether or not the formatter emits ANSI terminal escape codes for colors and other text formatting + + [default: always] + + Possible values: + - always: Colors on + - auto: Colors on + - never: Colors off + +Display: + -v, --verbosity... + Set the minimum log level. + + -v Errors + -vv Warnings + -vvv Info + -vvvv Debug + -vvvvv Traces (warning: very verbose!) + + -q, --quiet + Silence all log output +``` \ No newline at end of file diff --git a/book/cli/reth/stage/dump/merkle.md b/book/cli/reth/stage/dump/merkle.md new file mode 100644 index 000000000000..e69b51cdde9b --- /dev/null +++ b/book/cli/reth/stage/dump/merkle.md @@ -0,0 +1,87 @@ +# reth stage dump merkle + +Merkle stage + +```bash +$ reth stage dump merkle --help +Usage: reth stage dump merkle [OPTIONS] --output-db --from --to + +Options: + --output-db + The path to the new database folder. + + -f, --from + From which block + + -t, --to + To which block + + -d, --dry-run + If passed, it will dry-run a stage execution from the newly created database right after dumping + + --instance + Add a new instance of a node. + + Configures the ports of the node to avoid conflicts with the defaults. This is useful for running multiple nodes on the same machine. + + Max number of instances is 200. It is chosen in a way so that it's not possible to have port numbers that conflict with each other. + + Changes to the following port numbers: - DISCOVERY_PORT: default + `instance` - 1 - AUTH_PORT: default + `instance` * 100 - 100 - HTTP_RPC_PORT: default - `instance` + 1 - WS_RPC_PORT: default + `instance` * 2 - 2 + + [default: 1] + + -h, --help + Print help (see a summary with '-h') + +Logging: + --log.file.directory + The path to put log files in + + [default: /logs] + + --log.file.max-size + The maximum size (in MB) of one log file + + [default: 200] + + --log.file.max-files + The maximum amount of log files that will be stored. If set to 0, background file logging is disabled + + [default: 5] + + --log.file.filter + The filter to use for logs written to the log file + + [default: debug] + + --log.journald + Write logs to journald + + --log.journald.filter + The filter to use for logs written to journald + + [default: error] + + --color + Sets whether or not the formatter emits ANSI terminal escape codes for colors and other text formatting + + [default: always] + + Possible values: + - always: Colors on + - auto: Colors on + - never: Colors off + +Display: + -v, --verbosity... + Set the minimum log level. + + -v Errors + -vv Warnings + -vvv Info + -vvvv Debug + -vvvvv Traces (warning: very verbose!) + + -q, --quiet + Silence all log output +``` \ No newline at end of file diff --git a/book/cli/reth/stage/dump/storage-hashing.md b/book/cli/reth/stage/dump/storage-hashing.md new file mode 100644 index 000000000000..7ef46bb5f3ef --- /dev/null +++ b/book/cli/reth/stage/dump/storage-hashing.md @@ -0,0 +1,87 @@ +# reth stage dump storage-hashing + +StorageHashing stage + +```bash +$ reth stage dump storage-hashing --help +Usage: reth stage dump storage-hashing [OPTIONS] --output-db --from --to + +Options: + --output-db + The path to the new database folder. + + -f, --from + From which block + + -t, --to + To which block + + -d, --dry-run + If passed, it will dry-run a stage execution from the newly created database right after dumping + + --instance + Add a new instance of a node. + + Configures the ports of the node to avoid conflicts with the defaults. This is useful for running multiple nodes on the same machine. + + Max number of instances is 200. It is chosen in a way so that it's not possible to have port numbers that conflict with each other. + + Changes to the following port numbers: - DISCOVERY_PORT: default + `instance` - 1 - AUTH_PORT: default + `instance` * 100 - 100 - HTTP_RPC_PORT: default - `instance` + 1 - WS_RPC_PORT: default + `instance` * 2 - 2 + + [default: 1] + + -h, --help + Print help (see a summary with '-h') + +Logging: + --log.file.directory + The path to put log files in + + [default: /logs] + + --log.file.max-size + The maximum size (in MB) of one log file + + [default: 200] + + --log.file.max-files + The maximum amount of log files that will be stored. If set to 0, background file logging is disabled + + [default: 5] + + --log.file.filter + The filter to use for logs written to the log file + + [default: debug] + + --log.journald + Write logs to journald + + --log.journald.filter + The filter to use for logs written to journald + + [default: error] + + --color + Sets whether or not the formatter emits ANSI terminal escape codes for colors and other text formatting + + [default: always] + + Possible values: + - always: Colors on + - auto: Colors on + - never: Colors off + +Display: + -v, --verbosity... + Set the minimum log level. + + -v Errors + -vv Warnings + -vvv Info + -vvvv Debug + -vvvvv Traces (warning: very verbose!) + + -q, --quiet + Silence all log output +``` \ No newline at end of file diff --git a/book/cli/reth/stage/run.md b/book/cli/reth/stage/run.md new file mode 100644 index 000000000000..c7237cbb2332 --- /dev/null +++ b/book/cli/reth/stage/run.md @@ -0,0 +1,213 @@ +# reth stage run + +Run a single stage. + +```bash +$ reth stage run --help +Usage: reth stage run [OPTIONS] --from --to + +Arguments: + + The name of the stage to run + + [possible values: headers, bodies, senders, execution, account-hashing, storage-hashing, hashing, merkle, tx-lookup, account-history, storage-history, total-difficulty] + +Options: + --config + The path to the configuration file to use. + + --datadir + The path to the data dir for all reth files and subdirectories. + + Defaults to the OS-specific data directory: + + - Linux: `$XDG_DATA_HOME/reth/` or `$HOME/.local/share/reth/` + - Windows: `{FOLDERID_RoamingAppData}/reth/` + - macOS: `$HOME/Library/Application Support/reth/` + + [default: default] + + --chain + The chain this node is running. + Possible values are either a built-in chain or the path to a chain specification file. + + Built-in chains: + mainnet, sepolia, goerli, holesky, dev + + [default: mainnet] + + --metrics + Enable Prometheus metrics. + + The metrics will be served at the given interface and port. + + --from + The height to start at + + -t, --to + The end of the stage + + --batch-size + Batch size for stage execution and unwind + + -s, --skip-unwind + Normally, running the stage requires unwinding for stages that already have been run, in order to not rewrite to the same database slots. + + You can optionally skip the unwinding phase if you're syncing a block range that has not been synced before. + + --instance + Add a new instance of a node. + + Configures the ports of the node to avoid conflicts with the defaults. This is useful for running multiple nodes on the same machine. + + Max number of instances is 200. It is chosen in a way so that it's not possible to have port numbers that conflict with each other. + + Changes to the following port numbers: - DISCOVERY_PORT: default + `instance` - 1 - AUTH_PORT: default + `instance` * 100 - 100 - HTTP_RPC_PORT: default - `instance` + 1 - WS_RPC_PORT: default + `instance` * 2 - 2 + + [default: 1] + + -h, --help + Print help (see a summary with '-h') + +Networking: + -d, --disable-discovery + Disable the discovery service + + --disable-dns-discovery + Disable the DNS discovery + + --disable-discv4-discovery + Disable Discv4 discovery + + --discovery.addr + The UDP address to use for P2P discovery/networking + + [default: 0.0.0.0] + + --discovery.port + The UDP port to use for P2P discovery/networking + + [default: 30303] + + --trusted-peers + Comma separated enode URLs of trusted peers for P2P connections. + + --trusted-peers enode://abcd@192.168.0.1:30303 + + --trusted-only + Connect only to trusted peers + + --bootnodes + Comma separated enode URLs for P2P discovery bootstrap. + + Will fall back to a network-specific default if not specified. + + --peers-file + The path to the known peers file. Connected peers are dumped to this file on nodes + shutdown, and read on startup. Cannot be used with `--no-persist-peers`. + + --identity + Custom node identity + + [default: reth/v0.1.0-alpha.13-/aarch64-apple-darwin] + + --p2p-secret-key + Secret key to use for this node. + + This will also deterministically set the peer ID. If not specified, it will be set in the data dir for the chain being used. + + --no-persist-peers + Do not persist peers. + + --nat + NAT resolution method (any|none|upnp|publicip|extip:) + + [default: any] + + --addr + Network listening address + + [default: 0.0.0.0] + + --port + Network listening port + + [default: 30303] + + --max-outbound-peers + Maximum number of outbound requests. default: 100 + + --max-inbound-peers + Maximum number of inbound requests. default: 30 + +Database: + --db.log-level + Database logging level. Levels higher than "notice" require a debug build + + Possible values: + - fatal: Enables logging for critical conditions, i.e. assertion failures + - error: Enables logging for error conditions + - warn: Enables logging for warning conditions + - notice: Enables logging for normal but significant condition + - verbose: Enables logging for verbose informational + - debug: Enables logging for debug-level messages + - trace: Enables logging for trace debug-level messages + - extra: Enables logging for extra debug-level messages + + -c, --commit + Commits the changes in the database. WARNING: potentially destructive. + + Useful when you want to run diagnostics on the database. + +Logging: + --log.file.directory + The path to put log files in + + [default: /logs] + + --log.file.max-size + The maximum size (in MB) of one log file + + [default: 200] + + --log.file.max-files + The maximum amount of log files that will be stored. If set to 0, background file logging is disabled + + [default: 5] + + --log.file.filter + The filter to use for logs written to the log file + + [default: debug] + + --log.journald + Write logs to journald + + --log.journald.filter + The filter to use for logs written to journald + + [default: error] + + --color + Sets whether or not the formatter emits ANSI terminal escape codes for colors and other text formatting + + [default: always] + + Possible values: + - always: Colors on + - auto: Colors on + - never: Colors off + +Display: + -v, --verbosity... + Set the minimum log level. + + -v Errors + -vv Warnings + -vvv Info + -vvvv Debug + -vvvvv Traces (warning: very verbose!) + + -q, --quiet + Silence all log output +``` \ No newline at end of file diff --git a/book/cli/reth/stage/unwind.md b/book/cli/reth/stage/unwind.md new file mode 100644 index 000000000000..6e276803b548 --- /dev/null +++ b/book/cli/reth/stage/unwind.md @@ -0,0 +1,114 @@ +# reth stage unwind + +Unwinds a certain block range, deleting it from the database + +```bash +$ reth stage unwind --help +Usage: reth stage unwind [OPTIONS] + +Commands: + to-block Unwinds the database until the given block number (range is inclusive) + num-blocks Unwinds the given number of blocks from the database + help Print this message or the help of the given subcommand(s) + +Options: + --datadir + The path to the data dir for all reth files and subdirectories. + + Defaults to the OS-specific data directory: + + - Linux: `$XDG_DATA_HOME/reth/` or `$HOME/.local/share/reth/` + - Windows: `{FOLDERID_RoamingAppData}/reth/` + - macOS: `$HOME/Library/Application Support/reth/` + + [default: default] + + --chain + The chain this node is running. + Possible values are either a built-in chain or the path to a chain specification file. + + Built-in chains: + mainnet, sepolia, goerli, holesky, dev + + [default: mainnet] + + --instance + Add a new instance of a node. + + Configures the ports of the node to avoid conflicts with the defaults. This is useful for running multiple nodes on the same machine. + + Max number of instances is 200. It is chosen in a way so that it's not possible to have port numbers that conflict with each other. + + Changes to the following port numbers: - DISCOVERY_PORT: default + `instance` - 1 - AUTH_PORT: default + `instance` * 100 - 100 - HTTP_RPC_PORT: default - `instance` + 1 - WS_RPC_PORT: default + `instance` * 2 - 2 + + [default: 1] + + -h, --help + Print help (see a summary with '-h') + +Database: + --db.log-level + Database logging level. Levels higher than "notice" require a debug build + + Possible values: + - fatal: Enables logging for critical conditions, i.e. assertion failures + - error: Enables logging for error conditions + - warn: Enables logging for warning conditions + - notice: Enables logging for normal but significant condition + - verbose: Enables logging for verbose informational + - debug: Enables logging for debug-level messages + - trace: Enables logging for trace debug-level messages + - extra: Enables logging for extra debug-level messages + +Logging: + --log.file.directory + The path to put log files in + + [default: /logs] + + --log.file.max-size + The maximum size (in MB) of one log file + + [default: 200] + + --log.file.max-files + The maximum amount of log files that will be stored. If set to 0, background file logging is disabled + + [default: 5] + + --log.file.filter + The filter to use for logs written to the log file + + [default: debug] + + --log.journald + Write logs to journald + + --log.journald.filter + The filter to use for logs written to journald + + [default: error] + + --color + Sets whether or not the formatter emits ANSI terminal escape codes for colors and other text formatting + + [default: always] + + Possible values: + - always: Colors on + - auto: Colors on + - never: Colors off + +Display: + -v, --verbosity... + Set the minimum log level. + + -v Errors + -vv Warnings + -vvv Info + -vvvv Debug + -vvvvv Traces (warning: very verbose!) + + -q, --quiet + Silence all log output +``` \ No newline at end of file diff --git a/book/cli/reth/stage/unwind/num-blocks.md b/book/cli/reth/stage/unwind/num-blocks.md new file mode 100644 index 000000000000..0a9bc15c2adf --- /dev/null +++ b/book/cli/reth/stage/unwind/num-blocks.md @@ -0,0 +1,99 @@ +# reth stage unwind num-blocks + +Unwinds the given number of blocks from the database + +```bash +$ reth stage unwind num-blocks --help +Usage: reth stage unwind num-blocks [OPTIONS] + +Arguments: + + + +Options: + --datadir + The path to the data dir for all reth files and subdirectories. + + Defaults to the OS-specific data directory: + + - Linux: `$XDG_DATA_HOME/reth/` or `$HOME/.local/share/reth/` + - Windows: `{FOLDERID_RoamingAppData}/reth/` + - macOS: `$HOME/Library/Application Support/reth/` + + [default: default] + + --chain + The chain this node is running. + Possible values are either a built-in chain or the path to a chain specification file. + + Built-in chains: + mainnet, sepolia, goerli, holesky, dev + + [default: mainnet] + + --instance + Add a new instance of a node. + + Configures the ports of the node to avoid conflicts with the defaults. This is useful for running multiple nodes on the same machine. + + Max number of instances is 200. It is chosen in a way so that it's not possible to have port numbers that conflict with each other. + + Changes to the following port numbers: - DISCOVERY_PORT: default + `instance` - 1 - AUTH_PORT: default + `instance` * 100 - 100 - HTTP_RPC_PORT: default - `instance` + 1 - WS_RPC_PORT: default + `instance` * 2 - 2 + + [default: 1] + + -h, --help + Print help (see a summary with '-h') + +Logging: + --log.file.directory + The path to put log files in + + [default: /logs] + + --log.file.max-size + The maximum size (in MB) of one log file + + [default: 200] + + --log.file.max-files + The maximum amount of log files that will be stored. If set to 0, background file logging is disabled + + [default: 5] + + --log.file.filter + The filter to use for logs written to the log file + + [default: debug] + + --log.journald + Write logs to journald + + --log.journald.filter + The filter to use for logs written to journald + + [default: error] + + --color + Sets whether or not the formatter emits ANSI terminal escape codes for colors and other text formatting + + [default: always] + + Possible values: + - always: Colors on + - auto: Colors on + - never: Colors off + +Display: + -v, --verbosity... + Set the minimum log level. + + -v Errors + -vv Warnings + -vvv Info + -vvvv Debug + -vvvvv Traces (warning: very verbose!) + + -q, --quiet + Silence all log output +``` \ No newline at end of file diff --git a/book/cli/reth/stage/unwind/to-block.md b/book/cli/reth/stage/unwind/to-block.md new file mode 100644 index 000000000000..d3a5cc712af7 --- /dev/null +++ b/book/cli/reth/stage/unwind/to-block.md @@ -0,0 +1,99 @@ +# reth stage unwind to-block + +Unwinds the database until the given block number (range is inclusive) + +```bash +$ reth stage unwind to-block --help +Usage: reth stage unwind to-block [OPTIONS] + +Arguments: + + + +Options: + --datadir + The path to the data dir for all reth files and subdirectories. + + Defaults to the OS-specific data directory: + + - Linux: `$XDG_DATA_HOME/reth/` or `$HOME/.local/share/reth/` + - Windows: `{FOLDERID_RoamingAppData}/reth/` + - macOS: `$HOME/Library/Application Support/reth/` + + [default: default] + + --chain + The chain this node is running. + Possible values are either a built-in chain or the path to a chain specification file. + + Built-in chains: + mainnet, sepolia, goerli, holesky, dev + + [default: mainnet] + + --instance + Add a new instance of a node. + + Configures the ports of the node to avoid conflicts with the defaults. This is useful for running multiple nodes on the same machine. + + Max number of instances is 200. It is chosen in a way so that it's not possible to have port numbers that conflict with each other. + + Changes to the following port numbers: - DISCOVERY_PORT: default + `instance` - 1 - AUTH_PORT: default + `instance` * 100 - 100 - HTTP_RPC_PORT: default - `instance` + 1 - WS_RPC_PORT: default + `instance` * 2 - 2 + + [default: 1] + + -h, --help + Print help (see a summary with '-h') + +Logging: + --log.file.directory + The path to put log files in + + [default: /logs] + + --log.file.max-size + The maximum size (in MB) of one log file + + [default: 200] + + --log.file.max-files + The maximum amount of log files that will be stored. If set to 0, background file logging is disabled + + [default: 5] + + --log.file.filter + The filter to use for logs written to the log file + + [default: debug] + + --log.journald + Write logs to journald + + --log.journald.filter + The filter to use for logs written to journald + + [default: error] + + --color + Sets whether or not the formatter emits ANSI terminal escape codes for colors and other text formatting + + [default: always] + + Possible values: + - always: Colors on + - auto: Colors on + - never: Colors off + +Display: + -v, --verbosity... + Set the minimum log level. + + -v Errors + -vv Warnings + -vvv Info + -vvvv Debug + -vvvvv Traces (warning: very verbose!) + + -q, --quiet + Silence all log output +``` \ No newline at end of file diff --git a/book/cli/reth/test-vectors.md b/book/cli/reth/test-vectors.md new file mode 100644 index 000000000000..b79b399dd7f3 --- /dev/null +++ b/book/cli/reth/test-vectors.md @@ -0,0 +1,88 @@ +# reth test-vectors + +Generate Test Vectors + +```bash +$ reth test-vectors --help +Usage: reth test-vectors [OPTIONS] + +Commands: + tables Generates test vectors for specified tables. If no table is specified, generate for all + help Print this message or the help of the given subcommand(s) + +Options: + --chain + The chain this node is running. + Possible values are either a built-in chain or the path to a chain specification file. + + Built-in chains: + mainnet, sepolia, goerli, holesky, dev + + [default: mainnet] + + --instance + Add a new instance of a node. + + Configures the ports of the node to avoid conflicts with the defaults. This is useful for running multiple nodes on the same machine. + + Max number of instances is 200. It is chosen in a way so that it's not possible to have port numbers that conflict with each other. + + Changes to the following port numbers: - DISCOVERY_PORT: default + `instance` - 1 - AUTH_PORT: default + `instance` * 100 - 100 - HTTP_RPC_PORT: default - `instance` + 1 - WS_RPC_PORT: default + `instance` * 2 - 2 + + [default: 1] + + -h, --help + Print help (see a summary with '-h') + +Logging: + --log.file.directory + The path to put log files in + + [default: /logs] + + --log.file.max-size + The maximum size (in MB) of one log file + + [default: 200] + + --log.file.max-files + The maximum amount of log files that will be stored. If set to 0, background file logging is disabled + + [default: 5] + + --log.file.filter + The filter to use for logs written to the log file + + [default: debug] + + --log.journald + Write logs to journald + + --log.journald.filter + The filter to use for logs written to journald + + [default: error] + + --color + Sets whether or not the formatter emits ANSI terminal escape codes for colors and other text formatting + + [default: always] + + Possible values: + - always: Colors on + - auto: Colors on + - never: Colors off + +Display: + -v, --verbosity... + Set the minimum log level. + + -v Errors + -vv Warnings + -vvv Info + -vvvv Debug + -vvvvv Traces (warning: very verbose!) + + -q, --quiet + Silence all log output +``` \ No newline at end of file diff --git a/book/cli/reth/test-vectors/tables.md b/book/cli/reth/test-vectors/tables.md new file mode 100644 index 000000000000..f54af54b3acb --- /dev/null +++ b/book/cli/reth/test-vectors/tables.md @@ -0,0 +1,88 @@ +# reth test-vectors tables + +Generates test vectors for specified tables. If no table is specified, generate for all + +```bash +$ reth test-vectors tables --help +Usage: reth test-vectors tables [OPTIONS] [NAMES]... + +Arguments: + [NAMES]... + List of table names. Case-sensitive + +Options: + --chain + The chain this node is running. + Possible values are either a built-in chain or the path to a chain specification file. + + Built-in chains: + mainnet, sepolia, goerli, holesky, dev + + [default: mainnet] + + --instance + Add a new instance of a node. + + Configures the ports of the node to avoid conflicts with the defaults. This is useful for running multiple nodes on the same machine. + + Max number of instances is 200. It is chosen in a way so that it's not possible to have port numbers that conflict with each other. + + Changes to the following port numbers: - DISCOVERY_PORT: default + `instance` - 1 - AUTH_PORT: default + `instance` * 100 - 100 - HTTP_RPC_PORT: default - `instance` + 1 - WS_RPC_PORT: default + `instance` * 2 - 2 + + [default: 1] + + -h, --help + Print help (see a summary with '-h') + +Logging: + --log.file.directory + The path to put log files in + + [default: /logs] + + --log.file.max-size + The maximum size (in MB) of one log file + + [default: 200] + + --log.file.max-files + The maximum amount of log files that will be stored. If set to 0, background file logging is disabled + + [default: 5] + + --log.file.filter + The filter to use for logs written to the log file + + [default: debug] + + --log.journald + Write logs to journald + + --log.journald.filter + The filter to use for logs written to journald + + [default: error] + + --color + Sets whether or not the formatter emits ANSI terminal escape codes for colors and other text formatting + + [default: always] + + Possible values: + - always: Colors on + - auto: Colors on + - never: Colors off + +Display: + -v, --verbosity... + Set the minimum log level. + + -v Errors + -vv Warnings + -vvv Info + -vvvv Debug + -vvvvv Traces (warning: very verbose!) + + -q, --quiet + Silence all log output +``` \ No newline at end of file diff --git a/book/cli/stage.md b/book/cli/stage.md deleted file mode 100644 index 057267624a32..000000000000 --- a/book/cli/stage.md +++ /dev/null @@ -1,1215 +0,0 @@ -# `reth stage` - -Manipulate individual stages - -```bash -$ reth stage --help - -Usage: reth stage [OPTIONS] - -Commands: - run Run a single stage - drop Drop a stage's tables from the database - dump Dumps a stage from a range into a new database - unwind Unwinds a certain block range, deleting it from the database - help Print this message or the help of the given subcommand(s) - -Options: - --chain - The chain this node is running. - Possible values are either a built-in chain or the path to a chain specification file. - - Built-in chains: - mainnet, sepolia, goerli, holesky, dev - - [default: mainnet] - - --instance - Add a new instance of a node. - - Configures the ports of the node to avoid conflicts with the defaults. This is useful for running multiple nodes on the same machine. - - Max number of instances is 200. It is chosen in a way so that it's not possible to have port numbers that conflict with each other. - - Changes to the following port numbers: - DISCOVERY_PORT: default + `instance` - 1 - AUTH_PORT: default + `instance` * 100 - 100 - HTTP_RPC_PORT: default - `instance` + 1 - WS_RPC_PORT: default + `instance` * 2 - 2 - - [default: 1] - - -h, --help - Print help (see a summary with '-h') - -Logging: - --log.file.directory - The path to put log files in - - [default: /reth/logs] - - --log.file.max-size - The maximum size (in MB) of one log file - - [default: 200] - - --log.file.max-files - The maximum amount of log files that will be stored. If set to 0, background file logging is disabled - - [default: 5] - - --log.file.filter - The filter to use for logs written to the log file - - [default: debug] - - --log.journald - Write logs to journald - - --log.journald.filter - The filter to use for logs written to journald - - [default: error] - - --color - Sets whether or not the formatter emits ANSI terminal escape codes for colors and other text formatting - - [default: always] - - Possible values: - - always: Colors on - - auto: Colors on - - never: Colors off - -Display: - -v, --verbosity... - Set the minimum log level. - - -v Errors - -vv Warnings - -vvv Info - -vvvv Debug - -vvvvv Traces (warning: very verbose!) - - -q, --quiet - Silence all log output -``` - -## `reth stage drop` - -Drop a stage's tables from the database - -```bash -$ reth stage drop --help - -Usage: reth stage drop [OPTIONS] - -Options: - --datadir - The path to the data dir for all reth files and subdirectories. - - Defaults to the OS-specific data directory: - - - Linux: `$XDG_DATA_HOME/reth/` or `$HOME/.local/share/reth/` - - Windows: `{FOLDERID_RoamingAppData}/reth/` - - macOS: `$HOME/Library/Application Support/reth/` - - [default: default] - - --chain - The chain this node is running. - Possible values are either a built-in chain or the path to a chain specification file. - - Built-in chains: - mainnet, sepolia, goerli, holesky, dev - - [default: mainnet] - - --instance - Add a new instance of a node. - - Configures the ports of the node to avoid conflicts with the defaults. This is useful for running multiple nodes on the same machine. - - Max number of instances is 200. It is chosen in a way so that it's not possible to have port numbers that conflict with each other. - - Changes to the following port numbers: - DISCOVERY_PORT: default + `instance` - 1 - AUTH_PORT: default + `instance` * 100 - 100 - HTTP_RPC_PORT: default - `instance` + 1 - WS_RPC_PORT: default + `instance` * 2 - 2 - - [default: 1] - - -h, --help - Print help (see a summary with '-h') - -Database: - --db.log-level - Database logging level. Levels higher than "notice" require a debug build - - Possible values: - - fatal: Enables logging for critical conditions, i.e. assertion failures - - error: Enables logging for error conditions - - warn: Enables logging for warning conditions - - notice: Enables logging for normal but significant condition - - verbose: Enables logging for verbose informational - - debug: Enables logging for debug-level messages - - trace: Enables logging for trace debug-level messages - - extra: Enables logging for extra debug-level messages - - - [possible values: headers, bodies, senders, execution, account-hashing, storage-hashing, hashing, merkle, tx-lookup, account-history, storage-history, total-difficulty] - -Logging: - --log.file.directory - The path to put log files in - - [default: /reth/logs] - - --log.file.max-size - The maximum size (in MB) of one log file - - [default: 200] - - --log.file.max-files - The maximum amount of log files that will be stored. If set to 0, background file logging is disabled - - [default: 5] - - --log.file.filter - The filter to use for logs written to the log file - - [default: debug] - - --log.journald - Write logs to journald - - --log.journald.filter - The filter to use for logs written to journald - - [default: error] - - --color - Sets whether or not the formatter emits ANSI terminal escape codes for colors and other text formatting - - [default: always] - - Possible values: - - always: Colors on - - auto: Colors on - - never: Colors off - -Display: - -v, --verbosity... - Set the minimum log level. - - -v Errors - -vv Warnings - -vvv Info - -vvvv Debug - -vvvvv Traces (warning: very verbose!) - - -q, --quiet - Silence all log output -``` - -## `reth stage dump` - -Dumps a stage from a range into a new database - -```bash -$ reth stage dump --help - -Usage: reth stage dump [OPTIONS] - -Commands: - execution Execution stage - storage-hashing StorageHashing stage - account-hashing AccountHashing stage - merkle Merkle stage - help Print this message or the help of the given subcommand(s) - -Options: - --datadir - The path to the data dir for all reth files and subdirectories. - - Defaults to the OS-specific data directory: - - - Linux: `$XDG_DATA_HOME/reth/` or `$HOME/.local/share/reth/` - - Windows: `{FOLDERID_RoamingAppData}/reth/` - - macOS: `$HOME/Library/Application Support/reth/` - - [default: default] - - --chain - The chain this node is running. - Possible values are either a built-in chain or the path to a chain specification file. - - Built-in chains: - mainnet, sepolia, goerli, holesky, dev - - [default: mainnet] - - --instance - Add a new instance of a node. - - Configures the ports of the node to avoid conflicts with the defaults. This is useful for running multiple nodes on the same machine. - - Max number of instances is 200. It is chosen in a way so that it's not possible to have port numbers that conflict with each other. - - Changes to the following port numbers: - DISCOVERY_PORT: default + `instance` - 1 - AUTH_PORT: default + `instance` * 100 - 100 - HTTP_RPC_PORT: default - `instance` + 1 - WS_RPC_PORT: default + `instance` * 2 - 2 - - [default: 1] - - -h, --help - Print help (see a summary with '-h') - -Database: - --db.log-level - Database logging level. Levels higher than "notice" require a debug build - - Possible values: - - fatal: Enables logging for critical conditions, i.e. assertion failures - - error: Enables logging for error conditions - - warn: Enables logging for warning conditions - - notice: Enables logging for normal but significant condition - - verbose: Enables logging for verbose informational - - debug: Enables logging for debug-level messages - - trace: Enables logging for trace debug-level messages - - extra: Enables logging for extra debug-level messages - -Logging: - --log.file.directory - The path to put log files in - - [default: /reth/logs] - - --log.file.max-size - The maximum size (in MB) of one log file - - [default: 200] - - --log.file.max-files - The maximum amount of log files that will be stored. If set to 0, background file logging is disabled - - [default: 5] - - --log.file.filter - The filter to use for logs written to the log file - - [default: debug] - - --log.journald - Write logs to journald - - --log.journald.filter - The filter to use for logs written to journald - - [default: error] - - --color - Sets whether or not the formatter emits ANSI terminal escape codes for colors and other text formatting - - [default: always] - - Possible values: - - always: Colors on - - auto: Colors on - - never: Colors off - -Display: - -v, --verbosity... - Set the minimum log level. - - -v Errors - -vv Warnings - -vvv Info - -vvvv Debug - -vvvvv Traces (warning: very verbose!) - - -q, --quiet - Silence all log output -``` - -### `reth stage dump execution` - -Execution stage - -```bash -$ reth stage dump execution --help - -Usage: reth stage dump execution [OPTIONS] --output-db --from --to - -Options: - --output-db - The path to the new database folder. - - -f, --from - From which block - - -t, --to - To which block - - -d, --dry-run - If passed, it will dry-run a stage execution from the newly created database right after dumping - - --instance - Add a new instance of a node. - - Configures the ports of the node to avoid conflicts with the defaults. This is useful for running multiple nodes on the same machine. - - Max number of instances is 200. It is chosen in a way so that it's not possible to have port numbers that conflict with each other. - - Changes to the following port numbers: - DISCOVERY_PORT: default + `instance` - 1 - AUTH_PORT: default + `instance` * 100 - 100 - HTTP_RPC_PORT: default - `instance` + 1 - WS_RPC_PORT: default + `instance` * 2 - 2 - - [default: 1] - - -h, --help - Print help (see a summary with '-h') - -Logging: - --log.file.directory - The path to put log files in - - [default: /reth/logs] - - --log.file.max-size - The maximum size (in MB) of one log file - - [default: 200] - - --log.file.max-files - The maximum amount of log files that will be stored. If set to 0, background file logging is disabled - - [default: 5] - - --log.file.filter - The filter to use for logs written to the log file - - [default: debug] - - --log.journald - Write logs to journald - - --log.journald.filter - The filter to use for logs written to journald - - [default: error] - - --color - Sets whether or not the formatter emits ANSI terminal escape codes for colors and other text formatting - - [default: always] - - Possible values: - - always: Colors on - - auto: Colors on - - never: Colors off - -Display: - -v, --verbosity... - Set the minimum log level. - - -v Errors - -vv Warnings - -vvv Info - -vvvv Debug - -vvvvv Traces (warning: very verbose!) - - -q, --quiet - Silence all log output -``` - -### `reth stage dump storage-hashing` - -StorageHashing stage - -```bash -$ reth stage dump storage-hashing --help - -Usage: reth stage dump storage-hashing [OPTIONS] --output-db --from --to - -Options: - --output-db - The path to the new database folder. - - -f, --from - From which block - - -t, --to - To which block - - -d, --dry-run - If passed, it will dry-run a stage execution from the newly created database right after dumping - - --instance - Add a new instance of a node. - - Configures the ports of the node to avoid conflicts with the defaults. This is useful for running multiple nodes on the same machine. - - Max number of instances is 200. It is chosen in a way so that it's not possible to have port numbers that conflict with each other. - - Changes to the following port numbers: - DISCOVERY_PORT: default + `instance` - 1 - AUTH_PORT: default + `instance` * 100 - 100 - HTTP_RPC_PORT: default - `instance` + 1 - WS_RPC_PORT: default + `instance` * 2 - 2 - - [default: 1] - - -h, --help - Print help (see a summary with '-h') - -Logging: - --log.file.directory - The path to put log files in - - [default: /reth/logs] - - --log.file.max-size - The maximum size (in MB) of one log file - - [default: 200] - - --log.file.max-files - The maximum amount of log files that will be stored. If set to 0, background file logging is disabled - - [default: 5] - - --log.file.filter - The filter to use for logs written to the log file - - [default: debug] - - --log.journald - Write logs to journald - - --log.journald.filter - The filter to use for logs written to journald - - [default: error] - - --color - Sets whether or not the formatter emits ANSI terminal escape codes for colors and other text formatting - - [default: always] - - Possible values: - - always: Colors on - - auto: Colors on - - never: Colors off - -Display: - -v, --verbosity... - Set the minimum log level. - - -v Errors - -vv Warnings - -vvv Info - -vvvv Debug - -vvvvv Traces (warning: very verbose!) - - -q, --quiet - Silence all log output -``` - -### `reth stage dump account-hashing` - -AccountHashing stage - -```bash -$ reth stage dump account-hashing --help - -Usage: reth stage dump account-hashing [OPTIONS] --output-db --from --to - -Options: - --output-db - The path to the new database folder. - - -f, --from - From which block - - -t, --to - To which block - - -d, --dry-run - If passed, it will dry-run a stage execution from the newly created database right after dumping - - --instance - Add a new instance of a node. - - Configures the ports of the node to avoid conflicts with the defaults. This is useful for running multiple nodes on the same machine. - - Max number of instances is 200. It is chosen in a way so that it's not possible to have port numbers that conflict with each other. - - Changes to the following port numbers: - DISCOVERY_PORT: default + `instance` - 1 - AUTH_PORT: default + `instance` * 100 - 100 - HTTP_RPC_PORT: default - `instance` + 1 - WS_RPC_PORT: default + `instance` * 2 - 2 - - [default: 1] - - -h, --help - Print help (see a summary with '-h') - -Logging: - --log.file.directory - The path to put log files in - - [default: /reth/logs] - - --log.file.max-size - The maximum size (in MB) of one log file - - [default: 200] - - --log.file.max-files - The maximum amount of log files that will be stored. If set to 0, background file logging is disabled - - [default: 5] - - --log.file.filter - The filter to use for logs written to the log file - - [default: debug] - - --log.journald - Write logs to journald - - --log.journald.filter - The filter to use for logs written to journald - - [default: error] - - --color - Sets whether or not the formatter emits ANSI terminal escape codes for colors and other text formatting - - [default: always] - - Possible values: - - always: Colors on - - auto: Colors on - - never: Colors off - -Display: - -v, --verbosity... - Set the minimum log level. - - -v Errors - -vv Warnings - -vvv Info - -vvvv Debug - -vvvvv Traces (warning: very verbose!) - - -q, --quiet - Silence all log output -``` - -### `reth stage dump merkle` - -Merkle stage - -```bash -$ reth stage dump merkle --help - -Usage: reth stage dump merkle [OPTIONS] --output-db --from --to - -Options: - --output-db - The path to the new database folder. - - -f, --from - From which block - - -t, --to - To which block - - -d, --dry-run - If passed, it will dry-run a stage execution from the newly created database right after dumping - - --instance - Add a new instance of a node. - - Configures the ports of the node to avoid conflicts with the defaults. This is useful for running multiple nodes on the same machine. - - Max number of instances is 200. It is chosen in a way so that it's not possible to have port numbers that conflict with each other. - - Changes to the following port numbers: - DISCOVERY_PORT: default + `instance` - 1 - AUTH_PORT: default + `instance` * 100 - 100 - HTTP_RPC_PORT: default - `instance` + 1 - WS_RPC_PORT: default + `instance` * 2 - 2 - - [default: 1] - - -h, --help - Print help (see a summary with '-h') - -Logging: - --log.file.directory - The path to put log files in - - [default: /reth/logs] - - --log.file.max-size - The maximum size (in MB) of one log file - - [default: 200] - - --log.file.max-files - The maximum amount of log files that will be stored. If set to 0, background file logging is disabled - - [default: 5] - - --log.file.filter - The filter to use for logs written to the log file - - [default: debug] - - --log.journald - Write logs to journald - - --log.journald.filter - The filter to use for logs written to journald - - [default: error] - - --color - Sets whether or not the formatter emits ANSI terminal escape codes for colors and other text formatting - - [default: always] - - Possible values: - - always: Colors on - - auto: Colors on - - never: Colors off - -Display: - -v, --verbosity... - Set the minimum log level. - - -v Errors - -vv Warnings - -vvv Info - -vvvv Debug - -vvvvv Traces (warning: very verbose!) - - -q, --quiet - Silence all log output -``` - -## `reth stage run` - -Run a single stage. - -```bash -$ reth stage run --help - -Note that this won't use the Pipeline and as a result runs stages assuming that all the data can be held in memory. It is not recommended to run a stage for really large block ranges if your computer does not have a lot of memory to store all the data. - -Usage: reth stage run [OPTIONS] --from --to - -Arguments: - - The name of the stage to run - - [possible values: headers, bodies, senders, execution, account-hashing, storage-hashing, hashing, merkle, tx-lookup, account-history, storage-history, total-difficulty] - -Options: - --config - The path to the configuration file to use. - - --datadir - The path to the data dir for all reth files and subdirectories. - - Defaults to the OS-specific data directory: - - - Linux: `$XDG_DATA_HOME/reth/` or `$HOME/.local/share/reth/` - - Windows: `{FOLDERID_RoamingAppData}/reth/` - - macOS: `$HOME/Library/Application Support/reth/` - - [default: default] - - --chain - The chain this node is running. - Possible values are either a built-in chain or the path to a chain specification file. - - Built-in chains: - mainnet, sepolia, goerli, holesky, dev - - [default: mainnet] - - --metrics - Enable Prometheus metrics. - - The metrics will be served at the given interface and port. - - --from - The height to start at - - -t, --to - The end of the stage - - --batch-size - Batch size for stage execution and unwind - - -s, --skip-unwind - Normally, running the stage requires unwinding for stages that already have been run, in order to not rewrite to the same database slots. - - You can optionally skip the unwinding phase if you're syncing a block range that has not been synced before. - - --instance - Add a new instance of a node. - - Configures the ports of the node to avoid conflicts with the defaults. This is useful for running multiple nodes on the same machine. - - Max number of instances is 200. It is chosen in a way so that it's not possible to have port numbers that conflict with each other. - - Changes to the following port numbers: - DISCOVERY_PORT: default + `instance` - 1 - AUTH_PORT: default + `instance` * 100 - 100 - HTTP_RPC_PORT: default - `instance` + 1 - WS_RPC_PORT: default + `instance` * 2 - 2 - - [default: 1] - - -h, --help - Print help (see a summary with '-h') - -Networking: - -d, --disable-discovery - Disable the discovery service - - --disable-dns-discovery - Disable the DNS discovery - - --disable-discv4-discovery - Disable Discv4 discovery - - --discovery.addr - The UDP address to use for P2P discovery/networking - - [default: 0.0.0.0] - - --discovery.port - The UDP port to use for P2P discovery/networking - - [default: 30303] - - --trusted-peers - Comma separated enode URLs of trusted peers for P2P connections. - - --trusted-peers enode://abcd@192.168.0.1:30303 - - --trusted-only - Connect only to trusted peers - - --bootnodes - Comma separated enode URLs for P2P discovery bootstrap. - - Will fall back to a network-specific default if not specified. - - --peers-file - The path to the known peers file. Connected peers are dumped to this file on nodes - shutdown, and read on startup. Cannot be used with `--no-persist-peers`. - - --identity - Custom node identity - - [default: reth/v0.1.0-alpha.13-10a83e594/aarch64-apple-darwin] - - --p2p-secret-key - Secret key to use for this node. - - This will also deterministically set the peer ID. If not specified, it will be set in the data dir for the chain being used. - - --no-persist-peers - Do not persist peers. - - --nat - NAT resolution method (any|none|upnp|publicip|extip:) - - [default: any] - - --addr - Network listening address - - [default: 0.0.0.0] - - --port - Network listening port - - [default: 30303] - - --max-outbound-peers - Maximum number of outbound requests. default: 100 - - --max-inbound-peers - Maximum number of inbound requests. default: 30 - -Database: - --db.log-level - Database logging level. Levels higher than "notice" require a debug build - - Possible values: - - fatal: Enables logging for critical conditions, i.e. assertion failures - - error: Enables logging for error conditions - - warn: Enables logging for warning conditions - - notice: Enables logging for normal but significant condition - - verbose: Enables logging for verbose informational - - debug: Enables logging for debug-level messages - - trace: Enables logging for trace debug-level messages - - extra: Enables logging for extra debug-level messages - - -c, --commit - Commits the changes in the database. WARNING: potentially destructive. - - Useful when you want to run diagnostics on the database. - -Logging: - --log.file.directory - The path to put log files in - - [default: /reth/logs] - - --log.file.max-size - The maximum size (in MB) of one log file - - [default: 200] - - --log.file.max-files - The maximum amount of log files that will be stored. If set to 0, background file logging is disabled - - [default: 5] - - --log.file.filter - The filter to use for logs written to the log file - - [default: debug] - - --log.journald - Write logs to journald - - --log.journald.filter - The filter to use for logs written to journald - - [default: error] - - --color - Sets whether or not the formatter emits ANSI terminal escape codes for colors and other text formatting - - [default: always] - - Possible values: - - always: Colors on - - auto: Colors on - - never: Colors off - -Display: - -v, --verbosity... - Set the minimum log level. - - -v Errors - -vv Warnings - -vvv Info - -vvvv Debug - -vvvvv Traces (warning: very verbose!) - - -q, --quiet - Silence all log output -``` - -## `reth stage unwind` - -Unwinds a certain block range, deleting it from the database - -```bash -$ reth stage unwind --help - -Usage: reth stage unwind [OPTIONS] - -Commands: - to-block Unwinds the database until the given block number (range is inclusive) - num-blocks Unwinds the given number of blocks from the database - help Print this message or the help of the given subcommand(s) - -Options: - --datadir - The path to the data dir for all reth files and subdirectories. - - Defaults to the OS-specific data directory: - - - Linux: `$XDG_DATA_HOME/reth/` or `$HOME/.local/share/reth/` - - Windows: `{FOLDERID_RoamingAppData}/reth/` - - macOS: `$HOME/Library/Application Support/reth/` - - [default: default] - - --chain - The chain this node is running. - Possible values are either a built-in chain or the path to a chain specification file. - - Built-in chains: - mainnet, sepolia, goerli, holesky, dev - - [default: mainnet] - - --instance - Add a new instance of a node. - - Configures the ports of the node to avoid conflicts with the defaults. This is useful for running multiple nodes on the same machine. - - Max number of instances is 200. It is chosen in a way so that it's not possible to have port numbers that conflict with each other. - - Changes to the following port numbers: - DISCOVERY_PORT: default + `instance` - 1 - AUTH_PORT: default + `instance` * 100 - 100 - HTTP_RPC_PORT: default - `instance` + 1 - WS_RPC_PORT: default + `instance` * 2 - 2 - - [default: 1] - - -h, --help - Print help (see a summary with '-h') - -Database: - --db.log-level - Database logging level. Levels higher than "notice" require a debug build - - Possible values: - - fatal: Enables logging for critical conditions, i.e. assertion failures - - error: Enables logging for error conditions - - warn: Enables logging for warning conditions - - notice: Enables logging for normal but significant condition - - verbose: Enables logging for verbose informational - - debug: Enables logging for debug-level messages - - trace: Enables logging for trace debug-level messages - - extra: Enables logging for extra debug-level messages - -Logging: - --log.file.directory - The path to put log files in - - [default: /reth/logs] - - --log.file.max-size - The maximum size (in MB) of one log file - - [default: 200] - - --log.file.max-files - The maximum amount of log files that will be stored. If set to 0, background file logging is disabled - - [default: 5] - - --log.file.filter - The filter to use for logs written to the log file - - [default: debug] - - --log.journald - Write logs to journald - - --log.journald.filter - The filter to use for logs written to journald - - [default: error] - - --color - Sets whether or not the formatter emits ANSI terminal escape codes for colors and other text formatting - - [default: always] - - Possible values: - - always: Colors on - - auto: Colors on - - never: Colors off - -Display: - -v, --verbosity... - Set the minimum log level. - - -v Errors - -vv Warnings - -vvv Info - -vvvv Debug - -vvvvv Traces (warning: very verbose!) - - -q, --quiet - Silence all log output -``` - -### `reth stage unwind to-block` - -Unwinds the database until the given block number (range is inclusive) - -```bash -$ reth stage unwind to-block --help - -Usage: reth stage unwind to-block [OPTIONS] - -Arguments: - - - -Options: - --datadir - The path to the data dir for all reth files and subdirectories. - - Defaults to the OS-specific data directory: - - - Linux: `$XDG_DATA_HOME/reth/` or `$HOME/.local/share/reth/` - - Windows: `{FOLDERID_RoamingAppData}/reth/` - - macOS: `$HOME/Library/Application Support/reth/` - - [default: default] - - --chain - The chain this node is running. - Possible values are either a built-in chain or the path to a chain specification file. - - Built-in chains: - mainnet, sepolia, goerli, holesky, dev - - [default: mainnet] - - --instance - Add a new instance of a node. - - Configures the ports of the node to avoid conflicts with the defaults. This is useful for running multiple nodes on the same machine. - - Max number of instances is 200. It is chosen in a way so that it's not possible to have port numbers that conflict with each other. - - Changes to the following port numbers: - DISCOVERY_PORT: default + `instance` - 1 - AUTH_PORT: default + `instance` * 100 - 100 - HTTP_RPC_PORT: default - `instance` + 1 - WS_RPC_PORT: default + `instance` * 2 - 2 - - [default: 1] - - -h, --help - Print help (see a summary with '-h') - -Logging: - --log.file.directory - The path to put log files in - - [default: /reth/logs] - - --log.file.max-size - The maximum size (in MB) of one log file - - [default: 200] - - --log.file.max-files - The maximum amount of log files that will be stored. If set to 0, background file logging is disabled - - [default: 5] - - --log.file.filter - The filter to use for logs written to the log file - - [default: debug] - - --log.journald - Write logs to journald - - --log.journald.filter - The filter to use for logs written to journald - - [default: error] - - --color - Sets whether or not the formatter emits ANSI terminal escape codes for colors and other text formatting - - [default: always] - - Possible values: - - always: Colors on - - auto: Colors on - - never: Colors off - -Display: - -v, --verbosity... - Set the minimum log level. - - -v Errors - -vv Warnings - -vvv Info - -vvvv Debug - -vvvvv Traces (warning: very verbose!) - - -q, --quiet - Silence all log output -``` - -### `reth stage unwind num-blocks` - -Unwinds the given number of blocks from the database - -```bash -$ reth stage unwind num-blocks --help - -Usage: reth stage unwind num-blocks [OPTIONS] - -Arguments: - - - -Options: - --datadir - The path to the data dir for all reth files and subdirectories. - - Defaults to the OS-specific data directory: - - - Linux: `$XDG_DATA_HOME/reth/` or `$HOME/.local/share/reth/` - - Windows: `{FOLDERID_RoamingAppData}/reth/` - - macOS: `$HOME/Library/Application Support/reth/` - - [default: default] - - --chain - The chain this node is running. - Possible values are either a built-in chain or the path to a chain specification file. - - Built-in chains: - mainnet, sepolia, goerli, holesky, dev - - [default: mainnet] - - --instance - Add a new instance of a node. - - Configures the ports of the node to avoid conflicts with the defaults. This is useful for running multiple nodes on the same machine. - - Max number of instances is 200. It is chosen in a way so that it's not possible to have port numbers that conflict with each other. - - Changes to the following port numbers: - DISCOVERY_PORT: default + `instance` - 1 - AUTH_PORT: default + `instance` * 100 - 100 - HTTP_RPC_PORT: default - `instance` + 1 - WS_RPC_PORT: default + `instance` * 2 - 2 - - [default: 1] - - -h, --help - Print help (see a summary with '-h') - -Logging: - --log.file.directory - The path to put log files in - - [default: /reth/logs] - - --log.file.max-size - The maximum size (in MB) of one log file - - [default: 200] - - --log.file.max-files - The maximum amount of log files that will be stored. If set to 0, background file logging is disabled - - [default: 5] - - --log.file.filter - The filter to use for logs written to the log file - - [default: debug] - - --log.journald - Write logs to journald - - --log.journald.filter - The filter to use for logs written to journald - - [default: error] - - --color - Sets whether or not the formatter emits ANSI terminal escape codes for colors and other text formatting - - [default: always] - - Possible values: - - always: Colors on - - auto: Colors on - - never: Colors off - -Display: - -v, --verbosity... - Set the minimum log level. - - -v Errors - -vv Warnings - -vvv Info - -vvvv Debug - -vvvvv Traces (warning: very verbose!) - - -q, --quiet - Silence all log output -``` diff --git a/book/cli/test-vectors.md b/book/cli/test-vectors.md deleted file mode 100644 index 13dfd0e79319..000000000000 --- a/book/cli/test-vectors.md +++ /dev/null @@ -1,179 +0,0 @@ -# `reth test-vectors` - -Generate Test Vectors - -```bash -$ reth test-vectors --help - -Usage: reth test-vectors [OPTIONS] - -Commands: - tables Generates test vectors for specified tables. If no table is specified, generate for all - help Print this message or the help of the given subcommand(s) - -Options: - --chain - The chain this node is running. - Possible values are either a built-in chain or the path to a chain specification file. - - Built-in chains: - mainnet, sepolia, goerli, holesky, dev - - [default: mainnet] - - --instance - Add a new instance of a node. - - Configures the ports of the node to avoid conflicts with the defaults. This is useful for running multiple nodes on the same machine. - - Max number of instances is 200. It is chosen in a way so that it's not possible to have port numbers that conflict with each other. - - Changes to the following port numbers: - DISCOVERY_PORT: default + `instance` - 1 - AUTH_PORT: default + `instance` * 100 - 100 - HTTP_RPC_PORT: default - `instance` + 1 - WS_RPC_PORT: default + `instance` * 2 - 2 - - [default: 1] - - -h, --help - Print help (see a summary with '-h') - -Logging: - --log.file.directory - The path to put log files in - - [default: /reth/logs] - - --log.file.max-size - The maximum size (in MB) of one log file - - [default: 200] - - --log.file.max-files - The maximum amount of log files that will be stored. If set to 0, background file logging is disabled - - [default: 5] - - --log.file.filter - The filter to use for logs written to the log file - - [default: debug] - - --log.journald - Write logs to journald - - --log.journald.filter - The filter to use for logs written to journald - - [default: error] - - --color - Sets whether or not the formatter emits ANSI terminal escape codes for colors and other text formatting - - [default: always] - - Possible values: - - always: Colors on - - auto: Colors on - - never: Colors off - -Display: - -v, --verbosity... - Set the minimum log level. - - -v Errors - -vv Warnings - -vvv Info - -vvvv Debug - -vvvvv Traces (warning: very verbose!) - - -q, --quiet - Silence all log output -``` - -## `reth test-vectors tables` - -Generates test vectors for specified tables. If no table is specified, generate for all - -```bash -$ reth test-vectors tables --help - -Usage: reth test-vectors tables [OPTIONS] [NAMES]... - -Arguments: - [NAMES]... - List of table names. Case-sensitive - -Options: - --chain - The chain this node is running. - Possible values are either a built-in chain or the path to a chain specification file. - - Built-in chains: - mainnet, sepolia, goerli, holesky, dev - - [default: mainnet] - - --instance - Add a new instance of a node. - - Configures the ports of the node to avoid conflicts with the defaults. This is useful for running multiple nodes on the same machine. - - Max number of instances is 200. It is chosen in a way so that it's not possible to have port numbers that conflict with each other. - - Changes to the following port numbers: - DISCOVERY_PORT: default + `instance` - 1 - AUTH_PORT: default + `instance` * 100 - 100 - HTTP_RPC_PORT: default - `instance` + 1 - WS_RPC_PORT: default + `instance` * 2 - 2 - - [default: 1] - - -h, --help - Print help (see a summary with '-h') - -Logging: - --log.file.directory - The path to put log files in - - [default: /reth/logs] - - --log.file.max-size - The maximum size (in MB) of one log file - - [default: 200] - - --log.file.max-files - The maximum amount of log files that will be stored. If set to 0, background file logging is disabled - - [default: 5] - - --log.file.filter - The filter to use for logs written to the log file - - [default: debug] - - --log.journald - Write logs to journald - - --log.journald.filter - The filter to use for logs written to journald - - [default: error] - - --color - Sets whether or not the formatter emits ANSI terminal escape codes for colors and other text formatting - - [default: always] - - Possible values: - - always: Colors on - - auto: Colors on - - never: Colors off - -Display: - -v, --verbosity... - Set the minimum log level. - - -v Errors - -vv Warnings - -vvv Info - -vvvv Debug - -vvvvv Traces (warning: very verbose!) - - -q, --quiet - Silence all log output -``` diff --git a/book/cli/update.sh b/book/cli/update.sh index ca7722e3f337..bffacf287760 100755 --- a/book/cli/update.sh +++ b/book/cli/update.sh @@ -1,128 +1,16 @@ -#!/bin/bash - -# Define the build path. -build_path=$1 -if [ -z "$build_path" ]; then - echo "Build path variable is not defined. Exiting..." - exit 1 -fi -reth_path=./$build_path/debug/reth -echo "Using reth path: $reth_path (build path: $build_path)" - -# Define the path to the JSON configuration file. -json_file="./book/cli/config.json" -echo "Using config file: $json_file" - -# Read commands from JSON configuration file. -read_cmds_from_json() { - local json_file="$1" - jq -r '.commands | keys[]' "$json_file" -} - -# Read subcommands for a given command from JSON configuration file. -read_subcmds_from_json() { - local json_file="$1" - local cmd="$2" - jq -r ".commands[\"$cmd\"] | if type == \"object\" then keys[] else .[] end" "$json_file" -} - -# Read subsubcommands for a given command and subcommand from JSON configuration file. -read_subsubcmds_from_json() { - local json_file="$1" - local cmd="$2" - local subcmd="$3" - jq -r ".commands[\"$cmd\"][\"$subcmd\"][]" "$json_file" -} - -# Update the main documentation. -update_main_doc() { - local file_path="./book/cli/cli.md" - local cmd_help_output=$($reth_path --help) - sed -i -e '/## Commands/,$d' "$file_path" - cat >> "$file_path" << EOF -## Commands - -\`\`\`bash -$ reth --help -$cmd_help_output -\`\`\` -EOF -} - -# Update any `reth` command documentation. -update_cli_cmd() { - local cmd="$1" - local subcmds=("${@:2}") - echo "reth $cmd" - - local cmd_help_output=$($reth_path "$cmd" --help) - local description=$(echo "$cmd_help_output" | head -n 1) - cat > "./book/cli/$cmd.md" << EOF -# \`reth $cmd\` - -$(if [[ -n "$description" ]]; then echo "$description"; fi) - -\`\`\`bash -$ reth $cmd --help -$(echo "$cmd_help_output" | sed '1d') -\`\`\` -EOF - - for subcmd in "${subcmds[@]}"; do - echo " ├── $subcmd" - - local subcmd_help_output=$($reth_path "$cmd" "$subcmd" --help) - local subcmd_description=$(echo "$subcmd_help_output" | head -n 1) - cat >> "book/cli/$cmd.md" << EOF - -## \`reth $cmd $subcmd\` - -$(if [[ -n "$subcmd_description" ]]; then echo "$subcmd_description"; fi) - -\`\`\`bash -$ reth $cmd $subcmd --help -$(echo "$subcmd_help_output" | sed '1d') -\`\`\` -EOF - - # Read subsubcommands and update documentation - subsubcmds=($(read_subsubcmds_from_json "$json_file" "$cmd" "$subcmd")) - for subsubcmd in "${subsubcmds[@]}"; do - echo " ├── $subsubcmd" - - local subsubcmd_help_output=$($reth_path "$cmd" "$subcmd" "$subsubcmd" --help) - local subsubcmd_description=$(echo "$subsubcmd_help_output" | head -n 1) - cat >> "book/cli/$cmd.md" << EOF - -### \`reth $cmd $subcmd $subsubcmd\` - -$(if [[ -n "$subsubcmd_description" ]]; then echo "$subsubcmd_description"; fi) - -\`\`\`bash -$ reth $cmd $subcmd $subsubcmd --help -$(echo "$subsubcmd_help_output" | sed '1d') -\`\`\` -EOF - done - done -} - -# Update the book CLI documentation. -main() { - update_main_doc - - # Update commands doc. - cmds=($(read_cmds_from_json "$json_file")) - for cmd in "${cmds[@]}"; do - subcmds=($(read_subcmds_from_json "$json_file" "$cmd")) - update_cli_cmd "$cmd" "${subcmds[@]}" - done - - # Update default paths on both Linux and macOS to avoid triggering the CI. - sed -i -e 's/default: \/.*\/reth\//default: \/reth\//g' ./book/cli/*.md - rm ./book/cli/*.md-e - - echo "Book updated successfully." -} - -main +#!/usr/bin/env bash +set -eo pipefail + +BOOK_ROOT="$(dirname "$(dirname "$0")")" +RETH=${1:-"$(dirname "$BOOK_ROOT")/target/debug/reth"} + +cmd=( + "$(dirname "$0")/help.py" + --root-dir "$BOOK_ROOT/" + --root-indentation 2 + --root-summary + --out-dir "$BOOK_ROOT/cli/" + "$RETH" +) +echo "Running: $" "${cmd[*]}" +"${cmd[@]}" diff --git a/book/run/mainnet.md b/book/run/mainnet.md index e2f1c07e42a6..67e70b9dbf20 100644 --- a/book/run/mainnet.md +++ b/book/run/mainnet.md @@ -30,7 +30,7 @@ RUST_LOG=info reth node --full On differences between archive and full nodes, see [Pruning & Full Node](./pruning.md#basic-concepts) section. -> Note that these commands will not open any HTTP/WS ports by default. You can change this by adding the `--http`, `--ws` flags, respectively and using the `--http.api` and `--ws.api` flags to enable various [JSON-RPC APIs](../jsonrpc/intro.md). For more commands, see the [`reth node` CLI reference](../cli/node.md). +> Note that these commands will not open any HTTP/WS ports by default. You can change this by adding the `--http`, `--ws` flags, respectively and using the `--http.api` and `--ws.api` flags to enable various [JSON-RPC APIs](../jsonrpc/intro.md). For more commands, see the [`reth node` CLI reference](../cli/reth/node.md). The EL <> CL communication happens over the [Engine API](https://github.com/ethereum/execution-apis/blob/main/src/engine/common.md), which is by default exposed at `http://localhost:8551`. The connection is authenticated over JWT using a JWT secret which is auto-generated by Reth and placed in a file called `jwt.hex` in the data directory, which on Linux by default is `$HOME/.local/share/reth/` (`/Users//Library/Application Support/reth/mainnet/jwt.hex` in Mac). diff --git a/book/run/troubleshooting.md b/book/run/troubleshooting.md index f9a6bd25c8a3..fa888139f539 100644 --- a/book/run/troubleshooting.md +++ b/book/run/troubleshooting.md @@ -61,7 +61,7 @@ is less than 1 second. It will take the same time as initial sync. 1. Stop Reth -2. Drop the database using [`reth db drop`](../cli/db.md#reth-db-drop) +2. Drop the database using [`reth db drop`](../cli/reth/db/drop.md) 3. Start reth ### Database write error @@ -110,4 +110,4 @@ pthread_mutex_lock.c:438: __pthread_mutex_lock_full: Assertion `e != ESRCH || !r If you are using Docker, a possible solution is to run all database-accessing containers with `--pid=host` flag. -For more information, check out the `Containers` section in the [libmdbx README](https://github.com/erthink/libmdbx#containers). \ No newline at end of file +For more information, check out the `Containers` section in the [libmdbx README](https://github.com/erthink/libmdbx#containers). From db48d6b097ddf189bb968986159ebf449f6158fd Mon Sep 17 00:00:00 2001 From: Alexey Shekhirin Date: Thu, 14 Dec 2023 19:00:45 +0200 Subject: [PATCH 130/277] feat(bin): I/O metrics (#5710) --- Cargo.lock | 4 +++ bin/reth/Cargo.toml | 3 ++ bin/reth/src/prometheus_exporter.rs | 46 +++++++++++++++++++++++++++++ 3 files changed, 53 insertions(+) diff --git a/Cargo.lock b/Cargo.lock index af3eb7ed77ba..881de09d6d0d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5172,6 +5172,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "731e0d9356b0c25f16f33b5be79b1c57b562f141ebfcdb0ad8ac2c13a24293b4" dependencies = [ "bitflags 2.4.1", + "chrono", + "flate2", "hex", "lazy_static", "procfs-core", @@ -5185,6 +5187,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2d3554923a69f4ce04c4a754260c338f505ce22642d3830e049a399fc2059a29" dependencies = [ "bitflags 2.4.1", + "chrono", "hex", ] @@ -5565,6 +5568,7 @@ dependencies = [ "metrics-util", "pin-project", "pretty_assertions", + "procfs", "proptest", "rand 0.8.5", "rayon", diff --git a/bin/reth/Cargo.toml b/bin/reth/Cargo.toml index 1a0fbdb17338..1619c0cf8a3e 100644 --- a/bin/reth/Cargo.toml +++ b/bin/reth/Cargo.toml @@ -109,6 +109,9 @@ rayon.workspace = true jemallocator = { version = "0.5.0", optional = true } jemalloc-ctl = { version = "0.5.0", optional = true } +[target.'cfg(target_os = "linux")'.dependencies] +procfs = { version = "0.16.0" } + [features] default = ["jemalloc"] jemalloc = ["dep:jemallocator", "dep:jemalloc-ctl"] diff --git a/bin/reth/src/prometheus_exporter.rs b/bin/reth/src/prometheus_exporter.rs index 0ec5352c9b33..e681933ea36c 100644 --- a/bin/reth/src/prometheus_exporter.rs +++ b/bin/reth/src/prometheus_exporter.rs @@ -91,6 +91,7 @@ where Box::new(db_metrics_hook), Box::new(move || cloned_process.collect()), Box::new(collect_memory_stats), + Box::new(collect_io_stats), ]; serve_with_hooks(listen_addr, handle, hooks).await?; @@ -102,6 +103,7 @@ where describe_gauge!("db.freelist", "The number of pages on the freelist"); process.describe(); describe_memory_stats(); + describe_io_stats(); Ok(()) } @@ -192,3 +194,47 @@ fn collect_memory_stats() {} #[cfg(not(all(feature = "jemalloc", unix)))] fn describe_memory_stats() {} + +#[cfg(target_os = "linux")] +fn collect_io_stats() { + use metrics::absolute_counter; + + let Ok(process) = procfs::process::Process::myself() + .map_err(|error| error!(?error, "Failed to get currently running process")) + else { + return + }; + + let Ok(io) = process.io().map_err(|error| { + error!(?error, "Failed to get IO stats for the currently running process") + }) else { + return + }; + + absolute_counter!("io.rchar", io.rchar); + absolute_counter!("io.wchar", io.wchar); + absolute_counter!("io.syscr", io.syscr); + absolute_counter!("io.syscw", io.syscw); + absolute_counter!("io.read_bytes", io.read_bytes); + absolute_counter!("io.write_bytes", io.write_bytes); + absolute_counter!("io.cancelled_write_bytes", io.cancelled_write_bytes); +} + +#[cfg(target_os = "linux")] +fn describe_io_stats() { + use metrics::describe_counter; + + describe_counter!("io.rchar", "Characters read"); + describe_counter!("io.wchar", "Characters written"); + describe_counter!("io.syscr", "Read syscalls"); + describe_counter!("io.syscw", "Write syscalls"); + describe_counter!("io.read_bytes", Unit::Bytes, "Bytes read"); + describe_counter!("io.write_bytes", Unit::Bytes, "Bytes written"); + describe_counter!("io.cancelled_write_bytes", Unit::Bytes, "Cancelled write bytes"); +} + +#[cfg(not(target_os = "linux"))] +fn collect_io_stats() {} + +#[cfg(not(target_os = "linux"))] +fn describe_io_stats() {} From 9a26b7a8e54b5ac188cd5a8f0e724923d3158cbe Mon Sep 17 00:00:00 2001 From: solidoracle <105349716+solidoracle@users.noreply.github.com> Date: Thu, 14 Dec 2023 18:01:36 +0100 Subject: [PATCH 131/277] Canonicalization metrics improvements (#5705) Co-authored-by: Alexey Shekhirin --- .../src/providers/database/metrics.rs | 2 +- etc/grafana/dashboards/overview.json | 5039 ++--------------- 2 files changed, 341 insertions(+), 4700 deletions(-) diff --git a/crates/storage/provider/src/providers/database/metrics.rs b/crates/storage/provider/src/providers/database/metrics.rs index b36e9140ab9b..cdd4147292da 100644 --- a/crates/storage/provider/src/providers/database/metrics.rs +++ b/crates/storage/provider/src/providers/database/metrics.rs @@ -20,7 +20,7 @@ impl DurationsRecorder { /// `action` label. pub(crate) fn record_duration(&mut self, action: Action, duration: Duration) { self.actions.push((action, duration)); - Metrics::new_with_labels(&[("action", format!("{action:?}"))]).duration.record(duration); + Metrics::new_with_labels(&[("action", action.as_str())]).duration.record(duration); self.latest = Some(self.start.elapsed()); } diff --git a/etc/grafana/dashboards/overview.json b/etc/grafana/dashboards/overview.json index 5c1adb8d68fc..648a086b80b7 100644 --- a/etc/grafana/dashboards/overview.json +++ b/etc/grafana/dashboards/overview.json @@ -1,5 +1,5 @@ { -"__inputs": [ + "__inputs": [ { "name": "DS_PROMETHEUS", "label": "Prometheus", @@ -160,7 +160,7 @@ "showThresholdLabels": false, "showThresholdMarkers": true }, - "pluginVersion": "10.1.0", + "pluginVersion": "10.1.1", "targets": [ { "datasource": { @@ -225,7 +225,7 @@ "showUnfilled": true, "valueMode": "color" }, - "pluginVersion": "10.1.0", + "pluginVersion": "10.1.1", "targets": [ { "datasource": { @@ -610,7 +610,7 @@ "unit": "percentunit" } }, - "pluginVersion": "10.1.0", + "pluginVersion": "10.1.1", "targets": [ { "datasource": { @@ -1397,7 +1397,7 @@ }, "showHeader": true }, - "pluginVersion": "10.1.0", + "pluginVersion": "10.1.1", "targets": [ { "datasource": { @@ -1512,6 +1512,129 @@ "title": "Freelist", "type": "timeseries" }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "description": "", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "s" + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 58 + }, + "id": 189, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "expr": "reth_storage_providers_database_duration{instance=~\"$instance\", action=\"$database_action\", quantile=\"0.5\"}", + "instant": false, + "legendFormat": "{{quantile}} percentile", + "range": true, + "refId": "A" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "expr": "reth_storage_providers_database_duration{instance=~\"$instance\", action=\"$database_action\", quantile=\"0.95\"} ", + "hide": false, + "instant": false, + "legendFormat": "{{quantile}} percentile", + "range": true, + "refId": "B" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "exemplar": false, + "expr": "reth_storage_providers_database_duration{instance=~\"$instance\", action=\"$database_action\", quantile=\"1\"} ", + "hide": false, + "instant": false, + "legendFormat": "{{quantile}} percentile", + "range": true, + "refId": "C" + } + ], + "title": "Database duration per action", + "type": "timeseries" + }, { "collapsed": false, "gridPos": { @@ -4657,13 +4780,136 @@ "title": "Canonical Commit Latency time", "type": "timeseries" }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "description": "", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "s" + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 176 + }, + "id": 158, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "expr": "reth_blockchain_tree_make_canonical_duration{instance=~\"$instance\", action=~\"$blockchain_tree_action\", quantile=\"0.5\"} ", + "instant": false, + "legendFormat": "${blockchain_tree_action} {{quantile}} percentile", + "range": true, + "refId": "A" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "expr": "reth_blockchain_tree_make_canonical_duration{instance=~\"$instance\", action=~\"$blockchain_tree_action\", quantile=\"0.95\"} ", + "hide": false, + "instant": false, + "legendFormat": "${blockchain_tree_action} {{quantile}} percentile", + "range": true, + "refId": "B" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "exemplar": false, + "expr": "reth_blockchain_tree_make_canonical_duration{instance=~\"$instance\", action=~\"$blockchain_tree_action\", quantile=\"1\"} ", + "hide": false, + "instant": false, + "legendFormat": "${blockchain_tree_action} {{quantile}} percentile", + "range": true, + "refId": "C" + } + ], + "title": "Canonicalization duration per action", + "type": "timeseries" + }, { "collapsed": false, "gridPos": { "h": 1, "w": 24, "x": 0, - "y": 176 + "y": 184 }, "id": 87, "panels": [], @@ -4735,7 +4981,7 @@ "h": 8, "w": 12, "x": 0, - "y": 177 + "y": 185 }, "id": 83, "options": { @@ -4829,7 +5075,7 @@ "h": 8, "w": 12, "x": 12, - "y": 177 + "y": 185 }, "id": 84, "options": { @@ -4935,7 +5181,7 @@ "h": 8, "w": 12, "x": 0, - "y": 185 + "y": 193 }, "id": 85, "options": { @@ -4972,7 +5218,7 @@ "h": 1, "w": 24, "x": 0, - "y": 193 + "y": 201 }, "id": 68, "panels": [], @@ -5044,7 +5290,7 @@ "h": 8, "w": 12, "x": 0, - "y": 194 + "y": 202 }, "id": 60, "options": { @@ -5138,7 +5384,7 @@ "h": 8, "w": 12, "x": 12, - "y": 194 + "y": 202 }, "id": 62, "options": { @@ -5232,7 +5478,7 @@ "h": 8, "w": 12, "x": 0, - "y": 202 + "y": 210 }, "id": 64, "options": { @@ -5269,7 +5515,7 @@ "h": 1, "w": 24, "x": 0, - "y": 210 + "y": 218 }, "id": 97, "panels": [], @@ -5339,7 +5585,7 @@ "h": 8, "w": 12, "x": 0, - "y": 211 + "y": 219 }, "id": 98, "options": { @@ -5441,7 +5687,7 @@ "type": "prometheus", "uid": "${DS_PROMETHEUS}" }, -"description": "", + "description": "", "fieldConfig": { "defaults": { "color": { @@ -5500,7 +5746,7 @@ "h": 8, "w": 12, "x": 12, - "y": 211 + "y": 219 }, "id": 101, "options": { @@ -5527,7 +5773,7 @@ "legendFormat": "Resident", "range": true, "refId": "A" - } + } ], "title": "Memory", "type": "timeseries" @@ -5596,7 +5842,7 @@ "h": 8, "w": 12, "x": 0, - "y": 219 + "y": 227 }, "id": 99, "options": { @@ -5692,9 +5938,9 @@ "h": 8, "w": 12, "x": 12, - "y": 219 + "y": 227 }, - "id": 100, + "id": 100, "options": { "legend": { "calcs": [], @@ -5730,7 +5976,7 @@ "h": 1, "w": 24, "x": 0, - "y": 227 + "y": 235 }, "id": 105, "panels": [], @@ -5801,7 +6047,7 @@ "h": 8, "w": 12, "x": 0, - "y": 228 + "y": 236 }, "id": 106, "options": { @@ -5897,7 +6143,7 @@ "h": 8, "w": 12, "x": 12, - "y": 228 + "y": 236 }, "id": 107, "options": { @@ -5935,7 +6181,7 @@ "h": 1, "w": 24, "x": 0, - "y": 236 + "y": 244 }, "id": 108, "panels": [], @@ -6031,7 +6277,7 @@ "h": 8, "w": 12, "x": 0, - "y": 237 + "y": 245 }, "id": 109, "options": { @@ -6093,7 +6339,7 @@ "h": 8, "w": 12, "x": 12, - "y": 237 + "y": 245 }, "id": 111, "maxDataPoints": 25, @@ -6137,7 +6383,7 @@ "unit": "percentunit" } }, - "pluginVersion": "10.1.0", + "pluginVersion": "10.1.1", "targets": [ { "datasource": { @@ -6220,7 +6466,7 @@ "h": 8, "w": 12, "x": 0, - "y": 245 + "y": 253 }, "id": 120, "options": { @@ -6278,7 +6524,7 @@ "h": 8, "w": 12, "x": 12, - "y": 245 + "y": 253 }, "id": 112, "maxDataPoints": 25, @@ -6322,7 +6568,7 @@ "unit": "percentunit" } }, - "pluginVersion": "10.1.0", + "pluginVersion": "10.1.1", "targets": [ { "datasource": { @@ -6341,4682 +6587,77 @@ ], "title": "Call Latency time", "type": "heatmap" - }, - { - "collapsed": false, - "gridPos": { - "h": 1, - "w": 24, - "x": 0, - "y": 253 - }, - "id": 123, - "panels": [], - "repeat": "instance", - "repeatDirection": "h", - "title": "Database - Canonicalization Metrics", - "type": "row" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "description": "", - "fieldConfig": { - "defaults": { - "color": { - "mode": "palette-classic" - }, - "custom": { - "axisCenteredZero": false, - "axisColorMode": "text", - "axisLabel": "", - "axisPlacement": "auto", - "barAlignment": 0, - "drawStyle": "line", - "fillOpacity": 0, - "gradientMode": "none", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "insertNulls": false, - "lineInterpolation": "linear", - "lineWidth": 1, - "pointSize": 5, - "scaleDistribution": { - "type": "linear" - }, - "showPoints": "auto", - "spanNulls": false, - "stacking": { - "group": "A", - "mode": "none" - }, - "thresholdsStyle": { - "mode": "off" - } - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, - { - "color": "red", - "value": 80 - } - ] - }, - "unit": "ms" + } + ], + "refresh": "30s", + "revision": 1, + "schemaVersion": 38, + "style": "dark", + "tags": [], + "templating": { + "list": [ + { + "current": {}, + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" }, - "overrides": [] - }, - "gridPos": { - "h": 8, - "w": 12, - "x": 0, - "y": 254 - }, - "id": 167, - "options": { - "legend": { - "calcs": [], - "displayMode": "list", - "placement": "bottom", - "showLegend": true + "definition": "query_result(up)", + "hide": 0, + "includeAll": false, + "multi": false, + "name": "instance", + "options": [], + "query": { + "query": "query_result(up)", + "refId": "PrometheusVariableQueryEditor-VariableQuery" }, - "tooltip": { - "mode": "single", - "sort": "none" - } + "refresh": 1, + "regex": "/.*instance=\\\"([^\\\"]*).*/", + "skipUrlSync": false, + "sort": 0, + "type": "query" }, - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "editorMode": "code", - "expr": "reth_storage_providers_database_duration{action=\"insert storage hashing\", quantile=\"0.5\"}", - "instant": false, - "legendFormat": "{{quantile}} percentile", - "range": true, - "refId": "A" + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" }, - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "editorMode": "code", - "expr": "reth_storage_providers_database_duration{action=\"insert storage hashing\", quantile=\"0.9\"} ", - "hide": false, - "instant": false, - "legendFormat": "{{quantile}} percentile", - "range": true, - "refId": "B" + "definition": "label_values(reth_blockchain_tree_make_canonical_duration{instance=\"$instance\"},action)", + "hide": 0, + "includeAll": false, + "label": "Blockchain Tree Canonicalization Action", + "multi": false, + "name": "blockchain_tree_action", + "options": [], + "query": { + "query": "label_values(reth_blockchain_tree_make_canonical_duration{instance=\"$instance\"},action)", + "refId": "PrometheusVariableQueryEditor-VariableQuery" }, - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "editorMode": "code", - "expr": "reth_storage_providers_database_duration{action=\"insert storage hashing\", quantile=\"0.95\"} ", - "hide": false, - "instant": false, - "legendFormat": "{{quantile}} percentile", - "range": true, - "refId": "C" + "refresh": 1, + "regex": "", + "skipUrlSync": false, + "sort": 0, + "type": "query" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" }, - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "editorMode": "code", - "expr": "reth_storage_providers_database_duration{action=\"insert storage hashing\", quantile=\"0.99\"} ", - "hide": false, - "instant": false, - "legendFormat": "{{quantile}} percentile", - "range": true, - "refId": "D" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "editorMode": "code", - "exemplar": false, - "expr": "reth_storage_providers_database_duration{action=\"insert storage hashing\", quantile=\"0.999\"} ", - "hide": false, - "instant": false, - "legendFormat": "{{quantile}} percentile", - "range": true, - "refId": "E" - } - ], - "title": "Insert Storage Hashing", - "type": "timeseries" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "description": "", - "fieldConfig": { - "defaults": { - "color": { - "mode": "palette-classic" - }, - "custom": { - "axisCenteredZero": false, - "axisColorMode": "text", - "axisLabel": "", - "axisPlacement": "auto", - "barAlignment": 0, - "drawStyle": "line", - "fillOpacity": 0, - "gradientMode": "none", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "insertNulls": false, - "lineInterpolation": "linear", - "lineWidth": 1, - "pointSize": 5, - "scaleDistribution": { - "type": "linear" - }, - "showPoints": "auto", - "spanNulls": false, - "stacking": { - "group": "A", - "mode": "none" - }, - "thresholdsStyle": { - "mode": "off" - } - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, - { - "color": "red", - "value": 80 - } - ] - }, - "unit": "ms" - }, - "overrides": [] - }, - "gridPos": { - "h": 8, - "w": 12, - "x": 12, - "y": 254 - }, - "id": 168, - "options": { - "legend": { - "calcs": [], - "displayMode": "list", - "placement": "bottom", - "showLegend": true - }, - "tooltip": { - "mode": "single", - "sort": "none" - } - }, - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "editorMode": "code", - "expr": "reth_storage_providers_database_duration{action=\"insert account hashing\", quantile=\"0.5\"}", - "instant": false, - "legendFormat": "{{quantile}} percentile", - "range": true, - "refId": "A" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "editorMode": "code", - "expr": "reth_storage_providers_database_duration{action=\"insert account hashing\", quantile=\"0.9\"} ", - "hide": false, - "instant": false, - "legendFormat": "{{quantile}} percentile", - "range": true, - "refId": "B" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "editorMode": "code", - "expr": "reth_storage_providers_database_duration{action=\"insert account hashing\", quantile=\"0.95\"} ", - "hide": false, - "instant": false, - "legendFormat": "{{quantile}} percentile", - "range": true, - "refId": "C" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "editorMode": "code", - "expr": "reth_storage_providers_database_duration{action=\"insert account hashing\", quantile=\"0.99\"} ", - "hide": false, - "instant": false, - "legendFormat": "{{quantile}} percentile", - "range": true, - "refId": "D" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "editorMode": "code", - "exemplar": false, - "expr": "reth_storage_providers_database_duration{action=\"insert account hashing\", quantile=\"0.999\"} ", - "hide": false, - "instant": false, - "legendFormat": "{{quantile}} percentile", - "range": true, - "refId": "E" - } - ], - "title": "Insert Account Hashing", - "type": "timeseries" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "description": "", - "fieldConfig": { - "defaults": { - "color": { - "mode": "palette-classic" - }, - "custom": { - "axisCenteredZero": false, - "axisColorMode": "text", - "axisLabel": "", - "axisPlacement": "auto", - "barAlignment": 0, - "drawStyle": "line", - "fillOpacity": 0, - "gradientMode": "none", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "insertNulls": false, - "lineInterpolation": "linear", - "lineWidth": 1, - "pointSize": 5, - "scaleDistribution": { - "type": "linear" - }, - "showPoints": "auto", - "spanNulls": false, - "stacking": { - "group": "A", - "mode": "none" - }, - "thresholdsStyle": { - "mode": "off" - } - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, - { - "color": "red", - "value": 80 - } - ] - }, - "unit": "ms" - }, - "overrides": [] - }, - "gridPos": { - "h": 8, - "w": 12, - "x": 0, - "y": 262 - }, - "id": 169, - "options": { - "legend": { - "calcs": [], - "displayMode": "list", - "placement": "bottom", - "showLegend": true - }, - "tooltip": { - "mode": "single", - "sort": "none" - } - }, - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "editorMode": "code", - "expr": "reth_storage_providers_database_duration{action=\"insert merkle tree\", quantile=\"0.5\"}", - "instant": false, - "legendFormat": "{{quantile}} percentile", - "range": true, - "refId": "A" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "editorMode": "code", - "expr": "reth_storage_providers_database_duration{action=\"insert merkle tree\", quantile=\"0.9\"} ", - "hide": false, - "instant": false, - "legendFormat": "{{quantile}} percentile", - "range": true, - "refId": "B" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "editorMode": "code", - "expr": "reth_storage_providers_database_duration{action=\"insert merkle tree\", quantile=\"0.95\"} ", - "hide": false, - "instant": false, - "legendFormat": "{{quantile}} percentile", - "range": true, - "refId": "C" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "editorMode": "code", - "expr": "reth_storage_providers_database_duration{action=\"insert merkle tree\", quantile=\"0.99\"} ", - "hide": false, - "instant": false, - "legendFormat": "{{quantile}} percentile", - "range": true, - "refId": "D" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "editorMode": "code", - "exemplar": false, - "expr": "reth_storage_providers_database_duration{action=\"insert merkle tree\", quantile=\"0.999\"} ", - "hide": false, - "instant": false, - "legendFormat": "{{quantile}} percentile", - "range": true, - "refId": "E" - } - ], - "title": "Insert Merkle Tree", - "type": "timeseries" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "description": "", - "fieldConfig": { - "defaults": { - "color": { - "mode": "palette-classic" - }, - "custom": { - "axisCenteredZero": false, - "axisColorMode": "text", - "axisLabel": "", - "axisPlacement": "auto", - "barAlignment": 0, - "drawStyle": "line", - "fillOpacity": 0, - "gradientMode": "none", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "insertNulls": false, - "lineInterpolation": "linear", - "lineWidth": 1, - "pointSize": 5, - "scaleDistribution": { - "type": "linear" - }, - "showPoints": "auto", - "spanNulls": false, - "stacking": { - "group": "A", - "mode": "none" - }, - "thresholdsStyle": { - "mode": "off" - } - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, - { - "color": "red", - "value": 80 - } - ] - }, - "unit": "ms" - }, - "overrides": [] - }, - "gridPos": { - "h": 8, - "w": 12, - "x": 12, - "y": 262 - }, - "id": 170, - "options": { - "legend": { - "calcs": [], - "displayMode": "list", - "placement": "bottom", - "showLegend": true - }, - "tooltip": { - "mode": "single", - "sort": "none" - } - }, - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "editorMode": "code", - "expr": "reth_storage_providers_database_duration{action=\"insert block\", quantile=\"0.5\"}", - "instant": false, - "legendFormat": "{{quantile}} percentile", - "range": true, - "refId": "A" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "editorMode": "code", - "expr": "reth_storage_providers_database_duration{action=\"insert block\", quantile=\"0.9\"} ", - "hide": false, - "instant": false, - "legendFormat": "{{quantile}} percentile", - "range": true, - "refId": "B" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "editorMode": "code", - "expr": "reth_storage_providers_database_duration{action=\"insert block\", quantile=\"0.95\"} ", - "hide": false, - "instant": false, - "legendFormat": "{{quantile}} percentile", - "range": true, - "refId": "C" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "editorMode": "code", - "expr": "reth_storage_providers_database_duration{action=\"insert block\", quantile=\"0.99\"} ", - "hide": false, - "instant": false, - "legendFormat": "{{quantile}} percentile", - "range": true, - "refId": "D" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "editorMode": "code", - "exemplar": false, - "expr": "reth_storage_providers_database_duration{action=\"insert block\", quantile=\"0.999\"} ", - "hide": false, - "instant": false, - "legendFormat": "{{quantile}} percentile", - "range": true, - "refId": "E" - } - ], - "title": "Insert Block", - "type": "timeseries" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "description": "", - "fieldConfig": { - "defaults": { - "color": { - "mode": "palette-classic" - }, - "custom": { - "axisCenteredZero": false, - "axisColorMode": "text", - "axisLabel": "", - "axisPlacement": "auto", - "barAlignment": 0, - "drawStyle": "line", - "fillOpacity": 0, - "gradientMode": "none", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "insertNulls": false, - "lineInterpolation": "linear", - "lineWidth": 1, - "pointSize": 5, - "scaleDistribution": { - "type": "linear" - }, - "showPoints": "auto", - "spanNulls": false, - "stacking": { - "group": "A", - "mode": "none" - }, - "thresholdsStyle": { - "mode": "off" - } - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, - { - "color": "red", - "value": 80 - } - ] - }, - "unit": "ms" - }, - "overrides": [] - }, - "gridPos": { - "h": 8, - "w": 12, - "x": 0, - "y": 270 - }, - "id": 171, - "options": { - "legend": { - "calcs": [], - "displayMode": "list", - "placement": "bottom", - "showLegend": true - }, - "tooltip": { - "mode": "single", - "sort": "none" - } - }, - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "editorMode": "code", - "expr": "reth_storage_providers_database_duration{action=\"insert state\", quantile=\"0.5\"}", - "instant": false, - "legendFormat": "{{quantile}} percentile", - "range": true, - "refId": "A" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "editorMode": "code", - "expr": "reth_storage_providers_database_duration{action=\"insert state\", quantile=\"0.9\"} ", - "hide": false, - "instant": false, - "legendFormat": "{{quantile}} percentile", - "range": true, - "refId": "B" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "editorMode": "code", - "expr": "reth_storage_providers_database_duration{action=\"insert state\", quantile=\"0.95\"} ", - "hide": false, - "instant": false, - "legendFormat": "{{quantile}} percentile", - "range": true, - "refId": "C" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "editorMode": "code", - "expr": "reth_storage_providers_database_duration{action=\"insert state\", quantile=\"0.99\"} ", - "hide": false, - "instant": false, - "legendFormat": "{{quantile}} percentile", - "range": true, - "refId": "D" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "editorMode": "code", - "exemplar": false, - "expr": "reth_storage_providers_database_duration{action=\"insert state\", quantile=\"0.999\"} ", - "hide": false, - "instant": false, - "legendFormat": "{{quantile}} percentile", - "range": true, - "refId": "E" - } - ], - "title": "Insert State", - "type": "timeseries" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "description": "", - "fieldConfig": { - "defaults": { - "color": { - "mode": "palette-classic" - }, - "custom": { - "axisCenteredZero": false, - "axisColorMode": "text", - "axisLabel": "", - "axisPlacement": "auto", - "barAlignment": 0, - "drawStyle": "line", - "fillOpacity": 0, - "gradientMode": "none", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "insertNulls": false, - "lineInterpolation": "linear", - "lineWidth": 1, - "pointSize": 5, - "scaleDistribution": { - "type": "linear" - }, - "showPoints": "auto", - "spanNulls": false, - "stacking": { - "group": "A", - "mode": "none" - }, - "thresholdsStyle": { - "mode": "off" - } - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, - { - "color": "red", - "value": 80 - } - ] - }, - "unit": "ms" - }, - "overrides": [] - }, - "gridPos": { - "h": 8, - "w": 12, - "x": 12, - "y": 270 - }, - "id": 172, - "options": { - "legend": { - "calcs": [], - "displayMode": "list", - "placement": "bottom", - "showLegend": true - }, - "tooltip": { - "mode": "single", - "sort": "none" - } - }, - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "editorMode": "code", - "expr": "reth_storage_providers_database_duration{action=\"insert hashes\", quantile=\"0.5\"}", - "instant": false, - "legendFormat": "{{quantile}} percentile", - "range": true, - "refId": "A" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "editorMode": "code", - "expr": "reth_storage_providers_database_duration{action=\"insert hashes\", quantile=\"0.9\"} ", - "hide": false, - "instant": false, - "legendFormat": "{{quantile}} percentile", - "range": true, - "refId": "B" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "editorMode": "code", - "expr": "reth_storage_providers_database_duration{action=\"insert hashes\", quantile=\"0.95\"} ", - "hide": false, - "instant": false, - "legendFormat": "{{quantile}} percentile", - "range": true, - "refId": "C" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "editorMode": "code", - "expr": "reth_storage_providers_database_duration{action=\"insert hashes\", quantile=\"0.99\"} ", - "hide": false, - "instant": false, - "legendFormat": "{{quantile}} percentile", - "range": true, - "refId": "D" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "editorMode": "code", - "exemplar": false, - "expr": "reth_storage_providers_database_duration{action=\"insert hashes\", quantile=\"0.999\"} ", - "hide": false, - "instant": false, - "legendFormat": "{{quantile}} percentile", - "range": true, - "refId": "E" - } - ], - "title": "Insert Hashes", - "type": "timeseries" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "description": "", - "fieldConfig": { - "defaults": { - "color": { - "mode": "palette-classic" - }, - "custom": { - "axisCenteredZero": false, - "axisColorMode": "text", - "axisLabel": "", - "axisPlacement": "auto", - "barAlignment": 0, - "drawStyle": "line", - "fillOpacity": 0, - "gradientMode": "none", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "insertNulls": false, - "lineInterpolation": "linear", - "lineWidth": 1, - "pointSize": 5, - "scaleDistribution": { - "type": "linear" - }, - "showPoints": "auto", - "spanNulls": false, - "stacking": { - "group": "A", - "mode": "none" - }, - "thresholdsStyle": { - "mode": "off" - } - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, - { - "color": "red", - "value": 80 - } - ] - }, - "unit": "ms" - }, - "overrides": [] - }, - "gridPos": { - "h": 8, - "w": 12, - "x": 0, - "y": 278 - }, - "id": 173, - "options": { - "legend": { - "calcs": [], - "displayMode": "list", - "placement": "bottom", - "showLegend": true - }, - "tooltip": { - "mode": "single", - "sort": "none" - } - }, - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "editorMode": "code", - "expr": "reth_storage_providers_database_duration{action=\"insert history indices\", quantile=\"0.5\"}", - "instant": false, - "legendFormat": "{{quantile}} percentile", - "range": true, - "refId": "A" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "editorMode": "code", - "expr": "reth_storage_providers_database_duration{action=\"insert history indices\", quantile=\"0.9\"} ", - "hide": false, - "instant": false, - "legendFormat": "{{quantile}} percentile", - "range": true, - "refId": "B" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "editorMode": "code", - "expr": "reth_storage_providers_database_duration{action=\"insert history indices\", quantile=\"0.95\"} ", - "hide": false, - "instant": false, - "legendFormat": "{{quantile}} percentile", - "range": true, - "refId": "C" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "editorMode": "code", - "expr": "reth_storage_providers_database_duration{action=\"insert history indices\", quantile=\"0.99\"} ", - "hide": false, - "instant": false, - "legendFormat": "{{quantile}} percentile", - "range": true, - "refId": "D" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "editorMode": "code", - "exemplar": false, - "expr": "reth_storage_providers_database_duration{action=\"insert history indices\", quantile=\"0.999\"} ", - "hide": false, - "instant": false, - "legendFormat": "{{quantile}} percentile", - "range": true, - "refId": "E" - } - ], - "title": "Insert History Indices", - "type": "timeseries" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "description": "", - "fieldConfig": { - "defaults": { - "color": { - "mode": "palette-classic" - }, - "custom": { - "axisCenteredZero": false, - "axisColorMode": "text", - "axisLabel": "", - "axisPlacement": "auto", - "barAlignment": 0, - "drawStyle": "line", - "fillOpacity": 0, - "gradientMode": "none", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "insertNulls": false, - "lineInterpolation": "linear", - "lineWidth": 1, - "pointSize": 5, - "scaleDistribution": { - "type": "linear" - }, - "showPoints": "auto", - "spanNulls": false, - "stacking": { - "group": "A", - "mode": "none" - }, - "thresholdsStyle": { - "mode": "off" - } - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, - { - "color": "red", - "value": 80 - } - ] - }, - "unit": "ms" - }, - "overrides": [] - }, - "gridPos": { - "h": 8, - "w": 12, - "x": 12, - "y": 278 - }, - "id": 174, - "options": { - "legend": { - "calcs": [], - "displayMode": "list", - "placement": "bottom", - "showLegend": true - }, - "tooltip": { - "mode": "single", - "sort": "none" - } - }, - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "editorMode": "code", - "expr": "reth_storage_providers_database_duration{action=\"update pipeline stages\", quantile=\"0.5\"}", - "instant": false, - "legendFormat": "{{quantile}} percentile", - "range": true, - "refId": "A" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "editorMode": "code", - "expr": "reth_storage_providers_database_duration{action=\"update pipeline stages\", quantile=\"0.9\"} ", - "hide": false, - "instant": false, - "legendFormat": "{{quantile}} percentile", - "range": true, - "refId": "B" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "editorMode": "code", - "expr": "reth_storage_providers_database_duration{action=\"update pipeline stages\", quantile=\"0.95\"} ", - "hide": false, - "instant": false, - "legendFormat": "{{quantile}} percentile", - "range": true, - "refId": "C" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "editorMode": "code", - "expr": "reth_storage_providers_database_duration{action=\"update pipeline stages\", quantile=\"0.99\"} ", - "hide": false, - "instant": false, - "legendFormat": "{{quantile}} percentile", - "range": true, - "refId": "D" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "editorMode": "code", - "exemplar": false, - "expr": "reth_storage_providers_database_duration{action=\"update pipeline stages\", quantile=\"0.999\"} ", - "hide": false, - "instant": false, - "legendFormat": "{{quantile}} percentile", - "range": true, - "refId": "E" - } - ], - "title": "Update Pipeline Stages", - "type": "timeseries" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "description": "", - "fieldConfig": { - "defaults": { - "color": { - "mode": "palette-classic" - }, - "custom": { - "axisCenteredZero": false, - "axisColorMode": "text", - "axisLabel": "", - "axisPlacement": "auto", - "barAlignment": 0, - "drawStyle": "line", - "fillOpacity": 0, - "gradientMode": "none", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "insertNulls": false, - "lineInterpolation": "linear", - "lineWidth": 1, - "pointSize": 5, - "scaleDistribution": { - "type": "linear" - }, - "showPoints": "auto", - "spanNulls": false, - "stacking": { - "group": "A", - "mode": "none" - }, - "thresholdsStyle": { - "mode": "off" - } - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, - { - "color": "red", - "value": 80 - } - ] - }, - "unit": "ms" - }, - "overrides": [] - }, - "gridPos": { - "h": 8, - "w": 12, - "x": 0, - "y": 286 - }, - "id": 175, - "options": { - "legend": { - "calcs": [], - "displayMode": "list", - "placement": "bottom", - "showLegend": true - }, - "tooltip": { - "mode": "single", - "sort": "none" - } - }, - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "editorMode": "code", - "expr": "reth_storage_providers_database_duration{action=\"insert canonical headers\", quantile=\"0.5\"}", - "instant": false, - "legendFormat": "{{quantile}} percentile", - "range": true, - "refId": "A" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "editorMode": "code", - "expr": "reth_storage_providers_database_duration{action=\"insert canonical headers\", quantile=\"0.9\"} ", - "hide": false, - "instant": false, - "legendFormat": "{{quantile}} percentile", - "range": true, - "refId": "B" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "editorMode": "code", - "expr": "reth_storage_providers_database_duration{action=\"insert canonical headers\", quantile=\"0.95\"} ", - "hide": false, - "instant": false, - "legendFormat": "{{quantile}} percentile", - "range": true, - "refId": "C" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "editorMode": "code", - "expr": "reth_storage_providers_database_duration{action=\"insert canonical headers\", quantile=\"0.99\"} ", - "hide": false, - "instant": false, - "legendFormat": "{{quantile}} percentile", - "range": true, - "refId": "D" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "editorMode": "code", - "exemplar": false, - "expr": "reth_storage_providers_database_duration{action=\"insert canonical headers\", quantile=\"0.999\"} ", - "hide": false, - "instant": false, - "legendFormat": "{{quantile}} percentile", - "range": true, - "refId": "E" - } - ], - "title": "Insert Canonical Headers", - "type": "timeseries" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "description": "", - "fieldConfig": { - "defaults": { - "color": { - "mode": "palette-classic" - }, - "custom": { - "axisCenteredZero": false, - "axisColorMode": "text", - "axisLabel": "", - "axisPlacement": "auto", - "barAlignment": 0, - "drawStyle": "line", - "fillOpacity": 0, - "gradientMode": "none", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "insertNulls": false, - "lineInterpolation": "linear", - "lineWidth": 1, - "pointSize": 5, - "scaleDistribution": { - "type": "linear" - }, - "showPoints": "auto", - "spanNulls": false, - "stacking": { - "group": "A", - "mode": "none" - }, - "thresholdsStyle": { - "mode": "off" - } - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, - { - "color": "red", - "value": 80 - } - ] - }, - "unit": "ms" - }, - "overrides": [] - }, - "gridPos": { - "h": 8, - "w": 12, - "x": 12, - "y": 286 - }, - "id": 176, - "options": { - "legend": { - "calcs": [], - "displayMode": "list", - "placement": "bottom", - "showLegend": true - }, - "tooltip": { - "mode": "single", - "sort": "none" - } - }, - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "editorMode": "code", - "expr": "reth_storage_providers_database_duration{action=\"insert headers\", quantile=\"0.5\"}", - "instant": false, - "legendFormat": "{{quantile}} percentile", - "range": true, - "refId": "A" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "editorMode": "code", - "expr": "reth_storage_providers_database_duration{action=\"insert headers\", quantile=\"0.9\"} ", - "hide": false, - "instant": false, - "legendFormat": "{{quantile}} percentile", - "range": true, - "refId": "B" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "editorMode": "code", - "expr": "reth_storage_providers_database_duration{action=\"insert headers\", quantile=\"0.95\"} ", - "hide": false, - "instant": false, - "legendFormat": "{{quantile}} percentile", - "range": true, - "refId": "C" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "editorMode": "code", - "expr": "reth_storage_providers_database_duration{action=\"insert headers\", quantile=\"0.99\"} ", - "hide": false, - "instant": false, - "legendFormat": "{{quantile}} percentile", - "range": true, - "refId": "D" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "editorMode": "code", - "exemplar": false, - "expr": "reth_storage_providers_database_duration{action=\"insert headers\", quantile=\"0.999\"} ", - "hide": false, - "instant": false, - "legendFormat": "{{quantile}} percentile", - "range": true, - "refId": "E" - } - ], - "title": "Insert Headers", - "type": "timeseries" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "description": "", - "fieldConfig": { - "defaults": { - "color": { - "mode": "palette-classic" - }, - "custom": { - "axisCenteredZero": false, - "axisColorMode": "text", - "axisLabel": "", - "axisPlacement": "auto", - "barAlignment": 0, - "drawStyle": "line", - "fillOpacity": 0, - "gradientMode": "none", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "insertNulls": false, - "lineInterpolation": "linear", - "lineWidth": 1, - "pointSize": 5, - "scaleDistribution": { - "type": "linear" - }, - "showPoints": "auto", - "spanNulls": false, - "stacking": { - "group": "A", - "mode": "none" - }, - "thresholdsStyle": { - "mode": "off" - } - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, - { - "color": "red", - "value": 80 - } - ] - }, - "unit": "ms" - }, - "overrides": [] - }, - "gridPos": { - "h": 8, - "w": 12, - "x": 0, - "y": 294 - }, - "id": 177, - "options": { - "legend": { - "calcs": [], - "displayMode": "list", - "placement": "bottom", - "showLegend": true - }, - "tooltip": { - "mode": "single", - "sort": "none" - } - }, - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "editorMode": "code", - "expr": "reth_storage_providers_database_duration{action=\"insert header numbers\", quantile=\"0.5\"}", - "instant": false, - "legendFormat": "{{quantile}} percentile", - "range": true, - "refId": "A" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "editorMode": "code", - "expr": "reth_storage_providers_database_duration{action=\"insert header numbers\", quantile=\"0.9\"} ", - "hide": false, - "instant": false, - "legendFormat": "{{quantile}} percentile", - "range": true, - "refId": "B" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "editorMode": "code", - "expr": "reth_storage_providers_database_duration{action=\"insert header numbers\", quantile=\"0.95\"} ", - "hide": false, - "instant": false, - "legendFormat": "{{quantile}} percentile", - "range": true, - "refId": "C" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "editorMode": "code", - "expr": "reth_storage_providers_database_duration{action=\"insert header numbers\", quantile=\"0.99\"} ", - "hide": false, - "instant": false, - "legendFormat": "{{quantile}} percentile", - "range": true, - "refId": "D" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "editorMode": "code", - "exemplar": false, - "expr": "reth_storage_providers_database_duration{action=\"insert header numbers\", quantile=\"0.999\"} ", - "hide": false, - "instant": false, - "legendFormat": "{{quantile}} percentile", - "range": true, - "refId": "E" - } - ], - "title": "Insert Header Numbers", - "type": "timeseries" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "description": "", - "fieldConfig": { - "defaults": { - "color": { - "mode": "palette-classic" - }, - "custom": { - "axisCenteredZero": false, - "axisColorMode": "text", - "axisLabel": "", - "axisPlacement": "auto", - "barAlignment": 0, - "drawStyle": "line", - "fillOpacity": 0, - "gradientMode": "none", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "insertNulls": false, - "lineInterpolation": "linear", - "lineWidth": 1, - "pointSize": 5, - "scaleDistribution": { - "type": "linear" - }, - "showPoints": "auto", - "spanNulls": false, - "stacking": { - "group": "A", - "mode": "none" - }, - "thresholdsStyle": { - "mode": "off" - } - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, - { - "color": "red", - "value": 80 - } - ] - }, - "unit": "ms" - }, - "overrides": [] - }, - "gridPos": { - "h": 8, - "w": 12, - "x": 12, - "y": 294 - }, - "id": 178, - "options": { - "legend": { - "calcs": [], - "displayMode": "list", - "placement": "bottom", - "showLegend": true - }, - "tooltip": { - "mode": "single", - "sort": "none" - } - }, - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "editorMode": "code", - "expr": "reth_storage_providers_database_duration{action=\"insert header TD\", quantile=\"0.5\"}", - "instant": false, - "legendFormat": "{{quantile}} percentile", - "range": true, - "refId": "A" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "editorMode": "code", - "expr": "reth_storage_providers_database_duration{action=\"insert headers numbers\", quantile=\"0.9\"} ", - "hide": false, - "instant": false, - "legendFormat": "{{quantile}} percentile", - "range": true, - "refId": "B" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "editorMode": "code", - "expr": "reth_storage_providers_database_duration{action=\"insert header TD\", quantile=\"0.95\"} ", - "hide": false, - "instant": false, - "legendFormat": "{{quantile}} percentile", - "range": true, - "refId": "C" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "editorMode": "code", - "expr": "reth_storage_providers_database_duration{action=\"insert header TD\", quantile=\"0.99\"} ", - "hide": false, - "instant": false, - "legendFormat": "{{quantile}} percentile", - "range": true, - "refId": "D" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "editorMode": "code", - "exemplar": false, - "expr": "reth_storage_providers_database_duration{action=\"insert header TD\", quantile=\"0.999\"} ", - "hide": false, - "instant": false, - "legendFormat": "{{quantile}} percentile", - "range": true, - "refId": "E" - } - ], - "title": "Insert Header TD", - "type": "timeseries" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "description": "", - "fieldConfig": { - "defaults": { - "color": { - "mode": "palette-classic" - }, - "custom": { - "axisCenteredZero": false, - "axisColorMode": "text", - "axisLabel": "", - "axisPlacement": "auto", - "barAlignment": 0, - "drawStyle": "line", - "fillOpacity": 0, - "gradientMode": "none", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "insertNulls": false, - "lineInterpolation": "linear", - "lineWidth": 1, - "pointSize": 5, - "scaleDistribution": { - "type": "linear" - }, - "showPoints": "auto", - "spanNulls": false, - "stacking": { - "group": "A", - "mode": "none" - }, - "thresholdsStyle": { - "mode": "off" - } - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, - { - "color": "red", - "value": 80 - } - ] - }, - "unit": "ms" - }, - "overrides": [] - }, - "gridPos": { - "h": 8, - "w": 12, - "x": 0, - "y": 302 - }, - "id": 179, - "options": { - "legend": { - "calcs": [], - "displayMode": "list", - "placement": "bottom", - "showLegend": true - }, - "tooltip": { - "mode": "single", - "sort": "none" - } - }, - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "editorMode": "code", - "expr": "reth_storage_providers_database_duration{action=\"insert block ommers\", quantile=\"0.5\"}", - "instant": false, - "legendFormat": "{{quantile}} percentile", - "range": true, - "refId": "A" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "editorMode": "code", - "expr": "reth_storage_providers_database_duration{action=\"insert block ommers\", quantile=\"0.9\"} ", - "hide": false, - "instant": false, - "legendFormat": "{{quantile}} percentile", - "range": true, - "refId": "B" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "editorMode": "code", - "expr": "reth_storage_providers_database_duration{action=\"insert block ommers\", quantile=\"0.95\"} ", - "hide": false, - "instant": false, - "legendFormat": "{{quantile}} percentile", - "range": true, - "refId": "C" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "editorMode": "code", - "expr": "reth_storage_providers_database_duration{action=\"insert block ommers\", quantile=\"0.99\"} ", - "hide": false, - "instant": false, - "legendFormat": "{{quantile}} percentile", - "range": true, - "refId": "D" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "editorMode": "code", - "exemplar": false, - "expr": "reth_storage_providers_database_duration{action=\"insert block ommers\", quantile=\"0.999\"} ", - "hide": false, - "instant": false, - "legendFormat": "{{quantile}} percentile", - "range": true, - "refId": "E" - } - ], - "title": "Insert Block Ommers", - "type": "timeseries" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "description": "", - "fieldConfig": { - "defaults": { - "color": { - "mode": "palette-classic" - }, - "custom": { - "axisCenteredZero": false, - "axisColorMode": "text", - "axisLabel": "", - "axisPlacement": "auto", - "barAlignment": 0, - "drawStyle": "line", - "fillOpacity": 0, - "gradientMode": "none", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "insertNulls": false, - "lineInterpolation": "linear", - "lineWidth": 1, - "pointSize": 5, - "scaleDistribution": { - "type": "linear" - }, - "showPoints": "auto", - "spanNulls": false, - "stacking": { - "group": "A", - "mode": "none" - }, - "thresholdsStyle": { - "mode": "off" - } - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, - { - "color": "red", - "value": 80 - } - ] - }, - "unit": "ms" - }, - "overrides": [] - }, - "gridPos": { - "h": 8, - "w": 12, - "x": 12, - "y": 302 - }, - "id": 180, - "options": { - "legend": { - "calcs": [], - "displayMode": "list", - "placement": "bottom", - "showLegend": true - }, - "tooltip": { - "mode": "single", - "sort": "none" - } - }, - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "editorMode": "code", - "expr": "reth_storage_providers_database_duration{action=\"insert tx senders\", quantile=\"0.5\"}", - "instant": false, - "legendFormat": "{{quantile}} percentile", - "range": true, - "refId": "A" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "editorMode": "code", - "expr": "reth_storage_providers_database_duration{action=\"insert tx senders\", quantile=\"0.9\"} ", - "hide": false, - "instant": false, - "legendFormat": "{{quantile}} percentile", - "range": true, - "refId": "B" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "editorMode": "code", - "expr": "reth_storage_providers_database_duration{action=\"insert tx senders\", quantile=\"0.95\"} ", - "hide": false, - "instant": false, - "legendFormat": "{{quantile}} percentile", - "range": true, - "refId": "C" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "editorMode": "code", - "expr": "reth_storage_providers_database_duration{action=\"insert tx senders\", quantile=\"0.99\"} ", - "hide": false, - "instant": false, - "legendFormat": "{{quantile}} percentile", - "range": true, - "refId": "D" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "editorMode": "code", - "exemplar": false, - "expr": "reth_storage_providers_database_duration{action=\"insert tx senders\", quantile=\"0.999\"} ", - "hide": false, - "instant": false, - "legendFormat": "{{quantile}} percentile", - "range": true, - "refId": "E" - } - ], - "title": "Insert Tx Senders", - "type": "timeseries" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "description": "", - "fieldConfig": { - "defaults": { - "color": { - "mode": "palette-classic" - }, - "custom": { - "axisCenteredZero": false, - "axisColorMode": "text", - "axisLabel": "", - "axisPlacement": "auto", - "barAlignment": 0, - "drawStyle": "line", - "fillOpacity": 0, - "gradientMode": "none", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "insertNulls": false, - "lineInterpolation": "linear", - "lineWidth": 1, - "pointSize": 5, - "scaleDistribution": { - "type": "linear" - }, - "showPoints": "auto", - "spanNulls": false, - "stacking": { - "group": "A", - "mode": "none" - }, - "thresholdsStyle": { - "mode": "off" - } - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, - { - "color": "red", - "value": 80 - } - ] - }, - "unit": "ms" - }, - "overrides": [] - }, - "gridPos": { - "h": 8, - "w": 12, - "x": 0, - "y": 310 - }, - "id": 181, - "options": { - "legend": { - "calcs": [], - "displayMode": "list", - "placement": "bottom", - "showLegend": true - }, - "tooltip": { - "mode": "single", - "sort": "none" - } - }, - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "editorMode": "code", - "expr": "reth_storage_providers_database_duration{action=\"insert transactions\", quantile=\"0.5\"}", - "instant": false, - "legendFormat": "{{quantile}} percentile", - "range": true, - "refId": "A" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "editorMode": "code", - "expr": "reth_storage_providers_database_duration{action=\"insert transactions\", quantile=\"0.9\"} ", - "hide": false, - "instant": false, - "legendFormat": "{{quantile}} percentile", - "range": true, - "refId": "B" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "editorMode": "code", - "expr": "reth_storage_providers_database_duration{action=\"insert transactions\", quantile=\"0.95\"} ", - "hide": false, - "instant": false, - "legendFormat": "{{quantile}} percentile", - "range": true, - "refId": "C" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "editorMode": "code", - "expr": "reth_storage_providers_database_duration{action=\"insert transactions\", quantile=\"0.99\"} ", - "hide": false, - "instant": false, - "legendFormat": "{{quantile}} percentile", - "range": true, - "refId": "D" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "editorMode": "code", - "exemplar": false, - "expr": "reth_storage_providers_database_duration{action=\"insert transactions\", quantile=\"0.999\"} ", - "hide": false, - "instant": false, - "legendFormat": "{{quantile}} percentile", - "range": true, - "refId": "E" - } - ], - "title": "Insert Transactions", - "type": "timeseries" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "description": "", - "fieldConfig": { - "defaults": { - "color": { - "mode": "palette-classic" - }, - "custom": { - "axisCenteredZero": false, - "axisColorMode": "text", - "axisLabel": "", - "axisPlacement": "auto", - "barAlignment": 0, - "drawStyle": "line", - "fillOpacity": 0, - "gradientMode": "none", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "insertNulls": false, - "lineInterpolation": "linear", - "lineWidth": 1, - "pointSize": 5, - "scaleDistribution": { - "type": "linear" - }, - "showPoints": "auto", - "spanNulls": false, - "stacking": { - "group": "A", - "mode": "none" - }, - "thresholdsStyle": { - "mode": "off" - } - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, - { - "color": "red", - "value": 80 - } - ] - }, - "unit": "ms" - }, - "overrides": [] - }, - "gridPos": { - "h": 8, - "w": 12, - "x": 12, - "y": 310 - }, - "id": 182, - "options": { - "legend": { - "calcs": [], - "displayMode": "list", - "placement": "bottom", - "showLegend": true - }, - "tooltip": { - "mode": "single", - "sort": "none" - } - }, - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "editorMode": "code", - "expr": "reth_storage_providers_database_duration{action=\"insert tx hash numbers\", quantile=\"0.5\"}", - "instant": false, - "legendFormat": "{{quantile}} percentile", - "range": true, - "refId": "A" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "editorMode": "code", - "expr": "reth_storage_providers_database_duration{action=\"insert tx hash numbers\", quantile=\"0.9\"} ", - "hide": false, - "instant": false, - "legendFormat": "{{quantile}} percentile", - "range": true, - "refId": "B" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "editorMode": "code", - "expr": "reth_storage_providers_database_duration{action=\"insert tx hash numbers\", quantile=\"0.95\"} ", - "hide": false, - "instant": false, - "legendFormat": "{{quantile}} percentile", - "range": true, - "refId": "C" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "editorMode": "code", - "expr": "reth_storage_providers_database_duration{action=\"insert tx hash numbers\", quantile=\"0.99\"} ", - "hide": false, - "instant": false, - "legendFormat": "{{quantile}} percentile", - "range": true, - "refId": "D" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "editorMode": "code", - "exemplar": false, - "expr": "reth_storage_providers_database_duration{action=\"insert tx hash numbers\", quantile=\"0.999\"} ", - "hide": false, - "instant": false, - "legendFormat": "{{quantile}} percentile", - "range": true, - "refId": "E" - } - ], - "title": "Insert Tx Hash Numbers", - "type": "timeseries" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "description": "", - "fieldConfig": { - "defaults": { - "color": { - "mode": "palette-classic" - }, - "custom": { - "axisCenteredZero": false, - "axisColorMode": "text", - "axisLabel": "", - "axisPlacement": "auto", - "barAlignment": 0, - "drawStyle": "line", - "fillOpacity": 0, - "gradientMode": "none", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "insertNulls": false, - "lineInterpolation": "linear", - "lineWidth": 1, - "pointSize": 5, - "scaleDistribution": { - "type": "linear" - }, - "showPoints": "auto", - "spanNulls": false, - "stacking": { - "group": "A", - "mode": "none" - }, - "thresholdsStyle": { - "mode": "off" - } - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, - { - "color": "red", - "value": 80 - } - ] - }, - "unit": "ms" - }, - "overrides": [] - }, - "gridPos": { - "h": 8, - "w": 12, - "x": 0, - "y": 318 - }, - "id": 183, - "options": { - "legend": { - "calcs": [], - "displayMode": "list", - "placement": "bottom", - "showLegend": true - }, - "tooltip": { - "mode": "single", - "sort": "none" - } - }, - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "editorMode": "code", - "expr": "reth_storage_providers_database_duration{action=\"insert block withdrawals\", quantile=\"0.5\"}", - "instant": false, - "legendFormat": "{{quantile}} percentile", - "range": true, - "refId": "A" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "editorMode": "code", - "expr": "reth_storage_providers_database_duration{action=\"insert block withdrawals\", quantile=\"0.9\"} ", - "hide": false, - "instant": false, - "legendFormat": "{{quantile}} percentile", - "range": true, - "refId": "B" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "editorMode": "code", - "expr": "reth_storage_providers_database_duration{action=\"insert block withdrawals\", quantile=\"0.95\"} ", - "hide": false, - "instant": false, - "legendFormat": "{{quantile}} percentile", - "range": true, - "refId": "C" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "editorMode": "code", - "expr": "reth_storage_providers_database_duration{action=\"insert block withdrawals\", quantile=\"0.99\"} ", - "hide": false, - "instant": false, - "legendFormat": "{{quantile}} percentile", - "range": true, - "refId": "D" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "editorMode": "code", - "exemplar": false, - "expr": "reth_storage_providers_database_duration{action=\"insert block withdrawals\", quantile=\"0.999\"} ", - "hide": false, - "instant": false, - "legendFormat": "{{quantile}} percentile", - "range": true, - "refId": "E" - } - ], - "title": "Insert Block Withdrawals", - "type": "timeseries" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "description": "", - "fieldConfig": { - "defaults": { - "color": { - "mode": "palette-classic" - }, - "custom": { - "axisCenteredZero": false, - "axisColorMode": "text", - "axisLabel": "", - "axisPlacement": "auto", - "barAlignment": 0, - "drawStyle": "line", - "fillOpacity": 0, - "gradientMode": "none", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "insertNulls": false, - "lineInterpolation": "linear", - "lineWidth": 1, - "pointSize": 5, - "scaleDistribution": { - "type": "linear" - }, - "showPoints": "auto", - "spanNulls": false, - "stacking": { - "group": "A", - "mode": "none" - }, - "thresholdsStyle": { - "mode": "off" - } - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, - { - "color": "red", - "value": 80 - } - ] - }, - "unit": "ms" - }, - "overrides": [] - }, - "gridPos": { - "h": 8, - "w": 12, - "x": 12, - "y": 318 - }, - "id": 184, - "options": { - "legend": { - "calcs": [], - "displayMode": "list", - "placement": "bottom", - "showLegend": true - }, - "tooltip": { - "mode": "single", - "sort": "none" - } - }, - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "editorMode": "code", - "expr": "reth_storage_providers_database_duration{action=\"insert block body indices\", quantile=\"0.5\"}", - "instant": false, - "legendFormat": "{{quantile}} percentile", - "range": true, - "refId": "A" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "editorMode": "code", - "expr": "reth_storage_providers_database_duration{action=\"insert block body indices\", quantile=\"0.9\"} ", - "hide": false, - "instant": false, - "legendFormat": "{{quantile}} percentile", - "range": true, - "refId": "B" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "editorMode": "code", - "expr": "reth_storage_providers_database_duration{action=\"insert block body indices\", quantile=\"0.95\"} ", - "hide": false, - "instant": false, - "legendFormat": "{{quantile}} percentile", - "range": true, - "refId": "C" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "editorMode": "code", - "expr": "reth_storage_providers_database_duration{action=\"insert block body indices\", quantile=\"0.99\"} ", - "hide": false, - "instant": false, - "legendFormat": "{{quantile}} percentile", - "range": true, - "refId": "D" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "editorMode": "code", - "exemplar": false, - "expr": "reth_storage_providers_database_duration{action=\"insert block body indices\", quantile=\"0.999\"} ", - "hide": false, - "instant": false, - "legendFormat": "{{quantile}} percentile", - "range": true, - "refId": "E" - } - ], - "title": "Insert Block Body Indices", - "type": "timeseries" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "description": "", - "fieldConfig": { - "defaults": { - "color": { - "mode": "palette-classic" - }, - "custom": { - "axisCenteredZero": false, - "axisColorMode": "text", - "axisLabel": "", - "axisPlacement": "auto", - "barAlignment": 0, - "drawStyle": "line", - "fillOpacity": 0, - "gradientMode": "none", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "insertNulls": false, - "lineInterpolation": "linear", - "lineWidth": 1, - "pointSize": 5, - "scaleDistribution": { - "type": "linear" - }, - "showPoints": "auto", - "spanNulls": false, - "stacking": { - "group": "A", - "mode": "none" - }, - "thresholdsStyle": { - "mode": "off" - } - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, - { - "color": "red", - "value": 80 - } - ] - }, - "unit": "ms" - }, - "overrides": [] - }, - "gridPos": { - "h": 8, - "w": 12, - "x": 0, - "y": 326 - }, - "id": 185, - "options": { - "legend": { - "calcs": [], - "displayMode": "list", - "placement": "bottom", - "showLegend": true - }, - "tooltip": { - "mode": "single", - "sort": "none" - } - }, - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "editorMode": "code", - "expr": "reth_storage_providers_database_duration{action=\"insert transaction block\", quantile=\"0.5\"}", - "instant": false, - "legendFormat": "{{quantile}} percentile", - "range": true, - "refId": "A" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "editorMode": "code", - "expr": "reth_storage_providers_database_duration{action=\"insert transaction block\", quantile=\"0.9\"} ", - "hide": false, - "instant": false, - "legendFormat": "{{quantile}} percentile", - "range": true, - "refId": "B" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "editorMode": "code", - "expr": "reth_storage_providers_database_duration{action=\"insert transaction block\", quantile=\"0.95\"} ", - "hide": false, - "instant": false, - "legendFormat": "{{quantile}} percentile", - "range": true, - "refId": "C" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "editorMode": "code", - "expr": "reth_storage_providers_database_duration{action=\"insert transaction block\", quantile=\"0.99\"} ", - "hide": false, - "instant": false, - "legendFormat": "{{quantile}} percentile", - "range": true, - "refId": "D" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "editorMode": "code", - "exemplar": false, - "expr": "reth_storage_providers_database_duration{action=\"insert transaction block\", quantile=\"0.999\"} ", - "hide": false, - "instant": false, - "legendFormat": "{{quantile}} percentile", - "range": true, - "refId": "E" - } - ], - "title": "Insert Transaction Block", - "type": "timeseries" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "description": "", - "fieldConfig": { - "defaults": { - "color": { - "mode": "palette-classic" - }, - "custom": { - "axisCenteredZero": false, - "axisColorMode": "text", - "axisLabel": "", - "axisPlacement": "auto", - "barAlignment": 0, - "drawStyle": "line", - "fillOpacity": 0, - "gradientMode": "none", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "insertNulls": false, - "lineInterpolation": "linear", - "lineWidth": 1, - "pointSize": 5, - "scaleDistribution": { - "type": "linear" - }, - "showPoints": "auto", - "spanNulls": false, - "stacking": { - "group": "A", - "mode": "none" - }, - "thresholdsStyle": { - "mode": "off" - } - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, - { - "color": "red", - "value": 80 - } - ] - }, - "unit": "ms" - }, - "overrides": [] - }, - "gridPos": { - "h": 8, - "w": 12, - "x": 12, - "y": 326 - }, - "id": 186, - "options": { - "legend": { - "calcs": [], - "displayMode": "list", - "placement": "bottom", - "showLegend": true - }, - "tooltip": { - "mode": "single", - "sort": "none" - } - }, - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "editorMode": "code", - "expr": "reth_storage_providers_database_duration{action=\"recover signers\", quantile=\"0.5\"}", - "instant": false, - "legendFormat": "{{quantile}} percentile", - "range": true, - "refId": "A" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "editorMode": "code", - "expr": "reth_storage_providers_database_duration{action=\"recover signers\", quantile=\"0.9\"} ", - "hide": false, - "instant": false, - "legendFormat": "{{quantile}} percentile", - "range": true, - "refId": "B" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "editorMode": "code", - "expr": "reth_storage_providers_database_duration{action=\"recover signers\", quantile=\"0.95\"} ", - "hide": false, - "instant": false, - "legendFormat": "{{quantile}} percentile", - "range": true, - "refId": "C" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "editorMode": "code", - "expr": "reth_storage_providers_database_duration{action=\"recover signers\", quantile=\"0.99\"} ", - "hide": false, - "instant": false, - "legendFormat": "{{quantile}} percentile", - "range": true, - "refId": "D" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "editorMode": "code", - "exemplar": false, - "expr": "reth_storage_providers_database_duration{action=\"recover signers\", quantile=\"0.999\"} ", - "hide": false, - "instant": false, - "legendFormat": "{{quantile}} percentile", - "range": true, - "refId": "E" - } - ], - "title": "Recover Signers", - "type": "timeseries" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "description": "", - "fieldConfig": { - "defaults": { - "color": { - "mode": "palette-classic" - }, - "custom": { - "axisCenteredZero": false, - "axisColorMode": "text", - "axisLabel": "", - "axisPlacement": "auto", - "barAlignment": 0, - "drawStyle": "line", - "fillOpacity": 0, - "gradientMode": "none", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "insertNulls": false, - "lineInterpolation": "linear", - "lineWidth": 1, - "pointSize": 5, - "scaleDistribution": { - "type": "linear" - }, - "showPoints": "auto", - "spanNulls": false, - "stacking": { - "group": "A", - "mode": "none" - }, - "thresholdsStyle": { - "mode": "off" - } - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, - { - "color": "red", - "value": 80 - } - ] - }, - "unit": "ms" - }, - "overrides": [] - }, - "gridPos": { - "h": 8, - "w": 12, - "x": 0, - "y": 334 - }, - "id": 187, - "options": { - "legend": { - "calcs": [], - "displayMode": "list", - "placement": "bottom", - "showLegend": true - }, - "tooltip": { - "mode": "single", - "sort": "none" - } - }, - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "editorMode": "code", - "expr": "reth_storage_providers_database_duration{action=\"get next tx num\", quantile=\"0.5\"}", - "instant": false, - "legendFormat": "{{quantile}} percentile", - "range": true, - "refId": "A" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "editorMode": "code", - "expr": "reth_storage_providers_database_duration{action=\"get next tx num\", quantile=\"0.9\"} ", - "hide": false, - "instant": false, - "legendFormat": "{{quantile}} percentile", - "range": true, - "refId": "B" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "editorMode": "code", - "expr": "reth_storage_providers_database_duration{action=\"get next tx num\", quantile=\"0.95\"} ", - "hide": false, - "instant": false, - "legendFormat": "{{quantile}} percentile", - "range": true, - "refId": "C" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "editorMode": "code", - "expr": "reth_storage_providers_database_duration{action=\"get next tx num\", quantile=\"0.99\"} ", - "hide": false, - "instant": false, - "legendFormat": "{{quantile}} percentile", - "range": true, - "refId": "D" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "editorMode": "code", - "exemplar": false, - "expr": "reth_storage_providers_database_duration{action=\"get next tx num\", quantile=\"0.999\"} ", - "hide": false, - "instant": false, - "legendFormat": "{{quantile}} percentile", - "range": true, - "refId": "E" - } - ], - "title": "Get Next Tx Num", - "type": "timeseries" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "description": "", - "fieldConfig": { - "defaults": { - "color": { - "mode": "palette-classic" - }, - "custom": { - "axisCenteredZero": false, - "axisColorMode": "text", - "axisLabel": "", - "axisPlacement": "auto", - "barAlignment": 0, - "drawStyle": "line", - "fillOpacity": 0, - "gradientMode": "none", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "insertNulls": false, - "lineInterpolation": "linear", - "lineWidth": 1, - "pointSize": 5, - "scaleDistribution": { - "type": "linear" - }, - "showPoints": "auto", - "spanNulls": false, - "stacking": { - "group": "A", - "mode": "none" - }, - "thresholdsStyle": { - "mode": "off" - } - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, - { - "color": "red", - "value": 80 - } - ] - }, - "unit": "ms" - }, - "overrides": [] - }, - "gridPos": { - "h": 8, - "w": 12, - "x": 12, - "y": 334 - }, - "id": 188, - "options": { - "legend": { - "calcs": [], - "displayMode": "list", - "placement": "bottom", - "showLegend": true - }, - "tooltip": { - "mode": "single", - "sort": "none" - } - }, - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "editorMode": "code", - "expr": "reth_storage_providers_database_duration{action=\"get parent TD\", quantile=\"0.5\"}", - "instant": false, - "legendFormat": "{{quantile}} percentile", - "range": true, - "refId": "A" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "editorMode": "code", - "expr": "reth_storage_providers_database_duration{action=\"get parent TD\", quantile=\"0.9\"} ", - "hide": false, - "instant": false, - "legendFormat": "{{quantile}} percentile", - "range": true, - "refId": "B" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "editorMode": "code", - "expr": "reth_storage_providers_database_duration{action=\"get parent TD\", quantile=\"0.95\"} ", - "hide": false, - "instant": false, - "legendFormat": "{{quantile}} percentile", - "range": true, - "refId": "C" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "editorMode": "code", - "expr": "reth_storage_providers_database_duration{action=\"get parent TD\", quantile=\"0.99\"} ", - "hide": false, - "instant": false, - "legendFormat": "{{quantile}} percentile", - "range": true, - "refId": "D" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "editorMode": "code", - "exemplar": false, - "expr": "reth_storage_providers_database_duration{action=\"get parent TD\", quantile=\"0.999\"} ", - "hide": false, - "instant": false, - "legendFormat": "{{quantile}} percentile", - "range": true, - "refId": "E" - } - ], - "title": "Get Parent TD", - "type": "timeseries" - }, - { - "collapsed": false, - "gridPos": { - "h": 1, - "w": 24, - "x": 0, - "y": 342 - }, - "id": 133, - "panels": [], - "repeat": "instance", - "repeatDirection": "h", - "title": "Blockchain Tree - Canonicalization Metrics", - "type": "row" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "description": "", - "fieldConfig": { - "defaults": { - "color": { - "mode": "palette-classic" - }, - "custom": { - "axisCenteredZero": false, - "axisColorMode": "text", - "axisLabel": "", - "axisPlacement": "auto", - "barAlignment": 0, - "drawStyle": "line", - "fillOpacity": 0, - "gradientMode": "none", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "insertNulls": false, - "lineInterpolation": "linear", - "lineWidth": 1, - "pointSize": 5, - "scaleDistribution": { - "type": "linear" - }, - "showPoints": "auto", - "spanNulls": false, - "stacking": { - "group": "A", - "mode": "none" - }, - "thresholdsStyle": { - "mode": "off" - } - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, - { - "color": "red", - "value": 80 - } - ] - }, - "unit": "ms" - }, - "overrides": [] - }, - "gridPos": { - "h": 8, - "w": 12, - "x": 0, - "y": 343 - }, - "id": 158, - "options": { - "legend": { - "calcs": [], - "displayMode": "list", - "placement": "bottom", - "showLegend": true - }, - "tooltip": { - "mode": "single", - "sort": "none" - } - }, - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "editorMode": "code", - "expr": "reth_blockchain_tree_make_canonical_duration{action=\"clone old blocks\", quantile=\"0.5\"} ", - "instant": false, - "legendFormat": "{{quantile}} percentile", - "range": true, - "refId": "A" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "editorMode": "code", - "expr": "reth_blockchain_tree_make_canonical_duration{action=\"clone old blocks\", quantile=\"0.9\"} ", - "hide": false, - "instant": false, - "legendFormat": "{{quantile}} percentile", - "range": true, - "refId": "B" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "editorMode": "code", - "expr": "reth_blockchain_tree_make_canonical_duration{action=\"clone old blocks\", quantile=\"0.95\"} ", - "hide": false, - "instant": false, - "legendFormat": "{{quantile}} percentile", - "range": true, - "refId": "C" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "editorMode": "code", - "expr": "reth_blockchain_tree_make_canonical_duration{action=\"clone old blocks\", quantile=\"0.99\"} ", - "hide": false, - "instant": false, - "legendFormat": "{{quantile}} percentile", - "range": true, - "refId": "D" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "editorMode": "code", - "exemplar": false, - "expr": "reth_blockchain_tree_make_canonical_duration{action=\"clone old blocks\", quantile=\"0.999\"} ", - "hide": false, - "instant": false, - "legendFormat": "{{quantile}} percentile", - "range": true, - "refId": "E" - } - ], - "title": "Clone Old Blocks", - "type": "timeseries" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "description": "", - "fieldConfig": { - "defaults": { - "color": { - "mode": "palette-classic" - }, - "custom": { - "axisCenteredZero": false, - "axisColorMode": "text", - "axisLabel": "", - "axisPlacement": "auto", - "barAlignment": 0, - "drawStyle": "line", - "fillOpacity": 0, - "gradientMode": "none", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "insertNulls": false, - "lineInterpolation": "linear", - "lineWidth": 1, - "pointSize": 5, - "scaleDistribution": { - "type": "linear" - }, - "showPoints": "auto", - "spanNulls": false, - "stacking": { - "group": "A", - "mode": "none" - }, - "thresholdsStyle": { - "mode": "off" - } - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, - { - "color": "red", - "value": 80 - } - ] - }, - "unit": "ms" - }, - "overrides": [] - }, - "gridPos": { - "h": 8, - "w": 12, - "x": 12, - "y": 343 - }, - "id": 159, - "options": { - "legend": { - "calcs": [], - "displayMode": "list", - "placement": "bottom", - "showLegend": true - }, - "tooltip": { - "mode": "single", - "sort": "none" - } - }, - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "editorMode": "code", - "expr": "reth_blockchain_tree_make_canonical_duration{action=\"find canonical header\", quantile=\"0.5\"} ", - "instant": false, - "legendFormat": "{{quantile}} percentile", - "range": true, - "refId": "A" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "editorMode": "code", - "expr": "reth_blockchain_tree_make_canonical_duration{action=\"find canonical header\", quantile=\"0.9\"} ", - "hide": false, - "instant": false, - "legendFormat": "{{quantile}} percentile", - "range": true, - "refId": "B" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "editorMode": "code", - "expr": "reth_blockchain_tree_make_canonical_duration{action=\"find canonical header\", quantile=\"0.95\"} ", - "hide": false, - "instant": false, - "legendFormat": "{{quantile}} percentile", - "range": true, - "refId": "C" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "editorMode": "code", - "expr": "reth_blockchain_tree_make_canonical_duration{action=\"find canonical header\", quantile=\"0.99\"} ", - "hide": false, - "instant": false, - "legendFormat": "{{quantile}} percentile", - "range": true, - "refId": "D" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "editorMode": "code", - "exemplar": false, - "expr": "reth_blockchain_tree_make_canonical_duration{action=\"find canonical header\", quantile=\"0.999\"} ", - "hide": false, - "instant": false, - "legendFormat": "{{quantile}} percentile", - "range": true, - "refId": "E" - } - ], - "title": "Find Canonical Header", - "type": "timeseries" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "description": "", - "fieldConfig": { - "defaults": { - "color": { - "mode": "palette-classic" - }, - "custom": { - "axisCenteredZero": false, - "axisColorMode": "text", - "axisLabel": "", - "axisPlacement": "auto", - "barAlignment": 0, - "drawStyle": "line", - "fillOpacity": 0, - "gradientMode": "none", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "insertNulls": false, - "lineInterpolation": "linear", - "lineWidth": 1, - "pointSize": 5, - "scaleDistribution": { - "type": "linear" - }, - "showPoints": "auto", - "spanNulls": false, - "stacking": { - "group": "A", - "mode": "none" - }, - "thresholdsStyle": { - "mode": "off" - } - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, - { - "color": "red", - "value": 80 - } - ] - }, - "unit": "ms" - }, - "overrides": [] - }, - "gridPos": { - "h": 8, - "w": 12, - "x": 0, - "y": 351 - }, - "id": 160, - "options": { - "legend": { - "calcs": [], - "displayMode": "list", - "placement": "bottom", - "showLegend": true - }, - "tooltip": { - "mode": "single", - "sort": "none" - } - }, - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "editorMode": "code", - "expr": "reth_blockchain_tree_make_canonical_duration{action=\"split chain\", quantile=\"0.5\"} ", - "instant": false, - "legendFormat": "{{quantile}} percentile", - "range": true, - "refId": "A" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "editorMode": "code", - "expr": "reth_blockchain_tree_make_canonical_duration{action=\"split chain\", quantile=\"0.9\"} ", - "hide": false, - "instant": false, - "legendFormat": "{{quantile}} percentile", - "range": true, - "refId": "B" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "editorMode": "code", - "expr": "reth_blockchain_tree_make_canonical_duration{action=\"split chain\", quantile=\"0.95\"} ", - "hide": false, - "instant": false, - "legendFormat": "{{quantile}} percentile", - "range": true, - "refId": "C" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "editorMode": "code", - "expr": "reth_blockchain_tree_make_canonical_duration{action=\"split chain\", quantile=\"0.99\"} ", - "hide": false, - "instant": false, - "legendFormat": "{{quantile}} percentile", - "range": true, - "refId": "D" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "editorMode": "code", - "exemplar": false, - "expr": "reth_blockchain_tree_make_canonical_duration{action=\"split chain\", quantile=\"0.999\"} ", - "hide": false, - "instant": false, - "legendFormat": "{{quantile}} percentile", - "range": true, - "refId": "E" - } - ], - "title": "Split Chain", - "type": "timeseries" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "description": "", - "fieldConfig": { - "defaults": { - "color": { - "mode": "palette-classic" - }, - "custom": { - "axisCenteredZero": false, - "axisColorMode": "text", - "axisLabel": "", - "axisPlacement": "auto", - "barAlignment": 0, - "drawStyle": "line", - "fillOpacity": 0, - "gradientMode": "none", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "insertNulls": false, - "lineInterpolation": "linear", - "lineWidth": 1, - "pointSize": 5, - "scaleDistribution": { - "type": "linear" - }, - "showPoints": "auto", - "spanNulls": false, - "stacking": { - "group": "A", - "mode": "none" - }, - "thresholdsStyle": { - "mode": "off" - } - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, - { - "color": "red", - "value": 80 - } - ] - }, - "unit": "ms" - }, - "overrides": [] - }, - "gridPos": { - "h": 8, - "w": 12, - "x": 12, - "y": 351 - }, - "id": 162, - "options": { - "legend": { - "calcs": [], - "displayMode": "list", - "placement": "bottom", - "showLegend": true - }, - "tooltip": { - "mode": "single", - "sort": "none" - } - }, - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "editorMode": "code", - "expr": "reth_blockchain_tree_make_canonical_duration{action=\"split chain forks\", quantile=\"0.5\"} ", - "instant": false, - "legendFormat": "{{quantile}} percentile", - "range": true, - "refId": "A" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "editorMode": "code", - "expr": "reth_blockchain_tree_make_canonical_duration{action=\"split chain forks\", quantile=\"0.9\"} ", - "hide": false, - "instant": false, - "legendFormat": "{{quantile}} percentile", - "range": true, - "refId": "B" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "editorMode": "code", - "expr": "reth_blockchain_tree_make_canonical_duration{action=\"split chain forks\", quantile=\"0.95\"} ", - "hide": false, - "instant": false, - "legendFormat": "{{quantile}} percentile", - "range": true, - "refId": "C" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "editorMode": "code", - "expr": "reth_blockchain_tree_make_canonical_duration{action=\"split chain forks\", quantile=\"0.99\"} ", - "hide": false, - "instant": false, - "legendFormat": "{{quantile}} percentile", - "range": true, - "refId": "D" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "editorMode": "code", - "exemplar": false, - "expr": "reth_blockchain_tree_make_canonical_duration{action=\"split chain forks\", quantile=\"0.999\"} ", - "hide": false, - "instant": false, - "legendFormat": "{{quantile}} percentile", - "range": true, - "refId": "E" - } - ], - "title": "Split Chain Forks", - "type": "timeseries" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "description": "", - "fieldConfig": { - "defaults": { - "color": { - "mode": "palette-classic" - }, - "custom": { - "axisCenteredZero": false, - "axisColorMode": "text", - "axisLabel": "", - "axisPlacement": "auto", - "barAlignment": 0, - "drawStyle": "line", - "fillOpacity": 0, - "gradientMode": "none", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "insertNulls": false, - "lineInterpolation": "linear", - "lineWidth": 1, - "pointSize": 5, - "scaleDistribution": { - "type": "linear" - }, - "showPoints": "auto", - "spanNulls": false, - "stacking": { - "group": "A", - "mode": "none" - }, - "thresholdsStyle": { - "mode": "off" - } - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, - { - "color": "red", - "value": 80 - } - ] - }, - "unit": "ms" - }, - "overrides": [] - }, - "gridPos": { - "h": 8, - "w": 12, - "x": 0, - "y": 359 - }, - "id": 163, - "options": { - "legend": { - "calcs": [], - "displayMode": "list", - "placement": "bottom", - "showLegend": true - }, - "tooltip": { - "mode": "single", - "sort": "none" - } - }, - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "editorMode": "code", - "expr": "reth_blockchain_tree_make_canonical_duration{action=\"merge all chains\", quantile=\"0.5\"} ", - "instant": false, - "legendFormat": "{{quantile}} percentile", - "range": true, - "refId": "A" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "editorMode": "code", - "expr": "reth_blockchain_tree_make_canonical_duration{action=\"merge all chains\", quantile=\"0.9\"} ", - "hide": false, - "instant": false, - "legendFormat": "{{quantile}} percentile", - "range": true, - "refId": "B" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "editorMode": "code", - "expr": "reth_blockchain_tree_make_canonical_duration{action=\"merge all chains\", quantile=\"0.95\"} ", - "hide": false, - "instant": false, - "legendFormat": "{{quantile}} percentile", - "range": true, - "refId": "C" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "editorMode": "code", - "expr": "reth_blockchain_tree_make_canonical_duration{action=\"merge all chains\", quantile=\"0.99\"} ", - "hide": false, - "instant": false, - "legendFormat": "{{quantile}} percentile", - "range": true, - "refId": "D" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "editorMode": "code", - "exemplar": false, - "expr": "reth_blockchain_tree_make_canonical_duration{action=\"merge all chains\", quantile=\"0.999\"} ", - "hide": false, - "instant": false, - "legendFormat": "{{quantile}} percentile", - "range": true, - "refId": "E" - } - ], - "title": "Merge All Chains", - "type": "timeseries" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "description": "", - "fieldConfig": { - "defaults": { - "color": { - "mode": "palette-classic" - }, - "custom": { - "axisCenteredZero": false, - "axisColorMode": "text", - "axisLabel": "", - "axisPlacement": "auto", - "barAlignment": 0, - "drawStyle": "line", - "fillOpacity": 0, - "gradientMode": "none", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "insertNulls": false, - "lineInterpolation": "linear", - "lineWidth": 1, - "pointSize": 5, - "scaleDistribution": { - "type": "linear" - }, - "showPoints": "auto", - "spanNulls": false, - "stacking": { - "group": "A", - "mode": "none" - }, - "thresholdsStyle": { - "mode": "off" - } - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, - { - "color": "red", - "value": 80 - } - ] - }, - "unit": "ms" - }, - "overrides": [] - }, - "gridPos": { - "h": 8, - "w": 12, - "x": 12, - "y": 359 - }, - "id": 164, - "options": { - "legend": { - "calcs": [], - "displayMode": "list", - "placement": "bottom", - "showLegend": true - }, - "tooltip": { - "mode": "single", - "sort": "none" - } - }, - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "editorMode": "code", - "expr": "reth_blockchain_tree_make_canonical_duration{action=\"update canonical index\", quantile=\"0.5\"} ", - "instant": false, - "legendFormat": "{{quantile}} percentile", - "range": true, - "refId": "A" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "editorMode": "code", - "expr": "reth_blockchain_tree_make_canonical_duration{action=\"update canonical index\", quantile=\"0.9\"} ", - "hide": false, - "instant": false, - "legendFormat": "{{quantile}} percentile", - "range": true, - "refId": "B" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "editorMode": "code", - "expr": "reth_blockchain_tree_make_canonical_duration{action=\"update canonical index\", quantile=\"0.95\"} ", - "hide": false, - "instant": false, - "legendFormat": "{{quantile}} percentile", - "range": true, - "refId": "C" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "editorMode": "code", - "expr": "reth_blockchain_tree_make_canonical_duration{action=\"update canonical index\", quantile=\"0.99\"} ", - "hide": false, - "instant": false, - "legendFormat": "{{quantile}} percentile", - "range": true, - "refId": "D" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "editorMode": "code", - "exemplar": false, - "expr": "reth_blockchain_tree_make_canonical_duration{action=\"update canonical index\", quantile=\"0.999\"} ", - "hide": false, - "instant": false, - "legendFormat": "{{quantile}} percentile", - "range": true, - "refId": "E" - } - ], - "title": "Update Canonical Index", - "type": "timeseries" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "description": "", - "fieldConfig": { - "defaults": { - "color": { - "mode": "palette-classic" - }, - "custom": { - "axisCenteredZero": false, - "axisColorMode": "text", - "axisLabel": "", - "axisPlacement": "auto", - "barAlignment": 0, - "drawStyle": "line", - "fillOpacity": 0, - "gradientMode": "none", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "insertNulls": false, - "lineInterpolation": "linear", - "lineWidth": 1, - "pointSize": 5, - "scaleDistribution": { - "type": "linear" - }, - "showPoints": "auto", - "spanNulls": false, - "stacking": { - "group": "A", - "mode": "none" - }, - "thresholdsStyle": { - "mode": "off" - } - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, - { - "color": "red", - "value": 80 - } - ] - }, - "unit": "ms" - }, - "overrides": [] - }, - "gridPos": { - "h": 8, - "w": 12, - "x": 0, - "y": 367 - }, - "id": 165, - "options": { - "legend": { - "calcs": [], - "displayMode": "list", - "placement": "bottom", - "showLegend": true - }, - "tooltip": { - "mode": "single", - "sort": "none" - } - }, - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "editorMode": "code", - "expr": "reth_blockchain_tree_make_canonical_duration{action=\"commit canonical chain to database\", quantile=\"0.5\"} ", - "instant": false, - "legendFormat": "{{quantile}} percentile", - "range": true, - "refId": "A" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "editorMode": "code", - "expr": "reth_blockchain_tree_make_canonical_duration{action=\"commit canonical chain to database\", quantile=\"0.9\"} ", - "hide": false, - "instant": false, - "legendFormat": "{{quantile}} percentile", - "range": true, - "refId": "B" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "editorMode": "code", - "expr": "reth_blockchain_tree_make_canonical_duration{action=\"commit canonical chain to database\", quantile=\"0.95\"} ", - "hide": false, - "instant": false, - "legendFormat": "{{quantile}} percentile", - "range": true, - "refId": "C" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "editorMode": "code", - "expr": "reth_blockchain_tree_make_canonical_duration{action=\"commit canonical chain to database\", quantile=\"0.99\"} ", - "hide": false, - "instant": false, - "legendFormat": "{{quantile}} percentile", - "range": true, - "refId": "D" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "editorMode": "code", - "exemplar": false, - "expr": "reth_blockchain_tree_make_canonical_duration{action=\"commit canonical chain to database\", quantile=\"0.999\"} ", - "hide": false, - "instant": false, - "legendFormat": "{{quantile}} percentile", - "range": true, - "refId": "E" - } - ], - "title": "Commit Canonical Chain to Database", - "type": "timeseries" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "description": "", - "fieldConfig": { - "defaults": { - "color": { - "mode": "palette-classic" - }, - "custom": { - "axisCenteredZero": false, - "axisColorMode": "text", - "axisLabel": "", - "axisPlacement": "auto", - "barAlignment": 0, - "drawStyle": "line", - "fillOpacity": 0, - "gradientMode": "none", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "insertNulls": false, - "lineInterpolation": "linear", - "lineWidth": 1, - "pointSize": 5, - "scaleDistribution": { - "type": "linear" - }, - "showPoints": "auto", - "spanNulls": false, - "stacking": { - "group": "A", - "mode": "none" - }, - "thresholdsStyle": { - "mode": "off" - } - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, - { - "color": "red", - "value": 80 - } - ] - }, - "unit": "ms" - }, - "overrides": [] - }, - "gridPos": { - "h": 8, - "w": 12, - "x": 12, - "y": 367 - }, - "id": 166, - "options": { - "legend": { - "calcs": [], - "displayMode": "list", - "placement": "bottom", - "showLegend": true - }, - "tooltip": { - "mode": "single", - "sort": "none" - } - }, - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "editorMode": "code", - "expr": "reth_blockchain_tree_make_canonical_duration{action=\"revert canonical chain from database\", quantile=\"0.5\"} ", - "instant": false, - "legendFormat": "{{quantile}} percentile", - "range": true, - "refId": "A" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "editorMode": "code", - "expr": "reth_blockchain_tree_make_canonical_duration{action=\"revert canonical chain from database\", quantile=\"0.9\"} ", - "hide": false, - "instant": false, - "legendFormat": "{{quantile}} percentile", - "range": true, - "refId": "B" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "editorMode": "code", - "expr": "reth_blockchain_tree_make_canonical_duration{action=\"revert canonical chain from database\", quantile=\"0.95\"} ", - "hide": false, - "instant": false, - "legendFormat": "{{quantile}} percentile", - "range": true, - "refId": "C" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "editorMode": "code", - "expr": "reth_blockchain_tree_make_canonical_duration{action=\"revert canonical chain from database\", quantile=\"0.99\"} ", - "hide": false, - "instant": false, - "legendFormat": "{{quantile}} percentile", - "range": true, - "refId": "D" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "editorMode": "code", - "exemplar": false, - "expr": "reth_blockchain_tree_make_canonical_duration{action=\"revert canonical chain from database\", quantile=\"0.999\"} ", - "hide": false, - "instant": false, - "legendFormat": "{{quantile}} percentile", - "range": true, - "refId": "E" - } - ], - "title": "Revert Canonical Chain From Database", - "type": "timeseries" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "description": "", - "fieldConfig": { - "defaults": { - "color": { - "mode": "palette-classic" - }, - "custom": { - "axisCenteredZero": false, - "axisColorMode": "text", - "axisLabel": "", - "axisPlacement": "auto", - "barAlignment": 0, - "drawStyle": "line", - "fillOpacity": 0, - "gradientMode": "none", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "insertNulls": false, - "lineInterpolation": "linear", - "lineWidth": 1, - "pointSize": 5, - "scaleDistribution": { - "type": "linear" - }, - "showPoints": "auto", - "spanNulls": false, - "stacking": { - "group": "A", - "mode": "none" - }, - "thresholdsStyle": { - "mode": "off" - } - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, - { - "color": "red", - "value": 80 - } - ] - }, - "unit": "ms" - }, - "overrides": [] - }, - "gridPos": { - "h": 8, - "w": 12, - "x": 0, - "y": 375 - }, - "id": 161, - "options": { - "legend": { - "calcs": [], - "displayMode": "list", - "placement": "bottom", - "showLegend": true - }, - "tooltip": { - "mode": "single", - "sort": "none" - } - }, - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "editorMode": "code", - "expr": "reth_blockchain_tree_make_canonical_duration{action=\"insert old canonical chain\", quantile=\"0.5\"} ", - "instant": false, - "legendFormat": "{{quantile}} percentile", - "range": true, - "refId": "A" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "editorMode": "code", - "expr": "reth_blockchain_tree_make_canonical_duration{action=\"insert old canonical chain\", quantile=\"0.9\"} ", - "hide": false, - "instant": false, - "legendFormat": "{{quantile}} percentile", - "range": true, - "refId": "B" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "editorMode": "code", - "expr": "reth_blockchain_tree_make_canonical_duration{action=\"insert old canonical chain\", quantile=\"0.95\"} ", - "hide": false, - "instant": false, - "legendFormat": "{{quantile}} percentile", - "range": true, - "refId": "C" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "editorMode": "code", - "expr": "reth_blockchain_tree_make_canonical_duration{action=\"insert old canonical chain\", quantile=\"0.99\"} ", - "hide": false, - "instant": false, - "legendFormat": "{{quantile}} percentile", - "range": true, - "refId": "D" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "editorMode": "code", - "exemplar": false, - "expr": "reth_blockchain_tree_make_canonical_duration{action=\"insert old canonical chain\", quantile=\"0.999\"} ", - "hide": false, - "instant": false, - "legendFormat": "{{quantile}} percentile", - "range": true, - "refId": "E" - } - ], - "title": "Insert Old Canonical Chain", - "type": "timeseries" - } - ], - "refresh": "30s", - "revision": 1, - "schemaVersion": 38, - "style": "dark", - "tags": [], - "templating": { - "list": [ - { - "current": {}, - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "definition": "query_result(up)", - "hide": 0, - "includeAll": false, - "multi": false, - "name": "instance", - "options": [], - "query": { - "query": "query_result(up)", - "refId": "PrometheusVariableQueryEditor-VariableQuery" + "definition": "label_values(reth_storage_providers_database_duration{instance=\"$instance\"},action)", + "hide": 0, + "includeAll": false, + "label": "Database Storage Providers Action", + "multi": false, + "name": "database_action", + "options": [], + "query": { + "query": "label_values(reth_storage_providers_database_duration{instance=\"$instance\"},action)", + "refId": "PrometheusVariableQueryEditor-DatabaseQuery" }, "refresh": 1, - "regex": "/.*instance=\\\"([^\\\"]*).*/", + "regex": "", "skipUrlSync": false, "sort": 0, "type": "query" From a0781f08755a50212e4ea7aa0aaa59383d20e0f2 Mon Sep 17 00:00:00 2001 From: Matthias Seitz Date: Thu, 14 Dec 2023 18:02:33 +0100 Subject: [PATCH 132/277] chore: make CanonStateNotificationStream pub (#5759) --- crates/storage/provider/src/lib.rs | 13 +------------ crates/storage/provider/src/traits/mod.rs | 4 ++-- 2 files changed, 3 insertions(+), 14 deletions(-) diff --git a/crates/storage/provider/src/lib.rs b/crates/storage/provider/src/lib.rs index 194c60d500c5..45847067f5e0 100644 --- a/crates/storage/provider/src/lib.rs +++ b/crates/storage/provider/src/lib.rs @@ -15,18 +15,7 @@ /// Various provider traits. mod traits; -pub use traits::{ - AccountExtReader, AccountReader, BlockExecutionWriter, BlockExecutor, BlockExecutorStats, - BlockHashReader, BlockIdReader, BlockNumReader, BlockReader, BlockReaderIdExt, BlockSource, - BlockWriter, BlockchainTreePendingStateProvider, BundleStateDataProvider, CanonChainTracker, - CanonStateNotification, CanonStateNotificationSender, CanonStateNotifications, - CanonStateSubscriptions, ChainSpecProvider, ChangeSetReader, EvmEnvProvider, ExecutorFactory, - HashingWriter, HeaderProvider, HeaderSyncGap, HeaderSyncGapProvider, HeaderSyncMode, - HistoryWriter, PrunableBlockExecutor, PruneCheckpointReader, PruneCheckpointWriter, - ReceiptProvider, ReceiptProviderIdExt, StageCheckpointReader, StageCheckpointWriter, - StateProvider, StateProviderBox, StateProviderFactory, StateRootProvider, StorageReader, - TransactionVariant, TransactionsProvider, TransactionsProviderExt, WithdrawalsProvider, -}; +pub use traits::*; /// Provider trait implementations. pub mod providers; diff --git a/crates/storage/provider/src/traits/mod.rs b/crates/storage/provider/src/traits/mod.rs index 64f806f5f2b2..d6c023f82835 100644 --- a/crates/storage/provider/src/traits/mod.rs +++ b/crates/storage/provider/src/traits/mod.rs @@ -50,8 +50,8 @@ pub use executor::{BlockExecutor, BlockExecutorStats, ExecutorFactory, PrunableB mod chain; pub use chain::{ - CanonStateNotification, CanonStateNotificationSender, CanonStateNotifications, - CanonStateSubscriptions, + CanonStateNotification, CanonStateNotificationSender, CanonStateNotificationStream, + CanonStateNotifications, CanonStateSubscriptions, }; mod spec; From 06477320ef07692b595c50baf93e9c7cc081ad4e Mon Sep 17 00:00:00 2001 From: Thomas Coratger <60488569+tcoratger@users.noreply.github.com> Date: Thu, 14 Dec 2023 18:13:38 +0100 Subject: [PATCH 133/277] refactor(ef-tests): clean up `run` function (#5747) --- testing/ef-tests/src/cases/blockchain_test.rs | 88 ++++++++++--------- 1 file changed, 48 insertions(+), 40 deletions(-) diff --git a/testing/ef-tests/src/cases/blockchain_test.rs b/testing/ef-tests/src/cases/blockchain_test.rs index a162efdd577e..7e7c1dc6cb65 100644 --- a/testing/ef-tests/src/cases/blockchain_test.rs +++ b/testing/ef-tests/src/cases/blockchain_test.rs @@ -52,14 +52,19 @@ impl Case for BlockchainTestCase { }) } - // TODO: Clean up + /// Runs the test cases for the Ethereum Forks test suite. + /// + /// # Errors + /// Returns an error if the test is flagged for skipping or encounters issues during execution. fn run(&self) -> Result<(), Error> { + // If the test is marked for skipping, return a Skipped error immediately. if self.skip { - return Err(Error::Skipped) + return Err(Error::Skipped); } - for case in self.tests.values() { - if matches!( + // Iterate through test cases, filtering by the network type to exclude specific forks. + for case in self.tests.values().filter(|case| { + !matches!( case.network, ForkSpec::ByzantiumToConstantinopleAt5 | ForkSpec::Constantinople | @@ -68,16 +73,15 @@ impl Case for BlockchainTestCase { ForkSpec::MergeMeterInitCode | ForkSpec::MergePush0 | ForkSpec::Unknown - ) { - continue - } - - // Create the database + ) + }) { + // Create a new test database and initialize a provider for the test case. let db = create_test_rw_db(); - let factory = ProviderFactory::new(db.as_ref(), Arc::new(case.network.clone().into())); - let provider = factory.provider_rw().unwrap(); + let provider = ProviderFactory::new(db.as_ref(), Arc::new(case.network.clone().into())) + .provider_rw() + .unwrap(); - // Insert test state + // Insert initial test state into the provider. provider .insert_block( SealedBlock::new( @@ -90,43 +94,47 @@ impl Case for BlockchainTestCase { .map_err(|err| Error::RethError(err.into()))?; case.pre.write_to_db(provider.tx_ref())?; - let mut last_block = None; - for block in case.blocks.iter() { + // Decode and insert blocks, creating a chain of blocks for the test case. + let last_block = case.blocks.iter().try_fold(None, |_, block| { let decoded = SealedBlock::decode(&mut block.rlp.as_ref())?; provider .insert_block(decoded.clone(), None, None) .map_err(|err| Error::RethError(err.into()))?; - last_block = Some(decoded); - } - - // Call execution stage - { - let mut stage = ExecutionStage::new_with_factory( - reth_revm::EvmProcessorFactory::new(Arc::new(case.network.clone().into())), - ); - let target = last_block.as_ref().map(|b| b.number); - let _ = stage.execute(&provider, ExecInput { target, checkpoint: None }); - } - - // Validate post state - if let Some(state) = &case.post_state { - for (&address, account) in state.iter() { - account.assert_db(address, provider.tx_ref())?; + Ok::, Error>(Some(decoded)) + })?; + + // Execute the execution stage using the EVM processor factory for the test case + // network. + let _ = ExecutionStage::new_with_factory(reth_revm::EvmProcessorFactory::new( + Arc::new(case.network.clone().into()), + )) + .execute( + &provider, + ExecInput { target: last_block.as_ref().map(|b| b.number), checkpoint: None }, + ); + + // Validate the post-state for the test case. + match (&case.post_state, &case.post_state_hash) { + (Some(state), None) => { + // Validate accounts in the state against the provider's database. + for (&address, account) in state.iter() { + account.assert_db(address, provider.tx_ref())?; + } } - } else if let Some(expected_state_root) = case.post_state_hash { - // `insert_hashes` will insert hashed data, compute the state root and match it to - // expected internally - let last_block = last_block.unwrap_or_default(); - provider - .insert_hashes(0..=last_block.number, last_block.hash, expected_state_root) - .map_err(|err| Error::RethError(err.into()))?; - } else { - return Err(Error::MissingPostState) + (None, Some(expected_state_root)) => { + // Insert state hashes into the provider based on the expected state root. + let last_block = last_block.unwrap_or_default(); + provider + .insert_hashes(0..=last_block.number, last_block.hash, *expected_state_root) + .map_err(|err| Error::RethError(err.into()))?; + } + _ => return Err(Error::MissingPostState), } - // Drop provider without committing to the database. + // Drop the provider without committing to the database. drop(provider); } + Ok(()) } } From a8e2518aa0f4b14e79e8f90391686402f231a450 Mon Sep 17 00:00:00 2001 From: Matthias Seitz Date: Thu, 14 Dec 2023 18:25:05 +0100 Subject: [PATCH 134/277] feat: add SecretKey to networkconfig (#5761) --- bin/reth/src/cli/config.rs | 12 +++++++++++- crates/net/network/src/manager.rs | 7 +++++++ crates/net/network/src/network.rs | 10 ++++++++++ crates/net/network/src/session/mod.rs | 5 +++++ 4 files changed, 33 insertions(+), 1 deletion(-) diff --git a/bin/reth/src/cli/config.rs b/bin/reth/src/cli/config.rs index a549a31e1f3a..f516752f3380 100644 --- a/bin/reth/src/cli/config.rs +++ b/bin/reth/src/cli/config.rs @@ -111,7 +111,8 @@ pub trait PayloadBuilderConfig { fn compute_pending_block(&self) -> bool; } -/// A trait that can be used to apply additional configuration to the network. +/// A trait that represents the configured network and can be used to apply additional configuration +/// to the network. pub trait RethNetworkConfig { /// Adds a new additional protocol to the RLPx sub-protocol list. /// @@ -121,12 +122,21 @@ pub trait RethNetworkConfig { /// /// See also [ProtocolHandler](reth_network::protocol::ProtocolHandler) fn add_rlpx_sub_protocol(&mut self, protocol: impl IntoRlpxSubProtocol); + + /// Returns the secret key used for authenticating sessions. + fn secret_key(&self) -> secp256k1::SecretKey; + + // TODO add more network config methods here } impl RethNetworkConfig for reth_network::NetworkManager { fn add_rlpx_sub_protocol(&mut self, protocol: impl IntoRlpxSubProtocol) { reth_network::NetworkManager::add_rlpx_sub_protocol(self, protocol); } + + fn secret_key(&self) -> secp256k1::SecretKey { + self.secret_key() + } } /// A trait that provides all basic config values for the transaction pool and is implemented by the diff --git a/crates/net/network/src/manager.rs b/crates/net/network/src/manager.rs index bd96ba28072c..4441c2e51336 100644 --- a/crates/net/network/src/manager.rs +++ b/crates/net/network/src/manager.rs @@ -46,6 +46,7 @@ use reth_primitives::{ForkId, NodeRecord, PeerId, B256}; use reth_provider::{BlockNumReader, BlockReader}; use reth_rpc_types::{EthProtocolInfo, NetworkStatus}; use reth_tokio_util::EventListeners; +use secp256k1::SecretKey; use std::{ net::SocketAddr, pin::Pin, @@ -160,6 +161,11 @@ impl NetworkManager { pub fn bandwidth_meter(&self) -> &BandwidthMeter { self.handle.bandwidth_meter() } + + /// Returns the secret key used for authenticating sessions. + pub fn secret_key(&self) -> SecretKey { + self.swarm.sessions().secret_key() + } } impl NetworkManager @@ -245,6 +251,7 @@ where Arc::clone(&num_active_peers), listener_address, to_manager_tx, + secret_key, local_peer_id, peers_handle, network_mode, diff --git a/crates/net/network/src/network.rs b/crates/net/network/src/network.rs index 3448a788cbcd..3e2419a5b139 100644 --- a/crates/net/network/src/network.rs +++ b/crates/net/network/src/network.rs @@ -13,6 +13,7 @@ use reth_network_api::{ }; use reth_primitives::{Head, NodeRecord, PeerId, TransactionSigned, B256}; use reth_rpc_types::NetworkStatus; +use secp256k1::SecretKey; use std::{ net::SocketAddr, sync::{ @@ -41,6 +42,7 @@ impl NetworkHandle { num_active_peers: Arc, listener_address: Arc>, to_manager_tx: UnboundedSender, + secret_key: SecretKey, local_peer_id: PeerId, peers: PeersHandle, network_mode: NetworkMode, @@ -53,6 +55,7 @@ impl NetworkHandle { num_active_peers, to_manager_tx, listener_address, + secret_key, local_peer_id, peers, network_mode, @@ -153,6 +156,11 @@ impl NetworkHandle { pub fn tx_gossip_disabled(&self) -> bool { self.inner.tx_gossip_disabled } + + /// Returns the secret key used for authenticating sessions. + pub fn secret_key(&self) -> &SecretKey { + &self.inner.secret_key + } } // === API Implementations === @@ -328,6 +336,8 @@ struct NetworkInner { to_manager_tx: UnboundedSender, /// The local address that accepts incoming connections. listener_address: Arc>, + /// The secret key used for authenticating sessions. + secret_key: SecretKey, /// The identifier used by this node. local_peer_id: PeerId, /// Access to the all the nodes. diff --git a/crates/net/network/src/session/mod.rs b/crates/net/network/src/session/mod.rs index 98e5f9e59143..12827c6a77c9 100644 --- a/crates/net/network/src/session/mod.rs +++ b/crates/net/network/src/session/mod.rs @@ -171,6 +171,11 @@ impl SessionManager { self.status } + /// Returns the secret key used for authenticating sessions. + pub fn secret_key(&self) -> SecretKey { + self.secret_key + } + /// Returns the session hello message. pub fn hello_message(&self) -> HelloMessageWithProtocols { self.hello_message.clone() From 629b12a4f61a746ad5a1f0bf474caa7b77f66d84 Mon Sep 17 00:00:00 2001 From: Delweng Date: Fri, 15 Dec 2023 17:23:39 +0800 Subject: [PATCH 135/277] doc: --dev.block_time not available (#5764) Signed-off-by: jsvisa --- bin/reth/src/args/dev_args.rs | 2 +- book/cli/reth/node.md | 148 +++++++++++++++++----------------- 2 files changed, 75 insertions(+), 75 deletions(-) diff --git a/bin/reth/src/args/dev_args.rs b/bin/reth/src/args/dev_args.rs index 1bcfeeff2bbb..1f2df61327b5 100644 --- a/bin/reth/src/args/dev_args.rs +++ b/bin/reth/src/args/dev_args.rs @@ -29,7 +29,7 @@ pub struct DevArgs { /// Interval between blocks. /// /// Parses strings using [humantime::parse_duration] - /// --dev.block_time 12s + /// --dev.block-time 12s #[arg( long = "dev.block-time", help_heading = "Dev testnet", diff --git a/book/cli/reth/node.md b/book/cli/reth/node.md index a3a990250ef7..15a73bac0db5 100644 --- a/book/cli/reth/node.md +++ b/book/cli/reth/node.md @@ -9,13 +9,13 @@ Usage: reth node [OPTIONS] Options: --datadir The path to the data dir for all reth files and subdirectories. - + Defaults to the OS-specific data directory: - + - Linux: `$XDG_DATA_HOME/reth/` or `$HOME/.local/share/reth/` - Windows: `{FOLDERID_RoamingAppData}/reth/` - macOS: `$HOME/Library/Application Support/reth/` - + [default: default] --config @@ -24,21 +24,21 @@ Options: --chain The chain this node is running. Possible values are either a built-in chain or the path to a chain specification file. - + Built-in chains: mainnet, sepolia, goerli, holesky, dev - + [default: mainnet] --instance Add a new instance of a node. - + Configures the ports of the node to avoid conflicts with the defaults. This is useful for running multiple nodes on the same machine. - + Max number of instances is 200. It is chosen in a way so that it's not possible to have port numbers that conflict with each other. - + Changes to the following port numbers: - DISCOVERY_PORT: default + `instance` - 1 - AUTH_PORT: default + `instance` * 100 - 100 - HTTP_RPC_PORT: default - `instance` + 1 - WS_RPC_PORT: default + `instance` * 2 - 2 - + [default: 1] --trusted-setup-file @@ -50,7 +50,7 @@ Options: Metrics: --metrics Enable Prometheus metrics. - + The metrics will be served at the given interface and port. Networking: @@ -65,17 +65,17 @@ Networking: --discovery.addr The UDP address to use for P2P discovery/networking - + [default: 0.0.0.0] --discovery.port The UDP port to use for P2P discovery/networking - + [default: 30303] --trusted-peers Comma separated enode URLs of trusted peers for P2P connections. - + --trusted-peers enode://abcd@192.168.0.1:30303 --trusted-only @@ -83,7 +83,7 @@ Networking: --bootnodes Comma separated enode URLs for P2P discovery bootstrap. - + Will fall back to a network-specific default if not specified. --peers-file @@ -92,12 +92,12 @@ Networking: --identity Custom node identity - + [default: reth/v0.1.0-alpha.13-/aarch64-apple-darwin] --p2p-secret-key Secret key to use for this node. - + This will also deterministically set the peer ID. If not specified, it will be set in the data dir for the chain being used. --no-persist-peers @@ -105,17 +105,17 @@ Networking: --nat NAT resolution method (any|none|upnp|publicip|extip:) - + [default: any] --addr Network listening address - + [default: 0.0.0.0] --port Network listening port - + [default: 30303] --max-outbound-peers @@ -130,17 +130,17 @@ RPC: --http.addr Http server address to listen on - + [default: 127.0.0.1] --http.port Http server port to listen on - + [default: 8545] --http.api Rpc Modules to be configured for the HTTP server - + [possible values: admin, debug, eth, net, trace, txpool, web3, rpc, reth, ots, eth-call-bundle] --http.corsdomain @@ -151,12 +151,12 @@ RPC: --ws.addr Ws server address to listen on - + [default: 127.0.0.1] --ws.port Ws server port to listen on - + [default: 8546] --ws.origins @@ -164,7 +164,7 @@ RPC: --ws.api Rpc Modules to be configured for the WS server - + [possible values: admin, debug, eth, net, trace, txpool, web3, rpc, reth, ots, eth-call-bundle] --ipcdisable @@ -172,158 +172,158 @@ RPC: --ipcpath Filename for IPC socket/pipe within the datadir - + [default: .ipc] --authrpc.addr Auth server address to listen on - + [default: 127.0.0.1] --authrpc.port Auth server port to listen on - + [default: 8551] --authrpc.jwtsecret Path to a JWT secret to use for the authenticated engine-API RPC server. - + This will enforce JWT authentication for all requests coming from the consensus layer. - + If no path is provided, a secret will be generated and stored in the datadir under `//jwt.hex`. For mainnet this would be `~/.reth/mainnet/jwt.hex` by default. --rpc.jwtsecret Hex encoded JWT secret to authenticate the regular RPC server(s), see `--http.api` and `--ws.api`. - + This is __not__ used for the authenticated engine-API RPC server, see `--authrpc.jwtsecret`. --rpc-max-request-size Set the maximum RPC request payload size for both HTTP and WS in megabytes - + [default: 15] --rpc-max-response-size Set the maximum RPC response payload size for both HTTP and WS in megabytes - + [default: 150] [aliases: --rpc.returndata.limit] --rpc-max-subscriptions-per-connection Set the the maximum concurrent subscriptions per connection - + [default: 1024] --rpc-max-connections Maximum number of RPC server connections - + [default: 500] --rpc-max-tracing-requests Maximum number of concurrent tracing requests - + [default: 25] --rpc-max-blocks-per-filter Maximum number of blocks that could be scanned per filter request. (0 = entire chain) - + [default: 100000] --rpc-max-logs-per-response Maximum number of logs that can be returned in a single response. (0 = no limit) - + [default: 20000] --rpc-gas-cap Maximum gas limit for `eth_call` and call tracing RPC methods - + [default: 50000000] RPC State Cache: --rpc-cache.max-blocks Max number of blocks in cache - + [default: 5000] --rpc-cache.max-receipts Max number receipts in cache - + [default: 2000] --rpc-cache.max-envs Max number of bytes for cached env data - + [default: 1000] --rpc-cache.max-concurrent-db-requests Max number of concurrent database requests - + [default: 512] Gas Price Oracle: --gpo.blocks Number of recent blocks to check for gas price - + [default: 20] --gpo.ignoreprice Gas Price below which gpo will ignore transactions - + [default: 2] --gpo.maxprice Maximum transaction priority fee(or gasprice before London Fork) to be recommended by gpo - + [default: 500000000000] --gpo.percentile The percentile of gas prices to use for the estimate - + [default: 60] TxPool: --txpool.pending_max_count Max number of transaction in the pending sub-pool - + [default: 10000] --txpool.pending_max_size Max size of the pending sub-pool in megabytes - + [default: 20] --txpool.basefee_max_count Max number of transaction in the basefee sub-pool - + [default: 10000] --txpool.basefee_max_size Max size of the basefee sub-pool in megabytes - + [default: 20] --txpool.queued_max_count Max number of transaction in the queued sub-pool - + [default: 10000] --txpool.queued_max_size Max size of the queued sub-pool in megabytes - + [default: 20] --txpool.max_account_slots Max number of executable transaction slots guaranteed per account - + [default: 16] --txpool.pricebump Price bump (in %) for the transaction pool underpriced check - + [default: 10] --blobpool.pricebump Price bump percentage to replace an already existing blob transaction - + [default: 100] --txpool.nolocals @@ -332,33 +332,33 @@ TxPool: Builder: --builder.extradata Block extra data set by the payload builder - + [default: reth/v0.1.0-alpha.13/macos] --builder.gaslimit Target gas ceiling for built blocks - + [default: 30000000] --builder.interval The interval at which the job should build a new payload after the last (in seconds) - + [default: 1] --builder.deadline The deadline for when the payload builder job should resolve - + [default: 12] --builder.max-tasks Maximum number of tasks to spawn for building a payload - + [default: 3] Debug: --debug.continuous Prompt the downloader to download blocks one at a time. - + NOTE: This is for testing purposes only. --debug.terminate @@ -366,7 +366,7 @@ Debug: --debug.tip Set the chain tip manually for testing purposes. - + NOTE: This is a temporary flag --debug.max-block @@ -401,7 +401,7 @@ Database: Dev testnet: --dev Start the node in dev mode - + This mode uses a local proof-of-authority consensus engine with either fixed block times or automatically mined blocks. Disables network discovery and enables local http server. @@ -413,9 +413,9 @@ Dev testnet: --dev.block-time Interval between blocks. - + Parses strings using [humantime::parse_duration] - --dev.block_time 12s + --dev.block-time 12s Pruning: --full @@ -424,22 +424,22 @@ Pruning: Logging: --log.file.directory The path to put log files in - + [default: /logs] --log.file.max-size The maximum size (in MB) of one log file - + [default: 200] --log.file.max-files The maximum amount of log files that will be stored. If set to 0, background file logging is disabled - + [default: 5] --log.file.filter The filter to use for logs written to the log file - + [default: debug] --log.journald @@ -447,12 +447,12 @@ Logging: --log.journald.filter The filter to use for logs written to journald - + [default: error] --color Sets whether or not the formatter emits ANSI terminal escape codes for colors and other text formatting - + [default: always] Possible values: @@ -463,7 +463,7 @@ Logging: Display: -v, --verbosity... Set the minimum log level. - + -v Errors -vv Warnings -vvv Info @@ -472,4 +472,4 @@ Display: -q, --quiet Silence all log output -``` \ No newline at end of file +``` From 2df1e548be4d8e3b4c2687d7d9821b34855057ff Mon Sep 17 00:00:00 2001 From: Alexey Shekhirin Date: Fri, 15 Dec 2023 11:39:47 +0200 Subject: [PATCH 136/277] fix(cli): allow no PHF or compression in snapshots (#5765) --- bin/reth/src/db/snapshots/headers.rs | 4 ++-- bin/reth/src/db/snapshots/mod.rs | 25 +++++++++++++---------- bin/reth/src/db/snapshots/receipts.rs | 4 ++-- bin/reth/src/db/snapshots/transactions.rs | 4 ++-- 4 files changed, 20 insertions(+), 17 deletions(-) diff --git a/bin/reth/src/db/snapshots/headers.rs b/bin/reth/src/db/snapshots/headers.rs index 5253601e3aeb..b3f60e156ac9 100644 --- a/bin/reth/src/db/snapshots/headers.rs +++ b/bin/reth/src/db/snapshots/headers.rs @@ -26,7 +26,7 @@ impl Command { chain: Arc, compression: Compression, inclusion_filter: InclusionFilter, - phf: PerfectHashingFunction, + phf: Option, ) -> eyre::Result<()> { let factory = ProviderFactory::new(open_db_read_only(db_path, log_level)?, chain.clone()); let provider = factory.provider()?; @@ -34,7 +34,7 @@ impl Command { let block_range = self.block_ranges(tip).first().expect("has been generated before").clone(); - let filters = if self.with_filters { + let filters = if let Some(phf) = self.with_filters.then_some(phf).flatten() { Filters::WithFilters(inclusion_filter, phf) } else { Filters::WithoutFilters diff --git a/bin/reth/src/db/snapshots/mod.rs b/bin/reth/src/db/snapshots/mod.rs index 7e6980bf3ba9..faefb750e737 100644 --- a/bin/reth/src/db/snapshots/mod.rs +++ b/bin/reth/src/db/snapshots/mod.rs @@ -59,7 +59,7 @@ pub struct Command { only_bench: bool, /// Compression algorithms to use. - #[arg(long, short, value_delimiter = ',', default_value = "lz4")] + #[arg(long, short, value_delimiter = ',', default_value = "uncompressed")] compression: Vec, /// Flag to enable inclusion list filters and PHFs. @@ -79,11 +79,14 @@ impl Command { log_level: Option, chain: Arc, ) -> eyre::Result<()> { - let all_combinations = self - .segments - .iter() - .cartesian_product(self.compression.iter()) - .cartesian_product(self.phf.iter()); + let all_combinations = + self.segments.iter().cartesian_product(self.compression.iter()).cartesian_product( + if self.phf.is_empty() { + vec![None] + } else { + self.phf.iter().copied().map(Some).collect::>() + }, + ); { let db = open_db_read_only(db_path, None)?; @@ -91,8 +94,8 @@ impl Command { if !self.only_bench { for ((mode, compression), phf) in all_combinations.clone() { - let filters = if self.with_filters { - Filters::WithFilters(InclusionFilter::Cuckoo, *phf) + let filters = if let Some(phf) = self.with_filters.then_some(phf).flatten() { + Filters::WithFilters(InclusionFilter::Cuckoo, phf) } else { Filters::WithoutFilters }; @@ -124,7 +127,7 @@ impl Command { chain.clone(), *compression, InclusionFilter::Cuckoo, - *phf, + phf, )?, SnapshotSegment::Transactions => self.bench_transactions_snapshot( db_path, @@ -132,7 +135,7 @@ impl Command { chain.clone(), *compression, InclusionFilter::Cuckoo, - *phf, + phf, )?, SnapshotSegment::Receipts => self.bench_receipts_snapshot( db_path, @@ -140,7 +143,7 @@ impl Command { chain.clone(), *compression, InclusionFilter::Cuckoo, - *phf, + phf, )?, } } diff --git a/bin/reth/src/db/snapshots/receipts.rs b/bin/reth/src/db/snapshots/receipts.rs index ce028f79b84f..91a28c25aeb7 100644 --- a/bin/reth/src/db/snapshots/receipts.rs +++ b/bin/reth/src/db/snapshots/receipts.rs @@ -27,7 +27,7 @@ impl Command { chain: Arc, compression: Compression, inclusion_filter: InclusionFilter, - phf: PerfectHashingFunction, + phf: Option, ) -> eyre::Result<()> { let factory = ProviderFactory::new(open_db_read_only(db_path, log_level)?, chain.clone()); let provider = factory.provider()?; @@ -35,7 +35,7 @@ impl Command { let block_range = self.block_ranges(tip).first().expect("has been generated before").clone(); - let filters = if self.with_filters { + let filters = if let Some(phf) = self.with_filters.then_some(phf).flatten() { Filters::WithFilters(inclusion_filter, phf) } else { Filters::WithoutFilters diff --git a/bin/reth/src/db/snapshots/transactions.rs b/bin/reth/src/db/snapshots/transactions.rs index 690ca45b2427..6fd38ed58865 100644 --- a/bin/reth/src/db/snapshots/transactions.rs +++ b/bin/reth/src/db/snapshots/transactions.rs @@ -27,7 +27,7 @@ impl Command { chain: Arc, compression: Compression, inclusion_filter: InclusionFilter, - phf: PerfectHashingFunction, + phf: Option, ) -> eyre::Result<()> { let factory = ProviderFactory::new(open_db_read_only(db_path, log_level)?, chain.clone()); let provider = factory.provider()?; @@ -35,7 +35,7 @@ impl Command { let block_range = self.block_ranges(tip).first().expect("has been generated before").clone(); - let filters = if self.with_filters { + let filters = if let Some(phf) = self.with_filters.then_some(phf).flatten() { Filters::WithFilters(inclusion_filter, phf) } else { Filters::WithoutFilters From 764c7ae26ff2080538bc7fa729c11ece3d83f47a Mon Sep 17 00:00:00 2001 From: yjh Date: Fri, 15 Dec 2023 17:45:23 +0800 Subject: [PATCH 137/277] minor improve for compression dir (#5755) --- crates/primitives/src/compression/mod.rs | 11 +++++------ .../{receipt_dictionary.rs => receipt_dictionary.in} | 7 +++---- ...action_dictionary.rs => transaction_dictionary.in} | 7 +++---- 3 files changed, 11 insertions(+), 14 deletions(-) rename crates/primitives/src/compression/{receipt_dictionary.rs => receipt_dictionary.in} (99%) rename crates/primitives/src/compression/{transaction_dictionary.rs => transaction_dictionary.in} (99%) diff --git a/crates/primitives/src/compression/mod.rs b/crates/primitives/src/compression/mod.rs index 122d9ee2b421..48ad53aa0eb2 100644 --- a/crates/primitives/src/compression/mod.rs +++ b/crates/primitives/src/compression/mod.rs @@ -1,12 +1,11 @@ -mod receipt_dictionary; -mod transaction_dictionary; - -pub use receipt_dictionary::RECEIPT_DICTIONARY; -pub use transaction_dictionary::TRANSACTION_DICTIONARY; - use std::{cell::RefCell, thread_local}; use zstd::bulk::{Compressor, Decompressor}; +/// Compression/Decompression dictionary for `Receipt`. +pub static RECEIPT_DICTIONARY: [u8; 100000] = include!("./receipt_dictionary.in"); +/// Compression/Decompression dictionary for `Transaction`. +pub static TRANSACTION_DICTIONARY: [u8; 100000] = include!("./transaction_dictionary.in"); + // Reason for using static compressors is that dictionaries can be quite big, and zstd-rs // recommends to use one context/compressor per thread. Thus the usage of `thread_local`. thread_local! { diff --git a/crates/primitives/src/compression/receipt_dictionary.rs b/crates/primitives/src/compression/receipt_dictionary.in similarity index 99% rename from crates/primitives/src/compression/receipt_dictionary.rs rename to crates/primitives/src/compression/receipt_dictionary.in index f77e7004ae79..efa777e072eb 100644 --- a/crates/primitives/src/compression/receipt_dictionary.rs +++ b/crates/primitives/src/compression/receipt_dictionary.in @@ -1,5 +1,4 @@ -/// Compression/Decompression dictionary for `Receipt`. -pub static RECEIPT_DICTIONARY: [u8; 100000] = [ +[ 55, 164, 48, 236, 252, 66, 105, 97, 34, 16, 48, 92, 7, 241, 177, 245, 27, 152, 184, 38, 111, 9, 213, 108, 137, 172, 90, 25, 120, 100, 212, 93, 89, 5, 57, 190, 223, 171, 242, 231, 222, 91, 166, 131, 5, 32, 0, 6, 136, 193, 101, 2, 8, 76, 164, 28, 169, 7, 4, 128, 0, 131, 132, 3, 3, @@ -3949,5 +3948,5 @@ pub static RECEIPT_DICTIONARY: [u8; 100000] = [ 0, 28, 2, 62, 56, 0, 0, 28, 2, 144, 64, 0, 0, 28, 2, 226, 72, 0, 0, 20, 82, 8, 0, 0, 20, 82, 8, 0, 0, 20, 82, 8, 0, 0, 20, 164, 16, 0, 0, 20, 246, 24, 0, 0, 28, 1, 72, 32, 0, 0, 20, 82, 8, 0, 0, 20, 164, 16, 0, 0, 20, 246, 24, 0, 0, 28, 1, 72, 32, 0, 0, 28, 1, 154, 40, 0, 0, 28, 1, 236, - 48, 0, 0, 28, 2, 62, 56, 0, 0, 28, 2, -]; + 48, 0, 0, 28, 2, 62, 56, 0, 0, 28, 2 +] diff --git a/crates/primitives/src/compression/transaction_dictionary.rs b/crates/primitives/src/compression/transaction_dictionary.in similarity index 99% rename from crates/primitives/src/compression/transaction_dictionary.rs rename to crates/primitives/src/compression/transaction_dictionary.in index fc54b8904fc1..c70710388766 100644 --- a/crates/primitives/src/compression/transaction_dictionary.rs +++ b/crates/primitives/src/compression/transaction_dictionary.in @@ -1,5 +1,4 @@ -/// Compression/Decompression dictionary for `Transaction`. -pub static TRANSACTION_DICTIONARY: [u8; 100000] = [ +[ 55, 164, 48, 236, 228, 195, 180, 19, 23, 16, 24, 221, 1, 51, 63, 225, 72, 97, 70, 2, 203, 116, 147, 152, 246, 253, 59, 253, 143, 14, 45, 83, 179, 4, 72, 0, 6, 133, 66, 193, 128, 56, 32, 20, 160, 203, 245, 0, 4, 192, 64, 134, 7, 10, 20, 10, 5, 4, 10, 3, 130, 130, 4, 3, 130, 4, 17, 7, @@ -4538,5 +4537,5 @@ pub static TRANSACTION_DICTIONARY: [u8; 100000] = [ 83, 68, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 71, 66, 80, 85, 83, 68, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 85, 83, 68, 74, 80, 89, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 88, 65, 85, 85, 83, 68, 0, 0, 0, -]; + 0, 0, 88, 65, 85, 85, 83, 68, 0, 0, 0 +] \ No newline at end of file From 80ddf17769e16f6dc8bcf7f9fc9091fe5aae3804 Mon Sep 17 00:00:00 2001 From: Matthias Seitz Date: Fri, 15 Dec 2023 11:29:03 +0100 Subject: [PATCH 138/277] chore: add note about execute fn in docs (#5768) --- crates/storage/provider/src/traits/executor.rs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/crates/storage/provider/src/traits/executor.rs b/crates/storage/provider/src/traits/executor.rs index bec270faaf50..262c6618696b 100644 --- a/crates/storage/provider/src/traits/executor.rs +++ b/crates/storage/provider/src/traits/executor.rs @@ -38,6 +38,8 @@ pub trait BlockExecutor { ) -> Result<(), BlockExecutionError>; /// Executes the block and checks receipts. + /// + /// See [execute](BlockExecutor::execute) for more details. fn execute_and_verify_receipt( &mut self, block: &Block, @@ -55,6 +57,8 @@ pub trait BlockExecutor { /// 0, and so on). /// /// The second returned value represents the total gas used by this block of transactions. + /// + /// See [execute](BlockExecutor::execute) for more details. fn execute_transactions( &mut self, block: &Block, @@ -93,9 +97,9 @@ pub struct BlockExecutorStats { /// Time needed to merge transitions and create reverts. /// It this time transitions are applies to revm bundle state. pub merge_transitions_duration: Duration, - /// Time needed to caclulate receipt roots. + /// Time needed to calculate receipt roots. pub receipt_root_duration: Duration, - /// Time needed to recovere senders. + /// Time needed to recover senders. pub sender_recovery_duration: Duration, } From 49711061c31549a3c29f6eaf44546188b3baef0e Mon Sep 17 00:00:00 2001 From: Matthias Seitz Date: Fri, 15 Dec 2023 11:29:23 +0100 Subject: [PATCH 139/277] chore: add helper for getting the final total difficulty if known (#5769) --- crates/primitives/src/chain/spec.rs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/crates/primitives/src/chain/spec.rs b/crates/primitives/src/chain/spec.rs index 5d6efecbf733..7fe1d4ff7881 100644 --- a/crates/primitives/src/chain/spec.rs +++ b/crates/primitives/src/chain/spec.rs @@ -666,6 +666,11 @@ impl ChainSpec { self.genesis.timestamp } + /// Returns the final total difficulty if the Paris hardfork is known. + pub fn get_final_paris_total_difficulty(&self) -> Option { + self.paris_block_and_final_difficulty.map(|(_, final_difficulty)| final_difficulty) + } + /// Returns the final total difficulty if the given block number is after the Paris hardfork. /// /// Note: technically this would also be valid for the block before the paris upgrade, but this From 667972c20e5b26dd229878c474296f49a87a2891 Mon Sep 17 00:00:00 2001 From: Nikita Smirnov Date: Fri, 15 Dec 2023 12:38:03 +0200 Subject: [PATCH 140/277] feat: Report MDBX commit latency metrics (#5668) Co-authored-by: Nikita Smirnov Co-authored-by: Matthias Seitz --- .../storage/db/src/implementation/mdbx/tx.rs | 17 ++-- crates/storage/db/src/metrics.rs | 30 ++++++ .../storage/libmdbx-rs/benches/transaction.rs | 2 +- crates/storage/libmdbx-rs/src/environment.rs | 14 ++- crates/storage/libmdbx-rs/src/lib.rs | 2 +- crates/storage/libmdbx-rs/src/transaction.rs | 93 ++++++++++++++++++- crates/storage/libmdbx-rs/tests/cursor.rs | 2 +- .../storage/libmdbx-rs/tests/transaction.rs | 8 +- 8 files changed, 145 insertions(+), 23 deletions(-) diff --git a/crates/storage/db/src/implementation/mdbx/tx.rs b/crates/storage/db/src/implementation/mdbx/tx.rs index feb25138bfea..00e78c53b290 100644 --- a/crates/storage/db/src/implementation/mdbx/tx.rs +++ b/crates/storage/db/src/implementation/mdbx/tx.rs @@ -12,7 +12,7 @@ use crate::{ }; use parking_lot::RwLock; use reth_interfaces::db::{DatabaseWriteError, DatabaseWriteOperation}; -use reth_libmdbx::{ffi::DBI, Transaction, TransactionKind, WriteFlags, RW}; +use reth_libmdbx::{ffi::DBI, CommitLatency, Transaction, TransactionKind, WriteFlags, RW}; use reth_tracing::tracing::debug; use std::{ backtrace::Backtrace, @@ -99,14 +99,14 @@ impl Tx { fn execute_with_close_transaction_metric( mut self, outcome: TransactionOutcome, - f: impl FnOnce(Self) -> R, + f: impl FnOnce(Self) -> (R, Option), ) -> R { if let Some(mut metrics_handler) = self.metrics_handler.take() { metrics_handler.close_recorded = true; metrics_handler.log_backtrace_on_long_transaction(); let start = Instant::now(); - let result = f(self); + let (result, commit_latency) = f(self); let open_duration = metrics_handler.start.elapsed(); let close_duration = start.elapsed(); @@ -115,11 +115,12 @@ impl Tx { outcome, open_duration, Some(close_duration), + commit_latency, ); result } else { - f(self) + f(self).0 } } @@ -212,6 +213,7 @@ impl Drop for MetricsHandler { TransactionOutcome::Drop, self.start.elapsed(), None, + None, ); } } @@ -234,13 +236,16 @@ impl DbTx for Tx { fn commit(self) -> Result { self.execute_with_close_transaction_metric(TransactionOutcome::Commit, |this| { - this.inner.commit().map_err(|e| DatabaseError::Commit(e.into())) + match this.inner.commit().map_err(|e| DatabaseError::Commit(e.into())) { + Ok((v, latency)) => (Ok(v), Some(latency)), + Err(e) => (Err(e), None), + } }) } fn abort(self) { self.execute_with_close_transaction_metric(TransactionOutcome::Abort, |this| { - drop(this.inner) + (drop(this.inner), None) }) } diff --git a/crates/storage/db/src/metrics.rs b/crates/storage/db/src/metrics.rs index 921c699bb8cc..a81ff1623844 100644 --- a/crates/storage/db/src/metrics.rs +++ b/crates/storage/db/src/metrics.rs @@ -1,4 +1,5 @@ use metrics::{Gauge, Histogram}; +use reth_libmdbx::CommitLatency; use reth_metrics::{metrics::Counter, Metrics}; use std::time::{Duration, Instant}; @@ -100,6 +101,23 @@ pub(crate) struct TransactionMetrics { open_duration_seconds: Histogram, /// The time it took to close a database transaction close_duration_seconds: Histogram, + /// The time it took to prepare a transaction commit + commit_preparation_duration_seconds: Histogram, + /// Duration of GC update during transaction commit by wall clock + commit_gc_wallclock_duration_seconds: Histogram, + /// The time it took to conduct audit of a transaction commit + commit_audit_duration_seconds: Histogram, + /// The time it took to write dirty/modified data pages to a filesystem during transaction + /// commit + commit_write_duration_seconds: Histogram, + /// The time it took to sync written data to the disk/storage during transaction commit + commit_sync_duration_seconds: Histogram, + /// The time it took to release resources during transaction commit + commit_ending_duration_seconds: Histogram, + /// The total duration of a transaction commit + commit_whole_duration_seconds: Histogram, + /// User-mode CPU time spent on GC update during transaction commit + commit_gc_cputime_duration_seconds: Histogram, } impl TransactionMetrics { @@ -116,6 +134,7 @@ impl TransactionMetrics { outcome: TransactionOutcome, open_duration: Duration, close_duration: Option, + commit_latency: Option, ) { let metrics = Self::new_with_labels(&[(Labels::TransactionMode.as_str(), mode.as_str())]); metrics.open_total.decrement(1.0); @@ -129,6 +148,17 @@ impl TransactionMetrics { if let Some(close_duration) = close_duration { metrics.close_duration_seconds.record(close_duration) } + + if let Some(commit_latency) = commit_latency { + metrics.commit_preparation_duration_seconds.record(commit_latency.preparation()); + metrics.commit_gc_wallclock_duration_seconds.record(commit_latency.gc_wallclock()); + metrics.commit_audit_duration_seconds.record(commit_latency.audit()); + metrics.commit_write_duration_seconds.record(commit_latency.write()); + metrics.commit_sync_duration_seconds.record(commit_latency.sync()); + metrics.commit_ending_duration_seconds.record(commit_latency.ending()); + metrics.commit_whole_duration_seconds.record(commit_latency.whole()); + metrics.commit_gc_cputime_duration_seconds.record(commit_latency.gc_cputime()); + } } } diff --git a/crates/storage/libmdbx-rs/benches/transaction.rs b/crates/storage/libmdbx-rs/benches/transaction.rs index 01fb8ebdf0f3..72713acd3a05 100644 --- a/crates/storage/libmdbx-rs/benches/transaction.rs +++ b/crates/storage/libmdbx-rs/benches/transaction.rs @@ -68,7 +68,7 @@ fn bench_put_rand(c: &mut Criterion) { let txn = env.begin_ro_txn().unwrap(); let db = txn.open_db(None).unwrap(); txn.prime_for_permaopen(db); - let db = txn.commit_and_rebind_open_dbs().unwrap().1.remove(0); + let db = txn.commit_and_rebind_open_dbs().unwrap().2.remove(0); let mut items: Vec<(String, String)> = (0..n).map(|n| (get_key(n), get_data(n))).collect(); items.shuffle(&mut XorShiftRng::from_seed(Default::default())); diff --git a/crates/storage/libmdbx-rs/src/environment.rs b/crates/storage/libmdbx-rs/src/environment.rs index 0b83a243c0f4..c1cf3b68deee 100644 --- a/crates/storage/libmdbx-rs/src/environment.rs +++ b/crates/storage/libmdbx-rs/src/environment.rs @@ -2,7 +2,7 @@ use crate::{ database::Database, error::{mdbx_result, Error, Result}, flags::EnvironmentFlags, - transaction::{RO, RW}, + transaction::{CommitLatency, RO, RW}, Mode, Transaction, TransactionKind, }; use byteorder::{ByteOrder, NativeEndian}; @@ -303,7 +303,7 @@ unsafe impl Sync for EnvPtr {} pub(crate) enum TxnManagerMessage { Begin { parent: TxnPtr, flags: ffi::MDBX_txn_flags_t, sender: SyncSender> }, Abort { tx: TxnPtr, sender: SyncSender> }, - Commit { tx: TxnPtr, sender: SyncSender> }, + Commit { tx: TxnPtr, sender: SyncSender> }, } /// Environment statistics. @@ -603,9 +603,13 @@ impl EnvironmentBuilder { } TxnManagerMessage::Commit { tx, sender } => { sender - .send(mdbx_result(unsafe { - ffi::mdbx_txn_commit_ex(tx.0, ptr::null_mut()) - })) + .send({ + let mut latency = CommitLatency::new(); + mdbx_result(unsafe { + ffi::mdbx_txn_commit_ex(tx.0, latency.mdb_commit_latency()) + }) + .map(|v| (v, latency)) + }) .unwrap(); } }, diff --git a/crates/storage/libmdbx-rs/src/lib.rs b/crates/storage/libmdbx-rs/src/lib.rs index 14e20de72188..89232e8aeb80 100644 --- a/crates/storage/libmdbx-rs/src/lib.rs +++ b/crates/storage/libmdbx-rs/src/lib.rs @@ -19,7 +19,7 @@ pub use crate::{ }, error::{Error, Result}, flags::*, - transaction::{Transaction, TransactionKind, RO, RW}, + transaction::{CommitLatency, Transaction, TransactionKind, RO, RW}, }; pub mod ffi { pub use ffi::{MDBX_dbi as DBI, MDBX_log_level_t as LogLevel}; diff --git a/crates/storage/libmdbx-rs/src/transaction.rs b/crates/storage/libmdbx-rs/src/transaction.rs index 2330c3a3f891..5428eae3a46e 100644 --- a/crates/storage/libmdbx-rs/src/transaction.rs +++ b/crates/storage/libmdbx-rs/src/transaction.rs @@ -15,6 +15,7 @@ use std::{ mem::size_of, ptr, slice, sync::{atomic::AtomicBool, mpsc::sync_channel, Arc}, + time::Duration, }; mod private { @@ -166,8 +167,8 @@ where /// Commits the transaction. /// /// Any pending operations will be saved. - pub fn commit(self) -> Result { - self.commit_and_rebind_open_dbs().map(|v| v.0) + pub fn commit(self) -> Result<(bool, CommitLatency)> { + self.commit_and_rebind_open_dbs().map(|v| (v.0, v.1)) } pub fn prime_for_permaopen(&self, db: Database) { @@ -175,11 +176,15 @@ where } /// Commits the transaction and returns table handles permanently open until dropped. - pub fn commit_and_rebind_open_dbs(self) -> Result<(bool, Vec)> { + pub fn commit_and_rebind_open_dbs(self) -> Result<(bool, CommitLatency, Vec)> { let result = { let result = self.txn_execute(|txn| { if K::ONLY_CLEAN { - mdbx_result(unsafe { ffi::mdbx_txn_commit_ex(txn, ptr::null_mut()) }) + let mut latency = CommitLatency::new(); + mdbx_result(unsafe { + ffi::mdbx_txn_commit_ex(txn, latency.mdb_commit_latency()) + }) + .map(|v| (v, latency)) } else { let (sender, rx) = sync_channel(0); self.env() @@ -193,9 +198,10 @@ where self.inner.set_committed(); result }; - result.map(|v| { + result.map(|(v, latency)| { ( v, + latency, self.inner .primed_dbis .lock() @@ -535,6 +541,83 @@ impl TransactionPtr { } } +/// Commit latencies info. +/// +/// Contains information about latency of commit stages. +/// Inner struct stores this info in 1/65536 of seconds units. +#[repr(transparent)] +pub struct CommitLatency(ffi::MDBX_commit_latency); + +impl CommitLatency { + /// Create a new CommitLatency with zero'd inner struct `ffi::MDBX_commit_latency`. + pub(crate) fn new() -> Self { + unsafe { Self(std::mem::zeroed()) } + } + + /// Returns a mut pointer to `ffi::MDBX_commit_latency`. + pub(crate) fn mdb_commit_latency(&mut self) -> *mut ffi::MDBX_commit_latency { + &mut self.0 + } +} + +impl CommitLatency { + /// Duration of preparation (commit child transactions, update + /// sub-databases records and cursors destroying). + #[inline] + pub fn preparation(&self) -> Duration { + Self::time_to_duration(self.0.preparation) + } + + /// Duration of GC update by wall clock. + #[inline] + pub fn gc_wallclock(&self) -> Duration { + Self::time_to_duration(self.0.gc_wallclock) + } + + /// Duration of internal audit if enabled. + #[inline] + pub fn audit(&self) -> Duration { + Self::time_to_duration(self.0.audit) + } + + /// Duration of writing dirty/modified data pages to a filesystem, + /// i.e. the summary duration of a `write()` syscalls during commit. + #[inline] + pub fn write(&self) -> Duration { + Self::time_to_duration(self.0.write) + } + + /// Duration of syncing written data to the disk/storage, i.e. + /// the duration of a `fdatasync()` or a `msync()` syscall during commit. + #[inline] + pub fn sync(&self) -> Duration { + Self::time_to_duration(self.0.sync) + } + + /// Duration of transaction ending (releasing resources). + #[inline] + pub fn ending(&self) -> Duration { + Self::time_to_duration(self.0.ending) + } + + /// The total duration of a commit. + #[inline] + pub fn whole(&self) -> Duration { + Self::time_to_duration(self.0.whole) + } + + /// User-mode CPU time spent on GC update. + #[inline] + pub fn gc_cputime(&self) -> Duration { + Self::time_to_duration(self.0.gc_cputime) + } + + #[inline] + fn time_to_duration(time: u32) -> Duration { + Duration::from_nanos(time as u64 * (1_000_000_000 / 65_536)) + } +} + // SAFETY: Access to the transaction is synchronized by the lock. unsafe impl Send for TransactionPtr {} diff --git a/crates/storage/libmdbx-rs/tests/cursor.rs b/crates/storage/libmdbx-rs/tests/cursor.rs index 0e2ce403edf7..306b87c0de6a 100644 --- a/crates/storage/libmdbx-rs/tests/cursor.rs +++ b/crates/storage/libmdbx-rs/tests/cursor.rs @@ -109,7 +109,7 @@ fn test_iter() { for (key, data) in &items { txn.put(db.dbi(), key, data, WriteFlags::empty()).unwrap(); } - assert!(!txn.commit().unwrap()); + assert!(!txn.commit().unwrap().0); } let txn = env.begin_ro_txn().unwrap(); diff --git a/crates/storage/libmdbx-rs/tests/transaction.rs b/crates/storage/libmdbx-rs/tests/transaction.rs index 361bca0e2d49..8cdd2531c585 100644 --- a/crates/storage/libmdbx-rs/tests/transaction.rs +++ b/crates/storage/libmdbx-rs/tests/transaction.rs @@ -147,13 +147,13 @@ fn test_clear_db() { { let txn = env.begin_rw_txn().unwrap(); txn.put(txn.open_db(None).unwrap().dbi(), b"key", b"val", WriteFlags::empty()).unwrap(); - assert!(!txn.commit().unwrap()); + assert!(!txn.commit().unwrap().0); } { let txn = env.begin_rw_txn().unwrap(); txn.clear_db(txn.open_db(None).unwrap().dbi()).unwrap(); - assert!(!txn.commit().unwrap()); + assert!(!txn.commit().unwrap().0); } let txn = env.begin_ro_txn().unwrap(); @@ -177,7 +177,7 @@ fn test_drop_db() { .unwrap(); // Workaround for MDBX dbi drop issue txn.create_db(Some("canary"), DatabaseFlags::empty()).unwrap(); - assert!(!txn.commit().unwrap()); + assert!(!txn.commit().unwrap().0); } { let txn = env.begin_rw_txn().unwrap(); @@ -186,7 +186,7 @@ fn test_drop_db() { txn.drop_db(db).unwrap(); } assert!(matches!(txn.open_db(Some("test")).unwrap_err(), Error::NotFound)); - assert!(!txn.commit().unwrap()); + assert!(!txn.commit().unwrap().0); } } From ff183756bc61f32d386121cb26ba4dff18ea6a25 Mon Sep 17 00:00:00 2001 From: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Date: Fri, 15 Dec 2023 12:58:00 +0200 Subject: [PATCH 141/277] perf: reduce static data size (#5770) --- crates/primitives/src/compression/mod.rs | 28 +- .../src/compression/receipt_dictionary.bin | Bin 0 -> 100000 bytes .../src/compression/receipt_dictionary.in | 3952 -------------- .../compression/transaction_dictionary.bin | Bin 0 -> 100000 bytes .../src/compression/transaction_dictionary.in | 4541 ----------------- 5 files changed, 18 insertions(+), 8503 deletions(-) create mode 100644 crates/primitives/src/compression/receipt_dictionary.bin delete mode 100644 crates/primitives/src/compression/receipt_dictionary.in create mode 100644 crates/primitives/src/compression/transaction_dictionary.bin delete mode 100644 crates/primitives/src/compression/transaction_dictionary.in diff --git a/crates/primitives/src/compression/mod.rs b/crates/primitives/src/compression/mod.rs index 48ad53aa0eb2..f708e311a118 100644 --- a/crates/primitives/src/compression/mod.rs +++ b/crates/primitives/src/compression/mod.rs @@ -2,26 +2,34 @@ use std::{cell::RefCell, thread_local}; use zstd::bulk::{Compressor, Decompressor}; /// Compression/Decompression dictionary for `Receipt`. -pub static RECEIPT_DICTIONARY: [u8; 100000] = include!("./receipt_dictionary.in"); +pub static RECEIPT_DICTIONARY: &[u8] = include_bytes!("./receipt_dictionary.bin"); /// Compression/Decompression dictionary for `Transaction`. -pub static TRANSACTION_DICTIONARY: [u8; 100000] = include!("./transaction_dictionary.in"); +pub static TRANSACTION_DICTIONARY: &[u8] = include_bytes!("./transaction_dictionary.bin"); // Reason for using static compressors is that dictionaries can be quite big, and zstd-rs // recommends to use one context/compressor per thread. Thus the usage of `thread_local`. thread_local! { /// Thread Transaction compressor. - pub static TRANSACTION_COMPRESSOR: RefCell> = RefCell::new(Compressor::with_dictionary(0, &TRANSACTION_DICTIONARY) - .expect("Failed to initialize compressor.")); + pub static TRANSACTION_COMPRESSOR: RefCell> = RefCell::new( + Compressor::with_dictionary(0, TRANSACTION_DICTIONARY) + .expect("Failed to initialize compressor."), + ); /// Thread Transaction decompressor. - pub static TRANSACTION_DECOMPRESSOR: RefCell> = RefCell::new(Decompressor::with_dictionary(&TRANSACTION_DICTIONARY) - .expect("Failed to initialize decompressor.")); + pub static TRANSACTION_DECOMPRESSOR: RefCell> = RefCell::new( + Decompressor::with_dictionary(TRANSACTION_DICTIONARY) + .expect("Failed to initialize decompressor."), + ); /// Thread receipt compressor. - pub static RECEIPT_COMPRESSOR: RefCell> = RefCell::new(Compressor::with_dictionary(0, &RECEIPT_DICTIONARY) - .expect("Failed to initialize compressor.")); + pub static RECEIPT_COMPRESSOR: RefCell> = RefCell::new( + Compressor::with_dictionary(0, RECEIPT_DICTIONARY) + .expect("Failed to initialize compressor."), + ); /// Thread receipt decompressor. - pub static RECEIPT_DECOMPRESSOR: RefCell> = RefCell::new(Decompressor::with_dictionary(&RECEIPT_DICTIONARY) - .expect("Failed to initialize decompressor.")); + pub static RECEIPT_DECOMPRESSOR: RefCell> = RefCell::new( + Decompressor::with_dictionary(RECEIPT_DICTIONARY) + .expect("Failed to initialize decompressor."), + ); } diff --git a/crates/primitives/src/compression/receipt_dictionary.bin b/crates/primitives/src/compression/receipt_dictionary.bin new file mode 100644 index 0000000000000000000000000000000000000000..26c3f32e842c9c5172ff1e9af435952db1db2282 GIT binary patch literal 100000 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

hak_a1mMkI0STr}B*!APy%`9E(q(eD2&iTW#~E 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 z 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 literal 0 HcmV?d00001 diff --git a/crates/primitives/src/compression/transaction_dictionary.in b/crates/primitives/src/compression/transaction_dictionary.in deleted file mode 100644 index c70710388766..000000000000 --- a/crates/primitives/src/compression/transaction_dictionary.in +++ /dev/null @@ -1,4541 +0,0 @@ -[ - 55, 164, 48, 236, 228, 195, 180, 19, 23, 16, 24, 221, 1, 51, 63, 225, 72, 97, 70, 2, 203, 116, - 147, 152, 246, 253, 59, 253, 143, 14, 45, 83, 179, 4, 72, 0, 6, 133, 66, 193, 128, 56, 32, 20, - 160, 203, 245, 0, 4, 192, 64, 134, 7, 10, 20, 10, 5, 4, 10, 3, 130, 130, 4, 3, 130, 4, 17, 7, - 137, 132, 131, 130, 161, 7, 179, 89, 72, 162, 129, 80, 14, 4, 24, 115, 214, 26, 34, 104, 0, 0, - 100, 138, 228, 34, 163, 162, 96, 48, 20, 6, 2, 1, 0, 0, 0, 12, 6, 163, 193, 0, 48, 16, 0, 226, - 13, 0, 0, 0, 1, 0, 0, 0, 4, 0, 0, 0, 8, 0, 0, 0, 129, 82, 96, 32, 1, 80, 96, 1, 96, 0, 80, 128, - 84, 128, 96, 31, 1, 96, 32, 128, 145, 4, 2, 96, 32, 1, 96, 64, 81, 144, 129, 1, 96, 64, 82, - 128, 146, 145, 144, 129, 129, 82, 96, 32, 1, 130, 128, 84, 128, 21, 97, 1, 205, 87, 130, 1, - 145, 144, 96, 0, 82, 96, 32, 96, 0, 32, 144, 91, 129, 84, 129, 82, 144, 96, 1, 1, 144, 96, 32, - 1, 128, 131, 17, 97, 1, 176, 87, 130, 144, 3, 96, 31, 22, 130, 1, 145, 91, 80, 80, 80, 80, 80, - 144, 80, 97, 1, 217, 86, 91, 144, 86, 0, 209, 168, 46, 57, 228, 156, 199, 136, 99, 194, 212, - 220, 219, 107, 195, 195, 191, 94, 70, 87, 78, 8, 122, 249, 231, 28, 209, 27, 38, 153, 62, 123, - 93, 53, 5, 137, 246, 228, 206, 145, 153, 154, 57, 252, 201, 79, 94, 205, 58, 188, 39, 69, 135, - 151, 41, 73, 93, 107, 95, 157, 131, 172, 55, 81, 162, 72, 4, 12, 13, 211, 164, 176, 92, 82, 8, - 50, 190, 52, 59, 148, 248, 96, 18, 77, 196, 254, 226, 120, 253, 203, 211, 140, 16, 45, 136, 15, - 205, 169, 188, 255, 126, 12, 0, 1, 215, 159, 55, 149, 69, 134, 43, 77, 206, 254, 175, 221, 208, - 50, 199, 67, 106, 147, 68, 68, 242, 102, 45, 92, 13, 166, 233, 7, 56, 21, 10, 243, 49, 35, 231, - 181, 1, 49, 97, 188, 245, 200, 182, 193, 143, 129, 246, 143, 20, 45, 33, 59, 79, 81, 241, 50, - 127, 17, 24, 237, 240, 6, 25, 233, 130, 15, 226, 33, 174, 39, 152, 230, 50, 92, 13, 89, 97, - 172, 129, 140, 36, 25, 17, 164, 43, 234, 247, 156, 238, 90, 185, 154, 50, 56, 15, 91, 201, 46, - 164, 204, 4, 118, 68, 5, 95, 236, 108, 200, 3, 208, 144, 23, 159, 219, 178, 212, 105, 1, 205, - 181, 173, 1, 187, 251, 92, 73, 79, 108, 70, 50, 233, 1, 71, 231, 141, 137, 25, 121, 28, 0, 0, - 206, 150, 70, 227, 111, 56, 65, 50, 233, 222, 153, 31, 181, 155, 1, 185, 194, 198, 227, 198, - 35, 147, 91, 62, 67, 46, 173, 222, 188, 3, 196, 136, 158, 79, 5, 169, 137, 64, 159, 20, 73, 93, - 213, 145, 79, 218, 50, 91, 104, 151, 209, 163, 151, 134, 48, 110, 147, 244, 212, 78, 19, 7, 84, - 106, 166, 76, 4, 2, 254, 204, 5, 81, 111, 90, 57, 1, 95, 144, 205, 234, 217, 212, 132, 201, - 249, 139, 10, 106, 142, 195, 211, 138, 233, 13, 222, 62, 39, 52, 13, 237, 10, 74, 23, 100, 16, - 232, 1, 182, 155, 219, 169, 171, 201, 162, 53, 149, 96, 32, 80, 137, 189, 75, 194, 62, 102, 24, - 14, 122, 254, 247, 17, 90, 209, 57, 146, 230, 70, 102, 156, 133, 162, 187, 191, 109, 254, 79, - 132, 69, 203, 22, 57, 46, 7, 124, 102, 48, 198, 233, 89, 154, 215, 203, 55, 229, 203, 214, 119, - 222, 241, 245, 44, 166, 76, 4, 3, 99, 23, 5, 23, 176, 203, 149, 1, 95, 144, 23, 169, 97, 45, - 52, 157, 25, 254, 119, 202, 218, 91, 91, 66, 64, 162, 252, 1, 175, 158, 1, 63, 201, 109, 145, - 121, 220, 0, 1, 183, 60, 209, 23, 39, 242, 88, 144, 178, 44, 154, 32, 100, 155, 230, 250, 120, - 247, 144, 79, 171, 108, 88, 240, 16, 10, 4, 45, 144, 67, 1, 179, 38, 206, 44, 72, 97, 107, 196, - 216, 255, 143, 71, 238, 38, 114, 15, 228, 50, 132, 157, 216, 180, 214, 161, 207, 192, 27, 233, - 3, 204, 116, 65, 93, 166, 76, 4, 3, 99, 24, 5, 23, 176, 203, 149, 1, 95, 144, 125, 161, 151, - 123, 198, 100, 73, 138, 10, 201, 82, 160, 130, 92, 68, 121, 36, 208, 220, 224, 1, 84, 7, 207, - 135, 211, 0, 0, 0, 161, 96, 118, 201, 63, 112, 64, 78, 50, 129, 71, 177, 255, 56, 100, 42, 207, - 18, 184, 63, 10, 198, 139, 22, 207, 112, 205, 34, 22, 124, 20, 185, 239, 92, 31, 137, 4, 122, - 51, 137, 36, 58, 4, 152, 58, 57, 154, 217, 194, 183, 42, 13, 78, 71, 227, 199, 54, 199, 39, - 132, 143, 85, 226, 11, 166, 76, 4, 3, 99, 25, 5, 23, 176, 203, 149, 1, 95, 144, 117, 224, 75, - 128, 132, 221, 134, 63, 17, 70, 243, 160, 53, 222, 236, 94, 53, 166, 69, 187, 1, 84, 53, 189, - 122, 98, 168, 0, 0, 41, 129, 246, 40, 117, 26, 186, 166, 188, 139, 175, 37, 151, 145, 148, 222, - 193, 109, 191, 137, 106, 142, 216, 181, 177, 102, 102, 99, 210, 150, 97, 10, 31, 63, 254, 0, - 26, 51, 125, 7, 254, 107, 209, 164, 137, 226, 192, 212, 138, 235, 20, 39, 162, 220, 38, 244, 5, - 114, 112, 76, 86, 151, 144, 59, 166, 76, 4, 3, 99, 26, 5, 23, 176, 203, 149, 1, 95, 144, 8, 98, - 66, 210, 76, 153, 80, 53, 146, 44, 171, 166, 10, 116, 145, 230, 232, 175, 151, 35, 1, 85, 18, - 198, 79, 52, 132, 0, 1, 99, 47, 27, 37, 27, 131, 48, 111, 209, 160, 55, 44, 193, 215, 168, 125, - 210, 208, 193, 135, 17, 100, 192, 35, 34, 204, 48, 141, 180, 119, 213, 62, 202, 64, 223, 200, - 180, 2, 102, 82, 26, 0, 173, 217, 47, 41, 191, 175, 124, 249, 43, 76, 144, 191, 164, 41, 143, - 107, 113, 129, 44, 132, 236, 3, 166, 76, 4, 3, 99, 27, 5, 23, 176, 203, 149, 1, 95, 144, 174, - 221, 41, 180, 156, 90, 58, 250, 229, 114, 147, 94, 40, 58, 181, 234, 11, 76, 142, 46, 1, 100, - 133, 200, 253, 97, 236, 0, 0, 233, 66, 51, 16, 50, 134, 58, 5, 188, 215, 239, 61, 30, 56, 17, - 143, 114, 24, 109, 31, 128, 118, 185, 209, 113, 100, 100, 59, 199, 203, 90, 221, 126, 81, 99, - 93, 93, 8, 172, 89, 217, 22, 21, 192, 180, 54, 77, 177, 18, 28, 99, 29, 20, 24, 252, 16, 197, - 40, 228, 93, 251, 173, 166, 33, 166, 76, 4, 3, 99, 28, 5, 23, 176, 203, 149, 1, 95, 144, 57, - 178, 50, 149, 60, 171, 233, 94, 107, 140, 128, 224, 254, 11, 91, 147, 218, 88, 48, 58, 1, 103, - 232, 192, 120, 247, 220, 0, 0, 107, 169, 253, 73, 250, 54, 23, 1, 226, 229, 254, 36, 174, 242, - 47, 150, 156, 174, 235, 213, 159, 17, 86, 5, 106, 196, 68, 94, 138, 118, 184, 0, 34, 95, 146, - 225, 198, 40, 166, 156, 220, 190, 196, 232, 12, 104, 95, 141, 116, 110, 95, 21, 91, 53, 99, 97, - 3, 140, 173, 31, 28, 121, 137, 82, 160, 204, 4, 4, 232, 16, 231, 204, 1, 95, 144, 92, 112, 235, - 14, 137, 35, 204, 12, 65, 110, 143, 160, 2, 173, 22, 92, 156, 252, 251, 36, 10, 215, 142, 188, - 90, 198, 32, 0, 0, 1, 76, 230, 112, 123, 209, 7, 107, 187, 121, 225, 35, 198, 6, 37, 129, 80, - 255, 224, 43, 213, 70, 195, 31, 133, 94, 192, 123, 90, 107, 240, 181, 203, 82, 216, 134, 9, 63, - 163, 253, 182, 148, 142, 103, 58, 36, 222, 146, 149, 114, 200, 85, 107, 33, 13, 240, 56, 210, - 167, 163, 29, 77, 2, 177, 30, 162, 12, 0, 76, 4, 168, 23, 200, 0, 35, 24, 96, 91, 98, 1, 134, - 160, 90, 19, 21, 96, 19, 87, 96, 1, 96, 32, 82, 96, 0, 86, 91, 96, 0, 128, 96, 31, 96, 0, 57, - 96, 31, 86, 91, 96, 0, 243, 1, 56, 78, 255, 65, 149, 183, 22, 217, 184, 83, 205, 164, 17, 21, - 63, 5, 211, 37, 241, 27, 69, 173, 96, 193, 14, 147, 125, 195, 188, 215, 182, 221, 158, 232, - 126, 179, 140, 65, 97, 250, 142, 173, 68, 155, 99, 216, 111, 92, 120, 78, 35, 250, 169, 53, 60, - 80, 225, 68, 181, 92, 10, 26, 7, 6, 162, 12, 0, 26, 5, 85, 20, 162, 103, 47, 66, 14, 96, 128, - 96, 64, 129, 144, 82, 96, 0, 96, 96, 129, 144, 82, 96, 1, 128, 84, 145, 129, 144, 82, 145, 81, - 96, 255, 25, 22, 130, 85, 97, 0, 120, 144, 127, 177, 14, 45, 82, 118, 18, 7, 59, 38, 238, 205, - 253, 113, 126, 106, 50, 12, 244, 75, 74, 250, 194, 176, 115, 45, 159, 203, 226, 183, 250, 12, - 246, 96, 32, 96, 2, 131, 134, 22, 21, 97, 1, 0, 2, 96, 0, 25, 1, 144, 147, 22, 146, 144, 146, - 4, 96, 31, 1, 145, 144, 145, 4, 129, 1, 144, 91, 128, 130, 17, 21, 97, 1, 59, 87, 96, 0, 129, - 85, 96, 1, 1, 97, 0, 100, 86, 91, 80, 80, 96, 64, 81, 97, 48, 165, 56, 3, 128, 97, 48, 165, - 131, 57, 129, 1, 96, 64, 82, 128, 128, 81, 130, 1, 145, 144, 96, 32, 1, 128, 81, 130, 1, 145, - 144, 96, 32, 1, 128, 81, 130, 1, 145, 144, 96, 32, 1, 128, 81, 130, 1, 145, 144, 96, 32, 1, - 128, 81, 130, 1, 145, 144, 96, 32, 1, 80, 80, 96, 64, 128, 81, 96, 32, 129, 129, 1, 131, 82, - 96, 0, 145, 130, 144, 82, 130, 81, 144, 129, 1, 144, 146, 82, 128, 130, 82, 128, 84, 96, 1, 96, - 160, 96, 2, 10, 3, 25, 22, 51, 23, 144, 85, 96, 2, 128, 84, 96, 1, 129, 1, 128, 131, 85, 130, - 129, 131, 128, 21, 130, 144, 17, 97, 1, 63, 87, 96, 0, 131, 144, 82, 97, 1, 63, 144, 96, 6, - 144, 129, 2, 127, 64, 87, 135, 250, 18, 168, 35, 224, 242, 183, 99, 28, 196, 27, 59, 168, 130, - 139, 51, 33, 202, 129, 17, 17, 250, 117, 205, 58, 163, 187, 90, 206, 144, 129, 1, 145, 132, 2, - 1, 97, 2, 38, 86, 91, 80, 144, 86, 91, 80, 80, 80, 145, 144, 144, 96, 0, 82, 96, 32, 96, 0, 32, - 144, 96, 6, 2, 1, 96, 0, 96, 192, 96, 64, 81, 144, 129, 1, 96, 64, 82, 128, 138, 129, 82, 96, - 32, 1, 137, 129, 82, 96, 32, 1, 136, 129, 82, 96, 32, 1, 135, 129, 82, 96, 32, 1, 96, 64, 96, - 64, 81, 144, 129, 1, 96, 171, 4, 168, 23, 200, 0, 1, 95, 144, 26, 105, 174, 44, 246, 198, 120, - 105, 56, 17, 244, 53, 179, 179, 182, 94, 95, 97, 69, 100, 15, 96, 111, 148, 13, 44, 108, 0, 0, - 84, 104, 125, 80, 2, 160, 178, 173, 241, 243, 54, 206, 177, 236, 24, 52, 94, 240, 201, 117, - 233, 184, 153, 162, 56, 35, 225, 131, 191, 170, 180, 26, 196, 107, 206, 62, 5, 106, 24, 226, - 141, 52, 131, 232, 169, 134, 18, 44, 24, 95, 254, 210, 194, 72, 109, 229, 3, 148, 185, 13, 64, - 185, 249, 77, 166, 76, 4, 2, 213, 232, 4, 168, 23, 200, 0, 1, 95, 144, 22, 62, 208, 32, 85, 45, - 6, 240, 167, 99, 41, 101, 44, 156, 209, 59, 29, 194, 180, 31, 13, 224, 205, 154, 22, 27, 28, - 132, 1, 209, 102, 137, 103, 111, 119, 79, 74, 192, 82, 74, 13, 36, 226, 163, 240, 166, 153, 67, - 50, 103, 98, 26, 6, 187, 188, 240, 141, 54, 84, 29, 30, 206, 213, 71, 169, 112, 249, 108, 36, - 212, 32, 124, 15, 61, 43, 13, 9, 168, 30, 237, 33, 94, 130, 220, 164, 248, 35, 220, 216, 81, - 78, 37, 101, 166, 76, 4, 12, 114, 172, 4, 168, 23, 200, 0, 1, 95, 144, 194, 150, 99, 65, 152, - 204, 230, 120, 158, 239, 247, 73, 244, 217, 157, 78, 122, 245, 146, 176, 15, 95, 146, 248, 166, - 137, 108, 0, 0, 184, 12, 5, 172, 175, 250, 133, 166, 236, 78, 31, 144, 17, 174, 255, 0, 133, - 117, 145, 3, 241, 247, 102, 190, 234, 202, 79, 144, 113, 220, 230, 94, 213, 206, 174, 77, 114, - 216, 250, 4, 79, 108, 162, 196, 13, 60, 78, 207, 222, 239, 109, 171, 5, 77, 211, 64, 13, 119, - 246, 20, 129, 220, 86, 39, 162, 76, 4, 206, 4, 168, 23, 200, 0, 1, 224, 20, 188, 210, 241, 163, - 224, 51, 245, 254, 226, 80, 31, 57, 173, 86, 116, 200, 43, 128, 132, 172, 124, 230, 108, 80, - 226, 132, 0, 0, 1, 14, 88, 86, 68, 26, 230, 241, 202, 50, 91, 229, 232, 240, 66, 5, 52, 136, - 180, 132, 112, 36, 109, 184, 25, 56, 33, 223, 100, 52, 180, 226, 164, 22, 225, 219, 201, 157, - 97, 4, 116, 182, 126, 156, 121, 107, 92, 247, 112, 50, 218, 56, 70, 226, 197, 167, 219, 7, 62, - 54, 73, 31, 202, 151, 91, 166, 76, 4, 12, 114, 173, 4, 168, 23, 200, 0, 1, 95, 144, 46, 212, 2, - 212, 163, 195, 214, 130, 20, 209, 40, 102, 229, 172, 84, 232, 174, 12, 113, 107, 15, 90, 228, - 21, 159, 215, 32, 0, 0, 191, 167, 197, 64, 141, 101, 246, 29, 147, 169, 168, 111, 93, 178, 4, - 83, 116, 100, 40, 214, 251, 85, 161, 213, 253, 249, 0, 171, 7, 208, 24, 93, 230, 9, 84, 174, - 164, 78, 29, 159, 193, 39, 254, 30, 246, 221, 27, 227, 11, 222, 68, 60, 221, 105, 27, 156, 31, - 141, 247, 160, 14, 33, 110, 67, 166, 76, 4, 12, 114, 174, 4, 168, 23, 200, 0, 1, 95, 144, 33, - 208, 47, 23, 47, 150, 85, 27, 31, 11, 232, 194, 192, 185, 51, 163, 104, 83, 28, 211, 15, 78, - 118, 139, 111, 196, 8, 0, 1, 190, 30, 81, 160, 226, 84, 89, 93, 51, 55, 43, 81, 221, 98, 43, - 54, 49, 253, 231, 133, 176, 126, 154, 224, 112, 80, 206, 79, 15, 101, 91, 49, 215, 131, 244, - 89, 3, 222, 156, 170, 231, 52, 21, 59, 80, 118, 156, 33, 129, 191, 78, 240, 210, 102, 105, 172, - 91, 76, 156, 160, 81, 221, 66, 100, 166, 76, 4, 12, 114, 175, 4, 168, 23, 200, 0, 1, 95, 144, - 56, 58, 214, 99, 175, 209, 113, 99, 1, 252, 146, 212, 105, 217, 152, 91, 151, 207, 188, 176, - 15, 77, 31, 0, 81, 75, 116, 0, 1, 28, 69, 182, 216, 207, 128, 8, 23, 237, 50, 122, 196, 50, 61, - 139, 33, 83, 22, 159, 156, 7, 141, 117, 100, 233, 184, 43, 39, 192, 32, 118, 99, 158, 205, 66, - 65, 207, 183, 53, 249, 200, 238, 166, 158, 247, 218, 35, 65, 185, 89, 77, 50, 128, 90, 31, 154, - 210, 157, 49, 157, 170, 158, 13, 45, 166, 76, 4, 2, 213, 233, 4, 168, 23, 200, 0, 1, 95, 144, - 39, 30, 143, 192, 30, 28, 158, 22, 33, 253, 61, 16, 142, 52, 132, 80, 178, 73, 133, 65, 14, 7, - 176, 44, 47, 76, 36, 100, 1, 204, 37, 34, 164, 207, 97, 245, 39, 77, 242, 210, 177, 65, 242, - 181, 229, 35, 13, 230, 254, 144, 47, 196, 139, 173, 220, 44, 196, 243, 104, 134, 195, 49, 62, - 104, 192, 37, 90, 90, 232, 135, 146, 233, 74, 122, 85, 10, 159, 71, 150, 54, 208, 170, 193, 74, - 22, 90, 150, 60, 10, 62, 204, 197, 114, 166, 76, 4, 12, 114, 176, 4, 168, 23, 200, 0, 1, 95, - 144, 119, 29, 208, 38, 129, 199, 147, 235, 52, 239, 243, 69, 40, 48, 158, 54, 87, 248, 67, 251, - 15, 67, 52, 77, 40, 49, 160, 0, 1, 34, 233, 236, 225, 140, 196, 228, 72, 84, 168, 142, 86, 103, - 6, 136, 64, 188, 91, 101, 164, 170, 199, 190, 213, 191, 140, 140, 42, 46, 238, 206, 71, 227, - 246, 100, 17, 55, 56, 122, 12, 134, 14, 196, 197, 143, 23, 178, 197, 76, 61, 94, 25, 59, 221, - 201, 110, 2, 191, 53, 189, 81, 237, 209, 93, 166, 76, 4, 1, 127, 187, 4, 168, 23, 200, 0, 1, - 95, 144, 13, 9, 130, 162, 191, 68, 97, 153, 98, 221, 168, 13, 166, 18, 159, 86, 111, 245, 137, - 128, 68, 177, 238, 198, 22, 47, 0, 0, 1, 69, 242, 159, 108, 110, 191, 93, 192, 147, 25, 6, 55, - 134, 15, 80, 121, 182, 98, 196, 253, 222, 249, 140, 177, 94, 228, 15, 11, 218, 67, 240, 77, - 150, 24, 214, 87, 14, 191, 186, 64, 215, 126, 76, 93, 135, 62, 141, 203, 122, 226, 7, 128, 99, - 215, 32, 219, 65, 204, 36, 146, 35, 220, 211, 46, 166, 76, 4, 12, 114, 177, 4, 168, 23, 200, 0, - 1, 95, 144, 180, 200, 161, 80, 134, 215, 194, 226, 170, 85, 6, 255, 81, 227, 78, 62, 35, 208, - 83, 167, 15, 64, 66, 110, 42, 47, 156, 0, 0, 49, 134, 248, 121, 133, 172, 208, 182, 175, 214, - 31, 109, 165, 25, 165, 176, 166, 204, 156, 198, 172, 179, 57, 70, 246, 52, 156, 168, 86, 214, - 70, 126, 87, 139, 80, 220, 174, 253, 4, 183, 185, 225, 149, 202, 36, 105, 153, 55, 217, 136, - 12, 178, 137, 26, 239, 28, 68, 132, 150, 111, 41, 187, 43, 10, 164, 76, 4, 11, 33, 4, 168, 23, - 200, 0, 1, 95, 144, 226, 85, 66, 126, 222, 130, 50, 31, 15, 54, 90, 206, 240, 193, 227, 48, - 133, 198, 59, 207, 13, 113, 10, 167, 105, 18, 114, 0, 0, 13, 106, 212, 155, 24, 230, 55, 233, - 237, 36, 164, 100, 234, 163, 221, 176, 230, 17, 209, 241, 77, 87, 80, 200, 81, 210, 29, 11, - 152, 176, 4, 39, 177, 198, 158, 129, 78, 143, 236, 123, 48, 83, 47, 142, 189, 235, 5, 242, 199, - 126, 183, 53, 34, 216, 92, 146, 56, 149, 177, 189, 125, 57, 128, 30, 166, 76, 4, 12, 114, 178, - 4, 168, 23, 200, 0, 1, 95, 144, 34, 123, 179, 68, 125, 72, 249, 213, 102, 2, 80, 97, 59, 247, - 192, 176, 33, 166, 47, 39, 15, 63, 24, 185, 181, 207, 200, 0, 0, 150, 99, 217, 210, 20, 108, - 108, 7, 84, 228, 186, 92, 50, 65, 214, 200, 87, 62, 225, 200, 229, 36, 133, 150, 4, 181, 63, - 176, 227, 148, 207, 38, 102, 252, 236, 16, 232, 95, 104, 200, 252, 117, 52, 196, 60, 103, 134, - 182, 58, 72, 141, 118, 159, 162, 154, 31, 229, 93, 238, 62, 89, 53, 231, 33, 166, 76, 4, 12, - 114, 179, 4, 168, 23, 200, 0, 1, 95, 144, 214, 20, 204, 142, 125, 68, 230, 229, 212, 139, 155, - 62, 253, 95, 254, 195, 96, 152, 244, 3, 15, 59, 89, 141, 219, 86, 176, 0, 0, 81, 66, 59, 0, 96, - 212, 254, 129, 156, 102, 119, 230, 135, 103, 200, 170, 192, 199, 182, 63, 177, 79, 77, 79, 242, - 117, 71, 248, 201, 211, 90, 208, 47, 218, 197, 205, 218, 159, 230, 205, 118, 250, 236, 228, 82, - 223, 176, 60, 108, 180, 184, 213, 145, 88, 80, 65, 203, 250, 255, 232, 52, 137, 194, 118, 166, - 76, 4, 12, 114, 180, 4, 168, 23, 200, 0, 1, 95, 144, 234, 252, 38, 85, 150, 138, 100, 23, 200, - 0, 1, 95, 144, 250, 159, 21, 249, 110, 50, 68, 137, 115, 8, 169, 81, 99, 166, 40, 97, 104, 94, - 137, 23, 63, 79, 82, 238, 17, 144, 212, 0, 1, 233, 14, 188, 93, 246, 59, 76, 116, 46, 3, 148, - 183, 227, 196, 65, 137, 100, 201, 195, 39, 205, 170, 58, 0, 159, 62, 22, 224, 194, 41, 41, 160, - 43, 255, 39, 198, 183, 243, 92, 82, 154, 27, 124, 60, 91, 78, 119, 183, 160, 46, 165, 20, 71, - 86, 12, 75, 73, 198, 145, 177, 231, 86, 180, 46, 164, 76, 4, 1, 110, 4, 168, 23, 200, 0, 1, 95, - 144, 49, 8, 180, 105, 254, 139, 108, 173, 165, 94, 149, 108, 9, 160, 117, 39, 110, 26, 129, 48, - 162, 161, 4, 90, 179, 130, 194, 0, 1, 13, 255, 189, 177, 202, 100, 209, 184, 131, 135, 40, 17, - 71, 104, 249, 123, 58, 248, 132, 216, 179, 18, 222, 145, 135, 14, 254, 197, 226, 185, 175, 9, - 137, 34, 46, 245, 164, 117, 204, 44, 199, 250, 18, 156, 122, 187, 54, 100, 240, 134, 194, 240, - 128, 31, 37, 6, 57, 150, 82, 232, 18, 121, 136, 5, 166, 76, 4, 12, 74, 234, 4, 168, 23, 200, 0, - 1, 95, 144, 74, 41, 223, 138, 121, 103, 179, 102, 100, 242, 86, 241, 141, 172, 242, 229, 142, - 159, 30, 126, 63, 68, 90, 70, 29, 205, 120, 0, 0, 112, 226, 176, 173, 96, 231, 96, 110, 90, 90, - 126, 206, 88, 47, 90, 48, 200, 218, 170, 202, 191, 16, 100, 13, 221, 199, 89, 6, 209, 187, 1, - 29, 176, 150, 196, 240, 44, 172, 159, 142, 49, 166, 175, 190, 142, 46, 5, 202, 135, 161, 31, - 75, 76, 152, 131, 201, 140, 26, 77, 21, 142, 77, 251, 74, 166, 76, 4, 12, 74, 235, 4, 168, 23, - 200, 0, 1, 95, 144, 116, 233, 174, 143, 116, 127, 233, 167, 235, 175, 71, 228, 116, 155, 63, - 110, 59, 40, 127, 25, 60, 35, 222, 35, 97, 104, 92, 0, 0, 207, 219, 54, 139, 170, 41, 166, 35, - 241, 32, 97, 82, 34, 244, 25, 63, 248, 183, 10, 99, 96, 119, 232, 113, 250, 243, 56, 88, 53, - 70, 117, 32, 246, 7, 131, 130, 38, 13, 74, 226, 162, 247, 154, 44, 129, 127, 169, 50, 83, 180, - 114, 146, 141, 89, 208, 181, 102, 42, 228, 226, 159, 123, 139, 28, 166, 76, 4, 12, 74, 236, 4, - 168, 23, 200, 0, 1, 95, 144, 114, 41, 192, 63, 252, 218, 5, 219, 29, 189, 1, 160, 140, 236, - 109, 13, 15, 191, 6, 90, 58, 43, 122, 200, 142, 110, 0, 0, 1, 164, 133, 170, 161, 230, 72, 240, - 202, 195, 186, 108, 163, 125, 197, 26, 0, 109, 72, 153, 217, 17, 237, 4, 234, 231, 219, 118, - 57, 203, 132, 13, 205, 32, 249, 1, 8, 40, 156, 172, 7, 242, 225, 59, 230, 69, 218, 183, 116, - 45, 185, 168, 150, 37, 1, 164, 3, 199, 222, 248, 192, 83, 158, 194, 119, 164, 200, 3, 16, 131, - 9, 80, 47, 144, 0, 82, 8, 130, 137, 149, 140, 4, 153, 115, 94, 242, 205, 80, 37, 181, 247, 83, - 180, 110, 176, 71, 84, 10, 100, 119, 217, 230, 45, 227, 0, 133, 32, 31, 202, 43, 29, 90, 51, - 160, 1, 189, 64, 132, 150, 197, 167, 169, 206, 154, 171, 234, 135, 101, 233, 176, 230, 196, - 155, 199, 118, 216, 92, 245, 51, 162, 242, 97, 254, 109, 131, 252, 5, 180, 73, 108, 139, 37, - 52, 152, 26, 136, 11, 1, 225, 116, 195, 41, 41, 43, 240, 105, 42, 226, 116, 162, 72, 4, 1, 4, - 227, 178, 146, 0, 82, 8, 42, 179, 157, 243, 64, 86, 45, 20, 178, 50, 4, 186, 185, 62, 110, 79, - 171, 174, 1, 68, 1, 153, 69, 202, 38, 32, 0, 0, 0, 64, 183, 187, 151, 19, 195, 191, 241, 157, - 206, 245, 26, 166, 11, 83, 27, 197, 80, 164, 248, 229, 214, 66, 160, 141, 73, 89, 37, 159, 98, - 191, 255, 35, 196, 81, 140, 97, 107, 135, 122, 153, 23, 221, 205, 216, 125, 91, 173, 93, 67, - 171, 120, 117, 164, 114, 66, 113, 244, 186, 8, 85, 14, 21, 99, 160, 72, 4, 4, 227, 178, 146, 0, - 82, 8, 125, 199, 242, 178, 117, 5, 28, 150, 123, 89, 103, 149, 119, 41, 182, 33, 198, 198, 13, - 172, 2, 198, 138, 240, 187, 20, 0, 0, 0, 204, 176, 241, 68, 189, 197, 153, 208, 227, 244, 176, - 3, 233, 53, 185, 19, 255, 234, 203, 203, 90, 194, 25, 36, 177, 159, 17, 237, 47, 171, 185, 54, - 215, 208, 0, 37, 66, 21, 233, 14, 2, 181, 38, 91, 146, 54, 61, 64, 47, 82, 115, 208, 198, 196, - 131, 110, 1, 139, 213, 175, 57, 66, 130, 76, 160, 72, 4, 4, 227, 178, 146, 0, 82, 8, 124, 181, - 123, 90, 151, 234, 190, 148, 32, 92, 7, 137, 11, 228, 193, 173, 49, 228, 134, 168, 13, 224, - 182, 179, 167, 100, 0, 0, 0, 109, 143, 15, 22, 49, 183, 53, 97, 153, 81, 82, 72, 246, 135, 218, - 219, 56, 24, 20, 32, 234, 239, 25, 242, 159, 199, 147, 71, 243, 249, 227, 214, 29, 133, 36, - 201, 232, 54, 162, 70, 55, 115, 134, 208, 59, 194, 124, 130, 4, 80, 243, 29, 124, 100, 182, - 232, 0, 192, 152, 77, 228, 60, 148, 108, 164, 204, 3, 47, 37, 4, 168, 23, 200, 0, 1, 95, 144, - 148, 86, 43, 99, 223, 133, 186, 160, 47, 72, 68, 110, 240, 239, 132, 166, 183, 65, 198, 228, - 29, 241, 208, 1, 47, 164, 0, 0, 56, 204, 252, 152, 176, 160, 4, 11, 237, 67, 28, 218, 6, 195, - 75, 219, 239, 214, 123, 216, 83, 49, 198, 131, 16, 254, 69, 202, 205, 149, 87, 222, 7, 47, 55, - 204, 192, 60, 63, 190, 107, 216, 124, 43, 171, 117, 159, 149, 44, 255, 170, 255, 99, 68, 66, - 50, 17, 248, 104, 155, 71, 155, 191, 85, 166, 76, 4, 12, 74, 237, 4, 168, 23, 200, 0, 1, 95, - 144, 179, 48, 89, 142, 64, 75, 247, 119, 149, 66, 231, 157, 155, 240, 208, 241, 112, 23, 67, - 177, 57, 236, 72, 144, 120, 131, 200, 0, 0, 8, 218, 3, 199, 163, 111, 20, 102, 235, 218, 94, - 88, 72, 149, 58, 98, 131, 36, 152, 93, 193, 102, 139, 144, 254, 117, 2, 154, 148, 107, 232, - 107, 221, 90, 64, 234, 125, 124, 21, 141, 19, 16, 7, 40, 192, 245, 59, 237, 93, 59, 2, 2, 159, - 23, 175, 175, 50, 67, 224, 247, 210, 132, 131, 24, 166, 76, 4, 12, 74, 238, 4, 168, 23, 200, 0, - 1, 95, 144, 64, 214, 88, 179, 187, 52, 140, 227, 132, 171, 201, 12, 107, 144, 176, 254, 85, 58, - 14, 59, 56, 155, 245, 207, 183, 131, 128, 0, 0, 116, 100, 130, 146, 67, 42, 94, 229, 142, 105, - 59, 67, 50, 152, 191, 231, 135, 214, 66, 163, 24, 123, 150, 31, 59, 189, 179, 144, 83, 53, 110, - 68, 249, 63, 126, 58, 31, 244, 52, 39, 226, 156, 220, 253, 232, 158, 117, 166, 212, 52, 106, - 68, 153, 47, 170, 251, 171, 149, 12, 88, 60, 74, 170, 42, 166, 76, 4, 12, 74, 239, 4, 168, 23, - 200, 0, 1, 95, 144, 9, 119, 56, 54, 59, 22, 98, 89, 154, 208, 119, 126, 25, 70, 22, 1, 245, - 236, 141, 249, 54, 38, 78, 188, 207, 48, 152, 0, 1, 181, 185, 6, 124, 194, 121, 230, 198, 129, - 94, 151, 69, 133, 155, 120, 212, 244, 87, 172, 134, 50, 242, 170, 5, 198, 181, 187, 58, 66, 92, - 187, 50, 144, 76, 115, 92, 211, 116, 135, 214, 55, 85, 169, 117, 209, 101, 44, 119, 254, 77, - 116, 19, 156, 145, 234, 72, 163, 205, 115, 15, 3, 155, 8, 120, 166, 76, 4, 12, 74, 240, 4, 168, - 23, 200, 0, 1, 95, 144, 208, 223, 36, 253, 13, 252, 220, 56, 80, 209, 89, 13, 170, 29, 3, 202, - 119, 6, 177, 27, 44, 228, 131, 21, 84, 218, 64, 0, 1, 62, 80, 55, 79, 3, 102, 121, 4, 0, 224, - 58, 118, 121, 39, 237, 36, 189, 94, 119, 178, 249, 36, 60, 179, 236, 53, 138, 52, 144, 98, 4, - 3, 93, 146, 237, 156, 161, 189, 88, 203, 91, 62, 101, 110, 29, 133, 173, 101, 85, 167, 130, 73, - 87, 40, 87, 221, 21, 104, 166, 212, 10, 191, 27, 20, 166, 76, 4, 12, 74, 241, 4, 168, 23, 200, - 0, 1, 95, 144, 32, 203, 148, 160, 129, 214, 13, 189, 247, 100, 123, 43, 121, 10, 202, 226, 74, - 0, 45, 75, 35, 10, 199, 159, 0, 0, 0, 0, 0, 0, 69, 99, 145, 130, 68, 244, 0, 0, 0, 206, 210, - 128, 239, 33, 158, 27, 87, 230, 63, 20, 82, 245, 233, 241, 164, 3, 233, 103, 56, 62, 154, 248, - 231, 28, 179, 7, 30, 43, 208, 145, 239, 252, 176, 116, 136, 190, 75, 34, 84, 221, 90, 218, 248, - 41, 177, 2, 187, 71, 43, 196, 196, 84, 60, 167, 12, 198, 216, 61, 214, 178, 218, 19, 7, 164, - 76, 4, 2, 141, 4, 168, 23, 200, 0, 1, 95, 144, 35, 174, 221, 53, 227, 112, 134, 37, 211, 32, - 219, 62, 127, 222, 52, 180, 203, 71, 252, 84, 3, 187, 81, 75, 219, 41, 114, 0, 1, 69, 143, 15, - 102, 247, 24, 39, 115, 220, 147, 233, 168, 117, 92, 135, 122, 130, 99, 252, 199, 116, 75, 190, - 156, 173, 95, 133, 209, 97, 23, 150, 55, 219, 173, 128, 44, 17, 72, 6, 189, 157, 85, 6, 169, - 133, 64, 8, 247, 61, 85, 179, 76, 163, 193, 190, 83, 245, 177, 195, 53, 76, 153, 243, 33, 166, - 76, 4, 12, 45, 218, 4, 168, 23, 200, 0, 1, 95, 144, 161, 173, 95, 210, 86, 24, 72, 162, 235, - 182, 216, 235, 201, 172, 145, 239, 211, 127, 82, 214, 15, 36, 115, 243, 164, 57, 200, 0, 0, - 244, 221, 37, 128, 218, 149, 158, 117, 241, 81, 121, 142, 77, 17, 170, 76, 221, 40, 109, 53, - 133, 176, 86, 178, 140, 149, 76, 167, 1, 25, 192, 1, 221, 61, 76, 83, 159, 165, 58, 152, 20, - 206, 242, 198, 228, 155, 169, 218, 74, 217, 7, 52, 207, 92, 64, 136, 240, 159, 66, 238, 85, - 195, 85, 64, 166, 76, 4, 1, 121, 64, 4, 168, 23, 200, 0, 1, 95, 144, 100, 70, 252, 174, 58, - 105, 239, 112, 8, 22, 36, 211, 118, 33, 29, 186, 113, 145, 77, 176, 51, 133, 115, 20, 144, 163, - 64, 0, 0, 151, 97, 252, 241, 153, 214, 10, 74, 192, 124, 220, 231, 161, 136, 131, 9, 41, 99, - 50, 219, 164, 124, 63, 125, 250, 222, 115, 4, 74, 9, 176, 10, 242, 250, 113, 250, 103, 240, 69, - 128, 62, 168, 133, 27, 84, 37, 72, 195, 145, 246, 106, 40, 168, 92, 157, 21, 251, 36, 243, 56, - 37, 209, 35, 88, 166, 76, 4, 12, 45, 219, 4, 168, 23, 200, 0, 1, 95, 144, 21, 55, 26, 56, 178, - 98, 156, 191, 229, 44, 13, 187, 253, 15, 137, 247, 43, 57, 132, 129, 15, 28, 245, 174, 143, - 166, 184, 0, 1, 180, 170, 204, 110, 217, 109, 8, 9, 114, 129, 213, 245, 88, 59, 89, 90, 129, - 151, 25, 125, 127, 193, 78, 183, 77, 133, 100, 44, 120, 90, 76, 118, 100, 15, 179, 154, 152, - 244, 53, 180, 17, 207, 150, 18, 221, 200, 224, 89, 74, 172, 102, 179, 222, 171, 171, 193, 94, - 49, 14, 188, 137, 146, 252, 84, 166, 76, 4, 2, 174, 127, 4, 168, 23, 200, 0, 1, 95, 144, 115, - 239, 213, 228, 12, 9, 105, 246, 212, 28, 3, 106, 56, 191, 8, 36, 175, 124, 212, 216, 13, 242, - 170, 2, 144, 241, 14, 4, 0, 230, 153, 169, 133, 203, 144, 150, 54, 20, 225, 74, 221, 43, 99, - 248, 78, 232, 172, 160, 50, 109, 13, 9, 196, 1, 206, 45, 1, 64, 208, 196, 25, 79, 64, 200, 122, - 85, 82, 163, 109, 7, 111, 134, 219, 71, 205, 230, 61, 20, 102, 88, 29, 8, 177, 214, 225, 201, - 80, 130, 176, 46, 45, 74, 106, 166, 76, 4, 12, 45, 220, 4, 168, 23, 200, 0, 1, 95, 144, 247, - 108, 229, 8, 145, 164, 73, 48, 239, 109, 51, 207, 216, 21, 219, 75, 241, 56, 45, 196, 15, 27, - 6, 97, 82, 39, 44, 0, 1, 56, 40, 235, 164, 209, 115, 227, 217, 248, 84, 102, 119, 166, 153, - 148, 170, 36, 163, 50, 20, 83, 18, 41, 115, 115, 229, 252, 13, 32, 66, 136, 211, 163, 13, 153, - 218, 181, 230, 232, 64, 11, 62, 10, 180, 240, 150, 244, 122, 57, 152, 71, 67, 197, 194, 238, - 86, 152, 30, 21, 239, 59, 29, 113, 121, 166, 76, 4, 12, 45, 221, 4, 168, 23, 200, 0, 1, 95, - 144, 11, 160, 164, 80, 17, 47, 162, 233, 62, 246, 145, 149, 152, 186, 204, 208, 233, 162, 114, - 243, 15, 17, 122, 38, 71, 129, 68, 0, 1, 130, 249, 109, 58, 171, 96, 157, 172, 250, 116, 39, - 151, 178, 59, 252, 90, 15, 25, 140, 105, 69, 223, 251, 128, 171, 156, 213, 153, 175, 119, 175, - 16, 76, 185, 18, 245, 157, 83, 86, 80, 55, 59, 129, 25, 198, 143, 12, 219, 100, 48, 107, 174, - 118, 195, 166, 137, 53, 109, 68, 163, 35, 19, 151, 108, 166, 76, 4, 12, 45, 222, 4, 168, 23, - 200, 0, 1, 95, 144, 92, 218, 191, 94, 53, 198, 44, 66, 109, 249, 204, 237, 232, 225, 225, 146, - 253, 25, 36, 50, 15, 17, 59, 110, 71, 51, 132, 0, 0, 103, 222, 182, 182, 38, 241, 97, 211, 16, - 191, 126, 154, 108, 255, 4, 113, 148, 81, 231, 93, 139, 192, 13, 17, 227, 97, 55, 1, 119, 10, - 40, 82, 210, 181, 24, 104, 235, 22, 188, 18, 129, 103, 142, 111, 110, 145, 143, 166, 8, 191, - 149, 114, 140, 156, 90, 253, 255, 51, 250, 118, 174, 144, 66, 78, 166, 76, 4, 12, 45, 223, 4, - 168, 23, 200, 0, 1, 95, 144, 13, 98, 230, 102, 214, 129, 232, 190, 150, 144, 58, 196, 171, 211, - 83, 127, 40, 198, 18, 90, 15, 16, 21, 139, 182, 85, 192, 0, 1, 175, 201, 145, 67, 12, 59, 84, - 93, 178, 202, 249, 52, 12, 39, 91, 59, 51, 131, 86, 75, 163, 64, 84, 89, 6, 108, 136, 92, 254, - 133, 204, 38, 77, 97, 222, 132, 7, 226, 23, 224, 104, 36, 21, 31, 242, 136, 74, 242, 54, 211, - 216, 118, 67, 211, 237, 233, 6, 200, 15, 49, 166, 165, 115, 105, 166, 76, 4, 12, 45, 224, 4, - 168, 23, 200, 0, 1, 95, 144, 175, 214, 128, 49, 65, 40, 188, 46, 20, 99, 127, 69, 16, 106, 73, - 163, 72, 228, 117, 14, 15, 16, 6, 49, 135, 238, 96, 0, 1, 107, 175, 107, 23, 25, 6, 8, 63, 197, - 237, 209, 72, 211, 128, 152, 235, 19, 248, 224, 81, 166, 196, 142, 187, 89, 142, 66, 254, 39, - 130, 118, 52, 167, 190, 150, 148, 187, 90, 100, 242, 57, 84, 26, 31, 132, 71, 13, 228, 228, - 164, 85, 185, 204, 29, 154, 15, 44, 84, 185, 10, 220, 242, 136, 99, 164, 76, 0, 12, 60, 11, - 164, 59, 116, 0, 15, 66, 64, 169, 94, 232, 240, 34, 215, 170, 225, 135, 26, 128, 102, 138, 228, - 167, 54, 172, 50, 238, 92, 102, 184, 123, 42, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 45, 165, 101, - 149, 215, 13, 59, 223, 113, 2, 243, 93, 229, 39, 232, 251, 115, 104, 112, 164, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 182, 66, 48, 135, 212, 233, 248, 237, 133, 231, 251, 185, 245, 89, 4, 28, - 46, 100, 66, 214, 0, 154, 201, 175, 14, 64, 121, 155, 50, 173, 112, 199, 31, 61, 139, 84, 218, - 29, 72, 187, 23, 207, 61, 62, 202, 120, 204, 113, 159, 229, 1, 32, 185, 67, 121, 50, 98, 81, - 250, 75, 235, 133, 28, 113, 106, 204, 8, 187, 216, 215, 196, 112, 58, 0, 102, 45, 134, 84, 109, - 0, 232, 166, 148, 250, 28, 164, 76, 4, 178, 161, 4, 168, 23, 200, 0, 1, 95, 144, 185, 168, 37, - 28, 9, 169, 20, 251, 1, 177, 13, 17, 115, 29, 16, 167, 108, 230, 236, 187, 28, 35, 174, 57, - 173, 218, 119, 0, 1, 157, 251, 246, 39, 21, 7, 125, 184, 97, 199, 123, 162, 73, 34, 82, 22, - 237, 179, 39, 61, 76, 63, 95, 97, 75, 176, 240, 205, 101, 128, 47, 6, 185, 69, 250, 87, 150, - 106, 70, 35, 237, 30, 140, 133, 63, 255, 79, 119, 157, 13, 77, 93, 6, 232, 98, 78, 128, 117, - 122, 183, 178, 69, 169, 43, 164, 72, 4, 1, 120, 4, 168, 23, 200, 0, 82, 8, 113, 128, 235, 57, - 166, 38, 73, 56, 253, 179, 239, 253, 115, 65, 196, 114, 124, 56, 33, 83, 4, 76, 166, 19, 141, - 135, 30, 0, 0, 48, 187, 12, 215, 91, 47, 139, 58, 103, 83, 114, 99, 245, 145, 95, 27, 91, 209, - 223, 123, 40, 248, 180, 83, 145, 197, 23, 253, 111, 145, 15, 48, 131, 145, 218, 120, 192, 120, - 113, 159, 13, 253, 241, 174, 194, 147, 7, 220, 27, 0, 0, 0, 0, 0, 0, 0, 32, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 19, 83, 73, 77, 32, - 69, 116, 104, 101, 114, 101, 117, 109, 32, 67, 111, 117, 114, 115, 101, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 1, 180, 109, 143, 38, 172, 228, 56, 153, 109, 185, 216, 214, 145, 20, 141, 185, - 186, 44, 64, 6, 149, 223, 144, 43, 180, 30, 193, 248, 75, 176, 146, 193, 78, 232, 219, 122, - 252, 178, 201, 228, 162, 67, 40, 227, 88, 171, 10, 137, 167, 228, 161, 31, 200, 126, 200, 63, - 166, 30, 202, 149, 158, 245, 154, 94, 162, 72, 4, 6, 7, 127, 221, 152, 128, 82, 8, 174, 150, - 50, 171, 240, 208, 201, 227, 35, 250, 254, 160, 40, 24, 154, 16, 251, 40, 126, 66, 23, 213, - 102, 164, 150, 168, 100, 0, 1, 151, 100, 127, 209, 247, 240, 101, 230, 2, 29, 173, 42, 2, 138, - 121, 160, 165, 95, 84, 115, 107, 17, 220, 26, 148, 197, 87, 130, 68, 74, 210, 107, 29, 20, 43, - 209, 222, 160, 66, 151, 0, 80, 27, 33, 52, 102, 2, 79, 6, 198, 149, 222, 26, 240, 70, 71, 133, - 122, 192, 56, 233, 157, 101, 117, 162, 72, 4, 10, 4, 168, 23, 200, 0, 82, 8, 208, 42, 142, 11, - 250, 226, 44, 57, 249, 212, 230, 250, 223, 157, 70, 252, 122, 137, 115, 143, 1, 147, 156, 51, - 91, 24, 160, 0, 1, 150, 246, 194, 238, 229, 133, 116, 107, 117, 217, 15, 36, 198, 47, 133, 186, - 36, 226, 114, 76, 12, 248, 207, 38, 195, 178, 91, 32, 238, 92, 122, 118, 229, 88, 89, 231, 237, - 175, 22, 12, 35, 99, 250, 70, 180, 141, 239, 114, 167, 38, 168, 205, 235, 17, 6, 246, 65, 27, - 90, 128, 14, 143, 182, 111, 162, 76, 0, 2, 4, 168, 23, 200, 0, 2, 155, 173, 187, 155, 194, 68, - 215, 152, 18, 63, 222, 120, 63, 204, 28, 114, 211, 187, 140, 24, 148, 19, 201, 210, 122, 254, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 17, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 1, 0, 128, 21, 3, 205, 109, 108, 173, 35, 20, 66, 153, 206, 37, 81, 58, 31, 23, 210, 103, - 146, 156, 95, 115, 157, 189, 10, 156, 39, 183, 206, 80, 6, 193, 11, 122, 2, 87, 40, 26, 136, - 225, 164, 158, 104, 168, 58, 205, 198, 54, 42, 236, 31, 170, 37, 84, 28, 149, 196, 57, 33, 35, - 2, 166, 110, 164, 76, 4, 10, 165, 4, 168, 23, 200, 0, 1, 95, 144, 0, 255, 189, 162, 240, 219, - 122, 181, 58, 91, 59, 135, 97, 10, 28, 16, 235, 194, 63, 213, 29, 36, 178, 223, 172, 82, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 189, 195, 131, 112, 91, 114, - 131, 132, 255, 48, 49, 59, 205, 228, 34, 103, 187, 52, 138, 222, 142, 181, 149, 181, 199, 22, - 158, 149, 84, 140, 239, 119, 240, 45, 252, 93, 33, 26, 211, 168, 246, 168, 10, 7, 113, 144, 69, - 181, 27, 153, 98, 134, 73, 254, 24, 141, 8, 130, 222, 17, 130, 20, 229, 18, 166, 76, 4, 2, 129, - 142, 4, 168, 23, 200, 0, 1, 95, 144, 27, 158, 146, 5, 146, 229, 85, 126, 55, 105, 49, 154, 127, - 181, 107, 185, 66, 85, 174, 125, 14, 92, 253, 254, 131, 210, 238, 248, 1, 191, 43, 1, 191, 248, - 127, 254, 22, 102, 235, 106, 240, 86, 241, 13, 160, 148, 228, 83, 219, 98, 74, 28, 226, 89, - 214, 201, 125, 76, 235, 54, 35, 21, 33, 159, 218, 118, 199, 191, 3, 207, 18, 37, 93, 242, 137, - 100, 234, 32, 104, 191, 171, 30, 170, 77, 47, 74, 252, 21, 224, 35, 193, 143, 107, 166, 76, 4, - 2, 129, 143, 4, 168, 23, 200, 0, 1, 95, 144, 146, 116, 26, 169, 168, 198, 85, 247, 85, 165, - 136, 160, 48, 60, 169, 146, 156, 132, 49, 174, 14, 17, 112, 49, 249, 123, 96, 140, 1, 244, 65, - 66, 174, 205, 187, 67, 132, 51, 193, 38, 101, 197, 220, 72, 109, 34, 48, 181, 187, 199, 17, - 230, 0, 225, 98, 45, 7, 8, 2, 202, 194, 254, 92, 50, 3, 175, 195, 161, 32, 232, 213, 248, 93, - 50, 5, 139, 111, 236, 147, 165, 139, 35, 212, 194, 119, 225, 207, 86, 58, 183, 126, 29, 49, - 166, 76, 4, 2, 129, 144, 4, 168, 23, 200, 0, 1, 95, 144, 125, 209, 198, 229, 133, 208, 240, 20, - 218, 56, 175, 52, 97, 25, 81, 205, 156, 182, 11, 29, 13, 233, 234, 208, 199, 183, 10, 100, 0, - 191, 191, 207, 226, 112, 87, 18, 181, 1, 26, 150, 247, 215, 187, 183, 197, 242, 194, 194, 236, - 204, 16, 79, 51, 211, 150, 63, 194, 170, 224, 22, 160, 26, 156, 160, 86, 178, 185, 163, 122, - 240, 39, 125, 119, 165, 202, 148, 34, 154, 101, 244, 178, 175, 47, 199, 225, 36, 82, 161, 52, - 80, 11, 59, 77, 164, 76, 4, 7, 184, 4, 168, 23, 200, 0, 1, 95, 144, 208, 182, 202, 25, 136, - 219, 157, 174, 244, 112, 66, 93, 107, 77, 236, 252, 203, 135, 59, 113, 3, 34, 65, 57, 215, 30, - 176, 0, 0, 18, 85, 125, 248, 19, 134, 17, 234, 221, 45, 252, 180, 1, 241, 195, 244, 133, 155, - 255, 161, 222, 127, 206, 181, 185, 64, 89, 164, 129, 138, 100, 210, 147, 164, 166, 94, 142, - 145, 42, 64, 156, 213, 194, 160, 34, 140, 16, 84, 166, 48, 0, 91, 188, 47, 14, 29, 202, 149, - 144, 50, 225, 151, 205, 117, 166, 76, 4, 2, 129, 145, 4, 168, 23, 200, 0, 1, 95, 144, 39, 199, - 73, 163, 248, 46, 123, 167, 232, 236, 166, 171, 155, 254, 225, 150, 32, 128, 119, 43, 13, 225, - 192, 112, 31, 32, 86, 224, 1, 249, 89, 171, 20, 132, 141, 167, 120, 219, 84, 191, 78, 201, 60, - 251, 133, 57, 41, 35, 10, 87, 107, 246, 49, 243, 75, 192, 86, 203, 246, 248, 214, 174, 150, - 162, 111, 57, 35, 49, 144, 246, 195, 37, 62, 219, 93, 164, 123, 134, 1, 245, 190, 139, 28, 97, - 104, 78, 175, 197, 192, 184, 14, 28, 94, 164, 204, 4, 93, 130, 4, 168, 23, 200, 0, 3, 208, 144, - 120, 51, 237, 24, 141, 89, 160, 187, 192, 39, 83, 232, 216, 181, 57, 206, 204, 47, 112, 227, 4, - 75, 108, 101, 229, 36, 99, 248, 0, 1, 45, 229, 144, 111, 255, 164, 133, 140, 108, 4, 250, 81, - 180, 148, 157, 147, 83, 239, 127, 237, 190, 230, 235, 55, 184, 178, 110, 121, 233, 214, 57, - 211, 73, 166, 230, 109, 159, 232, 58, 21, 130, 202, 237, 215, 126, 231, 51, 233, 146, 26, 31, - 49, 144, 88, 140, 142, 180, 166, 147, 221, 146, 62, 112, 29, 162, 72, 4, 1, 4, 168, 23, 200, 0, - 207, 8, 8, 142, 111, 22, 35, 109, 177, 214, 86, 210, 198, 118, 7, 54, 107, 88, 239, 144, 140, - 125, 13, 95, 213, 32, 61, 204, 44, 168, 1, 201, 175, 241, 116, 162, 94, 251, 100, 177, 58, 157, - 208, 74, 149, 122, 14, 235, 137, 63, 42, 106, 1, 90, 5, 67, 12, 0, 173, 230, 95, 223, 231, 87, - 27, 216, 180, 11, 67, 102, 239, 207, 93, 86, 122, 140, 199, 43, 43, 4, 127, 25, 103, 45, 67, 9, - 211, 194, 133, 60, 219, 184, 179, 179, 72, 160, 72, 4, 15, 34, 77, 74, 0, 82, 8, 87, 177, 116, - 131, 156, 189, 10, 80, 59, 157, 252, 182, 85, 228, 244, 177, 180, 123, 50, 150, 1, 142, 194, - 189, 134, 250, 80, 0, 1, 114, 212, 177, 50, 157, 62, 196, 192, 96, 19, 40, 160, 211, 82, 231, - 108, 161, 30, 215, 93, 226, 61, 234, 3, 100, 63, 252, 83, 138, 32, 70, 193, 8, 138, 169, 12, - 219, 154, 17, 183, 124, 104, 14, 10, 228, 72, 38, 160, 206, 194, 246, 212, 68, 225, 220, 113, - 178, 151, 76, 104, 142, 135, 133, 31, 166, 76, 4, 2, 129, 146, 4, 168, 23, 200, 0, 1, 95, 144, - 219, 69, 17, 28, 201, 195, 4, 142, 239, 165, 37, 253, 185, 119, 242, 234, 164, 12, 214, 224, - 31, 241, 141, 21, 166, 76, 4, 3, 23, 39, 4, 168, 23, 200, 0, 1, 95, 144, 221, 121, 114, 144, - 40, 224, 192, 129, 149, 23, 41, 104, 216, 206, 141, 83, 159, 220, 193, 234, 1, 122, 97, 10, - 192, 69, 224, 0, 1, 111, 222, 210, 211, 164, 123, 102, 112, 137, 79, 184, 174, 87, 44, 17, 4, - 143, 22, 33, 72, 68, 180, 201, 252, 50, 50, 131, 240, 92, 6, 23, 138, 107, 224, 129, 162, 176, - 123, 91, 8, 213, 124, 89, 96, 156, 26, 236, 5, 164, 223, 170, 141, 29, 53, 114, 144, 240, 206, - 27, 73, 55, 236, 12, 66, 166, 76, 4, 3, 23, 40, 4, 168, 23, 200, 0, 1, 95, 144, 179, 255, 205, - 218, 5, 87, 223, 117, 156, 7, 125, 109, 103, 255, 36, 198, 70, 237, 206, 98, 1, 125, 127, 99, - 41, 105, 12, 0, 0, 8, 58, 253, 38, 73, 109, 251, 46, 194, 2, 166, 203, 109, 9, 20, 193, 212, - 42, 97, 186, 7, 33, 245, 50, 250, 98, 171, 196, 128, 46, 37, 86, 232, 253, 189, 238, 87, 40, - 131, 225, 17, 93, 32, 235, 110, 68, 111, 165, 40, 152, 37, 147, 199, 110, 106, 140, 198, 65, - 183, 199, 96, 50, 114, 100, 166, 76, 4, 3, 23, 41, 4, 168, 23, 200, 0, 1, 95, 144, 82, 41, 3, - 99, 237, 85, 162, 231, 172, 44, 53, 121, 191, 62, 35, 84, 61, 91, 102, 95, 1, 135, 103, 14, - 255, 11, 76, 0, 0, 148, 236, 212, 242, 89, 225, 143, 236, 75, 116, 185, 55, 207, 215, 3, 190, - 241, 171, 166, 65, 1, 227, 194, 21, 72, 125, 226, 92, 28, 98, 156, 52, 169, 161, 27, 173, 147, - 145, 242, 8, 253, 238, 166, 126, 108, 56, 182, 97, 172, 58, 103, 241, 16, 237, 144, 53, 12, 97, - 225, 229, 31, 21, 228, 126, 166, 76, 4, 3, 23, 42, 4, 168, 23, 200, 0, 1, 95, 144, 228, 202, - 11, 219, 92, 43, 180, 199, 0, 65, 252, 20, 16, 249, 74, 150, 133, 248, 51, 23, 1, 185, 128, - 143, 214, 176, 248, 0, 1, 61, 237, 135, 28, 115, 90, 244, 88, 165, 97, 225, 172, 132, 178, 87, - 244, 112, 169, 130, 125, 23, 249, 168, 24, 102, 94, 99, 51, 180, 82, 114, 203, 161, 102, 58, - 109, 162, 176, 152, 189, 36, 104, 4, 85, 137, 43, 35, 108, 126, 125, 101, 246, 130, 225, 164, - 75, 162, 179, 235, 178, 165, 240, 41, 127, 166, 76, 4, 3, 23, 43, 4, 168, 23, 200, 0, 1, 95, - 144, 74, 151, 220, 164, 28, 122, 158, 196, 227, 83, 232, 152, 139, 146, 192, 28, 254, 115, 217, - 130, 1, 215, 119, 152, 74, 137, 212, 0, 1, 29, 159, 53, 57, 133, 183, 215, 77, 112, 170, 107, - 168, 100, 159, 16, 176, 9, 43, 126, 219, 167, 47, 74, 223, 147, 254, 77, 241, 197, 109, 230, - 121, 31, 18, 214, 158, 9, 214, 204, 168, 64, 1, 219, 159, 253, 212, 157, 95, 77, 149, 230, 12, - 90, 198, 52, 77, 213, 248, 145, 78, 102, 203, 102, 76, 166, 76, 4, 3, 23, 44, 4, 168, 23, 200, - 0, 1, 95, 144, 85, 28, 19, 221, 91, 229, 67, 79, 154, 198, 210, 18, 66, 10, 155, 187, 44, 6, - 51, 103, 1, 238, 4, 56, 175, 64, 192, 0, 1, 147, 69, 15, 201, 217, 107, 236, 143, 68, 250, 211, - 134, 107, 118, 175, 48, 147, 230, 178, 254, 124, 84, 18, 98, 61, 96, 197, 156, 116, 246, 107, - 10, 188, 71, 39, 136, 76, 140, 72, 160, 145, 76, 14, 66, 25, 200, 147, 194, 22, 162, 241, 205, - 96, 255, 180, 49, 95, 69, 112, 100, 78, 243, 143, 108, 166, 76, 4, 3, 23, 45, 4, 168, 23, 200, - 0, 1, 95, 144, 152, 203, 70, 86, 50, 9, 64, 254, 154, 108, 22, 159, 248, 70, 235, 192, 56, 186, - 69, 1, 2, 15, 173, 240, 57, 204, 224, 0, 1, 169, 1, 43, 142, 55, 243, 85, 45, 147, 183, 57, - 148, 154, 255, 218, 128, 123, 189, 221, 153, 243, 222, 121, 142, 106, 22, 69, 169, 74, 253, - 238, 239, 122, 209, 112, 72, 3, 246, 53, 194, 43, 84, 144, 220, 254, 248, 18, 200, 141, 114, - 76, 249, 59, 186, 184, 44, 192, 89, 175, 147, 137, 235, 62, 115, 166, 76, 4, 3, 23, 46, 4, 168, - 23, 200, 0, 1, 95, 144, 239, 235, 232, 210, 192, 216, 142, 7, 127, 35, 15, 243, 144, 71, 199, - 66, 204, 250, 250, 78, 2, 52, 244, 14, 129, 64, 60, 0, 1, 97, 51, 97, 169, 28, 99, 160, 202, - 140, 140, 88, 113, 178, 233, 236, 44, 137, 201, 32, 199, 249, 245, 188, 5, 225, 150, 78, 122, - 202, 65, 118, 12, 42, 126, 152, 96, 222, 50, 39, 194, 58, 197, 170, 32, 161, 236, 48, 86, 49, - 31, 226, 0, 27, 33, 220, 223, 241, 45, 7, 4, 187, 239, 33, 89, 162, 204, 3, 6, 5, 31, 77, 92, - 0, 1, 216, 168, 176, 212, 180, 7, 244, 222, 49, 67, 76, 48, 38, 84, 154, 240, 75, 95, 156, 74, - 134, 55, 177, 162, 188, 46, 197, 0, 0, 1, 29, 136, 10, 210, 195, 25, 79, 118, 116, 213, 84, - 164, 244, 225, 141, 82, 63, 31, 83, 191, 201, 8, 179, 87, 206, 48, 11, 109, 50, 102, 110, 38, - 53, 203, 193, 227, 192, 4, 230, 32, 196, 204, 223, 71, 35, 22, 138, 207, 27, 92, 88, 119, 55, - 112, 140, 222, 167, 3, 140, 49, 157, 42, 109, 36, 160, 72, 4, 4, 227, 178, 146, 0, 82, 8, 221, - 248, 177, 98, 46, 209, 201, 122, 110, 107, 40, 76, 27, 104, 201, 131, 245, 94, 131, 191, 12, - 125, 113, 59, 73, 218, 0, 0, 1, 20, 98, 140, 241, 220, 122, 254, 204, 195, 130, 31, 27, 68, - 106, 254, 57, 5, 64, 37, 1, 74, 206, 68, 15, 4, 130, 4, 255, 118, 141, 158, 128, 77, 65, 21, - 84, 3, 122, 93, 96, 196, 244, 220, 97, 88, 227, 204, 60, 40, 59, 213, 23, 196, 109, 57, 224, - 43, 140, 67, 78, 176, 3, 98, 60, 166, 76, 4, 2, 80, 153, 4, 168, 23, 200, 0, 1, 95, 144, 51, - 139, 196, 146, 222, 202, 147, 239, 16, 159, 207, 248, 6, 81, 68, 251, 95, 224, 197, 52, 13, - 225, 201, 68, 225, 58, 54, 116, 0, 174, 221, 91, 142, 211, 53, 41, 34, 74, 31, 84, 138, 187, - 231, 237, 111, 183, 11, 106, 201, 191, 217, 181, 153, 202, 135, 97, 194, 88, 194, 251, 133, - 185, 77, 107, 203, 155, 200, 201, 196, 182, 70, 20, 8, 79, 176, 101, 32, 239, 248, 113, 93, 54, - 117, 206, 23, 4, 75, 69, 77, 54, 236, 107, 59, 164, 76, 0, 10, 170, 4, 168, 23, 200, 0, 3, 208, - 144, 236, 159, 108, 150, 52, 22, 95, 145, 226, 46, 88, 185, 14, 62, 222, 57, 61, 149, 158, 71, - 126, 218, 231, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16, 132, 176, 185, 240, 207, 120, 214, - 134, 239, 125, 218, 134, 183, 200, 158, 203, 146, 205, 218, 1, 64, 45, 54, 166, 89, 13, 8, 148, - 58, 147, 195, 142, 217, 202, 183, 153, 196, 87, 241, 179, 96, 237, 112, 215, 234, 120, 113, - 138, 119, 79, 246, 223, 68, 143, 12, 169, 158, 154, 188, 59, 245, 254, 218, 120, 192, 105, 157, - 67, 132, 159, 175, 137, 127, 133, 217, 193, 230, 219, 20, 158, 166, 105, 249, 84, 166, 76, 4, - 2, 80, 154, 4, 168, 23, 200, 0, 1, 95, 144, 31, 68, 2, 187, 46, 18, 23, 207, 180, 154, 18, 93, - 56, 147, 179, 32, 52, 250, 115, 141, 13, 228, 185, 53, 108, 139, 30, 104, 0, 106, 132, 158, - 173, 94, 10, 201, 165, 106, 2, 194, 146, 216, 113, 47, 23, 92, 38, 56, 218, 24, 160, 62, 96, - 252, 254, 106, 166, 147, 236, 240, 254, 81, 220, 147, 40, 84, 165, 14, 48, 244, 178, 83, 228, - 110, 170, 7, 96, 170, 123, 74, 83, 246, 219, 180, 76, 71, 118, 10, 93, 94, 87, 182, 96, 166, - 76, 4, 3, 23, 47, 4, 168, 23, 200, 0, 1, 95, 144, 174, 221, 41, 180, 156, 90, 58, 250, 229, - 114, 147, 94, 40, 58, 181, 234, 11, 76, 142, 46, 2, 122, 203, 49, 205, 187, 92, 0, 0, 15, 134, - 37, 189, 206, 214, 133, 240, 172, 205, 46, 195, 206, 198, 169, 196, 55, 135, 63, 37, 68, 151, - 30, 241, 239, 153, 231, 163, 21, 97, 29, 122, 41, 237, 73, 119, 19, 86, 78, 51, 155, 4, 92, - 173, 83, 148, 170, 185, 120, 66, 142, 91, 29, 99, 40, 25, 237, 249, 151, 60, 67, 5, 15, 10, 76, - 111, 2, 81, 120, 0, 1, 173, 224, 50, 210, 177, 246, 101, 203, 89, 69, 59, 158, 191, 45, 163, - 170, 106, 180, 85, 14, 94, 52, 199, 150, 113, 152, 87, 188, 151, 171, 167, 94, 125, 220, 208, - 149, 0, 84, 154, 218, 174, 243, 249, 159, 112, 117, 127, 87, 236, 30, 164, 197, 127, 177, 221, - 201, 62, 189, 237, 193, 60, 38, 107, 42, 166, 76, 4, 11, 72, 15, 4, 168, 23, 200, 0, 1, 95, - 144, 109, 222, 7, 88, 56, 112, 6, 59, 147, 145, 166, 243, 84, 248, 143, 189, 45, 238, 245, 233, - 15, 9, 249, 227, 76, 194, 40, 0, 1, 110, 88, 60, 135, 149, 221, 244, 231, 52, 104, 232, 202, 4, - 112, 49, 238, 249, 20, 30, 217, 190, 13, 238, 190, 65, 220, 100, 37, 195, 7, 56, 68, 82, 69, - 241, 164, 13, 180, 71, 193, 168, 10, 3, 151, 53, 11, 48, 204, 51, 236, 135, 122, 248, 7, 138, - 35, 199, 85, 109, 10, 62, 179, 205, 25, 166, 76, 4, 11, 72, 16, 4, 168, 23, 200, 0, 1, 95, 144, - 197, 190, 253, 241, 114, 19, 78, 240, 225, 160, 242, 25, 125, 249, 102, 217, 135, 231, 171, - 223, 15, 6, 78, 29, 185, 91, 196, 0, 0, 158, 49, 87, 197, 206, 132, 1, 22, 137, 109, 77, 32, - 48, 16, 239, 33, 254, 146, 99, 122, 18, 90, 25, 29, 131, 244, 118, 32, 34, 149, 51, 173, 75, - 228, 160, 32, 129, 242, 161, 218, 190, 221, 52, 73, 81, 196, 59, 61, 155, 25, 1, 231, 160, 62, - 161, 128, 228, 94, 240, 252, 65, 132, 223, 100, 166, 76, 4, 11, 72, 17, 4, 168, 23, 200, 0, 1, - 95, 144, 48, 55, 67, 195, 84, 193, 202, 100, 105, 221, 189, 13, 203, 211, 103, 192, 7, 85, 226, - 24, 14, 254, 56, 73, 190, 199, 84, 0, 1, 35, 9, 227, 105, 156, 31, 27, 92, 95, 131, 81, 166, - 245, 169, 158, 95, 178, 186, 205, 58, 105, 132, 156, 81, 17, 255, 58, 100, 15, 213, 244, 65, - 32, 20, 156, 155, 174, 173, 182, 32, 185, 39, 242, 195, 60, 213, 147, 210, 167, 26, 82, 225, - 146, 162, 36, 102, 3, 212, 121, 155, 82, 146, 28, 43, 166, 76, 4, 11, 72, 18, 4, 168, 23, 200, - 0, 1, 95, 144, 101, 137, 210, 43, 84, 100, 142, 93, 103, 220, 0, 142, 189, 12, 131, 109, 61, - 243, 156, 135, 14, 248, 241, 222, 206, 55, 84, 0, 0, 3, 0, 60, 121, 223, 91, 124, 227, 29, 134, - 253, 179, 181, 43, 35, 156, 206, 26, 131, 55, 186, 40, 127, 186, 54, 193, 91, 40, 170, 153, - 125, 13, 6, 218, 148, 165, 202, 66, 190, 141, 16, 108, 176, 40, 159, 141, 95, 233, 89, 91, 247, - 130, 150, 120, 145, 144, 69, 186, 242, 150, 81, 150, 225, 109, 166, 76, 4, 1, 101, 83, 4, 168, - 23, 200, 0, 1, 95, 144, 149, 55, 123, 25, 242, 62, 182, 129, 37, 253, 125, 84, 206, 4, 87, 214, - 246, 111, 189, 62, 70, 216, 248, 103, 66, 44, 168, 0, 1, 228, 250, 186, 39, 42, 49, 117, 230, - 142, 79, 46, 66, 13, 115, 89, 105, 122, 77, 187, 207, 225, 222, 166, 66, 104, 213, 143, 99, - 110, 223, 155, 183, 192, 87, 83, 94, 185, 53, 245, 132, 12, 74, 167, 81, 117, 255, 16, 167, - 152, 137, 246, 26, 54, 232, 151, 61, 224, 243, 53, 75, 91, 155, 102, 81, 166, 76, 4, 11, 72, - 19, 4, 168, 23, 200, 0, 1, 95, 144, 20, 196, 130, 149, 39, 77, 102, 223, 249, 79, 200, 21, 0, - 109, 201, 16, 141, 138, 59, 138, 14, 247, 231, 229, 207, 229, 228, 0, 1, 65, 74, 13, 13, 56, - 47, 101, 202, 252, 130, 246, 69, 184, 112, 45, 56, 188, 84, 77, 77, 212, 232, 50, 163, 143, - 199, 93, 186, 75, 174, 189, 208, 45, 196, 88, 81, 58, 94, 139, 181, 223, 185, 89, 103, 45, 92, - 185, 245, 10, 191, 253, 213, 111, 125, 66, 58, 227, 10, 125, 225, 139, 117, 82, 109, 166, 76, - 4, 2, 59, 208, 4, 168, 23, 200, 0, 1, 95, 144, 67, 192, 160, 74, 88, 41, 13, 126, 45, 164, 138, - 178, 224, 140, 70, 243, 143, 35, 168, 124, 1, 101, 88, 42, 147, 66, 110, 100, 0, 194, 48, 80, - 56, 134, 178, 234, 56, 168, 48, 175, 110, 45, 235, 9, 117, 132, 152, 178, 28, 141, 226, 252, - 88, 138, 205, 161, 232, 26, 13, 57, 90, 82, 190, 140, 28, 31, 54, 126, 43, 44, 97, 119, 221, - 89, 19, 36, 97, 94, 64, 39, 88, 49, 128, 25, 210, 0, 90, 241, 192, 156, 220, 207, 22, 166, 76, - 4, 11, 72, 20, 4, 168, 23, 200, 0, 1, 95, 144, 43, 23, 100, 33, 238, 187, 243, 3, 250, 148, - 173, 79, 231, 131, 98, 250, 158, 76, 166, 147, 14, 247, 214, 6, 176, 160, 208, 0, 0, 248, 40, - 68, 95, 56, 52, 231, 42, 197, 239, 191, 73, 43, 50, 212, 238, 114, 118, 50, 112, 10, 224, 216, - 108, 7, 172, 235, 87, 156, 11, 72, 69, 104, 13, 24, 104, 219, 4, 212, 65, 55, 127, 201, 143, - 108, 58, 107, 1, 100, 27, 166, 106, 167, 190, 152, 168, 196, 162, 74, 206, 242, 155, 47, 7, - 164, 76, 4, 168, 46, 4, 168, 23, 200, 0, 1, 95, 144, 24, 243, 216, 72, 136, 176, 34, 187, 187, - 37, 199, 251, 217, 74, 217, 238, 19, 39, 176, 22, 13, 243, 198, 208, 90, 12, 56, 0, 0, 155, 75, - 196, 201, 153, 102, 113, 97, 161, 249, 189, 213, 235, 203, 23, 200, 140, 126, 81, 243, 125, - 218, 103, 1, 182, 245, 248, 206, 237, 138, 107, 19, 54, 126, 172, 129, 99, 39, 157, 143, 129, - 98, 222, 84, 67, 174, 75, 15, 138, 193, 77, 236, 192, 131, 9, 253, 239, 223, 109, 16, 131, 156, - 71, 115, 162, 72, 4, 202, 4, 168, 23, 200, 0, 82, 8, 2, 123, 238, 252, 186, 215, 130, 250, 246, - 159, 173, 18, 222, 233, 126, 216, 148, 198, 133, 73, 15, 146, 131, 148, 68, 184, 68, 0, 1, 127, - 103, 147, 59, 189, 164, 33, 184, 195, 240, 150, 167, 35, 166, 163, 76, 133, 183, 172, 53, 53, - 253, 19, 221, 183, 83, 167, 207, 87, 17, 89, 53, 159, 76, 38, 252, 38, 158, 112, 30, 202, 202, - 185, 186, 54, 191, 171, 138, 170, 204, 35, 197, 21, 188, 154, 52, 249, 67, 209, 36, 235, 76, - 154, 18, 164, 76, 4, 168, 47, 4, 168, 23, 200, 0, 1, 95, 144, 100, 147, 233, 48, 76, 78, 207, - 131, 23, 150, 243, 170, 195, 230, 176, 179, 196, 72, 69, 186, 13, 229, 74, 184, 220, 225, 100, - 0, 1, 77, 129, 247, 107, 16, 61, 60, 16, 53, 201, 47, 255, 47, 121, 2, 12, 177, 183, 234, 185, - 174, 255, 249, 69, 211, 153, 10, 98, 111, 64, 213, 96, 197, 160, 90, 154, 1, 114, 74, 237, 214, - 161, 46, 56, 67, 81, 181, 252, 93, 136, 78, 162, 50, 225, 122, 39, 211, 24, 90, 230, 204, 99, - 131, 101, 162, 72, 4, 14, 4, 168, 23, 200, 0, 82, 8, 38, 20, 94, 14, 145, 234, 184, 244, 116, - 159, 255, 17, 197, 192, 239, 237, 254, 236, 47, 9, 15, 233, 87, 116, 246, 37, 216, 0, 0, 152, - 20, 125, 223, 31, 166, 79, 110, 106, 87, 156, 124, 88, 153, 127, 160, 91, 111, 97, 228, 106, 3, - 62, 170, 228, 204, 115, 104, 194, 245, 243, 222, 223, 94, 157, 122, 169, 14, 205, 14, 98, 252, - 89, 10, 68, 74, 26, 89, 134, 181, 160, 107, 55, 226, 36, 81, 162, 140, 201, 147, 43, 120, 40, - 110, 166, 76, 4, 11, 72, 21, 4, 168, 23, 200, 0, 1, 95, 144, 161, 143, 182, 151, 167, 144, 49, - 33, 128, 169, 18, 214, 103, 82, 52, 40, 65, 222, 217, 83, 14, 245, 5, 225, 14, 217, 60, 0, 1, - 96, 183, 2, 190, 1, 134, 84, 203, 137, 105, 18, 106, 25, 72, 247, 43, 78, 144, 202, 129, 238, - 15, 140, 172, 69, 192, 122, 171, 197, 109, 171, 105, 77, 32, 21, 92, 50, 167, 51, 230, 128, 6, - 134, 169, 168, 29, 238, 105, 203, 241, 181, 253, 11, 92, 59, 63, 46, 67, 164, 78, 174, 254, - 233, 60, 164, 76, 0, 138, 17, 4, 168, 23, 200, 0, 7, 161, 32, 136, 225, 49, 86, 135, 174, 196, - 138, 114, 120, 108, 107, 59, 63, 7, 82, 8, 182, 39, 19, 125, 36, 42, 229, 0, 0, 0, 168, 23, - 200, 0, 1, 95, 144, 193, 82, 51, 103, 79, 12, 183, 114, 158, 74, 184, 171, 115, 52, 48, 196, 5, - 128, 145, 77, 13, 247, 184, 57, 35, 209, 74, 216, 0, 177, 32, 153, 1, 242, 92, 118, 102, 89, - 54, 203, 66, 170, 217, 219, 194, 222, 86, 101, 104, 113, 187, 105, 117, 214, 234, 248, 120, - 190, 159, 226, 103, 105, 90, 127, 90, 229, 57, 53, 192, 105, 177, 91, 153, 163, 180, 34, 130, - 37, 157, 2, 0, 53, 236, 128, 158, 176, 194, 251, 87, 254, 99, 187, 97, 166, 76, 4, 11, 19, 208, - 4, 168, 23, 200, 0, 1, 95, 144, 183, 76, 153, 87, 9, 20, 37, 103, 185, 130, 173, 94, 164, 173, - 255, 96, 71, 78, 235, 14, 14, 6, 55, 158, 111, 32, 64, 0, 1, 178, 194, 158, 241, 57, 97, 186, - 59, 62, 1, 114, 83, 2, 65, 86, 100, 195, 36, 178, 30, 112, 255, 188, 74, 61, 210, 44, 56, 180, - 191, 78, 48, 236, 247, 42, 91, 51, 15, 77, 78, 64, 163, 147, 219, 76, 238, 94, 169, 213, 170, - 135, 39, 56, 247, 157, 228, 156, 3, 35, 126, 112, 128, 150, 98, 166, 76, 4, 2, 35, 248, 4, 168, - 23, 200, 0, 1, 95, 144, 157, 138, 45, 4, 227, 204, 24, 165, 148, 255, 135, 179, 11, 95, 198, - 248, 48, 111, 103, 153, 14, 7, 187, 122, 111, 224, 216, 44, 1, 200, 110, 78, 232, 92, 113, 234, - 189, 39, 196, 141, 103, 63, 43, 132, 106, 246, 89, 195, 42, 65, 101, 70, 187, 176, 161, 54, - 193, 46, 52, 117, 47, 114, 208, 37, 62, 73, 17, 240, 235, 166, 183, 220, 235, 165, 224, 151, - 26, 120, 76, 35, 237, 125, 146, 205, 221, 153, 153, 62, 204, 140, 61, 12, 57, 166, 204, 4, 1, - 56, 107, 6, 252, 35, 172, 0, 5, 22, 21, 236, 101, 107, 220, 80, 105, 47, 15, 47, 208, 246, 81, - 11, 143, 63, 161, 59, 237, 178, 167, 5, 107, 163, 215, 58, 243, 79, 0, 0, 0, 132, 71, 60, 200, - 89, 104, 150, 167, 52, 130, 119, 169, 244, 85, 84, 2, 106, 108, 199, 116, 187, 31, 81, 85, 239, - 56, 145, 129, 11, 121, 254, 64, 17, 185, 223, 225, 170, 255, 38, 29, 173, 235, 227, 103, 180, - 73, 175, 238, 25, 18, 72, 80, 214, 2, 96, 55, 212, 177, 231, 153, 175, 21, 74, 44, 160, 72, 4, - 4, 168, 23, 200, 0, 207, 8, 150, 252, 69, 83, 160, 12, 17, 124, 91, 11, 237, 149, 13, 214, 37, - 209, 193, 109, 200, 148, 1, 79, 100, 103, 221, 255, 174, 192, 0, 161, 227, 88, 220, 82, 126, - 177, 129, 100, 248, 223, 181, 143, 113, 222, 53, 46, 244, 4, 44, 58, 26, 148, 252, 117, 163, - 129, 43, 122, 32, 228, 252, 109, 155, 101, 87, 194, 160, 111, 175, 205, 156, 70, 29, 184, 181, - 4, 81, 161, 111, 32, 122, 64, 159, 254, 135, 96, 114, 106, 129, 66, 158, 15, 85, 166, 76, 4, - 11, 19, 209, 4, 168, 23, 200, 0, 1, 95, 144, 243, 219, 22, 131, 169, 43, 232, 217, 118, 114, - 202, 244, 117, 94, 201, 126, 245, 26, 114, 160, 14, 5, 194, 233, 155, 27, 192, 0, 0, 149, 103, - 110, 136, 129, 33, 57, 38, 20, 66, 155, 170, 75, 3, 99, 81, 126, 3, 9, 108, 143, 67, 246, 18, - 98, 129, 91, 86, 184, 8, 12, 190, 134, 210, 163, 242, 154, 16, 16, 78, 199, 67, 0, 125, 130, - 139, 48, 77, 219, 41, 47, 212, 169, 147, 94, 234, 146, 90, 100, 221, 68, 52, 120, 74, 162, 72, - 4, 2, 4, 168, 23, 200, 0, 207, 8, 150, 252, 69, 83, 160, 12, 17, 124, 91, 11, 237, 149, 13, - 214, 37, 209, 193, 109, 200, 148, 6, 235, 225, 99, 55, 71, 64, 0, 1, 111, 252, 22, 14, 52, 172, - 237, 34, 231, 212, 31, 69, 222, 63, 131, 207, 37, 45, 95, 100, 153, 90, 96, 107, 11, 63, 147, - 98, 211, 12, 55, 49, 44, 128, 117, 97, 181, 222, 18, 112, 20, 189, 27, 86, 177, 168, 210, 50, - 9, 23, 95, 168, 142, 225, 0, 227, 61, 48, 228, 214, 82, 63, 26, 53, 166, 76, 4, 11, 19, 210, 4, - 168, 23, 200, 0, 1, 95, 144, 47, 71, 119, 101, 215, 95, 3, 235, 184, 171, 118, 250, 92, 230, - 14, 45, 59, 165, 252, 63, 14, 4, 249, 82, 177, 152, 12, 0, 1, 224, 162, 30, 62, 104, 201, 128, - 216, 112, 128, 80, 240, 138, 171, 122, 204, 102, 106, 169, 143, 165, 97, 148, 42, 245, 223, - 198, 3, 165, 65, 60, 3, 113, 32, 19, 86, 43, 171, 236, 117, 186, 144, 111, 129, 141, 96, 23, - 137, 26, 132, 170, 17, 65, 246, 14, 104, 178, 248, 145, 132, 147, 228, 149, 97, 162, 12, 0, 23, - 4, 168, 23, 200, 0, 15, 66, 64, 96, 96, 96, 64, 82, 96, 64, 81, 96, 224, 128, 97, 10, 47, 131, - 57, 97, 1, 64, 96, 64, 82, 144, 81, 96, 128, 81, 96, 160, 81, 96, 192, 81, 147, 81, 97, 1, 0, - 81, 97, 1, 32, 81, 148, 149, 147, 148, 146, 147, 146, 96, 0, 128, 84, 96, 1, 96, 160, 96, 2, - 10, 3, 25, 144, 129, 22, 51, 23, 144, 145, 85, 96, 3, 128, 84, 96, 1, 128, 84, 116, 1, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 139, 2, 96, 160, 96, 2, 10, 96, 224, 96, 2, - 10, 3, 25, 145, 144, 149, 22, 115, 135, 167, 136, 26, 111, 41, 91, 10, 184, 132, 4, 31, 148, - 83, 186, 130, 175, 207, 167, 154, 23, 22, 147, 144, 147, 23, 144, 146, 85, 96, 2, 128, 84, 120, - 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 134, 2, 96, 1, 96, - 192, 96, 2, 10, 3, 112, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 137, 2, 96, 128, 96, - 2, 10, 96, 192, 96, 2, 10, 3, 25, 104, 1, 0, 0, 0, 0, 0, 0, 0, 0, 140, 2, 96, 64, 96, 2, 10, - 96, 128, 96, 2, 10, 3, 25, 96, 1, 96, 64, 96, 2, 10, 3, 25, 150, 135, 22, 143, 23, 22, 23, 22, - 23, 22, 23, 144, 145, 85, 105, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 138, 2, 96, 64, 96, 2, 10, 97, - 255, 255, 2, 25, 144, 147, 22, 146, 144, 146, 23, 145, 144, 145, 22, 130, 23, 144, 85, 80, 80, - 80, 80, 80, 80, 80, 97, 8, 233, 128, 97, 1, 70, 96, 0, 57, 96, 0, 243, 96, 96, 96, 64, 82, 54, - 21, 97, 0, 130, 87, 96, 224, 96, 2, 10, 96, 0, 53, 4, 99, 65, 108, 232, 111, 129, 20, 97, 0, - 132, 87, 128, 99, 65, 192, 225, 181, 20, 97, 1, 102, 87, 128, 99, 78, 105, 213, 96, 20, 97, 1, - 119, 87, 128, 99, 83, 236, 175, 131, 20, 97, 1, 156, 87, 128, 99, 95, 46, 240, 133, 20, 97, 2, - 97, 87, 128, 99, 109, 76, 230, 60, 20, 97, 3, 32, 87, 128, 99, 135, 125, 12, 215, 20, 97, 4, - 208, 87, 128, 99, 137, 61, 32, 232, 20, 97, 5, 180, 87, 128, 99, 190, 28, 118, 107, 20, 97, 5, - 212, 87, 128, 99, 200, 105, 27, 42, 20, 97, 5, 220, 87, 91, 0, 91, 97, 0, 130, 115, 116, 77, - 170, 191, 184, 92, 29, 86, 153, 128, 168, 74, 36, 166, 166, 92, 210, 55, 171, 88, 51, 96, 1, - 96, 160, 96, 2, 10, 3, 22, 20, 128, 21, 97, 0, 191, 87, 80, 96, 3, 84, 96, 72, 96, 2, 10, 144, - 4, 96, 255, 22, 96, 1, 20, 91, 128, 97, 0, 253, 87, 80, 51, 96, 1, 96, 160, 96, 2, 10, 3, 22, - 115, 251, 89, 168, 23, 164, 172, 113, 233, 121, 7, 210, 165, 238, 41, 182, 131, 151, 35, 254, - 209, 20, 128, 21, 97, 0, 253, 87, 80, 96, 3, 84, 96, 72, 96, 2, 10, 144, 4, 96, 255, 22, 96, 0, - 20, 91, 21, 97, 7, 99, 87, 96, 3, 128, 84, 104, 255, 0, 0, 0, 0, 0, 0, 0, 0, 25, 22, 104, 4, 0, - 0, 0, 0, 0, 0, 0, 0, 23, 144, 85, 96, 2, 128, 84, 103, 255, 255, 255, 255, 255, 255, 255, 255, - 25, 22, 66, 97, 3, 232, 2, 23, 144, 85, 96, 4, 128, 84, 96, 1, 129, 1, 128, 131, 85, 130, 129, - 131, 128, 21, 130, 144, 17, 0, 0, 0, 0, 0, 0, 0, 32, 79, 206, 94, 62, 37, 2, 97, 16, 0, 0, 0, - 0, 28, 71, 92, 198, 136, 239, 57, 111, 55, 53, 120, 183, 244, 199, 220, 144, 62, 109, 121, 69, - 254, 117, 21, 176, 162, 188, 30, 216, 15, 37, 130, 33, 191, 243, 142, 68, 15, 101, 200, 130, - 76, 109, 218, 6, 202, 72, 41, 199, 119, 61, 189, 184, 147, 191, 26, 7, 104, 223, 58, 176, 235, - 116, 196, 9, 164, 72, 4, 3, 28, 11, 164, 59, 116, 0, 82, 8, 15, 99, 207, 25, 102, 6, 82, 100, - 53, 218, 191, 139, 9, 199, 239, 166, 130, 188, 189, 246, 35, 118, 245, 181, 219, 130, 192, 0, - 1, 131, 236, 250, 243, 208, 200, 182, 171, 161, 68, 154, 62, 115, 198, 254, 145, 103, 132, 144, - 211, 223, 149, 247, 224, 99, 195, 158, 31, 223, 135, 15, 254, 65, 100, 213, 216, 210, 128, 113, - 242, 120, 102, 116, 169, 217, 23, 86, 16, 222, 3, 123, 181, 37, 17, 111, 249, 122, 97, 29, 91, - 147, 253, 162, 119, 164, 200, 3, 14, 66, 4, 168, 23, 200, 0, 82, 8, 104, 163, 2, 66, 53, 52, - 164, 127, 75, 49, 51, 120, 84, 130, 28, 98, 215, 129, 196, 235, 7, 72, 72, 64, 220, 80, 0, 0, - 214, 34, 123, 143, 111, 249, 162, 8, 45, 234, 158, 91, 175, 68, 163, 42, 217, 134, 167, 20, - 102, 145, 234, 197, 13, 78, 98, 59, 225, 52, 79, 247, 146, 114, 35, 34, 155, 29, 147, 66, 89, - 106, 171, 118, 209, 131, 142, 30, 182, 186, 173, 220, 202, 140, 92, 167, 145, 63, 96, 223, 233, - 80, 152, 111, 164, 76, 0, 8, 199, 4, 168, 23, 200, 0, 7, 161, 32, 155, 39, 177, 33, 16, 201, - 194, 2, 140, 124, 151, 249, 205, 66, 45, 211, 153, 165, 114, 47, 245, 83, 126, 222, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 187, 155, 194, 68, 215, 152, 18, 63, 222, 120, 63, 204, 28, 114, 211, - 187, 140, 24, 148, 19, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10, 134, 157, 121, 167, 5, 44, 127, - 27, 85, 168, 235, 171, 190, 163, 66, 15, 13, 30, 19, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 107, 199, 94, 45, 99, 16, 0, 0, 0, 85, 114, 249, 37, 51, 123, - 126, 28, 153, 253, 16, 190, 253, 200, 224, 47, 171, 122, 6, 232, 66, 239, 235, 8, 254, 81, 176, - 173, 179, 3, 146, 44, 228, 183, 91, 25, 218, 229, 107, 2, 105, 166, 164, 131, 164, 101, 179, 7, - 5, 201, 62, 239, 70, 117, 97, 23, 121, 205, 144, 129, 212, 81, 180, 20, 166, 76, 4, 2, 7, 25, - 4, 168, 23, 200, 0, 1, 95, 144, 28, 133, 60, 49, 186, 199, 200, 103, 159, 32, 169, 5, 23, 212, - 110, 82, 235, 73, 142, 208, 13, 226, 177, 30, 11, 173, 147, 108, 0, 129, 19, 67, 219, 83, 56, - 125, 114, 144, 233, 81, 101, 191, 76, 115, 156, 139, 87, 179, 201, 22, 209, 208, 4, 124, 182, - 106, 43, 249, 103, 126, 192, 22, 130, 222, 156, 98, 20, 141, 112, 1, 46, 152, 231, 7, 164, 71, - 157, 214, 81, 255, 8, 159, 217, 123, 43, 9, 54, 204, 114, 140, 31, 141, 113, 166, 76, 4, 10, - 212, 30, 4, 168, 23, 200, 0, 1, 95, 144, 72, 124, 176, 164, 169, 226, 80, 212, 39, 166, 111, - 207, 72, 74, 157, 164, 251, 36, 229, 92, 16, 163, 25, 99, 71, 86, 160, 0, 0, 25, 144, 140, 45, - 38, 142, 255, 187, 195, 68, 149, 218, 121, 244, 46, 136, 90, 225, 151, 152, 197, 178, 227, 248, - 12, 80, 120, 87, 156, 248, 82, 141, 26, 45, 228, 225, 239, 236, 131, 203, 127, 106, 57, 152, - 116, 249, 140, 210, 34, 110, 74, 77, 38, 157, 6, 45, 196, 143, 108, 168, 6, 3, 198, 90, 166, - 76, 4, 2, 7, 26, 4, 168, 23, 200, 0, 1, 95, 144, 201, 200, 171, 57, 108, 13, 132, 88, 48, 224, - 200, 139, 68, 32, 48, 152, 248, 254, 65, 177, 13, 238, 66, 83, 102, 36, 155, 12, 1, 80, 47, 78, - 55, 146, 41, 52, 32, 217, 126, 44, 116, 194, 221, 255, 145, 138, 245, 154, 89, 59, 177, 166, - 77, 114, 106, 194, 18, 232, 30, 101, 160, 8, 103, 215, 80, 206, 69, 5, 173, 103, 166, 141, 58, - 155, 0, 245, 51, 60, 186, 184, 6, 129, 249, 34, 143, 0, 39, 186, 193, 170, 245, 101, 32, 166, - 76, 4, 2, 7, 27, 4, 168, 23, 200, 0, 1, 95, 144, 162, 69, 130, 21, 12, 28, 145, 41, 175, 12, - 252, 222, 174, 33, 25, 201, 134, 216, 103, 123, 13, 228, 105, 224, 176, 2, 239, 8, 0, 233, 133, - 224, 165, 151, 254, 154, 166, 148, 58, 164, 199, 82, 210, 142, 229, 164, 155, 34, 86, 60, 64, - 226, 38, 180, 229, 153, 7, 25, 29, 185, 96, 34, 197, 50, 155, 107, 62, 57, 151, 87, 52, 223, - 210, 25, 230, 213, 108, 191, 131, 178, 17, 253, 37, 76, 129, 206, 201, 113, 79, 39, 10, 155, - 16, 162, 200, 4, 1, 6, 252, 35, 172, 0, 82, 8, 50, 190, 52, 59, 148, 248, 96, 18, 77, 196, 254, - 226, 120, 253, 203, 211, 140, 16, 45, 136, 5, 107, 197, 33, 50, 20, 218, 160, 0, 0, 17, 11, - 216, 255, 51, 101, 46, 217, 89, 40, 157, 109, 0, 243, 246, 253, 228, 187, 30, 195, 100, 194, - 103, 84, 56, 175, 143, 190, 26, 69, 45, 60, 16, 55, 215, 183, 202, 63, 151, 60, 229, 248, 72, - 88, 218, 193, 237, 48, 85, 169, 239, 165, 171, 61, 187, 2, 158, 102, 30, 215, 231, 197, 21, 97, - 162, 72, 4, 2, 6, 252, 35, 172, 0, 82, 8, 50, 190, 52, 59, 148, 248, 96, 18, 77, 196, 254, 226, - 120, 253, 203, 211, 140, 16, 45, 136, 80, 74, 145, 165, 49, 174, 228, 0, 1, 150, 203, 112, 225, - 10, 45, 26, 225, 158, 119, 239, 183, 41, 175, 143, 129, 254, 42, 233, 218, 55, 119, 83, 210, - 87, 235, 99, 99, 139, 62, 16, 95, 61, 12, 172, 133, 32, 12, 161, 205, 190, 230, 168, 99, 193, - 249, 3, 4, 147, 102, 96, 104, 226, 214, 84, 17, 190, 238, 210, 59, 243, 112, 62, 29, 162, 72, - 4, 10, 6, 252, 35, 172, 0, 82, 8, 50, 190, 52, 59, 148, 248, 96, 18, 77, 196, 254, 226, 120, - 253, 203, 211, 140, 16, 45, 136, 16, 103, 85, 147, 175, 112, 192, 0, 1, 227, 175, 39, 227, 4, - 19, 102, 148, 117, 95, 219, 13, 191, 29, 10, 1, 146, 78, 58, 251, 255, 32, 182, 100, 187, 138, - 94, 74, 226, 200, 93, 229, 185, 25, 83, 230, 237, 247, 255, 227, 122, 10, 242, 78, 82, 118, 13, - 110, 79, 90, 84, 46, 216, 147, 54, 179, 102, 136, 111, 72, 195, 21, 190, 46, 160, 76, 0, 4, - 227, 178, 146, 0, 2, 73, 240, 187, 155, 194, 68, 215, 152, 18, 63, 222, 120, 63, 204, 28, 114, - 211, 187, 140, 24, 148, 19, 169, 5, 156, 187, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 226, 43, 97, - 227, 180, 117, 130, 138, 230, 115, 233, 35, 173, 161, 95, 142, 173, 65, 73, 211, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 12, 247, 91, 10, 90, 212, 250, 88, 0, 0, - 147, 136, 69, 160, 13, 96, 211, 157, 98, 183, 71, 233, 123, 140, 23, 159, 168, 237, 237, 232, - 0, 84, 40, 160, 195, 240, 11, 84, 195, 26, 175, 248, 236, 57, 116, 127, 20, 154, 235, 133, 217, - 13, 198, 177, 211, 178, 185, 234, 83, 17, 137, 13, 161, 186, 223, 173, 247, 242, 233, 122, 191, - 126, 184, 93, 164, 76, 0, 2, 44, 4, 168, 23, 200, 0, 3, 208, 144, 139, 59, 59, 98, 76, 60, 3, - 151, 211, 218, 143, 216, 97, 81, 35, 147, 213, 29, 203, 172, 102, 122, 47, 88, 0, 2, 61, 16, - 230, 101, 157, 159, 229, 243, 154, 26, 237, 79, 77, 195, 126, 154, 203, 253, 233, 83, 220, 179, - 186, 166, 55, 232, 167, 118, 141, 52, 46, 66, 116, 248, 5, 228, 43, 221, 245, 231, 19, 119, 39, - 201, 253, 204, 46, 98, 45, 75, 36, 52, 43, 51, 249, 106, 44, 107, 215, 228, 17, 33, 21, 164, - 72, 0, 39, 97, 4, 168, 23, 200, 0, 211, 74, 187, 0, 0, 0, 0, 0, 0, 5, 163, 200, 138, 141, 150, - 248, 0, 0, 74, 200, 163, 108, 172, 23, 105, 47, 176, 159, 24, 60, 129, 224, 37, 217, 121, 215, - 27, 169, 124, 252, 144, 200, 251, 82, 107, 108, 96, 11, 146, 190, 16, 122, 177, 11, 94, 17, 58, - 189, 17, 205, 177, 94, 67, 183, 234, 80, 166, 120, 246, 116, 218, 28, 54, 173, 152, 25, 220, - 211, 207, 102, 80, 52, 164, 72, 0, 15, 109, 5, 161, 235, 229, 232, 211, 74, 187, 155, 194, 68, - 215, 152, 18, 63, 222, 120, 63, 204, 28, 114, 211, 187, 140, 24, 148, 19, 35, 184, 114, 221, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 213, 135, 98, 56, 190, 248, 51, 188, 192, 107, 83, 0, 213, 1, - 202, 141, 227, 157, 235, 57, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 223, 33, 250, 146, 34, 21, - 177, 165, 111, 90, 109, 98, 148, 230, 227, 108, 133, 160, 172, 251, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 172, 11, 206, 120, 178, 108, 0, 0, 132, 155, - 105, 47, 255, 206, 112, 101, 182, 162, 31, 77, 2, 104, 181, 209, 113, 197, 74, 110, 170, 189, - 41, 180, 36, 182, 153, 84, 218, 130, 53, 206, 173, 44, 86, 78, 44, 184, 68, 12, 174, 90, 84, - 67, 68, 104, 174, 167, 78, 30, 197, 177, 66, 194, 35, 109, 109, 232, 177, 25, 158, 14, 106, - 124, 164, 72, 0, 15, 110, 5, 161, 235, 229, 232, 211, 138, 187, 155, 194, 68, 215, 152, 18, 63, - 222, 120, 63, 204, 28, 114, 211, 187, 140, 24, 148, 19, 35, 184, 114, 221, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 162, 202, 201, 245, 63, 212, 251, 8, 112, 199, 48, 60, 60, 185, 234, 215, 227, - 21, 8, 12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 223, 33, 250, 146, 34, 21, 177, 165, 111, 90, - 109, 98, 148, 230, 227, 108, 133, 160, 172, 251, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 178, 59, 241, 255, 68, 234, 0, 1, 212, 144, 228, 207, 27, 185, - 19, 230, 92, 130, 101, 90, 222, 115, 10, 125, 150, 74, 34, 35, 93, 147, 8, 107, 104, 21, 124, - 105, 136, 66, 137, 71, 1, 255, 124, 184, 117, 138, 18, 136, 119, 16, 118, 178, 50, 186, 209, - 21, 236, 192, 24, 239, 81, 205, 119, 181, 22, 241, 90, 182, 204, 58, 178, 0, 162, 76, 0, 12, 5, - 149, 151, 155, 125, 1, 216, 168, 187, 155, 194, 68, 215, 152, 18, 63, 222, 120, 63, 204, 28, - 114, 211, 187, 140, 24, 148, 19, 169, 5, 156, 187, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 194, - 147, 71, 215, 14, 227, 155, 165, 133, 125, 198, 61, 110, 248, 225, 39, 151, 206, 141, 66, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 181, 227, 175, 22, 177, 136, - 0, 0, 0, 46, 29, 102, 80, 189, 131, 52, 137, 199, 118, 138, 108, 134, 125, 129, 244, 74, 28, - 114, 245, 200, 61, 225, 43, 224, 235, 218, 238, 170, 246, 31, 64, 40, 229, 93, 11, 55, 204, - 248, 8, 25, 136, 74, 20, 129, 207, 107, 178, 63, 238, 166, 212, 34, 229, 37, 65, 99, 148, 192, - 187, 21, 42, 3, 0, 164, 76, 4, 1, 245, 5, 147, 114, 162, 12, 1, 95, 144, 222, 149, 115, 247, - 216, 7, 74, 145, 76, 171, 76, 161, 165, 162, 225, 147, 50, 29, 131, 56, 15, 69, 215, 28, 106, - 9, 58, 0, 1, 250, 2, 121, 85, 41, 168, 65, 109, 153, 240, 218, 233, 135, 233, 48, 3, 207, 149, - 99, 29, 51, 3, 133, 27, 96, 82, 187, 99, 131, 37, 187, 183, 216, 224, 253, 168, 107, 255, 210, - 40, 122, 109, 151, 119, 105, 232, 156, 214, 23, 93, 209, 226, 7, 222, 64, 40, 200, 62, 78, 153, - 236, 105, 134, 105, 162, 76, 0, 10, 10, 157, 66, 219, 241, 2, 64, 194, 187, 155, 194, 68, 215, - 152, 18, 63, 222, 120, 63, 204, 28, 114, 211, 187, 140, 24, 148, 19, 169, 5, 156, 187, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 55, 102, 204, 39, 162, 145, 226, 247, 141, 59, 38, 217, 35, 95, 148, - 154, 16, 204, 129, 60, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 71, 13, 228, 223, 130, 0, 0, 0, 69, 27, 213, 230, 230, 132, 61, 117, 63, 103, 181, 195, 133, - 44, 223, 35, 246, 225, 206, 169, 236, 181, 206, 199, 214, 20, 74, 213, 215, 109, 111, 142, 216, - 67, 235, 11, 250, 234, 148, 181, 186, 7, 91, 60, 241, 51, 80, 127, 163, 95, 127, 151, 47, 56, - 67, 254, 63, 196, 88, 152, 25, 168, 118, 63, 164, 76, 4, 132, 158, 5, 189, 120, 39, 89, 2, 73, - 240, 101, 100, 244, 178, 83, 102, 107, 40, 36, 178, 178, 19, 73, 63, 234, 114, 42, 29, 193, 65, - 13, 206, 243, 58, 111, 131, 128, 0, 0, 63, 53, 247, 82, 61, 211, 198, 200, 208, 150, 24, 154, - 248, 106, 110, 233, 38, 131, 0, 176, 137, 37, 234, 173, 231, 214, 159, 25, 44, 85, 110, 228, - 88, 162, 225, 0, 204, 87, 76, 8, 7, 132, 82, 203, 227, 47, 137, 83, 182, 118, 232, 175, 209, - 174, 36, 35, 127, 247, 35, 106, 253, 62, 180, 55, 160, 12, 0, 5, 189, 120, 39, 89, 7, 161, 32, - 96, 96, 96, 64, 82, 96, 0, 128, 84, 96, 1, 96, 160, 96, 2, 10, 3, 25, 22, 51, 23, 144, 85, 97, - 1, 11, 128, 97, 0, 36, 96, 0, 57, 96, 0, 243, 96, 96, 96, 64, 82, 54, 21, 97, 0, 31, 87, 96, - 224, 96, 2, 10, 96, 0, 53, 4, 99, 245, 83, 126, 222, 129, 20, 97, 0, 48, 87, 91, 97, 0, 98, 96, - 0, 52, 17, 21, 97, 0, 100, 87, 97, 0, 2, 86, 91, 97, 0, 98, 96, 4, 53, 96, 36, 53, 96, 68, 53, - 96, 0, 128, 84, 51, 96, 1, 96, 160, 96, 2, 10, 3, 144, 129, 22, 145, 22, 20, 21, 97, 1, 5, 87, - 96, 0, 52, 17, 21, 97, 0, 102, 87, 97, 0, 2, 86, 91, 0, 91, 86, 91, 127, 169, 5, 156, 187, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 96, 96, 144, - 129, 82, 96, 1, 96, 160, 96, 2, 10, 3, 128, 133, 22, 96, 100, 82, 96, 132, 132, 144, 82, 133, - 146, 144, 131, 22, 145, 99, 169, 5, 156, 187, 145, 96, 164, 145, 96, 32, 145, 96, 68, 144, 130, - 144, 135, 97, 97, 218, 90, 3, 241, 21, 97, 0, 2, 87, 80, 80, 96, 64, 128, 81, 132, 129, 82, - 144, 81, 96, 1, 96, 160, 96, 2, 10, 3, 134, 22, 146, 145, 127, 208, 237, 136, 163, 240, 66, - 198, 187, 177, 227, 234, 64, 96, 121, 181, 242, 180, 177, 152, 175, 204, 170, 83, 93, 131, 127, - 76, 99, 171, 188, 77, 230, 145, 144, 129, 144, 3, 96, 32, 1, 144, 163, 91, 80, 80, 80, 80, 86, - 1, 160, 24, 112, 41, 1, 43, 102, 190, 77, 25, 67, 190, 78, 107, 138, 104, 237, 199, 146, 250, - 227, 92, 237, 207, 142, 133, 233, 204, 96, 52, 207, 215, 208, 42, 197, 50, 216, 17, 210, 167, - 248, 126, 142, 145, 211, 237, 147, 96, 50, 141, 48, 47, 91, 2, 201, 251, 49, 9, 180, 218, 33, - 107, 230, 104, 164, 76, 4, 132, 159, 5, 189, 120, 39, 89, 2, 73, 240, 84, 19, 17, 153, 247, 26, - 141, 59, 243, 227, 180, 175, 102, 172, 36, 152, 126, 44, 220, 185, 2, 41, 103, 253, 92, 169, - 160, 0, 0, 17, 19, 138, 215, 21, 14, 215, 28, 240, 221, 72, 173, 41, 229, 24, 252, 97, 17, 98, - 8, 50, 201, 97, 159, 50, 19, 107, 164, 73, 72, 147, 73, 86, 154, 25, 35, 227, 27, 237, 146, 68, - 235, 99, 74, 162, 197, 189, 231, 145, 87, 206, 131, 121, 45, 76, 115, 84, 169, 131, 178, 198, - 247, 202, 71, 162, 12, 0, 1, 5, 189, 120, 39, 89, 7, 161, 47, 67, 63, 166, 168, 12, 230, 252, - 140, 118, 170, 83, 224, 50, 117, 54, 176, 242, 11, 214, 80, 46, 40, 189, 231, 209, 224, 170, - 165, 34, 10, 204, 93, 61, 28, 22, 64, 41, 7, 81, 247, 30, 164, 76, 0, 1, 226, 4, 168, 23, 200, - 0, 4, 147, 224, 160, 15, 162, 36, 153, 71, 140, 195, 123, 235, 69, 2, 26, 31, 28, 159, 130, - 111, 114, 225, 118, 212, 56, 176, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 21, 1, 113, 179, 186, 61, 209, 96, 38, 184, 141, 114, 71, - 206, 55, 65, 250, 31, 105, 191, 229, 218, 80, 134, 243, 88, 159, 158, 38, 219, 34, 216, 248, - 49, 30, 140, 78, 253, 186, 40, 38, 115, 131, 160, 14, 253, 178, 56, 160, 111, 127, 58, 195, - 222, 121, 133, 247, 160, 37, 198, 228, 247, 251, 209, 76, 117, 164, 76, 4, 47, 191, 5, 3, 147, - 67, 111, 1, 95, 144, 214, 157, 49, 67, 173, 97, 131, 102, 246, 202, 6, 162, 213, 97, 148, 182, - 211, 213, 132, 107, 14, 114, 77, 194, 30, 25, 176, 0, 1, 108, 91, 13, 159, 7, 236, 151, 212, - 90, 90, 228, 118, 215, 61, 213, 139, 234, 49, 204, 147, 141, 123, 172, 161, 135, 105, 227, 225, - 151, 175, 103, 46, 93, 210, 56, 250, 138, 1, 118, 211, 245, 29, 139, 135, 34, 75, 17, 42, 83, - 179, 77, 130, 207, 2, 175, 10, 212, 247, 230, 36, 156, 242, 82, 48, 164, 76, 4, 47, 192, 5, 3, - 147, 67, 111, 1, 95, 144, 173, 196, 109, 40, 11, 87, 84, 147, 36, 237, 213, 79, 120, 172, 223, - 117, 121, 203, 3, 28, 29, 129, 142, 114, 37, 95, 64, 0, 0, 231, 119, 70, 201, 188, 94, 96, 173, - 159, 166, 125, 0, 157, 50, 240, 115, 232, 152, 17, 136, 4, 146, 140, 25, 158, 6, 11, 75, 250, - 254, 161, 127, 248, 87, 37, 200, 190, 129, 183, 45, 124, 253, 27, 39, 140, 95, 176, 49, 229, - 110, 76, 245, 227, 57, 203, 52, 113, 49, 176, 94, 190, 181, 13, 9, 164, 76, 4, 47, 193, 5, 3, - 147, 67, 111, 1, 95, 144, 45, 29, 148, 82, 30, 137, 102, 37, 248, 236, 63, 101, 23, 31, 168, - 34, 98, 218, 107, 207, 40, 221, 158, 220, 53, 162, 96, 0, 1, 33, 84, 60, 131, 135, 121, 101, - 83, 230, 10, 4, 8, 76, 63, 161, 225, 22, 218, 209, 250, 68, 33, 245, 124, 72, 93, 235, 80, 99, - 117, 44, 245, 98, 205, 37, 38, 106, 208, 90, 150, 49, 196, 192, 115, 88, 64, 24, 65, 95, 192, - 30, 176, 1, 125, 120, 175, 36, 161, 56, 108, 104, 178, 237, 114, 164, 76, 4, 47, 194, 5, 3, - 147, 67, 111, 1, 95, 144, 59, 4, 59, 126, 217, 78, 100, 3, 159, 10, 88, 125, 119, 143, 149, - 166, 254, 112, 235, 186, 71, 145, 226, 88, 244, 82, 224, 0, 1, 28, 60, 248, 16, 187, 189, 173, - 60, 244, 32, 157, 192, 76, 248, 20, 4, 44, 170, 211, 59, 177, 18, 158, 251, 147, 115, 96, 214, - 119, 113, 51, 104, 238, 1, 109, 182, 25, 143, 70, 44, 153, 81, 10, 20, 58, 255, 128, 114, 250, - 58, 89, 14, 218, 160, 190, 62, 167, 247, 25, 202, 219, 255, 68, 91, 164, 204, 3, 47, 195, 5, 3, - 147, 67, 111, 1, 95, 144, 40, 180, 11, 44, 215, 156, 135, 185, 93, 142, 23, 3, 69, 2, 213, 167, - 52, 49, 130, 171, 103, 19, 43, 118, 222, 80, 0, 0, 108, 133, 81, 220, 58, 0, 100, 1, 45, 60, - 252, 196, 205, 32, 72, 3, 157, 91, 206, 174, 75, 140, 13, 183, 194, 158, 104, 79, 54, 173, 70, - 175, 102, 94, 18, 18, 68, 237, 175, 245, 173, 194, 248, 206, 118, 37, 48, 156, 163, 219, 59, - 27, 247, 226, 57, 238, 93, 235, 74, 112, 32, 180, 67, 20, 164, 76, 4, 47, 196, 5, 3, 147, 67, - 111, 1, 95, 144, 93, 204, 164, 123, 114, 137, 27, 125, 91, 238, 201, 98, 50, 51, 188, 190, 131, - 163, 20, 118, 15, 217, 16, 250, 212, 3, 132, 0, 1, 5, 39, 118, 126, 19, 25, 58, 112, 188, 188, - 86, 24, 123, 174, 131, 156, 33, 1, 28, 131, 100, 93, 204, 74, 193, 14, 154, 223, 196, 203, 174, - 222, 84, 45, 96, 185, 251, 241, 242, 186, 255, 26, 219, 164, 209, 168, 240, 152, 209, 119, 77, - 92, 166, 102, 216, 2, 78, 50, 203, 30, 6, 181, 140, 58, 164, 76, 4, 47, 197, 4, 246, 67, 187, - 4, 1, 95, 144, 208, 87, 8, 164, 220, 185, 248, 144, 157, 99, 244, 203, 221, 3, 13, 41, 20, 37, - 173, 177, 147, 127, 161, 207, 235, 143, 80, 0, 1, 159, 112, 230, 69, 51, 160, 109, 103, 113, - 30, 31, 141, 12, 114, 140, 166, 223, 245, 42, 10, 250, 80, 204, 192, 133, 139, 110, 71, 204, - 151, 87, 47, 59, 199, 129, 135, 168, 238, 97, 115, 100, 56, 235, 230, 71, 21, 210, 202, 147, - 215, 53, 177, 91, 224, 196, 75, 2, 67, 1, 228, 82, 38, 170, 60, 164, 76, 4, 47, 198, 4, 246, - 67, 187, 4, 1, 95, 144, 87, 7, 115, 139, 58, 76, 8, 59, 207, 205, 25, 44, 20, 7, 89, 102, 43, - 140, 151, 116, 14, 115, 213, 217, 188, 130, 108, 0, 0, 31, 79, 109, 2, 13, 117, 245, 212, 150, - 133, 229, 137, 93, 179, 32, 88, 231, 91, 32, 167, 63, 112, 113, 254, 116, 69, 221, 14, 33, 246, - 209, 87, 78, 147, 247, 173, 27, 245, 135, 134, 198, 125, 234, 250, 195, 165, 176, 65, 181, 208, - 243, 100, 140, 46, 35, 196, 145, 56, 69, 118, 44, 65, 190, 19, 164, 76, 4, 47, 199, 4, 246, 67, - 187, 4, 1, 95, 144, 123, 49, 69, 96, 33, 37, 18, 149, 161, 30, 101, 131, 149, 150, 17, 98, 51, - 146, 229, 154, 6, 15, 81, 228, 85, 20, 20, 0, 0, 175, 89, 183, 69, 238, 131, 244, 100, 228, - 126, 53, 50, 99, 60, 66, 159, 144, 250, 87, 57, 240, 123, 67, 145, 194, 239, 103, 204, 245, - 165, 85, 66, 102, 52, 96, 190, 208, 116, 25, 48, 66, 113, 63, 123, 140, 58, 35, 239, 115, 144, - 157, 222, 204, 189, 147, 116, 13, 185, 232, 77, 163, 135, 249, 22, 164, 76, 4, 47, 200, 4, 246, - 67, 187, 4, 1, 95, 144, 49, 19, 151, 179, 26, 245, 205, 135, 252, 202, 137, 232, 44, 199, 95, - 161, 28, 234, 102, 115, 16, 10, 240, 149, 171, 249, 120, 0, 1, 54, 108, 130, 216, 235, 248, - 165, 230, 53, 5, 208, 128, 44, 20, 176, 210, 197, 235, 25, 7, 142, 74, 222, 211, 56, 64, 148, - 107, 236, 245, 156, 9, 114, 32, 236, 60, 2, 168, 67, 110, 252, 142, 86, 104, 151, 35, 203, 143, - 17, 154, 131, 171, 212, 145, 125, 204, 39, 120, 52, 28, 247, 188, 88, 35, 164, 76, 4, 47, 201, - 4, 246, 67, 187, 4, 1, 95, 144, 70, 85, 226, 106, 246, 241, 57, 112, 47, 220, 18, 11, 191, 218, - 231, 33, 114, 247, 63, 118, 1, 178, 253, 98, 164, 114, 72, 0, 0, 182, 242, 193, 214, 117, 84, - 217, 52, 185, 133, 34, 223, 80, 205, 101, 90, 213, 114, 122, 206, 50, 69, 252, 24, 22, 165, 98, - 144, 229, 67, 148, 99, 194, 60, 20, 27, 94, 67, 100, 228, 15, 228, 250, 213, 211, 180, 70, 39, - 59, 191, 157, 246, 205, 3, 2, 134, 47, 53, 246, 196, 183, 115, 152, 57, 164, 76, 4, 47, 202, 4, - 246, 67, 187, 4, 1, 95, 144, 178, 250, 154, 197, 6, 147, 118, 69, 33, 17, 86, 134, 69, 79, 66, - 92, 209, 221, 55, 214, 18, 185, 79, 89, 176, 35, 228, 0, 1, 254, 214, 69, 113, 3, 129, 146, 73, - 35, 249, 230, 61, 86, 20, 123, 149, 122, 167, 236, 17, 166, 213, 230, 222, 145, 185, 219, 198, - 91, 190, 119, 184, 253, 187, 185, 167, 79, 150, 118, 240, 139, 21, 142, 19, 121, 73, 37, 177, - 84, 150, 121, 145, 37, 250, 139, 51, 85, 63, 76, 195, 66, 37, 234, 124, 164, 76, 4, 47, 203, 4, - 246, 67, 187, 4, 1, 95, 144, 13, 144, 118, 114, 245, 60, 168, 76, 129, 62, 131, 40, 27, 160, - 99, 143, 49, 228, 76, 2, 100, 208, 104, 129, 87, 119, 17, 71, 126, 181, 84, 11, 172, 38, 173, - 104, 5, 89, 63, 190, 74, 164, 204, 3, 28, 112, 4, 168, 23, 200, 0, 1, 95, 144, 67, 137, 36, 58, - 237, 110, 90, 221, 167, 119, 28, 219, 95, 25, 197, 72, 19, 240, 79, 105, 48, 15, 157, 246, 97, - 212, 0, 0, 208, 33, 13, 242, 116, 250, 245, 148, 76, 20, 73, 85, 112, 126, 56, 235, 46, 244, - 159, 38, 83, 209, 184, 57, 119, 76, 0, 120, 250, 160, 237, 5, 118, 210, 244, 208, 148, 180, - 107, 153, 168, 24, 1, 173, 171, 163, 50, 9, 138, 210, 183, 245, 22, 217, 137, 161, 55, 206, 23, - 142, 33, 216, 39, 54, 166, 76, 4, 1, 199, 103, 4, 168, 23, 200, 0, 1, 95, 144, 150, 97, 176, - 48, 216, 148, 137, 157, 74, 9, 101, 153, 144, 188, 173, 250, 16, 95, 58, 129, 13, 227, 180, 37, - 10, 26, 49, 248, 1, 199, 7, 97, 133, 227, 51, 92, 223, 22, 196, 45, 187, 151, 170, 71, 203, - 158, 177, 29, 122, 104, 48, 125, 119, 184, 210, 132, 5, 164, 56, 46, 184, 217, 69, 194, 73, - 197, 77, 223, 115, 133, 111, 104, 136, 121, 2, 122, 100, 20, 127, 11, 29, 5, 53, 178, 231, 251, - 230, 232, 99, 1, 218, 92, 124, 162, 72, 4, 153, 4, 168, 23, 200, 0, 82, 8, 237, 107, 37, 179, - 178, 218, 178, 213, 217, 106, 198, 89, 170, 187, 248, 18, 240, 105, 53, 27, 14, 55, 121, 158, - 14, 49, 144, 0, 1, 65, 92, 202, 223, 222, 3, 165, 123, 58, 250, 170, 99, 235, 110, 131, 174, - 91, 250, 63, 190, 23, 113, 84, 233, 215, 16, 114, 55, 63, 199, 247, 231, 73, 129, 3, 254, 41, - 136, 110, 196, 80, 234, 53, 193, 123, 94, 82, 111, 206, 35, 58, 74, 219, 140, 132, 230, 190, - 53, 4, 60, 203, 179, 80, 121, 162, 76, 4, 10, 4, 168, 23, 200, 0, 1, 134, 160, 251, 177, 183, - 60, 79, 11, 218, 79, 103, 220, 162, 102, 206, 110, 244, 47, 82, 15, 187, 152, 17, 182, 107, 20, - 116, 64, 64, 0, 1, 138, 71, 138, 203, 114, 186, 40, 238, 168, 143, 34, 218, 17, 140, 1, 32, 50, - 9, 203, 131, 181, 221, 52, 116, 150, 208, 101, 173, 77, 119, 212, 86, 86, 0, 184, 160, 136, - 112, 215, 61, 126, 11, 144, 237, 60, 150, 38, 4, 68, 249, 177, 219, 126, 218, 7, 84, 18, 70, - 234, 27, 208, 61, 140, 108, 162, 200, 4, 50, 4, 168, 23, 200, 0, 82, 8, 15, 242, 65, 88, 34, - 10, 20, 57, 143, 4, 122, 128, 165, 19, 97, 125, 220, 79, 82, 137, 1, 242, 115, 100, 54, 220, - 124, 144, 0, 1, 44, 203, 83, 110, 4, 239, 147, 146, 46, 162, 160, 104, 121, 238, 39, 115, 91, - 206, 195, 155, 224, 71, 188, 144, 215, 106, 212, 1, 253, 142, 158, 77, 115, 232, 31, 181, 108, - 185, 234, 208, 28, 159, 50, 142, 30, 242, 180, 241, 20, 25, 57, 154, 198, 208, 41, 226, 138, - 190, 243, 11, 207, 2, 227, 108, 160, 72, 4, 4, 227, 178, 146, 0, 82, 8, 7, 141, 69, 203, 110, - 231, 223, 189, 252, 212, 22, 189, 163, 168, 27, 153, 152, 157, 15, 255, 97, 68, 248, 93, 94, - 182, 128, 0, 1, 188, 103, 245, 29, 166, 98, 157, 206, 197, 49, 178, 145, 115, 54, 194, 203, - 154, 108, 103, 156, 194, 164, 218, 159, 236, 109, 226, 221, 195, 247, 216, 121, 1, 86, 83, 81, - 216, 50, 56, 79, 40, 197, 168, 200, 17, 20, 58, 172, 147, 239, 230, 156, 224, 152, 219, 184, - 239, 247, 156, 142, 162, 40, 207, 120, 162, 72, 4, 19, 4, 168, 23, 200, 0, 82, 8, 145, 51, 122, - 48, 14, 3, 97, 189, 219, 46, 55, 125, 212, 232, 140, 203, 119, 150, 102, 61, 1, 109, 67, 162, - 26, 9, 196, 0, 0, 88, 72, 36, 175, 69, 174, 90, 10, 97, 68, 103, 212, 225, 76, 185, 203, 235, - 196, 20, 223, 159, 219, 145, 109, 25, 20, 107, 79, 242, 211, 86, 174, 101, 200, 111, 19, 225, - 66, 98, 165, 219, 67, 70, 16, 87, 40, 173, 119, 95, 193, 122, 249, 225, 105, 84, 76, 105, 162, - 67, 71, 225, 245, 191, 88, 162, 72, 4, 17, 4, 168, 23, 200, 0, 82, 8, 145, 51, 122, 48, 14, 3, - 97, 189, 219, 46, 55, 125, 212, 232, 140, 203, 119, 150, 102, 61, 1, 108, 254, 182, 50, 14, - 156, 0, 1, 55, 42, 198, 51, 181, 226, 171, 220, 120, 69, 118, 35, 78, 252, 114, 37, 54, 111, - 11, 108, 236, 195, 79, 247, 99, 10, 2, 169, 111, 83, 40, 72, 4, 185, 247, 78, 160, 145, 207, - 46, 223, 120, 243, 22, 189, 114, 200, 155, 20, 84, 231, 93, 78, 14, 147, 102, 228, 6, 157, 83, - 19, 138, 69, 113, 166, 76, 4, 1, 199, 104, 4, 168, 23, 200, 0, 1, 95, 144, 122, 146, 241, 113, - 87, 193, 214, 182, 130, 74, 170, 243, 18, 229, 205, 208, 140, 12, 43, 159, 14, 4, 158, 234, 1, - 93, 54, 12, 1, 141, 36, 104, 23, 94, 194, 131, 230, 213, 53, 236, 28, 151, 66, 13, 180, 93, - 125, 107, 184, 167, 91, 239, 55, 80, 196, 168, 122, 190, 58, 58, 88, 133, 115, 108, 162, 95, - 54, 225, 48, 100, 48, 251, 223, 238, 34, 97, 106, 153, 228, 180, 76, 207, 180, 35, 85, 218, - 250, 13, 210, 29, 186, 191, 91, 166, 76, 4, 1, 80, 221, 4, 168, 23, 200, 0, 1, 95, 144, 139, - 104, 120, 146, 198, 207, 136, 146, 93, 221, 119, 39, 24, 120, 42, 134, 78, 83, 83, 223, 68, - 202, 252, 31, 134, 46, 2, 0, 1, 209, 105, 61, 58, 182, 178, 122, 162, 105, 138, 112, 197, 241, - 1, 61, 73, 173, 203, 225, 150, 134, 112, 215, 196, 187, 168, 4, 219, 189, 73, 57, 185, 251, - 108, 224, 254, 33, 125, 182, 52, 130, 96, 178, 44, 123, 76, 131, 1, 3, 210, 2, 242, 80, 38, 30, - 219, 140, 158, 9, 142, 205, 115, 187, 19, 166, 76, 4, 1, 199, 105, 4, 168, 23, 200, 0, 1, 95, - 144, 205, 246, 125, 32, 184, 205, 29, 191, 136, 97, 193, 95, 132, 119, 84, 89, 15, 246, 169, - 95, 13, 230, 186, 162, 241, 88, 101, 84, 0, 187, 86, 1, 78, 165, 182, 149, 53, 149, 215, 121, - 104, 8, 105, 208, 192, 146, 86, 149, 165, 0, 184, 159, 134, 63, 179, 86, 5, 203, 115, 251, 186, - 144, 112, 179, 212, 83, 122, 26, 120, 119, 80, 94, 179, 209, 115, 32, 53, 121, 238, 73, 80, - 191, 4, 72, 96, 103, 172, 123, 230, 73, 170, 241, 30, 166, 76, 4, 1, 199, 106, 4, 168, 23, 200, - 0, 1, 95, 144, 127, 199, 123, 237, 89, 123, 243, 109, 125, 206, 202, 249, 209, 192, 216, 37, - 184, 172, 248, 91, 1, 147, 188, 93, 71, 164, 48, 28, 1, 229, 248, 56, 143, 112, 115, 56, 134, - 33, 215, 214, 24, 28, 149, 124, 94, 99, 91, 128, 45, 219, 224, 139, 218, 154, 202, 130, 70, 9, - 254, 205, 115, 71, 255, 71, 46, 226, 18, 97, 251, 86, 31, 4, 235, 228, 15, 182, 176, 36, 145, - 238, 244, 67, 193, 56, 118, 67, 234, 107, 36, 9, 158, 82, 8, 162, 76, 4, 1, 4, 168, 23, 200, 0, - 1, 134, 160, 251, 177, 183, 60, 79, 11, 218, 79, 103, 220, 162, 102, 206, 110, 244, 47, 82, 15, - 187, 152, 13, 254, 184, 117, 45, 255, 232, 0, 1, 219, 7, 51, 125, 253, 118, 38, 120, 209, 105, - 49, 235, 132, 145, 57, 227, 220, 88, 196, 110, 47, 229, 101, 188, 249, 94, 212, 4, 199, 246, - 187, 25, 88, 13, 55, 160, 149, 216, 240, 168, 99, 148, 214, 101, 6, 95, 40, 89, 109, 219, 58, - 242, 43, 137, 172, 51, 123, 67, 96, 81, 107, 214, 157, 111, 162, 72, 4, 27, 4, 168, 23, 200, 0, - 82, 8, 41, 213, 82, 124, 170, 120, 241, 148, 106, 64, 159, 166, 172, 175, 20, 160, 164, 160, - 39, 75, 1, 115, 119, 226, 65, 53, 156, 0, 1, 235, 205, 224, 61, 117, 248, 97, 16, 88, 27, 124, - 106, 50, 120, 176, 187, 54, 43, 17, 11, 16, 138, 117, 26, 216, 217, 52, 113, 74, 180, 192, 16, - 191, 71, 219, 122, 208, 120, 58, 7, 63, 102, 166, 92, 55, 203, 10, 117, 226, 167, 91, 125, 15, - 8, 82, 167, 55, 15, 238, 61, 193, 52, 255, 123, 164, 72, 4, 1, 49, 4, 168, 23, 200, 0, 82, 8, - 113, 128, 235, 1, 144, 161, 91, 80, 91, 80, 86, 91, 21, 97, 5, 193, 87, 97, 5, 212, 130, 97, 1, - 102, 86, 91, 21, 97, 5, 223, 87, 80, 97, 5, 195, 86, 91, 97, 5, 231, 97, 4, 184, 86, 91, 96, 1, - 84, 96, 250, 144, 16, 97, 5, 250, 87, 97, 5, 250, 97, 6, 15, 86, 91, 96, 1, 84, 96, 250, 144, - 16, 97, 5, 66, 87, 80, 97, 5, 195, 86, 91, 97, 6, 199, 91, 96, 1, 91, 96, 1, 84, 129, 16, 21, - 97, 5, 195, 87, 91, 96, 1, 84, 129, 16, 128, 21, 97, 6, 61, 87, 80, 96, 2, 129, 97, 1, 0, 129, - 16, 21, 97, 0, 2, 87, 1, 84, 96, 0, 20, 21, 91, 21, 97, 13, 199, 87, 96, 1, 1, 97, 6, 29, 86, - 91, 21, 97, 4, 109, 87, 96, 1, 96, 160, 96, 2, 10, 3, 131, 22, 96, 0, 144, 129, 82, 97, 1, 2, - 96, 32, 82, 96, 64, 129, 32, 84, 146, 80, 130, 20, 21, 97, 6, 120, 87, 80, 97, 5, 193, 86, 91, - 96, 1, 96, 1, 96, 0, 80, 84, 3, 96, 0, 96, 0, 80, 84, 17, 21, 97, 6, 147, 87, 80, 97, 5, 193, - 86, 91, 96, 0, 96, 2, 131, 97, 1, 0, 129, 16, 21, 97, 0, 2, 87, 80, 131, 1, 129, 144, 85, 96, - 1, 96, 160, 96, 2, 10, 3, 132, 22, 129, 82, 97, 1, 2, 96, 32, 82, 96, 64, 129, 32, 85, 97, 6, - 11, 97, 4, 184, 86, 91, 96, 64, 128, 81, 96, 1, 96, 160, 96, 2, 10, 3, 133, 22, 129, 82, 144, - 81, 127, 88, 97, 144, 118, 173, 245, 187, 9, 67, 209, 0, 239, 136, 213, 45, 124, 63, 214, 145, - 177, 157, 58, 144, 113, 181, 85, 182, 81, 251, 244, 24, 218, 145, 129, 144, 3, 96, 32, 1, 144, - 161, 80, 80, 80, 86, 91, 21, 97, 5, 193, 87, 96, 1, 84, 130, 17, 21, 97, 7, 29, 87, 80, 97, 5, - 195, 86, 91, 96, 0, 130, 144, 85, 97, 7, 42, 97, 4, 184, 86, 91, 96, 64, 128, 81, 131, 129, 82, - 144, 81, 127, 172, 189, 176, 132, 199, 33, 51, 42, 197, 159, 155, 142, 57, 33, 150, 201, 235, - 14, 73, 50, 134, 45, 168, 235, 155, 234, 240, 218, 212, 245, 80, 218, 145, 129, 144, 3, 96, 32, - 1, 144, 161, 80, 80, 86, 91, 80, 96, 1, 130, 1, 84, 96, 2, 130, 144, 10, 144, 129, 22, 96, 0, - 20, 21, 147, 80, 91, 80, 80, 80, 146, 145, 80, 80, 86, 91, 21, 97, 5, 193, 87, 80, 97, 1, 5, - 85, 86, 91, 21, 97, 5, 195, 87, 96, 0, 97, 1, 6, 85, 80, 86, 91, 21, 97, 5, 193, 87, 129, 96, - 1, 96, 160, 96, 2, 10, 3, 22, 255, 91, 21, 97, 9, 235, 87, 97, 7, 190, 132, 96, 0, 97, 14, 161, - 51, 97, 1, 102, 86, 91, 21, 97, 8, 125, 87, 127, 146, 202, 58, 128, 133, 62, 102, 99, 250, 49, - 250, 16, 185, 146, 37, 241, 141, 73, 2, 147, 155, 76, 83, 169, 202, 174, 144, 67, 246, 239, - 208, 4, 51, 133, 135, 134, 134, 96, 64, 81, 128, 134, 96, 1, 96, 160, 96, 2, 10, 3, 22, 129, - 82, 96, 32, 1, 133, 129, 82, 96, 32, 1, 132, 96, 1, 96, 160, 96, 2, 10, 3, 22, 129, 82, 96, 32, - 1, 128, 96, 32, 1, 130, 129, 3, 130, 82, 132, 132, 130, 129, 129, 82, 96, 32, 1, 146, 80, 128, - 130, 132, 55, 130, 1, 145, 80, 80, 150, 80, 80, 80, 80, 80, 80, 80, 96, 64, 81, 128, 145, 3, - 144, 161, 132, 96, 1, 96, 160, 96, 2, 10, 3, 22, 132, 132, 132, 96, 64, 81, 128, 131, 131, 128, - 130, 132, 55, 130, 1, 145, 80, 80, 146, 80, 80, 80, 96, 0, 96, 64, 81, 128, 131, 3, 129, 133, - 135, 97, 133, 2, 90, 3, 241, 80, 96, 0, 147, 80, 97, 9, 235, 146, 80, 80, 80, 86, 91, 96, 0, - 54, 67, 96, 64, 81, 128, 132, 132, 128, 130, 132, 55, 130, 1, 145, 80, 80, 130, 129, 82, 96, - 32, 1, 147, 80, 80, 80, 80, 96, 64, 81, 128, 145, 3, 144, 32, 144, 80, 128, 80, 97, 8, 176, - 129, 97, 1, 247, 86, 91, 21, 128, 21, 97, 8, 211, 87, 80, 96, 0, 129, 129, 82, 97, 1, 8, 96, - 32, 82, 96, 64, 129, 32, 84, 96, 1, 96, 160, 96, 2, 10, 3, 22, 20, 91, 21, 97, 9, 235, 87, 96, - 0, 129, 129, 82, 97, 1, 8, 96, 32, 144, 129, 82, 96, 64, 130, 32, 128, 84, 96, 1, 96, 160, 96, - 2, 10, 3, 25, 22, 136, 23, 129, 85, 96, 1, 129, 129, 1, 136, 144, 85, 96, 2, 145, 130, 1, 128, - 84, 129, 134, 82, 148, 132, 144, 32, 144, 148, 145, 130, 22, 21, 97, 1, 0, 2, 96, 0, 25, 1, - 144, 145, 22, 145, 144, 145, 4, 96, 31, 144, 129, 1, 146, 144, 146, 4, 129, 1, 145, 133, 145, - 144, 135, 144, 131, 144, 16, 97, 9, 243, 87, 96, 255, 25, 129, 53, 22, 131, 128, 1, 23, 133, - 85, 91, 80, 97, 9, 101, 146, 145, 80, 91, 128, 130, 17, 21, 97, 10, 35, 87, 96, 0, 129, 85, 96, - 1, 1, 97, 9, 81, 86, 91, 80, 80, 127, 23, 51, 203, 181, 54, 89, 215, 19, 183, 149, 128, 247, - 159, 63, 159, 242, 21, 247, 138, 124, 122, 164, 88, 144, 243, 184, 159, 197, 205, 223, 191, 50, - 129, 51, 134, 136, 135, 135, 96, 64, 81, 128, 135, 129, 82, 96, 32, 1, 134, 96, 1, 96, 160, 96, - 2, 10, 3, 22, 129, 82, 96, 32, 1, 133, 129, 82, 96, 32, 1, 132, 96, 1, 96, 160, 96, 2, 10, 3, - 22, 129, 82, 96, 32, 1, 128, 96, 32, 1, 130, 129, 3, 130, 82, 132, 132, 130, 129, 129, 82, 96, - 32, 1, 146, 80, 128, 130, 132, 55, 130, 1, 145, 80, 80, 151, 80, 80, 80, 80, 80, 80, 80, 80, - 96, 64, 81, 128, 145, 3, 144, 161, 91, 148, 147, 80, 80, 80, 80, 86, 91, 130, 128, 1, 96, 1, 1, - 133, 85, 130, 21, 97, 9, 73, 87, 145, 130, 1, 91, 130, 129, 17, 21, 97, 9, 73, 87, 130, 53, - 130, 96, 0, 80, 85, 145, 96, 32, 1, 145, 144, 96, 1, 1, 144, 97, 10, 5, 86, 91, 80, 144, 86, - 91, 21, 97, 10, 170, 87, 96, 0, 131, 129, 82, 97, 1, 8, 96, 32, 82, 96, 64, 129, 32, 84, 96, 1, - 96, 160, 96, 2, 10, 3, 22, 20, 97, 10, 170, 87, 96, 64, 128, 81, 96, 0, 145, 144, 145, 32, 128, - 84, 96, 1, 130, 129, 1, 84, 96, 2, 147, 132, 1, 128, 84, 96, 1, 96, 160, 96, 2, 10, 3, 148, - 144, 148, 22, 149, 145, 148, 144, 147, 145, 146, 131, 146, 133, 146, 145, 129, 22, 21, 97, 1, - 0, 2, 96, 0, 25, 1, 22, 4, 128, 21, 97, 10, 219, 87, 128, 96, 31, 16, 97, 10, 176, 87, 97, 1, - 0, 128, 131, 84, 4, 2, 131, 82, 145, 96, 32, 1, 145, 97, 10, 219, 86, 91, 80, 145, 144, 80, 86, - 91, 130, 1, 145, 144, 96, 0, 82, 96, 32, 96, 0, 32, 144, 91, 129, 84, 129, 82, 144, 96, 1, 1, - 144, 96, 32, 1, 128, 131, 17, 97, 10, 190, 87, 130, 144, 3, 96, 31, 22, 130, 1, 145, 91, 80, - 80, 145, 80, 80, 96, 0, 96, 64, 81, 128, 131, 3, 129, 133, 135, 97, 133, 2, 90, 3, 241, 80, 80, - 80, 96, 0, 132, 129, 82, 97, 1, 8, 96, 32, 144, 129, 82, 96, 64, 145, 130, 144, 32, 128, 84, - 96, 1, 128, 131, 1, 84, 133, 81, 51, 96, 1, 96, 160, 96, 2, 10, 3, 129, 129, 22, 131, 82, 150, - 130, 1, 140, 144, 82, 150, 129, 1, 130, 144, 82, 146, 144, 148, 22, 96, 96, 131, 1, 129, 144, - 82, 96, 160, 96, 128, 132, 1, 129, 129, 82, 96, 2, 149, 134, 1, 128, 84, 148, 133, 22, 21, 97, - 1, 0, 2, 96, 0, 25, 1, 144, 148, 22, 149, 144, 149, 4, 144, 132, 1, 129, 144, 82, 127, 231, - 201, 87, 192, 110, 154, 102, 44, 26, 108, 119, 54, 97, 121, 245, 183, 2, 185, 118, 81, 220, 40, - 238, 231, 213, 191, 29, 255, 110, 64, 187, 74, 151, 80, 138, 149, 148, 145, 147, 145, 144, 96, - 192, 131, 1, 144, 132, 144, 128, 21, 97, 11, 221, 87, 128, 96, 31, 16, 97, 11, 178, 87, 97, 1, - 0, 128, 131, 84, 8, 121, 67, 138, 219, 160, 71, 225, 126, 52, 94, 21, 147, 56, 116, 52, 184, - 15, 143, 35, 40, 0, 202, 204, 100, 190, 227, 189, 157, 230, 3, 80, 20, 109, 4, 133, 51, 22, - 219, 64, 206, 194, 55, 75, 145, 50, 59, 62, 215, 27, 78, 138, 51, 23, 36, 192, 229, 203, 64, - 174, 13, 203, 22, 208, 198, 255, 98, 218, 70, 142, 127, 88, 47, 204, 109, 250, 104, 97, 146, - 218, 33, 84, 177, 208, 90, 58, 166, 76, 4, 1, 150, 135, 4, 168, 23, 200, 0, 1, 95, 144, 238, - 194, 83, 171, 177, 146, 101, 4, 216, 28, 240, 66, 45, 42, 82, 249, 69, 191, 194, 125, 13, 228, - 205, 230, 107, 236, 180, 216, 0, 68, 249, 190, 64, 45, 237, 213, 102, 237, 229, 123, 245, 188, - 138, 55, 245, 176, 159, 151, 62, 96, 220, 127, 148, 219, 154, 136, 94, 193, 54, 122, 237, 77, - 113, 158, 136, 147, 247, 215, 244, 33, 12, 192, 159, 176, 213, 229, 212, 238, 38, 45, 52, 216, - 59, 140, 14, 34, 146, 55, 91, 107, 108, 226, 110, 164, 76, 0, 2, 36, 4, 168, 23, 200, 0, 2, - 147, 154, 75, 158, 13, 34, 77, 171, 204, 150, 25, 28, 172, 226, 211, 103, 168, 216, 183, 92, - 156, 129, 200, 238, 12, 107, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 232, 20, 199, 209, 16, 111, - 175, 42, 207, 27, 79, 6, 239, 64, 148, 254, 147, 29, 123, 224, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 71, 13, 228, 223, 130, 0, 1, 101, 108, 99, 111, 105, - 110, 95, 112, 114, 111, 100, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 128, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 1, 48, 204, 219, 93, 183, 71, 115, 208, 120, 167, 181, 169, 29, 205, 144, 76, 222, 225, 204, - 97, 250, 237, 154, 100, 85, 69, 105, 30, 175, 79, 37, 51, 59, 134, 150, 119, 243, 79, 82, 58, - 13, 206, 222, 234, 43, 43, 228, 189, 179, 153, 57, 45, 158, 209, 218, 164, 22, 71, 253, 250, - 188, 32, 192, 10, 166, 76, 4, 1, 150, 136, 4, 168, 23, 200, 0, 1, 95, 144, 139, 123, 231, 22, - 83, 6, 4, 47, 93, 146, 103, 91, 242, 232, 164, 73, 134, 149, 193, 198, 14, 150, 199, 171, 199, - 40, 64, 248, 1, 82, 225, 182, 220, 0, 10, 156, 153, 254, 12, 197, 159, 158, 76, 251, 31, 95, - 72, 152, 84, 91, 182, 190, 239, 242, 165, 38, 112, 175, 123, 211, 74, 52, 169, 115, 216, 75, - 74, 6, 230, 177, 44, 177, 98, 135, 205, 55, 169, 166, 116, 93, 166, 138, 148, 85, 86, 21, 99, - 119, 180, 237, 82, 167, 117, 162, 72, 4, 156, 4, 168, 23, 200, 0, 86, 34, 251, 177, 183, 60, - 79, 11, 218, 79, 103, 220, 162, 102, 206, 110, 244, 47, 82, 15, 187, 152, 17, 45, 176, 28, 184, - 162, 188, 0, 0, 63, 9, 224, 232, 128, 21, 250, 143, 200, 159, 137, 16, 79, 207, 157, 176, 110, - 170, 122, 167, 86, 187, 125, 81, 106, 40, 43, 239, 232, 79, 169, 59, 214, 244, 88, 54, 188, - 199, 251, 100, 31, 188, 33, 204, 236, 76, 176, 94, 229, 80, 170, 195, 91, 182, 95, 196, 93, - 150, 42, 244, 208, 8, 207, 3, 166, 76, 4, 1, 150, 137, 4, 168, 23, 200, 0, 1, 95, 144, 167, - 178, 151, 182, 138, 125, 247, 74, 37, 59, 59, 145, 24, 155, 113, 76, 165, 245, 246, 89, 2, 185, - 245, 217, 237, 194, 35, 0, 0, 29, 239, 187, 43, 1, 150, 135, 252, 237, 101, 53, 56, 47, 108, 0, - 175, 153, 231, 95, 37, 179, 216, 182, 145, 245, 152, 96, 11, 33, 39, 24, 56, 53, 45, 74, 172, - 38, 42, 130, 113, 165, 138, 86, 237, 119, 209, 24, 219, 117, 115, 99, 115, 243, 78, 112, 152, - 17, 120, 54, 203, 137, 95, 240, 32, 160, 204, 0, 4, 168, 23, 200, 0, 13, 187, 160, 87, 217, 11, - 100, 161, 165, 119, 73, 176, 249, 50, 241, 163, 57, 87, 146, 225, 46, 112, 85, 1, 169, 5, 156, - 187, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 193, 67, 206, 31, 146, 153, 252, 222, 203, 220, 226, - 150, 64, 240, 233, 135, 56, 160, 237, 205, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 26, 68, 8, 1, 131, 162, 54, 83, 182, 29, 138, 127, 20, 164, - 149, 163, 20, 11, 126, 222, 63, 7, 242, 87, 54, 253, 10, 142, 8, 94, 26, 250, 181, 11, 168, - 105, 208, 183, 141, 159, 232, 234, 97, 181, 108, 236, 146, 15, 110, 244, 44, 215, 254, 243, 94, - 121, 44, 24, 85, 231, 203, 135, 234, 38, 164, 61, 142, 11, 166, 76, 4, 1, 150, 138, 4, 168, 23, - 200, 0, 1, 95, 144, 19, 225, 89, 164, 66, 4, 20, 14, 7, 95, 196, 153, 74, 175, 52, 212, 100, 8, - 204, 93, 1, 97, 0, 215, 23, 187, 2, 60, 0, 183, 78, 143, 115, 240, 164, 177, 231, 169, 54, 162, - 175, 189, 6, 46, 190, 36, 179, 150, 200, 179, 106, 175, 179, 198, 198, 43, 107, 112, 178, 180, - 206, 38, 172, 27, 137, 210, 223, 235, 3, 206, 242, 67, 146, 224, 42, 242, 100, 103, 253, 35, - 79, 218, 10, 217, 16, 121, 36, 73, 29, 56, 27, 120, 2, 166, 76, 4, 1, 71, 86, 4, 168, 23, 200, - 0, 1, 95, 144, 88, 14, 111, 71, 85, 231, 104, 235, 159, 69, 107, 15, 105, 239, 110, 250, 156, - 128, 233, 17, 68, 177, 238, 198, 22, 47, 0, 0, 1, 219, 77, 196, 38, 162, 9, 9, 124, 39, 66, 95, - 139, 53, 35, 33, 105, 85, 42, 84, 25, 2, 239, 187, 152, 123, 139, 249, 101, 132, 222, 108, 94, - 38, 11, 63, 177, 91, 133, 129, 209, 182, 166, 166, 243, 156, 182, 235, 106, 205, 182, 81, 146, - 10, 21, 62, 49, 210, 92, 242, 168, 142, 11, 249, 93, 164, 204, 4, 117, 100, 4, 168, 23, 200, 0, - 2, 73, 240, 51, 172, 127, 159, 93, 93, 150, 15, 253, 23, 84, 132, 20, 187, 104, 228, 151, 24, - 33, 52, 5, 29, 214, 112, 142, 247, 70, 96, 0, 0, 91, 8, 247, 52, 245, 207, 222, 238, 199, 227, - 31, 99, 234, 83, 242, 230, 123, 154, 49, 0, 9, 133, 150, 217, 120, 201, 248, 161, 206, 190, 9, - 242, 36, 226, 146, 201, 251, 22, 75, 70, 207, 93, 91, 115, 145, 165, 233, 188, 242, 28, 239, - 59, 183, 92, 108, 216, 126, 176, 52, 247, 63, 27, 41, 32, 166, 76, 4, 1, 71, 87, 4, 168, 23, - 200, 0, 1, 95, 144, 220, 106, 84, 47, 151, 135, 105, 35, 88, 48, 25, 181, 38, 51, 95, 209, 251, - 13, 252, 106, 68, 212, 25, 88, 215, 50, 182, 0, 0, 156, 35, 91, 41, 228, 95, 246, 212, 163, 81, - 198, 168, 118, 164, 127, 9, 213, 40, 121, 41, 115, 17, 141, 110, 240, 247, 142, 62, 112, 21, - 56, 225, 194, 140, 229, 134, 14, 135, 204, 123, 25, 76, 128, 99, 152, 170, 189, 246, 232, 192, - 166, 231, 208, 136, 28, 213, 37, 164, 107, 119, 108, 243, 202, 24, 162, 200, 4, 14, 4, 168, 23, - 200, 0, 82, 8, 210, 68, 0, 174, 139, 254, 187, 24, 202, 73, 190, 134, 37, 138, 60, 116, 156, - 244, 104, 83, 54, 53, 162, 218, 162, 7, 151, 48, 0, 0, 1, 26, 106, 49, 219, 173, 102, 36, 92, - 7, 125, 154, 244, 113, 244, 229, 98, 129, 85, 80, 253, 221, 127, 48, 121, 79, 217, 79, 234, - 149, 244, 118, 53, 216, 237, 117, 159, 75, 180, 148, 89, 1, 250, 148, 155, 121, 126, 197, 79, - 248, 105, 163, 251, 108, 233, 143, 45, 23, 104, 130, 208, 61, 237, 3, 166, 76, 4, 1, 71, 88, 4, - 168, 23, 200, 0, 1, 95, 144, 208, 49, 17, 21, 137, 203, 74, 112, 136, 220, 184, 100, 237, 9, - 140, 194, 149, 112, 210, 191, 68, 182, 93, 71, 8, 19, 237, 100, 176, 136, 45, 80, 240, 119, 49, - 193, 49, 23, 71, 105, 187, 26, 105, 155, 5, 94, 82, 213, 101, 96, 157, 134, 56, 126, 197, 58, - 171, 185, 206, 83, 166, 76, 4, 1, 106, 192, 4, 174, 85, 185, 142, 1, 95, 144, 210, 142, 145, - 12, 181, 118, 24, 75, 238, 212, 118, 52, 76, 9, 142, 173, 225, 132, 157, 239, 13, 240, 252, 24, - 76, 157, 37, 96, 0, 208, 159, 90, 182, 63, 210, 212, 232, 130, 227, 99, 164, 94, 32, 225, 73, - 111, 99, 53, 244, 114, 249, 248, 23, 176, 192, 192, 214, 121, 140, 236, 140, 143, 250, 194, 6, - 230, 69, 24, 219, 192, 241, 49, 208, 13, 108, 20, 188, 138, 16, 110, 38, 1, 49, 73, 223, 79, - 165, 113, 156, 5, 117, 241, 60, 166, 76, 4, 1, 106, 193, 4, 168, 23, 200, 0, 1, 95, 144, 63, - 202, 191, 226, 251, 212, 73, 47, 179, 47, 218, 192, 151, 89, 184, 211, 112, 148, 45, 35, 13, - 248, 13, 8, 228, 137, 157, 16, 1, 238, 247, 35, 233, 13, 232, 21, 254, 26, 28, 186, 144, 242, - 72, 117, 216, 171, 72, 166, 203, 126, 148, 21, 125, 183, 243, 154, 191, 228, 228, 35, 155, 69, - 28, 75, 62, 197, 36, 80, 149, 36, 86, 111, 190, 226, 71, 39, 36, 247, 222, 0, 253, 53, 138, 87, - 233, 147, 65, 155, 28, 173, 219, 187, 16, 160, 72, 4, 4, 168, 23, 200, 0, 82, 8, 4, 144, 41, - 221, 65, 102, 30, 88, 249, 146, 113, 160, 17, 45, 253, 52, 105, 95, 112, 0, 6, 238, 221, 92, - 244, 227, 192, 0, 0, 182, 185, 82, 170, 43, 165, 219, 84, 69, 13, 234, 105, 214, 3, 231, 75, - 110, 2, 50, 216, 23, 52, 77, 201, 65, 70, 131, 108, 63, 156, 93, 14, 173, 224, 180, 246, 68, - 17, 243, 4, 243, 170, 44, 95, 86, 89, 222, 101, 160, 59, 39, 24, 108, 59, 50, 78, 228, 155, - 243, 78, 39, 31, 147, 72, 162, 204, 4, 152, 4, 168, 23, 200, 0, 1, 95, 144, 113, 128, 235, 57, - 166, 38, 73, 56, 253, 179, 239, 253, 115, 65, 196, 114, 124, 56, 33, 83, 8, 39, 8, 117, 186, - 77, 221, 65, 198, 1, 169, 143, 91, 55, 79, 82, 200, 50, 148, 60, 224, 202, 164, 123, 5, 58, - 163, 183, 156, 33, 145, 136, 17, 235, 56, 33, 174, 53, 141, 148, 172, 194, 255, 212, 94, 214, - 90, 121, 221, 207, 56, 40, 99, 29, 225, 132, 72, 212, 195, 242, 179, 22, 215, 222, 144, 55, - 177, 231, 88, 61, 63, 2, 96, 55, 164, 76, 4, 249, 150, 6, 252, 35, 172, 0, 5, 22, 21, 187, 155, - 194, 68, 215, 152, 18, 63, 222, 120, 63, 204, 28, 114, 211, 187, 140, 24, 148, 19, 184, 96, 21, - 120, 245, 97, 12, 0, 186, 172, 83, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 131, 128, 48, 186, - 124, 62, 131, 26, 106, 226, 167, 108, 72, 142, 160, 240, 13, 234, 86, 66, 1, 167, 246, 13, 152, - 39, 95, 80, 92, 240, 40, 212, 128, 15, 190, 65, 247, 125, 162, 61, 210, 175, 26, 215, 193, 100, - 65, 238, 72, 30, 199, 39, 113, 46, 119, 210, 122, 177, 110, 208, 11, 43, 239, 226, 115, 151, - 81, 58, 123, 13, 165, 24, 234, 69, 0, 1, 218, 74, 240, 141, 61, 206, 165, 152, 75, 160, 12, 0, - 5, 36, 188, 156, 219, 15, 66, 64, 96, 96, 96, 64, 82, 96, 0, 128, 84, 96, 1, 96, 160, 96, 2, - 10, 3, 25, 22, 51, 23, 144, 85, 97, 1, 115, 128, 97, 0, 36, 96, 0, 57, 96, 0, 243, 96, 96, 96, - 64, 82, 54, 21, 97, 0, 53, 87, 96, 224, 96, 2, 10, 96, 0, 53, 4, 99, 41, 181, 179, 12, 129, 20, - 97, 0, 126, 87, 128, 99, 65, 192, 225, 181, 20, 97, 1, 6, 87, 128, 99, 229, 34, 83, 129, 20, - 97, 1, 48, 87, 91, 97, 1, 113, 96, 0, 52, 17, 21, 97, 0, 124, 87, 52, 96, 96, 144, 129, 82, 96, - 88, 144, 96, 1, 96, 160, 96, 2, 10, 3, 51, 22, 144, 127, 144, 137, 8, 9, 198, 84, 241, 29, 110, - 114, 162, 143, 166, 1, 73, 119, 10, 13, 17, 236, 108, 146, 49, 157, 108, 235, 43, 176, 164, - 234, 26, 21, 144, 96, 32, 144, 163, 91, 86, 91, 97, 1, 113, 96, 4, 53, 96, 36, 53, 96, 68, 53, - 96, 0, 128, 84, 96, 1, 96, 160, 96, 2, 10, 3, 144, 129, 22, 51, 145, 144, 145, 22, 20, 21, 97, - 1, 0, 87, 127, 169, 5, 156, 187, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 96, 96, 144, 129, 82, 96, 1, 96, 160, 96, 2, 10, 3, 132, 129, 22, 96, 100, - 82, 96, 132, 132, 144, 82, 133, 146, 144, 131, 22, 145, 99, 169, 5, 156, 187, 145, 96, 164, - 145, 96, 68, 129, 131, 135, 97, 97, 218, 90, 3, 241, 21, 97, 0, 2, 87, 80, 80, 80, 91, 80, 80, - 80, 80, 86, 91, 97, 1, 113, 96, 0, 84, 96, 1, 96, 160, 96, 2, 10, 3, 144, 129, 22, 51, 145, - 144, 145, 22, 20, 21, 97, 0, 124, 87, 96, 0, 84, 96, 1, 96, 160, 96, 2, 10, 3, 22, 255, 91, 97, - 1, 113, 96, 0, 84, 96, 1, 96, 160, 96, 2, 10, 3, 144, 129, 22, 51, 145, 144, 145, 22, 20, 21, - 97, 0, 124, 87, 96, 0, 128, 84, 96, 1, 96, 160, 96, 2, 10, 3, 144, 129, 22, 145, 144, 48, 22, - 49, 96, 96, 130, 129, 129, 129, 133, 136, 131, 241, 80, 80, 80, 80, 80, 86, 91, 0, 1, 1, 225, - 136, 15, 53, 139, 127, 7, 117, 0, 108, 81, 237, 190, 16, 139, 45, 215, 225, 119, 94, 208, 106, - 127, 104, 254, 234, 152, 89, 192, 142, 56, 230, 82, 27, 197, 195, 115, 81, 195, 173, 99, 63, - 92, 241, 165, 27, 7, 171, 131, 197, 198, 213, 107, 56, 241, 128, 249, 161, 33, 239, 132, 227, - 83, 162, 72, 4, 8, 4, 168, 23, 200, 0, 82, 8, 145, 51, 122, 48, 14, 3, 97, 189, 219, 46, 55, - 125, 212, 232, 140, 203, 119, 150, 102, 61, 7, 16, 248, 131, 34, 187, 192, 0, 1, 249, 134, 231, - 117, 65, 143, 45, 2, 7, 125, 36, 126, 163, 186, 38, 202, 244, 244, 205, 122, 164, 10, 59, 129, - 12, 235, 69, 48, 209, 220, 68, 112, 108, 63, 107, 238, 120, 210, 220, 129, 41, 1, 90, 127, 16, - 109, 153, 237, 218, 222, 162, 169, 94, 176, 211, 190, 7, 50, 64, 226, 36, 99, 24, 64, 164, 200, - 4, 70, 56, 4, 168, 23, 200, 0, 86, 34, 98, 247, 114, 83, 65, 49, 227, 152, 169, 143, 221, 9, - 75, 83, 255, 85, 106, 251, 54, 228, 8, 33, 135, 134, 81, 164, 215, 0, 0, 1, 2, 133, 96, 103, - 77, 222, 43, 10, 127, 27, 223, 246, 24, 95, 153, 151, 104, 221, 25, 72, 74, 88, 89, 12, 210, - 158, 49, 176, 97, 131, 51, 125, 151, 229, 84, 174, 78, 65, 182, 243, 111, 59, 58, 196, 19, 84, - 183, 46, 6, 88, 255, 99, 58, 36, 66, 108, 23, 47, 216, 123, 2, 3, 120, 36, 162, 72, 4, 45, 11, - 164, 59, 116, 0, 82, 8, 199, 80, 237, 161, 28, 78, 204, 113, 13, 39, 55, 22, 227, 45, 231, 225, - 63, 143, 162, 204, 20, 209, 18, 13, 123, 22, 0, 0, 0, 25, 247, 164, 138, 44, 66, 128, 3, 72, - 209, 175, 24, 10, 111, 106, 78, 38, 255, 53, 26, 44, 69, 112, 187, 183, 244, 168, 252, 178, - 160, 108, 243, 157, 125, 186, 80, 215, 20, 33, 224, 1, 124, 183, 167, 139, 103, 20, 78, 131, - 72, 8, 75, 219, 56, 8, 207, 186, 213, 137, 90, 190, 91, 182, 106, 162, 72, 4, 16, 6, 252, 35, - 172, 0, 82, 8, 50, 190, 52, 59, 148, 248, 96, 18, 77, 196, 254, 226, 120, 253, 203, 211, 140, - 16, 45, 136, 13, 224, 25, 189, 189, 203, 152, 0, 0, 57, 176, 23, 49, 52, 4, 170, 103, 7, 77, - 111, 17, 167, 172, 172, 24, 137, 85, 216, 158, 134, 37, 59, 55, 238, 250, 138, 77, 104, 48, 6, - 211, 74, 132, 53, 242, 213, 122, 195, 128, 97, 80, 40, 250, 78, 18, 12, 55, 76, 143, 141, 48, - 103, 239, 24, 198, 121, 121, 73, 59, 160, 69, 151, 29, 162, 200, 4, 3, 6, 252, 35, 172, 0, 82, - 8, 205, 52, 254, 74, 98, 171, 186, 42, 247, 183, 19, 103, 84, 236, 87, 112, 226, 55, 232, 74, - 242, 224, 235, 184, 116, 115, 26, 222, 140, 132, 206, 1, 174, 130, 189, 31, 6, 218, 123, 169, - 48, 66, 16, 93, 30, 97, 140, 241, 93, 174, 139, 138, 252, 57, 166, 76, 4, 2, 172, 59, 4, 168, - 23, 200, 0, 1, 95, 144, 240, 165, 79, 59, 103, 105, 94, 198, 98, 48, 182, 232, 158, 123, 1, 86, - 139, 60, 141, 147, 15, 120, 101, 84, 46, 36, 128, 0, 1, 199, 53, 181, 189, 42, 170, 231, 153, - 9, 217, 83, 164, 90, 67, 27, 168, 107, 114, 230, 17, 232, 31, 238, 8, 53, 83, 76, 219, 120, - 196, 232, 42, 100, 123, 212, 62, 157, 222, 226, 254, 16, 203, 68, 203, 100, 164, 77, 139, 162, - 238, 174, 174, 213, 82, 75, 157, 52, 241, 50, 91, 241, 251, 253, 122, 164, 76, 4, 36, 184, 4, - 168, 23, 200, 0, 1, 95, 144, 242, 136, 245, 207, 46, 249, 233, 163, 8, 56, 52, 227, 14, 29, - 191, 9, 161, 233, 55, 67, 1, 124, 94, 101, 57, 136, 112, 0, 0, 37, 150, 73, 124, 235, 251, 109, - 101, 62, 94, 240, 117, 39, 146, 222, 22, 111, 250, 22, 67, 189, 176, 59, 157, 75, 61, 15, 118, - 100, 239, 224, 203, 28, 218, 148, 118, 176, 18, 234, 4, 90, 213, 50, 168, 28, 62, 141, 130, - 225, 112, 67, 86, 247, 5, 119, 211, 58, 22, 193, 115, 240, 150, 112, 61, 166, 76, 4, 2, 172, - 60, 4, 168, 23, 200, 0, 1, 95, 144, 184, 239, 226, 167, 118, 144, 116, 27, 135, 61, 231, 170, - 160, 203, 191, 14, 101, 147, 134, 69, 15, 152, 45, 131, 137, 131, 124, 0, 1, 251, 111, 20, 87, - 195, 102, 152, 32, 69, 228, 132, 192, 53, 174, 117, 226, 118, 199, 152, 0, 89, 134, 63, 3, 91, - 23, 134, 25, 186, 135, 137, 43, 172, 81, 137, 128, 129, 255, 65, 12, 73, 98, 59, 137, 121, 254, - 97, 210, 181, 129, 115, 143, 36, 201, 58, 100, 33, 176, 46, 137, 39, 86, 105, 81, 164, 76, 4, - 36, 185, 4, 168, 23, 200, 0, 1, 95, 144, 147, 70, 217, 55, 158, 158, 174, 131, 128, 182, 173, - 135, 202, 117, 113, 104, 127, 23, 2, 73, 1, 110, 222, 241, 110, 151, 100, 0, 0, 116, 25, 246, - 88, 208, 16, 118, 104, 205, 135, 73, 198, 90, 211, 239, 144, 64, 55, 249, 172, 164, 158, 35, - 159, 92, 180, 162, 222, 55, 75, 236, 123, 189, 130, 200, 172, 79, 23, 49, 140, 52, 153, 21, - 247, 192, 27, 160, 219, 113, 49, 91, 81, 151, 211, 206, 217, 117, 72, 83, 182, 63, 169, 200, - 53, 166, 76, 4, 2, 172, 61, 4, 168, 23, 200, 0, 1, 95, 144, 54, 42, 35, 85, 44, 60, 56, 110, - 119, 71, 32, 12, 109, 221, 190, 87, 113, 250, 198, 32, 15, 162, 234, 4, 18, 18, 64, 0, 1, 52, - 69, 71, 169, 101, 26, 93, 65, 119, 157, 139, 78, 138, 102, 58, 166, 253, 65, 109, 89, 37, 118, - 82, 55, 129, 110, 17, 245, 38, 160, 163, 26, 71, 196, 227, 47, 139, 68, 74, 237, 45, 200, 153, - 233, 97, 190, 97, 109, 65, 77, 16, 182, 204, 148, 43, 186, 6, 19, 183, 212, 167, 127, 200, 32, - 164, 204, 3, 36, 186, 4, 168, 23, 200, 0, 1, 95, 144, 70, 85, 226, 106, 246, 241, 57, 112, 47, - 220, 18, 11, 191, 218, 231, 33, 114, 247, 63, 118, 173, 106, 39, 196, 67, 244, 0, 1, 61, 249, - 168, 190, 254, 159, 216, 94, 179, 69, 74, 97, 152, 224, 61, 96, 250, 135, 51, 29, 107, 230, 59, - 6, 148, 8, 159, 3, 227, 143, 14, 35, 81, 240, 65, 31, 85, 253, 39, 137, 102, 181, 156, 23, 97, - 168, 66, 200, 35, 22, 10, 55, 88, 103, 14, 231, 7, 223, 213, 37, 52, 1, 225, 12, 166, 76, 4, 2, - 172, 62, 4, 168, 23, 200, 0, 1, 95, 144, 63, 32, 68, 56, 43, 49, 182, 217, 42, 42, 20, 254, 78, - 189, 37, 141, 42, 133, 218, 223, 15, 170, 54, 32, 138, 124, 132, 0, 1, 95, 134, 179, 129, 103, - 86, 158, 27, 124, 252, 8, 167, 152, 56, 167, 98, 204, 30, 223, 133, 95, 141, 219, 130, 144, - 116, 99, 220, 156, 211, 2, 65, 218, 73, 232, 113, 130, 128, 24, 100, 217, 39, 197, 98, 138, - 193, 15, 32, 138, 9, 220, 80, 59, 87, 29, 172, 23, 247, 157, 125, 151, 6, 145, 95, 164, 76, 4, - 36, 187, 4, 168, 23, 200, 0, 1, 95, 144, 178, 250, 154, 197, 6, 147, 118, 69, 33, 17, 86, 134, - 69, 79, 66, 92, 209, 221, 55, 214, 15, 49, 233, 246, 166, 244, 92, 0, 1, 101, 176, 208, 221, - 129, 42, 95, 53, 248, 94, 177, 233, 78, 192, 145, 25, 135, 135, 191, 156, 150, 62, 93, 224, 59, - 56, 160, 137, 168, 72, 250, 205, 125, 191, 216, 90, 237, 82, 68, 26, 54, 88, 177, 226, 108, - 251, 106, 115, 160, 73, 78, 146, 133, 104, 12, 5, 129, 121, 169, 99, 141, 127, 84, 3, 166, 76, - 4, 2, 172, 63, 4, 168, 23, 200, 0, 1, 95, 144, 160, 233, 213, 190, 166, 70, 74, 22, 175, 192, - 242, 106, 95, 215, 6, 211, 125, 89, 23, 209, 15, 173, 143, 157, 237, 172, 108, 0, 1, 11, 71, 1, - 84, 77, 210, 184, 28, 236, 10, 51, 222, 101, 118, 175, 138, 14, 183, 34, 44, 15, 151, 162, 160, - 223, 177, 127, 222, 218, 106, 62, 92, 90, 18, 240, 86, 57, 229, 68, 238, 35, 190, 171, 30, 146, - 121, 73, 100, 101, 77, 119, 169, 10, 69, 46, 240, 216, 52, 95, 56, 32, 195, 110, 114, 164, 76, - 4, 36, 188, 4, 168, 23, 200, 0, 1, 95, 144, 137, 231, 8, 120, 115, 217, 249, 234, 248, 76, 146, - 34, 10, 224, 196, 32, 191, 190, 76, 126, 13, 238, 37, 44, 127, 157, 64, 0, 0, 207, 183, 82, 13, - 149, 60, 65, 16, 240, 41, 47, 214, 187, 243, 177, 98, 139, 198, 42, 23, 10, 107, 145, 95, 11, - 14, 30, 190, 193, 143, 198, 238, 110, 21, 116, 135, 67, 246, 63, 126, 139, 208, 223, 6, 98, - 253, 162, 95, 144, 91, 227, 20, 220, 141, 85, 106, 170, 150, 124, 54, 46, 125, 184, 103, 166, - 76, 4, 2, 172, 64, 4, 168, 23, 200, 0, 1, 95, 144, 199, 230, 43, 241, 251, 196, 159, 70, 162, - 148, 198, 38, 19, 254, 199, 121, 56, 171, 226, 48, 15, 178, 35, 100, 69, 232, 196, 0, 1, 216, - 118, 109, 114, 238, 147, 26, 138, 77, 24, 240, 2, 184, 92, 118, 206, 38, 0, 69, 93, 111, 251, - 90, 73, 220, 49, 144, 53, 24, 154, 237, 29, 157, 177, 206, 44, 55, 220, 45, 19, 144, 100, 248, - 2, 123, 164, 73, 202, 168, 199, 172, 36, 216, 179, 136, 82, 61, 202, 184, 149, 195, 153, 120, - 80, 164, 76, 4, 36, 189, 4, 168, 23, 200, 0, 1, 95, 144, 166, 2, 244, 114, 25, 178, 88, 153, - 81, 2, 164, 101, 215, 18, 248, 181, 184, 110, 194, 174, 2, 36, 94, 148, 179, 171, 240, 0, 1, - 80, 136, 122, 83, 109, 244, 237, 30, 55, 133, 248, 210, 135, 83, 123, 238, 132, 136, 124, 138, - 95, 235, 59, 184, 200, 135, 159, 113, 77, 230, 12, 145, 173, 46, 179, 242, 71, 78, 202, 79, - 166, 81, 28, 237, 64, 145, 159, 171, 64, 191, 242, 182, 188, 101, 64, 187, 163, 116, 107, 27, - 144, 103, 253, 94, 166, 76, 4, 2, 172, 65, 4, 168, 23, 200, 0, 1, 95, 144, 165, 186, 180, 108, - 240, 153, 11, 196, 106, 12, 156, 81, 117, 246, 1, 235, 72, 111, 74, 195, 15, 181, 159, 252, - 236, 98, 124, 0, 1, 98, 100, 163, 112, 162, 242, 143, 173, 90, 101, 241, 123, 233, 251, 201, - 31, 246, 43, 23, 176, 136, 192, 155, 109, 145, 58, 123, 23, 155, 234, 101, 10, 141, 214, 185, - 140, 227, 75, 134, 57, 200, 52, 27, 110, 7, 69, 229, 188, 223, 67, 182, 78, 26, 3, 29, 99, 159, - 156, 100, 56, 202, 117, 85, 4, 160, 72, 0, 4, 168, 23, 200, 0, 194, 236, 187, 155, 194, 68, - 215, 152, 18, 63, 222, 120, 63, 204, 28, 114, 211, 187, 140, 24, 148, 19, 9, 94, 167, 179, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 251, 177, 183, 60, 79, 11, 218, 79, 103, 220, 162, 102, 206, - 110, 244, 47, 82, 15, 187, 152, 255, 255, 255, 255, 255, 255, 255, 246, 156, 25, 209, 155, 189, - 236, 51, 47, 229, 9, 170, 245, 219, 112, 73, 154, 0, 88, 48, 204, 53, 253, 142, 17, 23, 155, - 120, 89, 126, 164, 204, 4, 221, 246, 6, 252, 35, 172, 0, 5, 22, 21, 50, 211, 254, 174, 189, 10, - 242, 184, 165, 162, 13, 203, 238, 21, 183, 66, 168, 255, 133, 135, 17, 106, 240, 18, 238, 65, - 71, 60, 0, 1, 82, 166, 236, 134, 46, 235, 9, 52, 126, 109, 96, 238, 19, 38, 62, 115, 163, 158, - 185, 248, 8, 150, 171, 134, 184, 240, 75, 66, 154, 106, 229, 133, 220, 138, 151, 149, 66, 109, - 160, 161, 187, 182, 169, 26, 138, 85, 95, 218, 222, 218, 61, 36, 12, 16, 106, 140, 147, 131, - 119, 232, 57, 156, 47, 5, 164, 204, 4, 221, 247, 6, 252, 35, 172, 0, 5, 22, 21, 124, 37, 192, - 184, 240, 15, 74, 115, 91, 203, 192, 254, 210, 11, 181, 46, 158, 57, 167, 180, 1, 21, 142, 70, - 9, 19, 208, 0, 0, 1, 58, 139, 59, 163, 83, 33, 102, 115, 250, 113, 104, 32, 100, 15, 222, 2, - 233, 8, 161, 167, 73, 82, 62, 178, 219, 57, 42, 145, 55, 220, 31, 226, 104, 245, 15, 127, 121, - 53, 108, 168, 139, 229, 20, 180, 205, 131, 48, 194, 86, 46, 48, 100, 124, 21, 106, 5, 116, 78, - 79, 26, 190, 143, 23, 53, 166, 76, 4, 1, 77, 115, 4, 168, 23, 200, 0, 1, 95, 144, 119, 109, - 199, 69, 186, 130, 79, 131, 200, 240, 213, 73, 163, 83, 187, 110, 175, 58, 120, 206, 14, 12, - 228, 253, 233, 246, 152, 12, 1, 167, 99, 37, 197, 81, 46, 27, 63, 94, 153, 222, 83, 46, 13, - 182, 209, 143, 232, 11, 242, 122, 0, 197, 84, 67, 39, 73, 238, 249, 136, 194, 24, 114, 82, 14, - 60, 109, 192, 95, 60, 10, 45, 61, 29, 159, 162, 96, 154, 233, 166, 216, 1, 200, 3, 201, 135, - 72, 219, 60, 43, 125, 88, 192, 97, 162, 76, 4, 4, 4, 168, 23, 200, 0, 1, 95, 144, 168, 38, 87, - 147, 108, 234, 45, 52, 243, 145, 133, 57, 184, 125, 53, 252, 19, 7, 246, 241, 79, 112, 66, 194, - 156, 57, 116, 0, 0, 10, 10, 19, 83, 91, 89, 55, 77, 177, 216, 8, 241, 144, 99, 245, 174, 3, - 246, 213, 109, 140, 155, 59, 0, 193, 63, 62, 237, 133, 224, 29, 135, 196, 87, 68, 89, 217, 242, - 144, 148, 86, 224, 37, 173, 79, 213, 179, 165, 230, 247, 135, 159, 46, 45, 254, 9, 61, 253, 54, - 164, 25, 109, 78, 92, 164, 76, 4, 2, 48, 4, 168, 23, 200, 0, 1, 95, 144, 166, 165, 238, 31, - 186, 34, 50, 114, 54, 194, 51, 171, 114, 127, 100, 171, 214, 78, 148, 221, 3, 11, 31, 160, 64, - 204, 232, 0, 1, 137, 155, 137, 38, 240, 233, 21, 217, 85, 251, 113, 21, 243, 251, 138, 81, 12, - 89, 207, 105, 33, 225, 248, 173, 71, 187, 216, 114, 47, 45, 244, 53, 205, 60, 85, 221, 76, 78, - 0, 134, 24, 108, 145, 251, 96, 196, 186, 147, 136, 60, 199, 140, 157, 36, 201, 178, 243, 231, - 225, 201, 25, 74, 223, 106, 162, 76, 4, 7, 4, 168, 23, 200, 0, 2, 50, 219, 187, 155, 194, 68, - 215, 152, 18, 63, 222, 120, 63, 204, 28, 114, 211, 187, 140, 24, 148, 19, 6, 240, 91, 89, 211, - 178, 0, 0, 1, 232, 114, 100, 52, 92, 0, 165, 26, 83, 253, 91, 228, 85, 180, 12, 123, 220, 201, - 244, 0, 130, 244, 163, 2, 59, 104, 162, 116, 5, 200, 246, 237, 139, 114, 61, 232, 180, 156, - 177, 173, 35, 32, 191, 82, 115, 102, 50, 118, 76, 199, 208, 239, 129, 166, 195, 200, 99, 192, - 6, 90, 104, 87, 47, 25, 164, 76, 4, 2, 49, 4, 168, 23, 200, 0, 1, 95, 144, 171, 76, 252, 179, - 178, 239, 14, 65, 222, 191, 252, 34, 124, 71, 171, 238, 14, 13, 156, 25, 11, 64, 147, 43, 249, - 44, 92, 0, 1, 221, 141, 70, 95, 74, 50, 14, 57, 243, 144, 200, 212, 107, 249, 103, 76, 121, - 135, 72, 187, 165, 255, 132, 207, 251, 121, 127, 167, 93, 33, 73, 6, 189, 118, 203, 96, 222, - 103, 171, 196, 250, 232, 152, 71, 245, 231, 163, 236, 67, 121, 54, 21, 119, 8, 252, 158, 139, - 222, 36, 57, 246, 110, 242, 44, 164, 76, 0, 25, 171, 4, 168, 23, 200, 0, 45, 198, 192, 199, - 105, 107, 39, 131, 13, 216, 170, 72, 35, 161, 203, 168, 68, 12, 39, 195, 106, 222, 196, 145, - 183, 245, 237, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 3, 226, 0, 68, 200, 21, 5, 174, 2, 84, 99, 92, 34, 36, 34, 137, 44, 237, 36, 149, 27, - 244, 186, 194, 154, 244, 172, 225, 130, 46, 54, 254, 17, 166, 16, 189, 179, 175, 22, 142, 74, - 57, 174, 227, 155, 248, 4, 61, 10, 41, 157, 34, 173, 123, 193, 65, 115, 132, 109, 120, 214, - 107, 5, 116, 175, 77, 17, 164, 76, 4, 2, 50, 4, 168, 23, 200, 0, 1, 95, 144, 194, 246, 175, - 242, 59, 53, 168, 159, 63, 9, 181, 0, 87, 60, 43, 134, 82, 44, 64, 23, 106, 8, 43, 126, 119, - 54, 204, 0, 1, 66, 138, 74, 42, 60, 7, 137, 208, 83, 253, 238, 133, 104, 105, 1, 15, 181, 49, - 233, 209, 126, 24, 7, 66, 113, 191, 3, 253, 187, 150, 97, 252, 127, 66, 44, 40, 167, 203, 46, - 74, 117, 246, 148, 44, 87, 198, 140, 36, 53, 4, 148, 161, 147, 52, 23, 249, 6, 6, 176, 216, - 115, 96, 82, 50, 162, 76, 4, 12, 4, 168, 23, 200, 0, 1, 134, 160, 187, 155, 194, 68, 215, 152, - 18, 63, 222, 120, 63, 204, 28, 114, 211, 187, 140, 24, 148, 19, 1, 99, 69, 120, 93, 138, 0, 0, - 0, 53, 46, 195, 214, 106, 50, 115, 14, 88, 17, 159, 118, 209, 118, 230, 149, 140, 33, 220, 13, - 77, 174, 99, 77, 118, 43, 213, 142, 124, 190, 117, 202, 44, 208, 217, 110, 235, 133, 254, 253, - 175, 66, 204, 176, 41, 143, 171, 120, 65, 175, 156, 36, 111, 0, 179, 77, 158, 209, 240, 52, - 203, 220, 202, 46, 164, 76, 4, 2, 51, 4, 168, 23, 200, 0, 1, 95, 144, 203, 58, 58, 204, 42, - 128, 103, 143, 26, 62, 199, 45, 101, 147, 171, 71, 204, 241, 184, 224, 30, 160, 4, 124, 117, - 46, 204, 0, 1, 143, 60, 254, 25, 31, 224, 47, 39, 83, 229, 36, 101, 235, 220, 157, 120, 218, - 227, 113, 252, 138, 58, 35, 19, 226, 184, 79, 36, 192, 228, 216, 16, 117, 110, 132, 88, 4, 41, - 103, 227, 71, 103, 233, 82, 9, 94, 163, 78, 130, 42, 138, 124, 144, 36, 121, 158, 188, 244, 36, - 148, 138, 149, 123, 58, 164, 76, 4, 2, 52, 4, 168, 23, 200, 0, 1, 95, 144, 204, 150, 61, 94, - 194, 172, 17, 177, 44, 9, 73, 251, 38, 175, 165, 250, 167, 205, 221, 1, 72, 55, 83, 157, 105, - 206, 92, 0, 0, 233, 229, 210, 31, 68, 232, 5, 72, 133, 42, 172, 182, 51, 48, 242, 134, 147, - 213, 201, 226, 78, 75, 124, 213, 195, 220, 217, 31, 113, 173, 28, 128, 67, 43, 227, 228, 111, - 57, 251, 191, 107, 67, 248, 48, 98, 28, 32, 163, 143, 124, 249, 59, 175, 121, 61, 143, 57, 121, - 104, 106, 234, 232, 195, 109, 162, 76, 0, 49, 4, 168, 23, 200, 0, 15, 66, 64, 253, 199, 123, - 156, 183, 50, 235, 140, 137, 107, 21, 46, 40, 41, 69, 33, 245, 246, 46, 103, 230, 55, 164, 75, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 33, 141, 78, 123, - 166, 178, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 253, - 231, 118, 251, 204, 57, 0, 58, 79, 225, 241, 19, 115, 46, 66, 159, 156, 116, 128, 49, 110, 185, - 253, 35, 203, 128, 118, 156, 252, 50, 139, 10, 233, 162, 209, 40, 34, 147, 54, 242, 219, 27, - 195, 10, 249, 114, 15, 122, 68, 62, 34, 147, 54, 142, 145, 160, 18, 32, 133, 41, 248, 186, 241, - 13, 36, 119, 254, 43, 0, 224, 107, 164, 76, 0, 127, 131, 4, 168, 23, 200, 0, 7, 161, 32, 51, - 117, 238, 25, 22, 129, 82, 96, 32, 1, 145, 80, 91, 80, 147, 80, 80, 80, 80, 96, 32, 96, 64, 81, - 128, 131, 3, 129, 96, 0, 135, 97, 97, 218, 90, 3, 241, 21, 97, 0, 2, 87, 80, 80, 96, 64, 81, - 81, 145, 80, 97, 2, 142, 144, 80, 86, 91, 96, 128, 96, 32, 96, 4, 128, 53, 128, 130, 1, 53, 96, - 31, 129, 1, 132, 144, 4, 144, 147, 2, 132, 1, 96, 64, 82, 96, 96, 131, 129, 82, 97, 5, 113, - 148, 146, 147, 96, 36, 147, 145, 146, 132, 1, 145, 129, 144, 131, 130, 128, 130, 132, 55, 80, - 148, 150, 80, 80, 80, 80, 80, 80, 80, 96, 3, 84, 96, 0, 144, 96, 1, 96, 160, 96, 2, 10, 3, 144, - 129, 22, 51, 144, 145, 22, 20, 21, 97, 5, 160, 87, 96, 1, 96, 0, 144, 84, 144, 97, 1, 0, 10, - 144, 4, 96, 1, 96, 160, 96, 2, 10, 3, 22, 96, 1, 96, 160, 96, 2, 10, 3, 22, 99, 98, 68, 26, - 238, 131, 96, 64, 81, 130, 96, 224, 96, 2, 10, 2, 129, 82, 96, 4, 1, 128, 128, 96, 32, 1, 130, - 129, 3, 130, 82, 131, 129, 129, 81, 129, 82, 96, 32, 1, 145, 80, 128, 81, 144, 96, 32, 1, 144, - 128, 131, 131, 130, 144, 96, 0, 96, 4, 96, 32, 132, 96, 31, 1, 4, 96, 15, 2, 96, 3, 1, 241, 80, - 144, 80, 144, 129, 1, 144, 96, 31, 22, 128, 21, 97, 4, 174, 87, 128, 130, 3, 128, 81, 96, 1, - 131, 96, 32, 3, 97, 1, 0, 10, 3, 25, 22, 129, 82, 96, 32, 1, 145, 80, 91, 80, 146, 80, 80, 80, - 96, 32, 96, 64, 81, 128, 131, 3, 129, 96, 0, 135, 97, 97, 218, 90, 3, 241, 21, 97, 0, 2, 87, - 80, 80, 96, 64, 81, 81, 145, 80, 97, 0, 203, 144, 80, 86, 91, 97, 5, 113, 96, 0, 84, 91, 144, - 86, 91, 97, 5, 113, 96, 4, 53, 96, 3, 84, 96, 0, 144, 51, 96, 1, 96, 160, 96, 2, 10, 3, 144, - 129, 22, 145, 22, 20, 21, 97, 5, 160, 87, 129, 144, 85, 96, 1, 97, 0, 203, 86, 91, 97, 5, 131, - 96, 1, 84, 127, 56, 204, 72, 49, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 96, 96, 144, 129, 82, 96, 0, 145, 96, 1, 96, 160, 96, 2, 10, 3, 22, 144, - 99, 56, 204, 72, 49, 144, 96, 100, 144, 96, 32, 144, 96, 4, 129, 135, 135, 97, 97, 218, 90, 3, - 241, 21, 97, 0, 2, 87, 80, 80, 96, 64, 81, 81, 145, 80, 97, 4, 224, 144, 80, 86, 91, 96, 64, - 128, 81, 145, 130, 82, 81, 144, 129, 144, 3, 96, 32, 1, 144, 243, 91, 96, 64, 128, 81, 96, 1, - 96, 160, 96, 2, 10, 3, 146, 144, 146, 22, 130, 82, 81, 144, 129, 144, 3, 96, 32, 1, 144, 243, - 91, 97, 0, 2, 86, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 221, 189, 31, 110, 40, 67, 237, 148, 77, - 237, 221, 46, 207, 241, 220, 29, 35, 145, 173, 195, 1, 221, 255, 137, 209, 24, 48, 1, 203, 93, - 163, 120, 246, 157, 249, 194, 197, 214, 155, 68, 182, 245, 161, 237, 93, 114, 141, 25, 125, - 229, 211, 156, 91, 115, 210, 27, 164, 147, 35, 86, 97, 56, 167, 147, 9, 84, 80, 174, 119, 127, - 20, 179, 209, 67, 190, 57, 87, 171, 132, 29, 241, 18, 41, 112, 54, 160, 76, 4, 23, 72, 118, - 232, 0, 1, 134, 160, 22, 122, 147, 51, 191, 88, 37, 86, 243, 91, 212, 209, 106, 126, 128, 225, - 145, 170, 100, 118, 16, 92, 238, 196, 116, 131, 116, 0, 1, 167, 156, 215, 58, 223, 209, 159, - 201, 79, 12, 177, 150, 64, 53, 213, 15, 168, 247, 13, 37, 152, 28, 159, 5, 21, 212, 247, 164, - 31, 6, 58, 101, 183, 13, 196, 35, 187, 63, 166, 114, 153, 249, 26, 51, 44, 229, 243, 114, 12, - 148, 72, 228, 59, 16, 254, 78, 89, 154, 237, 94, 175, 35, 59, 8, 164, 72, 4, 4, 90, 4, 168, 23, - 200, 0, 82, 8, 200, 14, 116, 153, 163, 106, 133, 243, 214, 38, 10, 78, 2, 150, 149, 72, 6, 21, - 101, 107, 4, 68, 88, 41, 137, 253, 4, 0, 0, 238, 116, 122, 8, 138, 38, 250, 198, 38, 192, 195, - 107, 253, 207, 189, 37, 18, 71, 87, 121, 32, 16, 88, 160, 181, 200, 110, 35, 159, 246, 219, 76, - 139, 124, 46, 147, 1, 75, 236, 116, 154, 215, 235, 60, 164, 201, 252, 54, 14, 83, 192, 180, - 199, 67, 179, 0, 0, 136, 44, 119, 44, 22, 164, 126, 166, 76, 4, 1, 50, 114, 4, 168, 23, 200, 0, - 1, 95, 144, 161, 201, 133, 226, 9, 165, 35, 89, 89, 74, 67, 250, 63, 133, 137, 128, 27, 218, - 189, 117, 68, 177, 238, 198, 22, 47, 0, 0, 0, 208, 47, 137, 77, 204, 238, 234, 165, 59, 255, 6, - 11, 126, 60, 204, 86, 224, 188, 215, 15, 230, 193, 155, 213, 10, 27, 178, 100, 4, 69, 181, 103, - 231, 219, 184, 73, 231, 37, 17, 26, 182, 154, 164, 13, 146, 105, 47, 71, 146, 87, 219, 24, 189, - 38, 244, 1, 147, 207, 92, 45, 55, 204, 227, 93, 164, 12, 0, 1, 47, 4, 168, 23, 200, 0, 45, 198, - 192, 96, 96, 96, 64, 129, 129, 82, 96, 1, 128, 84, 96, 160, 96, 2, 10, 96, 255, 2, 25, 22, 144, - 85, 128, 97, 3, 51, 131, 57, 96, 160, 144, 82, 81, 96, 128, 81, 96, 3, 128, 84, 96, 1, 96, 160, - 96, 2, 10, 3, 25, 22, 144, 146, 23, 144, 145, 85, 96, 2, 85, 97, 2, 234, 128, 97, 0, 73, 96, 0, - 57, 96, 0, 243, 96, 96, 96, 64, 82, 54, 21, 97, 0, 97, 87, 96, 224, 96, 2, 10, 96, 0, 53, 4, - 99, 5, 244, 116, 168, 129, 20, 97, 0, 99, 87, 128, 99, 90, 28, 88, 48, 20, 97, 0, 118, 87, 128, - 99, 107, 113, 141, 185, 20, 97, 1, 105, 87, 128, 99, 148, 164, 62, 92, 20, 97, 1, 150, 87, 128, - 99, 189, 222, 171, 119, 20, 97, 1, 170, 87, 128, 99, 193, 116, 220, 227, 20, 97, 1, 181, 87, - 128, 99, 254, 13, 148, 193, 20, 97, 2, 21, 87, 91, 0, 91, 97, 2, 37, 96, 1, 84, 96, 1, 96, 160, - 96, 2, 10, 3, 22, 91, 144, 86, 91, 97, 2, 66, 91, 96, 1, 84, 96, 64, 128, 81, 127, 205, 20, 61, - 69, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 129, - 82, 144, 81, 96, 0, 146, 96, 1, 96, 160, 96, 2, 10, 3, 22, 145, 99, 205, 20, 61, 69, 145, 96, - 4, 130, 129, 1, 146, 96, 32, 146, 145, 144, 130, 144, 3, 1, 129, 135, 135, 97, 97, 218, 90, 3, - 241, 21, 97, 0, 2, 87, 80, 80, 96, 64, 81, 81, 51, 96, 1, 96, 160, 96, 2, 10, 3, 144, 129, 22, - 145, 22, 20, 144, 80, 128, 21, 97, 1, 98, 87, 80, 96, 64, 128, 81, 96, 2, 84, 96, 1, 84, 127, - 186, 6, 26, 250, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 131, 82, 146, 81, 144, 146, 96, 1, 96, 160, 96, 2, 10, 3, 22, 145, 99, 186, 6, 26, 250, - 145, 96, 4, 130, 129, 1, 146, 96, 32, 146, 145, 144, 130, 144, 3, 1, 129, 136, 135, 97, 97, - 218, 90, 3, 241, 21, 97, 0, 2, 87, 80, 80, 80, 96, 64, 81, 128, 81, 144, 96, 32, 1, 80, 18, 21, - 91, 144, 80, 97, 0, 115, 86, 91, 97, 2, 66, 96, 4, 53, 96, 0, 128, 84, 115, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 25, 22, 130, - 23, 144, 85, 96, 1, 91, 145, 144, 80, 86, 91, 97, 2, 37, 96, 0, 84, 96, 1, 96, 160, 96, 2, 10, - 3, 22, 97, 0, 115, 86, 91, 97, 2, 66, 96, 2, 84, 97, 0, 115, 86, 91, 97, 2, 66, 96, 4, 53, 96, - 1, 84, 96, 0, 144, 96, 255, 96, 160, 96, 2, 10, 144, 145, 4, 22, 21, 21, 97, 2, 84, 87, 80, 96, - 1, 128, 84, 115, 255, 255, 255, 253, 203, 211, 140, 16, 45, 136, 5, 26, 107, 111, 74, 20, 65, - 32, 0, 0, 113, 68, 233, 120, 12, 255, 137, 46, 18, 123, 61, 90, 130, 186, 229, 45, 111, 27, 59, - 69, 138, 162, 103, 176, 46, 1, 52, 34, 1, 40, 252, 204, 61, 17, 186, 79, 232, 19, 154, 249, 44, - 65, 217, 151, 106, 114, 255, 196, 244, 41, 38, 165, 147, 51, 218, 169, 236, 76, 1, 32, 151, - 127, 251, 109, 162, 76, 0, 1, 4, 168, 23, 200, 0, 47, 239, 216, 214, 84, 189, 211, 47, 201, - 148, 113, 69, 94, 134, 194, 231, 247, 215, 182, 67, 126, 145, 121, 9, 94, 167, 179, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 245, 27, 196, 99, 63, 89, 36, 70, 92, 140, 99, 23, 22, 159, 175, 62, - 67, 18, 232, 47, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16, 67, - 86, 26, 136, 41, 48, 0, 0, 1, 88, 102, 216, 174, 177, 42, 235, 195, 130, 42, 65, 215, 206, 56, - 4, 109, 98, 26, 34, 134, 249, 200, 207, 138, 138, 56, 128, 87, 151, 250, 185, 209, 146, 216, - 244, 195, 245, 70, 232, 77, 106, 67, 119, 112, 235, 211, 163, 158, 124, 151, 184, 80, 3, 200, - 115, 64, 238, 160, 124, 150, 99, 229, 148, 96, 162, 204, 4, 5, 4, 168, 23, 200, 0, 1, 216, 168, - 223, 130, 166, 138, 65, 27, 37, 96, 147, 198, 186, 220, 42, 30, 180, 91, 255, 205, 79, 75, 1, - 160, 85, 105, 13, 157, 184, 0, 0, 1, 186, 110, 145, 35, 195, 30, 200, 195, 33, 200, 12, 5, 109, - 83, 218, 69, 178, 211, 117, 238, 151, 41, 166, 3, 171, 121, 194, 154, 55, 94, 254, 84, 123, 77, - 66, 129, 195, 40, 136, 239, 177, 42, 87, 204, 235, 206, 4, 129, 219, 121, 180, 222, 124, 252, - 116, 24, 249, 163, 37, 218, 128, 100, 125, 4, 162, 200, 4, 27, 6, 252, 35, 172, 0, 82, 8, 50, - 190, 52, 59, 148, 248, 96, 18, 77, 196, 254, 226, 120, 253, 203, 211, 140, 16, 45, 136, 2, 12, - 252, 205, 30, 75, 56, 8, 0, 0, 180, 210, 29, 8, 30, 188, 53, 237, 125, 189, 243, 10, 172, 200, - 212, 245, 34, 124, 96, 50, 226, 16, 65, 116, 250, 230, 9, 187, 177, 138, 115, 149, 174, 197, - 99, 152, 229, 174, 130, 155, 183, 212, 241, 34, 26, 108, 217, 254, 115, 13, 50, 215, 185, 84, - 21, 243, 162, 109, 76, 18, 75, 201, 79, 111, 162, 72, 4, 32, 6, 252, 35, 172, 0, 82, 8, 50, - 190, 52, 59, 148, 248, 96, 18, 77, 196, 254, 226, 120, 253, 203, 211, 140, 16, 45, 136, 68, - 178, 185, 9, 75, 34, 156, 0, 1, 67, 16, 218, 15, 127, 136, 36, 65, 114, 143, 129, 178, 120, 7, - 103, 208, 219, 163, 143, 101, 202, 91, 62, 108, 76, 231, 116, 192, 58, 79, 85, 177, 207, 196, - 200, 48, 205, 66, 180, 57, 85, 15, 91, 231, 217, 177, 136, 213, 83, 234, 86, 49, 139, 101, 88, - 151, 156, 59, 134, 179, 62, 48, 1, 35, 162, 200, 4, 62, 6, 252, 35, 172, 0, 82, 8, 50, 190, 52, - 59, 148, 248, 96, 18, 77, 196, 254, 226, 120, 253, 203, 211, 140, 16, 45, 136, 2, 43, 61, 214, - 9, 73, 43, 160, 0, 0, 242, 252, 197, 106, 219, 150, 219, 231, 180, 238, 215, 210, 60, 192, 231, - 79, 65, 192, 57, 140, 163, 96, 49, 47, 245, 211, 60, 107, 174, 6, 97, 224, 120, 85, 149, 171, - 2, 234, 111, 14, 172, 225, 55, 244, 12, 47, 172, 73, 85, 137, 165, 246, 142, 80, 59, 244, 22, - 52, 135, 234, 16, 139, 215, 107, 162, 72, 4, 68, 6, 252, 35, 172, 0, 82, 8, 50, 190, 52, 59, - 148, 248, 96, 18, 77, 196, 254, 226, 120, 253, 203, 211, 140, 16, 45, 136, 27, 153, 186, 186, - 90, 39, 104, 0, 0, 138, 125, 171, 60, 210, 129, 218, 67, 191, 247, 154, 147, 72, 156, 110, 193, - 78, 162, 171, 16, 185, 138, 81, 218, 20, 19, 192, 25, 144, 55, 126, 56, 47, 69, 23, 22, 209, - 182, 62, 222, 119, 81, 129, 105, 136, 73, 75, 75, 39, 189, 212, 72, 221, 12, 212, 247, 146, 86, - 245, 150, 5, 174, 6, 24, 164, 204, 4, 4, 131, 4, 168, 23, 200, 0, 1, 95, 144, 159, 61, 134, - 157, 186, 58, 67, 28, 149, 88, 99, 82, 56, 213, 156, 75, 153, 130, 176, 154, 1, 10, 234, 197, - 153, 26, 61, 0, 0, 1, 201, 242, 7, 138, 165, 95, 61, 144, 2, 221, 91, 77, 123, 130, 84, 209, - 201, 31, 8, 88, 163, 172, 94, 240, 132, 185, 9, 230, 35, 179, 246, 88, 49, 19, 67, 132, 165, - 41, 236, 32, 55, 59, 216, 67, 119, 68, 102, 143, 21, 154, 201, 99, 68, 59, 139, 170, 221, 42, - 41, 79, 218, 108, 39, 86, 162, 76, 0, 91, 4, 168, 23, 200, 0, 10, 44, 42, 44, 172, 110, 75, 17, - 214, 181, 143, 109, 60, 28, 157, 95, 232, 250, 168, 159, 96, 229, 162, 219, 133, 189, 120, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 233, 34, 180, 10, 208, 75, 21, 165, 37, 191, 47, 247, 185, 77, - 79, 131, 83, 174, 251, 159, 1, 171, 27, 30, 65, 168, 110, 90, 64, 200, 69, 4, 234, 174, 41, - 241, 188, 222, 28, 115, 245, 54, 56, 117, 33, 195, 173, 132, 228, 232, 103, 118, 18, 253, 132, - 113, 148, 160, 224, 181, 142, 254, 80, 0, 97, 110, 241, 210, 7, 254, 38, 211, 208, 57, 5, 143, - 240, 32, 172, 125, 220, 19, 220, 1, 13, 166, 76, 4, 1, 20, 66, 4, 168, 23, 200, 0, 1, 95, 144, - 178, 180, 24, 139, 118, 69, 48, 66, 164, 215, 153, 117, 28, 9, 82, 41, 229, 13, 64, 194, 13, - 231, 168, 195, 31, 122, 139, 236, 0, 199, 252, 137, 34, 23, 197, 252, 62, 199, 224, 19, 3, 169, - 0, 44, 15, 14, 62, 51, 211, 127, 122, 125, 164, 49, 220, 214, 3, 231, 68, 99, 252, 35, 3, 215, - 213, 100, 89, 188, 225, 232, 88, 212, 74, 255, 89, 154, 127, 51, 139, 220, 102, 238, 122, 104, - 156, 103, 115, 213, 18, 69, 208, 120, 30, 164, 76, 4, 15, 216, 4, 168, 23, 200, 0, 3, 208, 144, - 136, 30, 213, 59, 34, 206, 135, 216, 99, 122, 67, 145, 55, 185, 236, 19, 53, 0, 147, 215, 61, - 102, 200, 198, 198, 166, 240, 0, 0, 138, 142, 40, 251, 164, 107, 172, 85, 72, 217, 58, 56, 35, - 102, 241, 6, 129, 168, 130, 165, 119, 45, 20, 227, 8, 208, 127, 205, 35, 213, 154, 82, 125, 8, - 38, 74, 7, 221, 236, 40, 150, 132, 248, 120, 59, 148, 99, 212, 248, 177, 19, 202, 5, 115, 159, - 98, 176, 166, 253, 47, 175, 191, 245, 7, 160, 76, 0, 4, 168, 23, 200, 0, 2, 73, 240, 224, 183, - 146, 124, 74, 242, 55, 101, 203, 81, 49, 74, 14, 5, 33, 169, 100, 95, 14, 42, 169, 5, 156, 187, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 153, 90, 231, 157, 96, 54, 91, 42, 12, 82, 165, 173, 25, - 20, 13, 251, 141, 69, 242, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 1, 220, 214, 80, 0, 1, 138, 227, 254, 247, 199, 178, 55, 231, 116, 67, 129, 127, - 50, 240, 238, 12, 216, 70, 7, 244, 125, 162, 144, 36, 96, 218, 73, 222, 91, 229, 165, 121, 249, - 74, 130, 252, 216, 255, 188, 192, 239, 58, 136, 190, 99, 15, 12, 142, 75, 99, 206, 215, 134, - 225, 198, 136, 38, 25, 172, 216, 167, 54, 54, 107, 164, 204, 3, 15, 217, 4, 168, 23, 200, 0, 3, - 208, 144, 98, 114, 246, 24, 111, 197, 235, 8, 56, 159, 60, 136, 189, 99, 99, 55, 211, 218, 140, - 240, 75, 150, 137, 94, 228, 56, 0, 1, 90, 203, 68, 199, 92, 87, 245, 202, 35, 222, 202, 115, - 73, 5, 191, 40, 40, 139, 121, 170, 102, 156, 233, 117, 177, 65, 187, 196, 54, 249, 184, 223, - 243, 15, 182, 171, 175, 159, 242, 159, 25, 149, 148, 201, 233, 198, 155, 50, 230, 243, 21, 148, - 152, 241, 67, 124, 21, 10, 17, 74, 212, 163, 46, 0, 164, 76, 4, 202, 167, 6, 252, 35, 172, 0, - 5, 22, 21, 174, 176, 86, 193, 168, 147, 71, 63, 11, 121, 209, 182, 51, 156, 37, 135, 73, 66, - 200, 118, 146, 50, 145, 238, 159, 92, 228, 153, 182, 36, 187, 206, 47, 140, 27, 216, 124, 7, - 43, 60, 240, 163, 119, 99, 74, 166, 76, 4, 8, 87, 118, 4, 168, 23, 200, 0, 1, 95, 144, 139, 59, - 53, 253, 11, 253, 171, 167, 99, 23, 71, 113, 179, 16, 131, 158, 84, 42, 205, 24, 41, 89, 213, - 99, 20, 56, 52, 0, 0, 136, 113, 3, 134, 142, 18, 46, 129, 155, 163, 47, 191, 213, 225, 27, 131, - 212, 165, 36, 101, 38, 183, 59, 254, 101, 73, 78, 143, 195, 107, 141, 207, 192, 61, 180, 13, - 193, 140, 252, 81, 148, 216, 227, 1, 72, 39, 224, 243, 27, 68, 19, 57, 177, 38, 31, 191, 132, - 13, 148, 49, 60, 189, 11, 25, 166, 76, 4, 8, 87, 119, 4, 168, 23, 200, 0, 1, 95, 144, 137, 37, - 234, 80, 148, 40, 86, 164, 229, 16, 100, 217, 143, 4, 60, 168, 87, 64, 29, 64, 40, 153, 145, - 53, 13, 133, 148, 0, 1, 239, 61, 194, 1, 22, 227, 1, 72, 145, 170, 223, 34, 156, 125, 111, 91, - 190, 193, 245, 55, 28, 144, 53, 124, 18, 47, 222, 139, 204, 125, 229, 169, 241, 92, 54, 242, - 188, 25, 211, 187, 227, 106, 74, 105, 154, 253, 28, 80, 60, 160, 71, 113, 206, 0, 82, 23, 100, - 80, 34, 87, 208, 150, 1, 84, 162, 76, 4, 215, 4, 168, 23, 200, 0, 1, 95, 144, 45, 120, 3, 30, - 31, 163, 5, 68, 87, 34, 52, 5, 162, 83, 185, 4, 9, 160, 244, 70, 4, 0, 78, 179, 127, 175, 2, 0, - 0, 40, 16, 208, 145, 59, 85, 126, 225, 90, 151, 62, 60, 113, 176, 1, 194, 31, 72, 85, 153, 183, - 49, 9, 136, 18, 162, 32, 204, 183, 232, 43, 161, 226, 54, 116, 94, 218, 169, 20, 157, 232, 194, - 230, 28, 236, 184, 142, 64, 210, 25, 1, 159, 254, 38, 8, 191, 127, 249, 132, 65, 184, 16, 153, - 28, 166, 76, 4, 8, 87, 120, 4, 168, 23, 200, 0, 1, 95, 144, 44, 109, 162, 136, 3, 151, 74, 144, - 71, 58, 112, 74, 190, 143, 65, 155, 244, 184, 128, 145, 38, 223, 236, 177, 107, 56, 248, 0, 1, - 170, 116, 101, 156, 47, 3, 118, 28, 207, 19, 216, 71, 248, 52, 26, 90, 3, 143, 157, 45, 13, - 126, 77, 120, 84, 109, 129, 106, 87, 214, 225, 17, 241, 135, 87, 177, 207, 57, 174, 84, 108, - 66, 118, 151, 238, 178, 0, 7, 227, 219, 22, 145, 245, 235, 110, 143, 203, 88, 6, 67, 33, 121, - 200, 98, 164, 72, 4, 1, 21, 4, 168, 23, 200, 0, 86, 34, 251, 177, 183, 60, 79, 11, 218, 79, - 103, 220, 162, 102, 206, 110, 244, 47, 82, 15, 187, 152, 14, 213, 154, 118, 109, 62, 160, 0, 1, - 92, 40, 183, 165, 31, 245, 5, 1, 58, 34, 139, 218, 49, 205, 48, 148, 183, 11, 27, 241, 108, - 130, 151, 183, 77, 246, 233, 153, 167, 251, 129, 182, 161, 46, 103, 122, 132, 130, 79, 182, - 153, 139, 31, 167, 218, 5, 71, 19, 142, 182, 99, 108, 203, 192, 26, 93, 230, 97, 108, 69, 24, - 9, 226, 82, 166, 76, 4, 8, 87, 121, 4, 168, 23, 200, 0, 1, 95, 144, 220, 106, 12, 231, 18, 83, - 132, 205, 27, 163, 221, 79, 109, 124, 50, 251, 172, 40, 239, 236, 23, 144, 207, 107, 119, 250, - 0, 0, 0, 158, 167, 141, 69, 29, 60, 157, 230, 73, 40, 133, 166, 139, 238, 33, 113, 51, 60, 130, - 138, 204, 227, 192, 235, 233, 218, 220, 209, 164, 26, 207, 0, 65, 238, 157, 202, 34, 171, 10, - 8, 122, 126, 104, 85, 63, 95, 138, 57, 233, 61, 69, 174, 164, 14, 68, 102, 30, 209, 164, 105, - 94, 240, 43, 14, 166, 76, 4, 8, 87, 122, 4, 168, 23, 200, 0, 1, 95, 144, 127, 172, 60, 71, 3, - 57, 243, 149, 253, 185, 151, 184, 252, 161, 149, 67, 117, 196, 132, 125, 20, 190, 194, 148, - 164, 61, 28, 0, 0, 246, 83, 222, 29, 94, 188, 89, 65, 36, 49, 97, 212, 235, 162, 113, 215, 5, - 122, 160, 110, 58, 185, 211, 95, 153, 137, 147, 175, 162, 40, 73, 7, 131, 134, 22, 8, 210, 249, - 156, 28, 252, 101, 246, 179, 108, 220, 59, 13, 2, 47, 116, 235, 175, 58, 54, 195, 174, 184, - 152, 238, 108, 205, 179, 98, 162, 76, 4, 10, 4, 168, 23, 200, 0, 2, 50, 219, 187, 155, 194, 68, - 215, 152, 18, 63, 222, 120, 63, 204, 28, 114, 211, 187, 140, 24, 148, 19, 249, 204, 216, 161, - 197, 8, 0, 0, 0, 199, 241, 211, 34, 180, 14, 35, 74, 234, 83, 54, 19, 202, 190, 203, 141, 113, - 38, 117, 19, 82, 58, 9, 19, 241, 88, 223, 39, 170, 106, 94, 101, 72, 206, 156, 241, 13, 178, - 16, 72, 111, 228, 92, 142, 202, 44, 221, 44, 73, 23, 242, 232, 84, 121, 101, 76, 50, 110, 20, - 166, 129, 130, 184, 40, 166, 76, 4, 8, 87, 123, 4, 168, 23, 200, 0, 1, 95, 144, 170, 155, 74, - 104, 26, 186, 92, 14, 222, 9, 38, 169, 217, 77, 180, 110, 132, 54, 245, 65, 20, 189, 53, 59, - 66, 251, 152, 0, 0, 121, 190, 109, 38, 45, 48, 21, 38, 220, 15, 215, 8, 206, 23, 46, 214, 219, - 127, 157, 52, 190, 157, 10, 137, 110, 44, 194, 18, 59, 234, 10, 109, 145, 205, 93, 155, 38, 81, - 111, 108, 210, 41, 65, 240, 172, 118, 172, 176, 173, 143, 238, 32, 114, 67, 178, 14, 201, 77, - 119, 227, 185, 187, 234, 20, 162, 76, 0, 1, 7, 127, 221, 152, 128, 1, 216, 168, 187, 155, 194, - 68, 215, 152, 18, 63, 222, 120, 63, 204, 28, 114, 211, 187, 140, 24, 148, 19, 169, 5, 156, 187, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 142, 249, 169, 81, 115, 51, 11, 51, 37, 41, 185, 108, 155, - 102, 63, 159, 53, 31, 140, 98, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 13, 224, 182, 179, 167, 100, 0, 0, 0, 98, 146, 245, 44, 137, 135, 238, 227, 21, 90, - 102, 39, 20, 95, 39, 97, 49, 245, 254, 222, 173, 198, 93, 116, 40, 241, 103, 194, 119, 120, 6, - 196, 10, 86, 242, 145, 121, 49, 68, 233, 137, 43, 249, 198, 58, 159, 27, 27, 67, 234, 58, 212, - 63, 252, 203, 158, 97, 39, 41, 202, 6, 37, 205, 124, 162, 72, 4, 6, 4, 168, 23, 200, 0, 82, 8, - 97, 145, 45, 4, 183, 238, 243, 186, 245, 250, 140, 65, 195, 166, 143, 217, 40, 69, 241, 173, - 15, 65, 20, 136, 95, 44, 240, 0, 1, 176, 129, 172, 26, 49, 173, 246, 132, 180, 78, 241, 1, 254, - 72, 255, 23, 255, 70, 12, 198, 204, 77, 111, 10, 160, 29, 174, 193, 40, 238, 111, 46, 79, 244, - 161, 5, 150, 142, 89, 56, 186, 154, 227, 12, 122, 84, 61, 141, 197, 126, 189, 4, 240, 162, 143, - 49, 147, 195, 31, 162, 107, 57, 66, 123, 166, 76, 4, 8, 87, 124, 4, 168, 23, 200, 0, 1, 95, - 144, 156, 241, 139, 62, 179, 115, 195, 131, 164, 45, 6, 19, 107, 163, 49, 215, 102, 237, 159, - 187, 19, 238, 35, 241, 49, 92, 116, 0, 0, 0, 41, 80, 122, 161, 140, 31, 190, 129, 150, 32, 80, - 2, 152, 240, 77, 200, 20, 164, 211, 3, 239, 141, 157, 244, 194, 161, 86, 71, 204, 48, 153, 24, - 70, 198, 90, 2, 219, 210, 108, 173, 87, 18, 30, 43, 227, 181, 100, 167, 79, 80, 67, 173, 44, - 207, 42, 225, 16, 132, 249, 36, 81, 3, 82, 166, 76, 4, 8, 87, 125, 4, 168, 23, 200, 0, 1, 95, - 144, 160, 155, 172, 199, 156, 227, 13, 157, 67, 128, 113, 144, 52, 84, 91, 8, 224, 214, 86, - 223, 22, 134, 186, 71, 10, 200, 132, 0, 1, 71, 12, 232, 9, 121, 175, 14, 80, 34, 231, 19, 145, - 198, 131, 250, 40, 125, 159, 103, 196, 199, 64, 93, 51, 251, 89, 73, 14, 252, 86, 160, 9, 29, - 191, 72, 49, 35, 81, 229, 200, 203, 239, 253, 17, 127, 8, 146, 60, 209, 52, 200, 68, 46, 50, - 115, 143, 4, 33, 70, 2, 251, 127, 154, 20, 166, 76, 4, 8, 87, 126, 4, 168, 23, 200, 0, 1, 95, - 144, 85, 82, 229, 2, 180, 17, 69, 129, 150, 146, 170, 100, 74, 5, 92, 175, 237, 195, 243, 22, - 162, 76, 4, 1, 4, 168, 23, 200, 0, 1, 95, 144, 90, 110, 47, 197, 151, 35, 181, 228, 24, 238, - 81, 226, 240, 247, 151, 183, 108, 162, 167, 130, 1, 99, 69, 120, 93, 138, 0, 0, 1, 114, 253, - 120, 37, 146, 72, 28, 238, 197, 143, 201, 203, 131, 203, 151, 189, 114, 56, 3, 106, 176, 150, - 13, 43, 130, 189, 82, 63, 32, 95, 94, 176, 13, 66, 133, 55, 2, 79, 45, 112, 112, 166, 179, 30, - 97, 24, 216, 208, 128, 36, 223, 80, 86, 10, 67, 247, 165, 147, 84, 104, 121, 251, 74, 57, 166, - 76, 4, 1, 34, 207, 4, 168, 23, 200, 0, 1, 95, 144, 186, 97, 57, 19, 249, 169, 175, 29, 188, - 150, 184, 129, 33, 185, 245, 227, 116, 215, 111, 97, 70, 218, 114, 146, 61, 120, 216, 0, 1, - 140, 98, 24, 35, 84, 18, 4, 81, 85, 60, 206, 244, 133, 74, 105, 200, 213, 33, 101, 28, 140, - 139, 55, 93, 208, 190, 187, 167, 75, 63, 168, 114, 122, 184, 51, 72, 126, 11, 20, 112, 156, 47, - 103, 105, 54, 74, 244, 54, 88, 36, 214, 162, 37, 183, 42, 131, 214, 213, 174, 25, 153, 52, 50, - 38, 164, 76, 4, 10, 103, 46, 144, 237, 208, 0, 2, 59, 94, 187, 155, 194, 68, 215, 152, 18, 63, - 222, 120, 63, 204, 28, 114, 211, 187, 140, 24, 148, 19, 138, 199, 35, 4, 137, 232, 0, 0, 186, - 172, 83, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 64, 185, 184, 137, 162, 31, 241, 83, 77, 1, - 141, 113, 220, 64, 97, 34, 235, 207, 63, 90, 0, 91, 244, 201, 194, 85, 131, 72, 53, 196, 180, - 193, 248, 104, 236, 213, 80, 58, 194, 175, 72, 7, 52, 132, 13, 92, 151, 124, 119, 126, 112, - 157, 206, 132, 146, 162, 124, 170, 2, 83, 21, 16, 16, 74, 239, 255, 145, 143, 27, 85, 99, 228, - 201, 24, 225, 242, 96, 61, 228, 95, 150, 122, 187, 35, 55, 162, 76, 4, 5, 4, 168, 23, 200, 0, - 1, 95, 144, 187, 155, 194, 68, 215, 152, 18, 63, 222, 120, 63, 204, 28, 114, 211, 187, 140, 24, - 148, 19, 41, 162, 36, 26, 246, 44, 0, 0, 1, 105, 111, 9, 81, 10, 75, 222, 208, 118, 40, 240, - 108, 50, 196, 18, 2, 218, 172, 85, 95, 230, 51, 17, 72, 39, 108, 11, 135, 146, 40, 33, 153, - 144, 103, 237, 71, 251, 138, 91, 226, 58, 240, 76, 210, 137, 56, 204, 98, 177, 37, 26, 79, 134, - 245, 77, 124, 176, 137, 92, 175, 140, 250, 204, 23, 164, 76, 0, 1, 147, 4, 168, 23, 200, 0, 15, - 66, 64, 205, 121, 199, 38, 144, 117, 15, 7, 154, 230, 171, 108, 205, 126, 122, 237, 192, 60, - 119, 32, 230, 55, 164, 75, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 4, 185, 111, 55, 182, 66, 185, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 4, 140, 134, 159, 48, 163, 228, 0, 54, 22, 121, 114, 233, 131, 222, 52, 87, - 86, 87, 141, 88, 139, 47, 114, 174, 140, 169, 9, 159, 111, 221, 130, 223, 110, 12, 163, 254, - 193, 161, 174, 91, 32, 113, 148, 105, 106, 84, 223, 202, 50, 2, 223, 109, 192, 153, 19, 222, - 159, 104, 178, 14, 189, 96, 33, 176, 103, 101, 17, 149, 209, 147, 59, 162, 204, 4, 1, 4, 168, - 23, 200, 0, 1, 216, 168, 99, 89, 221, 96, 0, 52, 69, 187, 155, 91, 190, 110, 18, 212, 104, 217, - 81, 60, 105, 211, 15, 45, 199, 212, 127, 21, 96, 0, 0, 1, 223, 204, 76, 228, 252, 255, 45, 14, - 214, 216, 195, 195, 78, 154, 60, 31, 9, 178, 213, 150, 11, 10, 188, 249, 4, 115, 97, 132, 100, - 240, 182, 181, 216, 250, 46, 71, 117, 25, 79, 3, 110, 224, 33, 166, 188, 72, 216, 83, 189, 132, - 10, 199, 94, 97, 62, 18, 15, 132, 158, 167, 31, 43, 7, 86, 160, 200, 4, 4, 227, 178, 146, 0, - 82, 8, 187, 155, 194, 68, 215, 152, 18, 63, 222, 120, 63, 204, 28, 114, 211, 187, 140, 24, 148, - 19, 1, 21, 142, 70, 9, 19, 208, 0, 0, 0, 117, 160, 63, 11, 213, 114, 146, 54, 196, 162, 22, - 121, 125, 212, 221, 197, 216, 39, 152, 143, 110, 75, 134, 248, 86, 168, 15, 1, 113, 138, 199, - 199, 11, 85, 65, 100, 128, 161, 211, 120, 86, 63, 112, 128, 30, 195, 23, 212, 167, 219, 107, - 30, 221, 112, 111, 145, 140, 196, 179, 80, 34, 47, 26, 105, 162, 76, 4, 4, 4, 227, 178, 146, 0, - 1, 76, 8, 187, 155, 194, 68, 215, 152, 18, 63, 222, 120, 63, 204, 28, 114, 211, 187, 140, 24, - 148, 19, 13, 224, 182, 179, 167, 100, 0, 0, 1, 20, 41, 86, 245, 13, 251, 91, 120, 241, 248, - 218, 73, 181, 32, 128, 151, 71, 46, 6, 149, 31, 223, 124, 175, 116, 225, 56, 201, 241, 36, 175, - 223, 23, 21, 103, 57, 208, 219, 241, 202, 79, 51, 137, 51, 223, 240, 195, 72, 130, 188, 175, - 157, 29, 160, 202, 40, 16, 191, 225, 32, 88, 144, 3, 21, 166, 76, 4, 8, 62, 60, 4, 168, 23, - 200, 0, 1, 95, 144, 72, 78, 125, 143, 56, 66, 110, 152, 112, 238, 71, 42, 53, 82, 165, 176, 37, - 35, 154, 36, 59, 183, 10, 234, 52, 200, 212, 0, 1, 181, 167, 99, 231, 76, 18, 122, 168, 172, - 26, 61, 217, 71, 63, 54, 99, 75, 31, 192, 1, 199, 12, 249, 119, 155, 151, 214, 40, 34, 177, - 220, 228, 118, 141, 190, 254, 145, 199, 93, 102, 24, 16, 85, 125, 107, 102, 169, 64, 189, 223, - 90, 189, 23, 202, 124, 94, 136, 233, 62, 0, 67, 169, 239, 75, 162, 204, 4, 5, 4, 227, 178, 146, - 0, 1, 76, 8, 187, 155, 194, 68, 215, 152, 18, 63, 222, 120, 63, 204, 28, 114, 211, 187, 140, - 24, 148, 19, 2, 154, 34, 65, 175, 98, 192, 0, 0, 1, 64, 206, 96, 176, 30, 123, 247, 243, 20, - 143, 113, 23, 29, 146, 192, 87, 130, 64, 38, 164, 196, 192, 109, 177, 102, 157, 65, 212, 126, - 110, 91, 248, 10, 157, 152, 56, 193, 248, 175, 126, 12, 110, 33, 238, 184, 225, 143, 182, 41, - 191, 2, 13, 153, 185, 214, 185, 177, 142, 3, 143, 60, 136, 45, 107, 162, 200, 4, 24, 4, 227, - 178, 146, 0, 82, 8, 253, 145, 243, 192, 211, 146, 242, 3, 45, 29, 65, 156, 122, 45, 245, 250, - 38, 115, 54, 115, 1, 201, 247, 141, 40, 147, 228, 0, 0, 1, 122, 174, 229, 148, 254, 135, 246, - 230, 140, 180, 178, 55, 188, 35, 254, 47, 247, 74, 241, 37, 243, 213, 172, 137, 104, 83, 137, - 116, 9, 217, 52, 225, 217, 161, 197, 247, 227, 115, 249, 135, 192, 226, 114, 70, 111, 22, 205, - 223, 178, 26, 145, 46, 222, 35, 101, 144, 72, 47, 252, 135, 52, 218, 125, 107, 162, 204, 4, 1, - 4, 168, 23, 200, 0, 1, 216, 168, 61, 206, 241, 156, 134, 139, 21, 211, 78, 218, 66, 110, 199, - 224, 75, 24, 182, 1, 112, 2, 13, 141, 114, 107, 113, 119, 168, 0, 0, 0, 195, 235, 100, 208, - 249, 22, 245, 169, 131, 203, 67, 199, 143, 214, 83, 138, 174, 60, 35, 240, 54, 78, 156, 37, 93, - 153, 148, 100, 227, 14, 251, 98, 250, 24, 75, 201, 79, 185, 67, 39, 115, 99, 154, 205, 70, 177, - 169, 222, 8, 197, 39, 118, 133, 27, 30, 15, 61, 126, 152, 200, 119, 96, 1, 22, 162, 76, 4, 3, - 4, 168, 23, 200, 0, 1, 95, 144, 14, 228, 226, 208, 154, 236, 53, 189, 240, 128, 131, 182, 73, - 3, 58, 192, 164, 26, 167, 94, 27, 176, 28, 199, 136, 9, 36, 224, 1, 189, 111, 73, 163, 9, 104, - 179, 65, 147, 150, 103, 47, 3, 208, 189, 226, 120, 224, 215, 47, 35, 192, 65, 87, 217, 12, 202, - 79, 163, 151, 87, 65, 136, 113, 31, 122, 24, 91, 115, 93, 174, 168, 100, 123, 92, 220, 168, 69, - 201, 245, 97, 203, 120, 184, 23, 15, 74, 7, 212, 119, 115, 41, 240, 94, 164, 76, 0, 1, 221, 4, - 168, 23, 200, 0, 2, 73, 240, 224, 183, 146, 124, 74, 242, 55, 101, 203, 81, 49, 74, 14, 5, 33, - 169, 100, 95, 14, 42, 169, 5, 156, 187, 0, 0, 0, 76, 218, 138, 210, 0, 1, 110, 194, 97, 199, - 88, 223, 40, 205, 8, 98, 242, 167, 195, 183, 84, 227, 69, 163, 202, 81, 221, 234, 87, 148, 92, - 83, 38, 172, 15, 26, 121, 53, 38, 239, 124, 255, 198, 158, 56, 104, 244, 115, 114, 220, 255, - 237, 245, 91, 45, 44, 50, 93, 77, 40, 119, 7, 199, 114, 44, 206, 52, 27, 20, 14, 166, 76, 4, 7, - 250, 84, 4, 168, 23, 200, 0, 1, 95, 144, 248, 97, 98, 106, 204, 205, 31, 138, 77, 239, 125, - 141, 215, 97, 186, 134, 232, 111, 204, 47, 25, 68, 124, 130, 169, 191, 68, 0, 0, 173, 208, 72, - 90, 190, 1, 58, 235, 8, 78, 118, 222, 69, 173, 248, 81, 163, 19, 12, 80, 134, 240, 147, 55, 89, - 26, 225, 34, 173, 223, 222, 203, 235, 75, 36, 227, 184, 20, 160, 168, 192, 185, 15, 205, 82, 8, - 153, 155, 199, 235, 101, 45, 222, 35, 118, 18, 249, 34, 85, 61, 44, 86, 192, 100, 166, 76, 4, - 7, 250, 85, 4, 168, 23, 200, 0, 1, 95, 144, 247, 133, 151, 114, 199, 183, 11, 182, 55, 119, 87, - 89, 220, 115, 206, 67, 115, 118, 90, 158, 19, 26, 65, 136, 90, 54, 180, 0, 0, 198, 85, 177, - 185, 1, 66, 155, 197, 221, 95, 0, 175, 39, 240, 92, 83, 188, 6, 43, 100, 160, 11, 168, 33, 190, - 159, 193, 225, 184, 34, 99, 55, 110, 251, 130, 254, 147, 207, 123, 42, 237, 39, 187, 214, 234, - 28, 182, 196, 145, 54, 99, 58, 238, 29, 226, 70, 108, 14, 97, 71, 141, 154, 164, 65, 162, 76, - 0, 6, 7, 127, 224, 165, 192, 3, 20, 28, 240, 22, 4, 40, 168, 85, 42, 201, 187, 126, 5, 13, 144, - 238, 173, 228, 221, 213, 40, 67, 78, 113, 217, 45, 1, 48, 152, 14, 148, 221, 98, 12, 50, 71, - 131, 99, 40, 162, 92, 244, 251, 87, 181, 252, 228, 229, 222, 172, 59, 205, 99, 173, 81, 114, - 106, 122, 102, 255, 245, 174, 149, 199, 62, 76, 51, 151, 31, 103, 36, 247, 56, 43, 147, 67, - 214, 40, 82, 145, 102, 148, 40, 169, 207, 229, 194, 168, 133, 133, 54, 164, 76, 4, 216, 127, 4, - 168, 23, 200, 0, 1, 95, 144, 103, 36, 239, 240, 137, 249, 221, 121, 195, 129, 19, 125, 246, - 134, 120, 189, 87, 75, 55, 224, 15, 81, 138, 204, 211, 170, 140, 116, 0, 195, 53, 214, 36, 254, - 85, 237, 116, 167, 241, 32, 29, 67, 206, 22, 109, 232, 186, 76, 155, 239, 73, 217, 4, 7, 214, - 27, 135, 137, 226, 197, 63, 204, 96, 236, 230, 142, 128, 45, 56, 40, 200, 72, 193, 154, 32, - 117, 153, 214, 178, 213, 24, 255, 84, 89, 169, 81, 24, 198, 252, 233, 114, 163, 104, 162, 72, - 4, 38, 4, 168, 23, 200, 0, 82, 8, 215, 48, 38, 176, 73, 77, 37, 66, 127, 61, 26, 119, 67, 186, - 190, 27, 221, 248, 160, 53, 13, 165, 60, 217, 217, 52, 176, 0, 0, 0, 57, 209, 155, 241, 61, 75, - 32, 227, 91, 210, 144, 45, 227, 179, 84, 222, 87, 6, 160, 218, 6, 198, 128, 97, 165, 86, 45, - 210, 199, 179, 94, 2, 155, 129, 212, 241, 41, 168, 35, 153, 58, 197, 150, 157, 188, 147, 62, - 48, 114, 128, 4, 63, 166, 251, 242, 202, 110, 157, 225, 84, 71, 242, 3, 162, 72, 4, 33, 4, 168, - 23, 200, 0, 82, 8, 215, 48, 38, 176, 73, 77, 37, 66, 127, 61, 26, 119, 67, 186, 190, 27, 221, - 248, 160, 53, 65, 233, 66, 116, 117, 221, 172, 0, 1, 81, 113, 129, 46, 159, 111, 134, 149, 110, - 21, 184, 249, 81, 29, 97, 242, 63, 217, 66, 198, 138, 76, 106, 250, 159, 149, 181, 87, 38, 107, - 61, 79, 54, 60, 146, 215, 98, 26, 108, 193, 92, 214, 103, 110, 2, 61, 0, 141, 62, 20, 148, 241, - 138, 55, 254, 250, 4, 195, 89, 109, 195, 108, 8, 92, 162, 200, 4, 38, 4, 168, 23, 200, 0, 82, - 8, 215, 48, 38, 176, 73, 77, 37, 66, 127, 61, 26, 119, 67, 186, 190, 27, 221, 248, 160, 53, 2, - 220, 89, 169, 187, 40, 220, 64, 0, 1, 171, 246, 138, 255, 102, 80, 96, 17, 147, 93, 55, 131, - 114, 141, 33, 206, 213, 77, 146, 143, 88, 156, 96, 101, 114, 231, 164, 131, 202, 88, 177, 197, - 17, 39, 213, 11, 69, 217, 81, 159, 20, 21, 92, 14, 199, 120, 221, 157, 111, 219, 9, 119, 195, - 231, 129, 36, 34, 171, 144, 56, 203, 25, 253, 30, 162, 72, 4, 3, 4, 168, 23, 200, 0, 82, 8, - 201, 109, 93, 77, 84, 189, 24, 234, 158, 252, 197, 134, 196, 236, 130, 183, 196, 108, 111, 107, - 4, 14, 76, 228, 121, 122, 180, 0, 0, 247, 67, 11, 24, 186, 184, 142, 5, 255, 158, 126, 141, 48, - 86, 199, 48, 169, 146, 83, 195, 25, 226, 151, 120, 103, 16, 50, 21, 195, 110, 147, 226, 185, - 45, 200, 240, 82, 253, 205, 10, 21, 36, 226, 162, 121, 110, 232, 74, 23, 96, 184, 252, 143, 73, - 198, 2, 203, 40, 14, 234, 20, 211, 243, 68, 166, 76, 4, 7, 250, 86, 4, 168, 23, 200, 0, 1, 95, - 144, 159, 215, 123, 174, 103, 237, 95, 237, 191, 135, 5, 73, 170, 223, 188, 49, 141, 206, 213, - 65, 45, 107, 233, 158, 57, 225, 200, 0, 1, 176, 68, 249, 73, 38, 174, 120, 193, 46, 10, 5, 227, - 41, 234, 227, 42, 60, 106, 174, 65, 90, 33, 191, 81, 8, 129, 138, 11, 98, 128, 20, 168, 207, - 107, 170, 19, 230, 47, 90, 103, 186, 1, 222, 36, 141, 253, 239, 38, 235, 164, 135, 140, 198, - 233, 220, 199, 186, 219, 111, 143, 177, 109, 8, 86, 166, 76, 4, 7, 250, 87, 4, 168, 23, 200, 0, - 1, 95, 144, 190, 38, 139, 143, 224, 24, 166, 161, 125, 46, 83, 74, 80, 16, 246, 167, 78, 220, - 131, 106, 45, 163, 35, 184, 232, 246, 60, 0, 1, 210, 80, 190, 169, 164, 63, 201, 231, 178, 243, - 68, 167, 36, 135, 230, 132, 234, 153, 62, 215, 132, 66, 200, 10, 221, 81, 210, 218, 214, 110, - 35, 143, 104, 81, 206, 78, 231, 241, 82, 17, 149, 188, 122, 125, 106, 212, 2, 10, 136, 6, 149, - 154, 119, 140, 172, 249, 120, 126, 78, 20, 143, 172, 228, 22, 166, 76, 4, 7, 250, 88, 4, 168, - 23, 200, 0, 1, 95, 144, 243, 38, 2, 37, 10, 92, 188, 160, 160, 254, 8, 36, 181, 19, 90, 102, - 50, 180, 167, 198, 66, 117, 232, 238, 138, 99, 20, 0, 1, 52, 203, 86, 101, 218, 247, 40, 210, - 191, 15, 51, 95, 24, 23, 226, 61, 64, 141, 60, 22, 158, 112, 225, 104, 43, 212, 64, 222, 15, - 108, 80, 18, 51, 253, 160, 61, 223, 47, 198, 146, 160, 195, 104, 201, 190, 40, 130, 21, 209, - 202, 95, 23, 58, 191, 242, 210, 197, 244, 34, 213, 172, 45, 57, 97, 166, 76, 4, 7, 250, 89, 4, - 168, 23, 200, 0, 1, 95, 144, 104, 185, 211, 195, 109, 60, 52, 248, 119, 189, 241, 129, 1, 252, - 87, 118, 221, 20, 251, 119, 41, 166, 70, 7, 144, 249, 128, 0, 1, 12, 184, 127, 8, 241, 126, 75, - 123, 83, 87, 74, 49, 129, 13, 164, 105, 57, 64, 111, 41, 156, 148, 165, 120, 242, 104, 120, 40, - 166, 74, 52, 56, 253, 140, 223, 206, 161, 247, 196, 222, 165, 16, 178, 220, 110, 14, 149, 44, - 98, 187, 76, 8, 112, 165, 244, 140, 70, 208, 230, 139, 27, 213, 61, 37, 166, 76, 4, 7, 250, 90, - 4, 168, 23, 200, 0, 1, 95, 144, 169, 219, 57, 187, 100, 67, 150, 32, 12, 30, 146, 80, 175, 141, - 174, 215, 157, 200, 125, 138, 44, 210, 30, 128, 126, 51, 56, 0, 0, 246, 146, 210, 15, 92, 254, - 76, 130, 61, 112, 230, 105, 104, 32, 207, 252, 5, 150, 120, 108, 198, 45, 220, 89, 35, 57, 51, - 63, 58, 46, 71, 193, 101, 80, 137, 33, 220, 214, 87, 27, 144, 203, 187, 35, 149, 208, 254, 183, - 89, 252, 67, 18, 101, 22, 188, 53, 67, 98, 121, 173, 43, 18, 123, 60, 166, 76, 4, 7, 250, 91, - 4, 168, 23, 200, 0, 1, 95, 144, 85, 124, 40, 6, 182, 206, 166, 90, 102, 244, 21, 187, 46, 209, - 206, 137, 123, 52, 7, 69, 38, 87, 244, 110, 17, 245, 244, 0, 0, 169, 89, 233, 232, 87, 89, 231, - 136, 61, 207, 65, 146, 220, 122, 124, 155, 115, 143, 115, 84, 167, 82, 188, 32, 115, 162, 72, - 4, 13, 4, 168, 23, 200, 0, 82, 8, 145, 51, 122, 48, 14, 3, 97, 189, 219, 46, 55, 125, 212, 232, - 140, 203, 119, 150, 102, 61, 41, 162, 36, 26, 246, 44, 0, 0, 1, 181, 97, 111, 118, 95, 75, 92, - 244, 162, 190, 158, 251, 249, 183, 57, 95, 92, 159, 206, 222, 83, 217, 67, 113, 181, 220, 33, - 211, 61, 149, 82, 249, 126, 174, 178, 255, 92, 16, 245, 161, 166, 170, 77, 208, 44, 113, 101, - 107, 178, 191, 46, 234, 23, 237, 188, 243, 192, 157, 187, 102, 172, 96, 211, 85, 166, 76, 4, 7, - 190, 156, 4, 168, 23, 200, 0, 1, 95, 144, 172, 206, 38, 131, 155, 38, 73, 205, 122, 231, 241, - 73, 224, 204, 25, 117, 66, 217, 244, 154, 14, 92, 222, 224, 116, 174, 108, 0, 1, 40, 60, 107, - 206, 140, 148, 121, 252, 234, 201, 103, 54, 7, 186, 111, 183, 121, 221, 111, 195, 104, 195, - 135, 102, 184, 87, 48, 10, 217, 112, 169, 28, 182, 198, 133, 103, 170, 62, 96, 3, 10, 208, 84, - 62, 5, 116, 37, 160, 186, 109, 21, 115, 26, 124, 205, 190, 29, 14, 40, 243, 120, 209, 153, 58, - 166, 76, 4, 7, 190, 157, 4, 168, 23, 200, 0, 1, 95, 144, 44, 86, 100, 220, 80, 225, 157, 215, - 158, 105, 129, 210, 85, 223, 193, 232, 65, 62, 141, 234, 14, 92, 10, 170, 20, 234, 120, 0, 0, - 144, 212, 136, 96, 91, 57, 155, 187, 100, 235, 89, 151, 153, 213, 98, 16, 225, 225, 77, 181, - 226, 13, 10, 173, 211, 13, 107, 60, 255, 196, 107, 166, 248, 33, 103, 126, 119, 34, 34, 207, - 27, 167, 158, 186, 14, 65, 167, 119, 173, 185, 246, 57, 125, 215, 141, 187, 114, 99, 202, 26, - 115, 53, 243, 78, 166, 76, 4, 7, 190, 158, 4, 168, 23, 200, 0, 1, 95, 144, 38, 251, 59, 193, - 13, 133, 153, 135, 80, 47, 87, 20, 60, 31, 215, 71, 204, 141, 27, 81, 14, 92, 1, 150, 110, 143, - 160, 0, 0, 217, 14, 4, 73, 73, 152, 111, 63, 201, 17, 32, 3, 50, 82, 159, 204, 6, 211, 55, 89, - 239, 108, 52, 248, 108, 80, 231, 82, 126, 53, 122, 176, 72, 188, 221, 220, 73, 50, 100, 107, - 225, 68, 156, 179, 26, 251, 98, 68, 237, 59, 253, 206, 94, 105, 116, 177, 234, 158, 10, 28, 53, - 126, 208, 126, 166, 76, 4, 7, 190, 159, 4, 168, 23, 200, 0, 1, 95, 144, 15, 34, 183, 167, 97, - 76, 77, 206, 33, 212, 159, 120, 227, 53, 65, 108, 140, 207, 118, 4, 14, 87, 98, 60, 39, 50, - 132, 0, 1, 63, 32, 39, 249, 144, 70, 234, 232, 58, 51, 177, 137, 124, 172, 1, 187, 196, 46, - 201, 195, 213, 151, 149, 16, 199, 26, 63, 14, 15, 81, 124, 134, 221, 42, 101, 237, 183, 63, - 110, 55, 88, 138, 177, 144, 107, 236, 54, 240, 222, 23, 147, 161, 12, 215, 184, 192, 249, 97, - 108, 62, 199, 23, 183, 127, 166, 76, 4, 7, 190, 160, 4, 168, 23, 200, 0, 1, 95, 144, 47, 157, - 255, 83, 86, 106, 68, 8, 204, 5, 241, 230, 50, 195, 146, 217, 129, 25, 56, 76, 14, 86, 226, 94, - 126, 48, 56, 0, 0, 180, 22, 153, 219, 143, 6, 139, 4, 227, 202, 225, 22, 47, 118, 176, 240, - 202, 53, 140, 64, 25, 151, 20, 126, 129, 169, 56, 130, 12, 196, 195, 65, 159, 19, 200, 93, 233, - 72, 36, 106, 245, 81, 76, 92, 232, 231, 78, 190, 134, 194, 62, 26, 68, 141, 198, 43, 45, 2, 15, - 96, 116, 148, 50, 124, 160, 72, 4, 11, 164, 59, 116, 0, 82, 8, 106, 213, 220, 100, 251, 224, - 126, 16, 206, 193, 141, 16, 56, 27, 78, 53, 191, 187, 3, 24, 13, 140, 246, 116, 160, 98, 112, - 0, 1, 21, 196, 181, 176, 225, 233, 169, 236, 96, 236, 245, 195, 143, 138, 145, 236, 14, 105, - 157, 225, 35, 105, 195, 43, 45, 197, 241, 105, 117, 80, 156, 182, 216, 243, 136, 68, 26, 21, 2, - 186, 184, 56, 107, 151, 101, 54, 210, 98, 170, 229, 72, 99, 244, 238, 210, 95, 53, 32, 47, 18, - 106, 201, 57, 37, 162, 72, 4, 1, 11, 164, 59, 116, 0, 82, 8, 14, 194, 185, 52, 125, 88, 205, - 20, 49, 253, 194, 252, 61, 190, 192, 33, 236, 46, 117, 29, 23, 158, 183, 251, 127, 183, 0, 0, - 0, 88, 77, 162, 61, 130, 174, 162, 3, 126, 225, 31, 117, 113, 14, 206, 157, 2, 237, 209, 67, - 168, 28, 64, 24, 157, 243, 245, 241, 98, 19, 10, 172, 50, 87, 138, 195, 140, 110, 25, 124, 50, - 89, 88, 181, 226, 97, 57, 254, 7, 54, 184, 21, 205, 10, 164, 27, 2, 39, 150, 17, 253, 235, 148, - 104, 166, 76, 4, 7, 190, 161, 4, 168, 23, 200, 0, 1, 95, 144, 8, 53, 5, 168, 51, 186, 242, 167, - 92, 117, 29, 87, 100, 92, 219, 27, 178, 49, 175, 236, 14, 85, 131, 138, 102, 131, 64, 0, 1, - 107, 119, 238, 28, 64, 31, 134, 181, 217, 254, 160, 73, 223, 62, 11, 104, 27, 56, 7, 66, 123, - 214, 241, 96, 25, 242, 209, 94, 17, 73, 38, 214, 210, 5, 7, 204, 40, 250, 129, 220, 209, 215, - 195, 142, 55, 244, 39, 109, 217, 255, 157, 232, 49, 119, 121, 252, 136, 199, 0, 11, 132, 194, - 195, 49, 166, 76, 4, 7, 190, 162, 4, 168, 23, 200, 0, 1, 95, 144, 112, 157, 44, 105, 173, 210, - 220, 251, 71, 205, 8, 9, 205, 36, 154, 181, 145, 14, 230, 58, 14, 84, 238, 214, 151, 23, 136, - 0, 0, 131, 37, 119, 230, 85, 53, 136, 245, 58, 14, 156, 29, 101, 244, 243, 33, 222, 70, 71, - 183, 176, 6, 215, 168, 101, 225, 36, 180, 100, 100, 100, 21, 188, 163, 63, 255, 192, 18, 181, - 20, 52, 16, 19, 119, 158, 36, 70, 91, 58, 114, 0, 151, 89, 9, 6, 63, 226, 55, 24, 158, 116, - 108, 221, 72, 166, 76, 4, 7, 190, 163, 4, 168, 23, 200, 0, 1, 95, 144, 153, 24, 30, 212, 140, - 124, 81, 83, 38, 34, 218, 35, 226, 14, 43, 56, 57, 36, 118, 167, 14, 83, 43, 202, 227, 155, - 196, 0, 0, 4, 76, 237, 106, 18, 107, 124, 150, 50, 26, 83, 28, 110, 0, 0, 181, 43, 223, 131, - 203, 71, 250, 239, 179, 43, 228, 33, 151, 4, 249, 22, 213, 252, 134, 147, 207, 55, 251, 6, 84, - 119, 249, 191, 37, 245, 165, 233, 221, 243, 191, 228, 200, 34, 2, 186, 0, 170, 187, 168, 25, - 11, 162, 220, 83, 166, 76, 4, 7, 190, 164, 4, 168, 23, 200, 0, 1, 95, 144, 9, 140, 209, 154, - 69, 109, 219, 131, 39, 239, 208, 233, 68, 213, 31, 246, 141, 177, 115, 76, 14, 76, 233, 211, - 166, 100, 220, 0, 1, 252, 20, 233, 156, 3, 169, 237, 162, 168, 4, 178, 7, 78, 158, 93, 241, - 189, 32, 191, 77, 196, 176, 90, 195, 112, 147, 139, 229, 1, 145, 76, 59, 205, 174, 247, 254, 9, - 172, 70, 168, 76, 156, 200, 158, 117, 23, 104, 74, 163, 144, 37, 125, 90, 212, 210, 103, 178, - 69, 88, 192, 142, 225, 59, 50, 166, 76, 4, 7, 190, 165, 4, 168, 23, 200, 0, 1, 95, 144, 202, - 13, 208, 135, 133, 189, 135, 214, 46, 110, 17, 234, 114, 87, 91, 161, 3, 208, 121, 222, 14, 76, - 89, 41, 155, 152, 148, 0, 0, 202, 159, 120, 249, 168, 107, 143, 110, 147, 222, 37, 29, 12, 142, - 11, 136, 69, 45, 5, 107, 97, 136, 155, 25, 53, 237, 122, 181, 164, 163, 232, 84, 167, 249, 179, - 42, 238, 246, 33, 43, 19, 130, 251, 73, 226, 186, 218, 93, 200, 196, 1, 253, 24, 69, 188, 158, - 90, 134, 177, 24, 190, 134, 131, 21, 166, 76, 4, 1, 23, 8, 4, 168, 23, 200, 0, 1, 95, 144, 216, - 152, 213, 232, 41, 113, 124, 114, 231, 67, 139, 173, 89, 48, 118, 104, 109, 125, 22, 74, 68, - 182, 93, 71, 8, 19, 144, 0, 1, 130, 109, 239, 72, 48, 51, 16, 67, 119, 211, 183, 55, 163, 244, - 8, 205, 128, 46, 237, 7, 28, 193, 121, 168, 41, 170, 147, 94, 93, 162, 4, 46, 116, 42, 210, - 185, 230, 240, 102, 20, 6, 229, 192, 47, 208, 204, 54, 189, 212, 182, 16, 21, 57, 131, 117, 91, - 203, 164, 81, 37, 216, 106, 45, 83, 166, 76, 4, 7, 190, 166, 4, 168, 23, 200, 0, 1, 95, 144, - 65, 65, 111, 97, 212, 44, 164, 27, 111, 60, 96, 157, 245, 161, 36, 195, 212, 144, 250, 51, 125, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 96, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 141, 126, - 164, 198, 128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 162, 63, 211, 10, 171, 245, 173, 83, - 186, 179, 9, 61, 205, 73, 72, 225, 92, 236, 128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 80, 4, 0, 0, 0, 77, 47, 224, 214, 251, 52, 5, - 94, 84, 21, 13, 77, 231, 48, 208, 177, 248, 87, 236, 84, 234, 175, 111, 4, 0, 0, 0, 0, 0, 0, 0, - 0, 1, 22, 77, 75, 18, 106, 235, 253, 79, 231, 156, 59, 35, 15, 209, 171, 245, 21, 155, 16, 135, - 156, 61, 143, 153, 191, 127, 236, 165, 104, 9, 182, 172, 220, 26, 87, 75, 39, 6, 24, 72, 58, - 127, 233, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 199, 118, 71, 120, 43, 113, 193, - 111, 165, 181, 174, 186, 175, 66, 103, 204, 252, 116, 161, 229, 7, 4, 253, 160, 88, 99, 22, 67, - 3, 227, 171, 46, 99, 178, 227, 8, 124, 112, 93, 241, 151, 68, 6, 30, 74, 49, 234, 23, 140, 130, - 120, 85, 19, 98, 97, 25, 3, 109, 60, 239, 12, 121, 155, 126, 160, 72, 4, 4, 168, 23, 200, 0, - 82, 8, 4, 144, 41, 221, 65, 102, 30, 88, 249, 146, 113, 160, 17, 45, 253, 52, 105, 95, 112, 0, - 50, 189, 146, 232, 141, 147, 0, 0, 1, 151, 51, 128, 152, 60, 40, 213, 171, 223, 94, 216, 16, - 228, 147, 29, 222, 141, 229, 146, 247, 99, 174, 54, 176, 195, 157, 92, 85, 21, 94, 139, 114, - 67, 241, 7, 76, 60, 208, 4, 137, 221, 20, 141, 231, 10, 144, 41, 207, 144, 99, 230, 129, 171, - 253, 43, 188, 128, 239, 104, 8, 23, 219, 239, 105, 166, 76, 4, 7, 108, 239, 4, 168, 23, 200, 0, - 1, 95, 144, 7, 215, 27, 252, 38, 58, 245, 117, 130, 37, 250, 183, 158, 22, 86, 162, 42, 201, - 130, 74, 34, 90, 2, 73, 134, 37, 200, 0, 0, 63, 138, 228, 217, 2, 196, 2, 99, 16, 246, 181, - 143, 252, 197, 47, 164, 160, 224, 43, 251, 12, 156, 33, 31, 183, 120, 160, 142, 154, 173, 109, - 117, 153, 70, 227, 113, 86, 104, 80, 12, 13, 53, 242, 107, 115, 132, 38, 235, 32, 101, 82, 55, - 229, 195, 184, 58, 20, 250, 153, 202, 74, 51, 30, 60, 166, 76, 4, 7, 108, 240, 4, 168, 23, 200, - 0, 1, 95, 144, 102, 200, 196, 18, 125, 154, 184, 89, 83, 28, 142, 227, 248, 242, 2, 90, 251, - 161, 186, 168, 41, 226, 21, 2, 81, 233, 16, 0, 1, 110, 191, 29, 14, 147, 97, 150, 177, 115, 73, - 146, 129, 245, 179, 28, 157, 58, 252, 246, 191, 25, 65, 170, 82, 130, 48, 59, 134, 198, 237, - 193, 2, 114, 168, 3, 232, 4, 170, 173, 153, 127, 89, 30, 172, 245, 62, 77, 135, 178, 83, 239, - 142, 188, 74, 187, 92, 129, 74, 102, 97, 164, 240, 156, 89, 166, 76, 4, 7, 108, 241, 4, 168, - 23, 200, 0, 1, 95, 144, 92, 252, 123, 139, 158, 218, 18, 168, 239, 86, 55, 57, 158, 62, 36, - 194, 250, 152, 178, 151, 32, 25, 102, 255, 57, 145, 164, 0, 1, 148, 150, 140, 59, 247, 145, - 228, 96, 159, 250, 56, 4, 88, 21, 102, 249, 202, 219, 14, 166, 134, 187, 119, 58, 11, 198, 221, - 186, 68, 14, 108, 180, 68, 57, 39, 104, 135, 205, 216, 66, 82, 134, 195, 186, 68, 145, 222, - 228, 29, 235, 115, 59, 12, 90, 242, 250, 48, 206, 90, 137, 10, 21, 19, 22, 166, 76, 4, 7, 108, - 242, 4, 168, 23, 200, 0, 1, 95, 144, 150, 222, 183, 115, 226, 168, 118, 9, 252, 53, 54, 118, - 115, 199, 51, 9, 199, 209, 38, 162, 34, 209, 13, 57, 245, 48, 244, 0, 0, 38, 11, 226, 145, 73, - 214, 125, 202, 202, 244, 57, 15, 77, 96, 106, 11, 14, 13, 231, 147, 82, 138, 172, 26, 248, 197, - 213, 53, 120, 185, 216, 247, 59, 95, 253, 51, 146, 88, 67, 76, 54, 57, 53, 198, 208, 144, 161, - 229, 31, 122, 100, 31, 77, 70, 80, 44, 147, 102, 169, 243, 20, 142, 196, 33, 162, 72, 4, 2, 11, - 164, 59, 116, 0, 82, 8, 176, 22, 57, 89, 179, 51, 168, 239, 169, 223, 183, 163, 173, 172, 65, - 237, 149, 151, 130, 56, 22, 16, 208, 147, 104, 223, 0, 0, 0, 98, 150, 112, 82, 65, 75, 152, 31, - 111, 135, 147, 131, 243, 57, 254, 181, 97, 100, 223, 6, 149, 232, 228, 134, 249, 102, 7, 163, - 126, 168, 131, 99, 207, 59, 130, 171, 58, 20, 13, 194, 75, 2, 54, 90, 191, 171, 190, 39, 225, - 231, 193, 156, 7, 137, 251, 75, 207, 16, 250, 158, 85, 39, 76, 99, 164, 76, 4, 232, 250, 4, - 168, 23, 200, 0, 1, 95, 144, 198, 210, 167, 71, 4, 231, 164, 132, 119, 146, 59, 157, 225, 107, - 222, 111, 113, 238, 66, 108, 32, 81, 92, 23, 227, 157, 88, 0, 1, 68, 236, 129, 86, 175, 231, - 13, 59, 147, 51, 191, 13, 39, 89, 243, 45, 211, 151, 94, 107, 170, 196, 68, 107, 124, 69, 75, - 174, 139, 149, 190, 189, 25, 115, 159, 6, 85, 40, 67, 79, 198, 172, 14, 159, 54, 241, 143, 95, - 253, 43, 48, 150, 52, 69, 194, 1, 234, 240, 91, 6, 62, 186, 142, 123, 160, 72, 4, 4, 168, 23, - 200, 0, 82, 8, 4, 144, 41, 221, 65, 102, 30, 88, 249, 146, 113, 160, 17, 45, 253, 52, 105, 95, - 112, 0, 21, 80, 229, 172, 10, 26, 192, 0, 1, 90, 197, 45, 40, 92, 48, 72, 201, 172, 17, 251, - 132, 202, 101, 126, 202, 5, 72, 158, 122, 147, 23, 239, 251, 109, 186, 176, 87, 173, 27, 178, - 211, 152, 249, 127, 79, 9, 31, 241, 212, 76, 126, 7, 81, 26, 97, 77, 46, 40, 70, 145, 113, 128, - 204, 200, 172, 243, 91, 109, 219, 11, 88, 170, 14, 164, 76, 4, 232, 251, 4, 168, 23, 200, 0, 1, - 95, 144, 211, 130, 67, 41, 183, 144, 213, 224, 197, 156, 32, 0, 174, 82, 228, 55, 157, 236, - 213, 65, 33, 188, 77, 20, 168, 206, 68, 0, 1, 235, 26, 190, 251, 74, 141, 63, 52, 140, 8, 69, - 113, 5, 58, 11, 12, 244, 122, 220, 162, 164, 61, 185, 244, 120, 104, 46, 48, 54, 129, 141, 172, - 42, 1, 100, 87, 113, 85, 60, 232, 190, 108, 67, 58, 39, 154, 94, 235, 176, 102, 6, 184, 12, - 253, 12, 193, 86, 169, 96, 170, 87, 173, 110, 86, 166, 76, 4, 7, 108, 243, 4, 168, 23, 200, 0, - 1, 95, 144, 108, 191, 232, 223, 62, 156, 4, 18, 242, 70, 127, 203, 19, 102, 237, 173, 116, 97, - 95, 225, 32, 235, 236, 48, 21, 67, 16, 0, 0, 104, 141, 170, 145, 59, 9, 166, 202, 10, 116, 72, - 234, 12, 146, 239, 176, 198, 242, 138, 64, 220, 130, 159, 79, 173, 181, 105, 237, 33, 35, 37, - 191, 73, 191, 121, 157, 23, 111, 78, 140, 195, 142, 152, 120, 75, 240, 205, 236, 145, 136, 90, - 60, 135, 246, 187, 216, 211, 241, 150, 88, 46, 68, 129, 105, 164, 76, 4, 232, 252, 4, 168, 23, - 200, 0, 1, 95, 144, 1, 196, 158, 35, 233, 219, 50, 83, 215, 103, 64, 160, 209, 225, 184, 32, - 39, 43, 17, 88, 31, 127, 142, 233, 83, 167, 12, 0, 0, 50, 80, 81, 21, 99, 234, 94, 47, 231, 34, - 50, 16, 14, 158, 161, 183, 159, 1, 112, 128, 144, 95, 40, 43, 230, 106, 36, 182, 138, 200, 67, - 116, 97, 117, 172, 244, 60, 19, 2, 88, 49, 164, 242, 252, 33, 123, 212, 212, 244, 226, 95, 3, - 182, 146, 98, 156, 38, 156, 16, 109, 146, 12, 62, 111, 166, 76, 4, 7, 108, 244, 4, 168, 23, - 200, 0, 1, 95, 144, 171, 40, 18, 27, 157, 28, 122, 56, 142, 63, 117, 109, 67, 38, 177, 42, 120, - 198, 174, 15, 32, 152, 85, 96, 209, 223, 48, 0, 0, 128, 7, 2, 187, 210, 63, 215, 65, 240, 165, - 101, 251, 94, 236, 20, 129, 189, 166, 76, 200, 164, 225, 16, 232, 214, 84, 53, 96, 126, 138, - 97, 33, 66, 10, 101, 59, 27, 115, 18, 150, 113, 146, 101, 28, 204, 109, 166, 76, 4, 2, 51, 216, - 4, 168, 23, 200, 0, 1, 95, 144, 113, 234, 252, 96, 118, 110, 124, 11, 39, 122, 152, 111, 142, - 88, 9, 177, 14, 235, 183, 109, 102, 211, 126, 193, 38, 214, 216, 0, 0, 47, 68, 77, 213, 128, - 72, 116, 224, 79, 6, 135, 121, 93, 99, 178, 4, 41, 108, 234, 127, 158, 208, 29, 142, 161, 111, - 100, 207, 9, 18, 37, 167, 218, 7, 66, 77, 95, 31, 82, 51, 170, 126, 114, 9, 58, 132, 188, 240, - 73, 97, 10, 166, 94, 16, 54, 75, 199, 8, 143, 248, 217, 236, 146, 65, 162, 76, 0, 90, 4, 168, - 23, 200, 0, 18, 79, 128, 65, 242, 116, 192, 2, 63, 131, 57, 29, 228, 224, 115, 60, 96, 157, - 245, 161, 36, 195, 212, 19, 249, 85, 225, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 64, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 144, 4, 0, 0, 0, 75, 126, 2, 245, 61, 47, 29, 244, - 208, 205, 93, 153, 234, 65, 131, 154, 229, 92, 161, 99, 47, 77, 233, 4, 0, 0, 0, 0, 0, 0, 0, 0, - 118, 152, 80, 41, 200, 47, 58, 23, 106, 250, 141, 189, 86, 155, 79, 233, 88, 184, 39, 194, 251, - 43, 10, 217, 237, 124, 84, 41, 230, 134, 192, 18, 236, 20, 19, 87, 75, 39, 6, 24, 68, 251, 106, - 153, 4, 0, 0, 0, 124, 185, 102, 184, 80, 147, 247, 225, 76, 65, 48, 70, 245, 43, 165, 188, 114, - 100, 36, 227, 138, 195, 64, 5, 0, 0, 0, 0, 0, 0, 0, 0, 90, 186, 211, 123, 156, 172, 168, 44, - 18, 29, 40, 201, 248, 250, 86, 15, 150, 210, 73, 214, 11, 95, 239, 232, 3, 70, 146, 244, 142, - 146, 208, 132, 71, 23, 19, 87, 75, 39, 6, 24, 212, 47, 41, 215, 4, 0, 0, 0, 37, 190, 92, 103, - 170, 183, 253, 37, 65, 98, 247, 143, 180, 47, 152, 148, 5, 102, 143, 145, 44, 70, 80, 3, 0, 0, - 0, 0, 0, 0, 0, 0, 231, 116, 21, 130, 20, 246, 169, 76, 240, 52, 246, 255, 91, 238, 6, 79, 135, - 34, 29, 155, 67, 203, 0, 182, 82, 120, 217, 124, 133, 198, 88, 190, 233, 24, 19, 87, 75, 39, 6, - 24, 100, 54, 96, 202, 0, 0, 0, 48, 65, 54, 239, 208, 58, 125, 193, 126, 22, 78, 125, 213, 153, - 2, 118, 162, 81, 234, 160, 196, 184, 52, 134, 2, 0, 0, 0, 0, 0, 0, 0, 0, 107, 238, 203, 223, - 102, 51, 156, 231, 42, 222, 56, 190, 66, 151, 49, 51, 163, 63, 246, 107, 233, 135, 96, 100, 65, - 7, 119, 228, 231, 243, 241, 12, 141, 25, 19, 87, 75, 39, 6, 24, 54, 46, 16, 168, 0, 0, 0, 32, - 15, 51, 4, 73, 5, 86, 195, 195, 154, 126, 12, 21, 227, 84, 196, 213, 185, 212, 64, 181, 189, - 240, 253, 4, 0, 0, 0, 0, 0, 0, 0, 0, 216, 232, 199, 222, 202, 50, 84, 221, 46, 181, 79, 211, - 144, 85, 49, 146, 101, 177, 35, 192, 235, 139, 55, 9, 187, 18, 79, 193, 125, 87, 121, 151, 242, - 28, 19, 87, 75, 39, 6, 24, 160, 88, 253, 62, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, - 200, 255, 153, 34, 206, 96, 9, 240, 93, 233, 247, 181, 249, 244, 139, 252, 20, 108, 2, 163, 77, - 136, 91, 6, 90, 99, 15, 235, 165, 77, 29, 94, 194, 110, 5, 140, 186, 138, 118, 91, 238, 188, - 250, 214, 24, 104, 46, 234, 80, 159, 168, 25, 49, 222, 229, 160, 119, 51, 58, 147, 45, 203, - 158, 41, 166, 76, 4, 2, 51, 217, 4, 168, 23, 200, 0, 1, 95, 144, 242, 249, 113, 140, 107, 43, - 5, 172, 86, 242, 62, 109, 1, 132, 59, 39, 94, 253, 182, 103, 103, 201, 35, 230, 180, 79, 212, - 0, 1, 212, 139, 6, 86, 111, 227, 183, 38, 212, 250, 95, 245, 218, 110, 160, 45, 57, 228, 42, - 220, 242, 75, 168, 73, 143, 199, 118, 101, 230, 208, 218, 56, 114, 224, 142, 123, 33, 236, 77, - 186, 125, 228, 162, 186, 72, 246, 142, 142, 166, 167, 143, 178, 228, 61, 26, 143, 153, 39, 8, - 238, 193, 231, 176, 115, 162, 76, 4, 38, 4, 168, 23, 200, 0, 1, 95, 144, 105, 234, 107, 49, - 239, 48, 93, 107, 153, 187, 45, 76, 157, 153, 69, 111, 161, 8, 176, 42, 55, 162, 59, 214, 82, - 31, 132, 0, 1, 204, 190, 148, 225, 229, 237, 50, 14, 73, 193, 87, 111, 189, 160, 163, 15, 139, - 163, 59, 119, 54, 85, 151, 19, 201, 68, 50, 137, 170, 154, 124, 243, 239, 217, 131, 230, 47, - 51, 73, 182, 238, 56, 164, 109, 156, 85, 112, 165, 0, 53, 64, 233, 78, 42, 97, 45, 251, 19, - 170, 100, 26, 13, 92, 43, 166, 76, 4, 2, 51, 218, 4, 168, 23, 200, 0, 1, 95, 144, 63, 153, 246, - 11, 94, 7, 215, 244, 50, 86, 18, 24, 112, 58, 39, 164, 161, 243, 92, 31, 104, 146, 233, 104, - 33, 247, 128, 0, 1, 175, 54, 101, 29, 169, 146, 80, 224, 155, 126, 6, 238, 93, 39, 8, 13, 205, - 190, 223, 217, 93, 20, 217, 211, 162, 32, 120, 52, 126, 158, 114, 254, 184, 78, 30, 92, 253, - 96, 27, 139, 219, 253, 62, 133, 194, 168, 204, 13, 47, 155, 216, 183, 190, 87, 99, 117, 130, - 68, 165, 61, 128, 239, 246, 109, 160, 72, 4, 4, 168, 23, 200, 0, 82, 8, 158, 99, 22, 244, 75, - 174, 238, 229, 212, 26, 16, 112, 81, 108, 197, 250, 71, 186, 242, 39, 13, 223, 56, 182, 200, - 149, 192, 0, 0, 217, 121, 157, 85, 20, 226, 86, 179, 235, 58, 14, 208, 181, 175, 175, 31, 175, - 38, 80, 212, 48, 220, 132, 153, 212, 75, 204, 25, 71, 106, 44, 55, 205, 220, 121, 234, 35, 105, - 23, 109, 117, 21, 151, 11, 251, 25, 166, 147, 135, 129, 55, 142, 122, 158, 183, 80, 163, 156, - 129, 31, 143, 59, 137, 120, 166, 76, 4, 2, 51, 219, 4, 168, 23, 200, 0, 1, 95, 144, 37, 216, - 85, 167, 161, 64, 172, 111, 51, 14, 34, 2, 249, 108, 111, 98, 235, 18, 108, 193, 108, 127, 186, - 96, 22, 195, 96, 0, 0, 47, 151, 153, 16, 188, 51, 237, 241, 234, 248, 195, 67, 167, 4, 105, 96, - 23, 252, 132, 97, 237, 251, 249, 23, 129, 188, 193, 12, 206, 35, 31, 76, 130, 43, 200, 82, 130, - 242, 178, 128, 237, 112, 38, 29, 214, 238, 71, 158, 244, 115, 143, 26, 128, 112, 216, 47, 20, - 101, 15, 116, 243, 137, 129, 4, 162, 72, 4, 60, 4, 168, 23, 200, 0, 86, 34, 251, 177, 183, 60, - 79, 11, 218, 79, 103, 220, 162, 102, 206, 110, 244, 47, 82, 15, 187, 152, 4, 164, 166, 111, - 118, 250, 252, 0, 1, 118, 45, 72, 48, 109, 188, 200, 17, 252, 115, 209, 164, 177, 123, 165, 2, - 216, 228, 67, 67, 144, 242, 227, 151, 132, 31, 78, 169, 130, 242, 70, 93, 29, 66, 136, 156, 34, - 112, 205, 236, 213, 177, 16, 132, 145, 11, 162, 223, 113, 149, 182, 127, 210, 19, 129, 37, 72, - 171, 195, 198, 43, 239, 94, 75, 166, 76, 4, 2, 51, 220, 4, 168, 23, 200, 0, 1, 95, 144, 255, - 58, 112, 216, 213, 105, 45, 208, 93, 113, 23, 95, 162, 158, 133, 101, 247, 69, 15, 87, 110, - 244, 106, 28, 179, 240, 188, 0, 0, 130, 182, 61, 50, 52, 3, 165, 126, 209, 108, 0, 197, 73, - 104, 197, 182, 93, 210, 193, 250, 67, 71, 2, 44, 180, 8, 200, 6, 26, 144, 10, 112, 33, 180, 35, - 86, 154, 0, 241, 176, 2, 121, 38, 241, 156, 32, 10, 115, 191, 157, 254, 189, 60, 117, 244, 110, - 35, 218, 29, 85, 184, 126, 171, 96, 160, 200, 4, 4, 168, 23, 200, 0, 82, 96, 32, 129, 144, 82, - 96, 64, 144, 145, 32, 1, 84, 91, 145, 144, 80, 86, 91, 96, 1, 96, 160, 96, 2, 10, 3, 96, 4, 53, - 129, 22, 96, 0, 144, 129, 82, 96, 1, 96, 32, 129, 129, 82, 96, 64, 146, 131, 144, 32, 128, 84, - 146, 1, 84, 131, 81, 146, 144, 148, 22, 130, 82, 129, 1, 146, 144, 146, 82, 128, 81, 145, 130, - 144, 3, 1, 144, 243, 91, 97, 5, 11, 96, 4, 53, 96, 0, 96, 0, 96, 0, 96, 0, 96, 1, 96, 0, 80, - 96, 0, 96, 3, 96, 0, 80, 135, 129, 84, 129, 16, 21, 97, 0, 2, 87, 80, 128, 84, 127, 194, 87, - 90, 14, 158, 89, 60, 0, 249, 89, 248, 201, 47, 18, 219, 40, 105, 195, 57, 90, 59, 5, 2, 208, - 94, 37, 22, 68, 111, 113, 248, 91, 137, 1, 84, 96, 1, 96, 160, 96, 2, 10, 3, 22, 144, 146, 82, - 96, 32, 146, 144, 146, 82, 96, 64, 144, 146, 32, 145, 134, 144, 129, 16, 21, 97, 0, 2, 87, 96, - 0, 145, 130, 82, 127, 194, 87, 90, 14, 158, 89, 60, 0, 249, 89, 248, 201, 47, 18, 219, 40, 105, - 195, 57, 90, 59, 5, 2, 208, 94, 37, 22, 68, 111, 113, 248, 91, 1, 144, 144, 84, 130, 84, 96, 1, - 132, 1, 84, 97, 1, 0, 147, 144, 147, 10, 144, 145, 4, 96, 1, 96, 160, 96, 2, 10, 3, 144, 129, - 22, 150, 80, 22, 147, 80, 145, 80, 80, 145, 147, 144, 146, 80, 86, 91, 97, 0, 169, 96, 2, 84, - 129, 86, 91, 97, 0, 163, 96, 4, 53, 96, 36, 53, 96, 1, 96, 160, 96, 2, 10, 3, 130, 129, 22, 96, - 0, 144, 129, 82, 96, 1, 96, 32, 82, 96, 64, 144, 32, 84, 51, 130, 22, 145, 22, 20, 21, 97, 5, - 188, 87, 96, 64, 96, 0, 32, 128, 84, 96, 1, 96, 160, 96, 2, 10, 3, 25, 22, 144, 145, 23, 144, - 85, 80, 86, 91, 97, 0, 169, 96, 4, 53, 96, 1, 96, 160, 96, 2, 10, 3, 129, 22, 96, 0, 144, 129, - 82, 96, 1, 96, 32, 129, 144, 82, 96, 64, 130, 32, 1, 84, 20, 21, 97, 3, 38, 86, 91, 97, 0, 163, - 96, 4, 53, 96, 36, 53, 96, 0, 84, 51, 96, 1, 96, 160, 96, 2, 10, 3, 144, 129, 22, 145, 22, 20, - 21, 97, 5, 188, 87, 96, 64, 81, 96, 1, 96, 160, 96, 2, 10, 3, 131, 22, 144, 96, 0, 144, 131, - 144, 130, 129, 129, 129, 133, 136, 131, 241, 80, 80, 80, 80, 80, 80, 80, 86, 91, 97, 5, 53, 96, - 4, 53, 96, 1, 96, 160, 96, 2, 10, 3, 129, 129, 22, 96, 0, 144, 129, 82, 96, 1, 96, 32, 82, 96, - 64, 144, 32, 84, 22, 97, 3, 38, 86, 91, 96, 64, 128, 81, 96, 1, 96, 160, 96, 2, 10, 3, 148, - 133, 22, 129, 82, 146, 144, 147, 22, 96, 32, 131, 1, 82, 129, 131, 1, 82, 144, 81, 144, 129, - 144, 3, 96, 96, 1, 144, 243, 91, 96, 64, 128, 81, 96, 1, 96, 160, 96, 2, 10, 3, 146, 144, 146, - 22, 130, 82, 81, 144, 129, 144, 3, 96, 32, 1, 144, 243, 91, 80, 80, 96, 3, 128, 84, 132, 147, - 80, 144, 145, 80, 96, 0, 25, 129, 1, 144, 129, 16, 21, 97, 0, 2, 87, 80, 128, 84, 96, 0, 145, - 144, 145, 82, 127, 194, 87, 90, 14, 158, 89, 60, 0, 249, 89, 248, 201, 47, 18, 219, 40, 105, - 195, 57, 90, 59, 5, 2, 208, 94, 37, 22, 68, 111, 113, 248, 90, 1, 128, 84, 96, 1, 96, 160, 96, - 2, 10, 3, 25, 22, 144, 145, 23, 144, 85, 96, 2, 128, 84, 96, 1, 1, 144, 85, 80, 86, 91, 80, - 144, 86, 91, 80, 80, 80, 80, 91, 80, 80, 86, 91, 86, 1, 38, 202, 181, 215, 11, 252, 100, 32, - 74, 63, 3, 41, 91, 180, 126, 22, 24, 14, 206, 49, 27, 133, 157, 222, 69, 122, 103, 154, 77, - 144, 192, 186, 18, 73, 151, 50, 30, 159, 143, 108, 172, 227, 229, 51, 26, 238, 253, 213, 194, - 169, 30, 196, 219, 36, 176, 78, 188, 45, 127, 32, 249, 210, 150, 1, 164, 204, 4, 2, 127, 5, 94, - 57, 66, 169, 1, 95, 144, 80, 176, 37, 212, 57, 170, 179, 160, 123, 152, 172, 15, 176, 140, 113, - 151, 206, 227, 183, 185, 2, 181, 227, 175, 22, 177, 136, 0, 0, 0, 240, 5, 135, 10, 45, 221, - 245, 151, 134, 212, 142, 77, 16, 225, 83, 5, 34, 166, 174, 78, 45, 153, 45, 201, 241, 101, 215, - 51, 126, 212, 213, 64, 149, 108, 155, 177, 52, 54, 146, 17, 50, 187, 250, 76, 8, 81, 154, 191, - 174, 103, 180, 195, 214, 34, 239, 105, 13, 22, 218, 72, 95, 44, 113, 75, 164, 76, 0, 13, 216, - 4, 241, 205, 36, 126, 1, 95, 144, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, - 56, 48, 74, 99, 80, 48, 85, 76, 90, 57, 101, 85, 90, 114, 103, 110, 53, 84, 85, 85, 48, 122, - 76, 111, 56, 110, 115, 109, 77, 53, 116, 90, 114, 66, 111, 99, 73, 113, 50, 82, 70, 103, 69, - 118, 97, 79, 78, 81, 80, 111, 166, 150, 100, 133, 167, 54, 131, 151, 87, 4, 246, 150, 35, 23, - 84, 53, 53, 134, 163, 39, 102, 54, 148, 148, 245, 53, 147, 37, 35, 115, 7, 99, 5, 23, 39, 119, - 38, 182, 229, 4, 211, 37, 149, 148, 70, 245, 166, 116, 71, 87, 10, 84, 88, 56, 72, 121, 115, - 97, 113, 122, 51, 99, 112, 65, 68, 55, 76, 65, 86, 99, 113, 48, 55, 104, 104, 119, 77, 50, 85, - 86, 121, 73, 51, 109, 118, 84, 76, 74, 85, 97, 51, 85, 110, 116, 101, 115, 120, 69, 68, 67, - 100, 166, 117, 150, 54, 133, 102, 151, 38, 227, 147, 35, 51, 4, 51, 6, 86, 215, 118, 181, 151, - 68, 212, 179, 102, 151, 23, 164, 231, 84, 197, 52, 196, 69, 103, 116, 179, 134, 71, 135, 166, - 102, 227, 134, 213, 117, 19, 84, 213, 38, 70, 250, 76, 89, 65, 115, 99, 55, 117, 90, 74, 66, - 69, 119, 50, 86, 103, 71, 80, 49, 99, 84, 111, 52, 87, 97, 97, 83, 88, 56, 55, 118, 101, 66, - 81, 118, 54, 113, 71, 107, 50, 90, 107, 73, 77, 68, 107, 73, 101, 106, 75, 49, 166, 150, 53, - 39, 68, 150, 228, 39, 117, 163, 115, 116, 229, 103, 148, 100, 20, 231, 36, 212, 68, 148, 245, - 70, 23, 119, 54, 195, 150, 118, 134, 181, 38, 150, 182, 20, 55, 151, 116, 87, 132, 214, 163, - 150, 182, 132, 21, 84, 149, 38, 147, 131, 4, 166, 53, 3, 5, 84, 197, 163, 150, 85, 85, 167, 38, - 118, 227, 85, 69, 85, 83, 7, 164, 198, 243, 134, 231, 54, 212, 211, 87, 69, 167, 36, 38, 246, - 52, 151, 19, 37, 36, 102, 116, 87, 102, 20, 244, 229, 21, 6, 250, 105, 102, 72, 90, 115, 104, - 57, 117, 112, 79, 105, 98, 49, 117, 67, 83, 88, 106, 50, 118, 99, 105, 73, 79, 83, 89, 50, 82, - 55, 48, 118, 48, 81, 114, 119, 114, 107, 110, 80, 77, 50, 89, 89, 68, 111, 90, 103, 68, 117, - 112, 165, 69, 131, 132, 135, 151, 54, 23, 23, 163, 54, 55, 4, 20, 67, 116, 196, 21, 102, 55, - 19, 3, 118, 134, 135, 116, 211, 37, 85, 103, 148, 147, 54, 215, 101, 68, 196, 165, 86, 19, 53, - 86, 231, 70, 87, 55, 132, 84, 68, 54, 74, 103, 89, 99, 104, 86, 105, 114, 110, 57, 50, 51, 48, - 67, 48, 101, 109, 119, 107, 89, 116, 77, 75, 54, 105, 113, 122, 78, 117, 76, 83, 76, 68, 86, - 119, 75, 56, 100, 120, 122, 102, 110, 56, 109, 87, 81, 53, 77, 82, 100, 111, 164, 197, 148, 23, - 54, 51, 119, 85, 164, 164, 36, 87, 115, 37, 102, 116, 117, 3, 22, 53, 70, 243, 69, 118, 22, 21, - 53, 131, 131, 119, 102, 84, 37, 23, 99, 103, 20, 118, 179, 37, 166, 180, 148, 212, 70, 180, - 150, 86, 164, 179, 26, 105, 99, 82, 116, 73, 110, 66, 119, 90, 55, 55, 78, 86, 121, 70, 65, 78, - 114, 77, 68, 73, 79, 84, 97, 119, 115, 108, 57, 103, 104, 107, 82, 105, 107, 97, 67, 121, 119, - 69, 120, 77, 106, 57, 107, 104, 65, 85, 73, 82, 105, 0, 59, 10, 216, 153, 201, 30, 136, 237, - 42, 165, 137, 20, 54, 28, 228, 187, 38, 84, 106, 108, 191, 81, 214, 86, 234, 69, 56, 0, 8, 13, - 69, 165, 250, 42, 246, 51, 50, 116, 111, 166, 76, 4, 6, 205, 200, 4, 168, 23, 200, 0, 1, 95, - 144, 96, 54, 237, 210, 43, 185, 3, 84, 70, 200, 122, 46, 135, 7, 15, 42, 74, 198, 39, 26, 25, - 101, 96, 121, 214, 97, 136, 0, 0, 2, 11, 186, 183, 40, 255, 39, 144, 223, 178, 162, 195, 145, - 70, 185, 246, 201, 213, 255, 253, 181, 47, 124, 71, 206, 100, 228, 79, 48, 237, 158, 161, 214, - 86, 88, 66, 85, 71, 124, 207, 105, 188, 66, 167, 74, 252, 194, 9, 22, 6, 209, 241, 147, 233, - 24, 32, 126, 121, 56, 176, 41, 95, 162, 14, 166, 76, 4, 6, 205, 201, 4, 168, 23, 200, 0, 1, 95, - 144, 156, 250, 132, 110, 5, 27, 114, 76, 246, 118, 94, 84, 86, 244, 14, 167, 141, 4, 2, 247, - 24, 6, 180, 16, 144, 16, 84, 0, 0, 150, 193, 200, 1, 219, 229, 143, 7, 197, 21, 35, 95, 7, 190, - 10, 101, 255, 117, 204, 96, 230, 163, 237, 52, 49, 172, 122, 29, 16, 21, 148, 75, 183, 156, 45, - 55, 200, 41, 215, 190, 16, 78, 251, 203, 162, 135, 59, 47, 189, 180, 150, 90, 172, 159, 90, - 121, 250, 55, 63, 60, 83, 96, 47, 62, 166, 76, 4, 6, 205, 202, 4, 168, 23, 200, 0, 1, 95, 144, - 207, 50, 88, 111, 175, 44, 128, 47, 40, 76, 207, 232, 103, 28, 228, 238, 217, 47, 137, 146, 23, - 233, 113, 238, 32, 16, 0, 0, 1, 10, 192, 241, 47, 228, 203, 148, 51, 162, 70, 77, 54, 110, 242, - 177, 6, 136, 193, 118, 88, 28, 81, 241, 36, 2, 64, 30, 139, 250, 85, 89, 140, 144, 80, 136, - 152, 0, 147, 227, 3, 207, 169, 197, 244, 97, 17, 80, 91, 177, 55, 4, 100, 129, 231, 23, 154, - 176, 185, 130, 26, 157, 213, 193, 97, 164, 76, 4, 186, 207, 4, 168, 23, 200, 0, 1, 95, 144, - 179, 55, 210, 174, 146, 25, 170, 7, 9, 179, 227, 67, 223, 87, 20, 219, 155, 250, 38, 243, 22, - 46, 219, 60, 88, 243, 212, 0, 1, 162, 158, 167, 95, 81, 110, 90, 116, 22, 139, 197, 6, 159, 42, - 149, 220, 193, 54, 121, 227, 164, 46, 134, 182, 41, 56, 67, 135, 80, 46, 114, 254, 195, 85, - 104, 4, 162, 8, 171, 233, 68, 190, 117, 85, 236, 170, 58, 98, 102, 51, 206, 49, 151, 200, 107, - 181, 236, 101, 229, 192, 184, 235, 23, 89, 164, 76, 0, 35, 211, 4, 168, 23, 200, 0, 3, 13, 64, - 174, 80, 107, 178, 142, 215, 155, 41, 198, 150, 138, 181, 39, 209, 239, 220, 95, 57, 147, 49, - 2, 91, 187, 229, 31, 233, 97, 223, 124, 61, 39, 235, 226, 186, 38, 204, 112, 70, 88, 217, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 245, 225, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 87, 16, 1, 43, 1, 170, 239, 178, 157, 59, 208, 27, 37, 157, - 166, 73, 131, 181, 89, 70, 168, 177, 67, 236, 104, 12, 203, 229, 115, 223, 77, 153, 74, 206, - 25, 217, 174, 137, 125, 120, 253, 153, 223, 242, 194, 82, 77, 152, 102, 183, 153, 207, 161, - 162, 201, 240, 62, 191, 75, 54, 12, 91, 155, 50, 227, 87, 11, 206, 89, 166, 76, 4, 6, 205, 203, - 4, 168, 23, 200, 0, 1, 95, 144, 133, 146, 255, 253, 90, 25, 42, 235, 200, 26, 227, 3, 68, 87, - 136, 156, 54, 157, 91, 8, 26, 234, 219, 8, 182, 59, 152, 0, 0, 137, 192, 124, 249, 62, 0, 224, - 83, 57, 49, 254, 10, 180, 100, 88, 137, 40, 77, 212, 101, 126, 182, 165, 124, 252, 203, 173, - 48, 150, 202, 178, 131, 211, 104, 188, 228, 109, 105, 120, 26, 160, 181, 72, 85, 212, 251, 140, - 2, 140, 179, 238, 178, 222, 39, 201, 216, 64, 171, 146, 21, 153, 145, 177, 113, 166, 76, 4, 6, - 205, 204, 4, 168, 23, 200, 0, 1, 95, 144, 40, 141, 156, 93, 225, 192, 108, 46, 64, 14, 164, - 179, 132, 39, 107, 151, 241, 50, 197, 199, 23, 151, 189, 90, 83, 108, 216, 0, 0, 80, 118, 55, - 212, 69, 205, 234, 90, 234, 36, 45, 251, 45, 80, 94, 156, 106, 246, 93, 57, 245, 131, 207, 183, - 38, 244, 144, 206, 181, 205, 41, 251, 64, 3, 228, 83, 153, 114, 123, 95, 149, 177, 158, 223, - 146, 19, 129, 203, 131, 224, 118, 49, 250, 69, 119, 19, 244, 114, 49, 140, 175, 174, 126, 43, - 166, 76, 4, 2, 209, 230, 4, 168, 23, 200, 0, 1, 95, 144, 151, 157, 217, 193, 106, 184, 101, - 111, 73, 251, 32, 245, 226, 110, 233, 225, 157, 249, 190, 130, 1, 97, 161, 205, 82, 76, 228, 0, - 1, 26, 214, 177, 124, 190, 63, 155, 216, 50, 213, 198, 204, 212, 42, 17, 77, 51, 181, 57, 187, - 14, 111, 45, 123, 149, 21, 190, 237, 143, 151, 251, 174, 91, 187, 230, 177, 49, 97, 15, 246, - 234, 132, 244, 219, 172, 255, 70, 231, 189, 11, 0, 153, 192, 53, 168, 149, 199, 56, 180, 22, - 56, 43, 248, 2, 164, 76, 4, 186, 208, 4, 168, 23, 200, 0, 1, 95, 144, 188, 249, 66, 68, 199, - 254, 147, 149, 245, 195, 212, 108, 111, 235, 9, 173, 67, 164, 159, 247, 21, 111, 229, 143, 26, - 142, 128, 0, 1, 98, 58, 64, 221, 11, 200, 59, 135, 82, 132, 209, 93, 218, 42, 108, 12, 43, 86, - 140, 207, 32, 77, 127, 44, 171, 253, 169, 71, 161, 130, 84, 88, 118, 5, 53, 52, 32, 157, 175, - 68, 184, 169, 176, 171, 109, 215, 59, 73, 191, 242, 164, 32, 99, 205, 88, 50, 128, 128, 5, 110, - 70, 6, 68, 66, 166, 204, 3, 2, 209, 231, 4, 168, 23, 200, 0, 1, 95, 144, 18, 83, 100, 210, 128, - 220, 193, 88, 248, 84, 105, 252, 103, 180, 79, 108, 50, 227, 187, 44, 39, 10, 223, 179, 170, - 144, 0, 1, 83, 114, 191, 7, 109, 18, 246, 18, 93, 185, 216, 12, 12, 159, 192, 40, 77, 167, 109, - 168, 89, 10, 61, 175, 54, 218, 2, 0, 38, 120, 82, 251, 101, 138, 99, 82, 175, 142, 22, 162, - 249, 197, 109, 175, 26, 25, 138, 97, 27, 204, 179, 250, 145, 79, 67, 93, 40, 133, 100, 4, 121, - 194, 244, 26, 166, 76, 4, 6, 205, 205, 4, 168, 23, 200, 0, 1, 95, 144, 202, 23, 215, 201, 126, - 147, 38, 165, 145, 104, 58, 101, 9, 235, 153, 127, 98, 119, 170, 50, 21, 208, 243, 67, 57, 46, - 88, 0, 1, 230, 134, 207, 194, 89, 159, 165, 233, 47, 240, 40, 131, 131, 250, 140, 80, 73, 4, - 129, 89, 1, 183, 154, 235, 134, 192, 148, 143, 167, 226, 17, 43, 144, 40, 207, 89, 166, 16, 32, - 32, 87, 180, 171, 240, 30, 211, 127, 179, 58, 194, 23, 227, 155, 105, 158, 199, 48, 151, 173, - 186, 126, 68, 232, 104, 166, 76, 4, 2, 209, 232, 4, 168, 23, 200, 0, 1, 95, 144, 76, 144, 8, - 54, 209, 86, 142, 230, 243, 44, 20, 109, 183, 194, 182, 229, 231, 0, 154, 119, 3, 8, 115, 63, - 217, 179, 40, 0, 1, 12, 69, 38, 110, 111, 20, 233, 94, 94, 161, 59, 189, 83, 64, 103, 152, 49, - 42, 247, 118, 235, 115, 76, 120, 242, 95, 136, 65, 162, 218, 183, 101, 5, 57, 84, 191, 57, 1, - 160, 183, 171, 5, 132, 107, 215, 232, 101, 193, 13, 56, 48, 7, 51, 15, 6, 253, 59, 29, 77, 29, - 117, 23, 86, 28, 166, 76, 4, 6, 205, 206, 4, 168, 23, 200, 0, 1, 95, 144, 76, 220, 162, 77, - 113, 171, 215, 250, 28, 220, 28, 224, 98, 52, 38, 131, 167, 88, 226, 252, 22, 60, 200, 244, - 135, 6, 208, 0, 1, 240, 102, 251, 1, 115, 59, 59, 140, 174, 88, 72, 8, 18, 25, 213, 235, 91, - 73, 65, 17, 27, 12, 254, 186, 130, 24, 108, 150, 16, 57, 67, 59, 173, 8, 156, 153, 26, 154, 49, - 17, 224, 207, 238, 198, 243, 230, 54, 193, 20, 226, 133, 0, 183, 199, 161, 209, 176, 204, 137, - 84, 24, 255, 20, 11, 166, 76, 4, 2, 209, 233, 4, 168, 23, 200, 0, 1, 95, 89, 224, 220, 190, 23, - 110, 190, 225, 78, 104, 239, 138, 95, 46, 159, 180, 120, 246, 158, 227, 220, 142, 89, 55, 103, - 142, 220, 216, 112, 6, 146, 141, 33, 17, 55, 180, 51, 130, 234, 143, 142, 7, 214, 94, 137, 125, - 234, 189, 109, 97, 127, 148, 117, 160, 165, 120, 203, 213, 173, 255, 74, 58, 162, 72, 4, 11, 4, - 168, 23, 200, 0, 82, 8, 173, 57, 211, 34, 42, 19, 137, 90, 69, 73, 161, 172, 169, 44, 94, 4, - 50, 104, 124, 253, 14, 38, 70, 155, 168, 23, 192, 0, 1, 146, 28, 141, 216, 199, 135, 112, 100, - 92, 171, 4, 120, 170, 56, 207, 196, 96, 192, 121, 50, 70, 118, 146, 114, 191, 216, 251, 102, - 25, 56, 78, 173, 128, 95, 245, 224, 27, 247, 105, 64, 208, 40, 208, 92, 38, 114, 232, 173, 208, - 246, 42, 102, 62, 0, 209, 57, 206, 178, 235, 28, 189, 82, 156, 37, 162, 76, 4, 58, 4, 168, 23, - 200, 0, 1, 95, 144, 19, 101, 206, 242, 247, 156, 10, 192, 230, 15, 5, 241, 15, 148, 34, 82, - 122, 203, 71, 216, 14, 107, 251, 173, 30, 148, 152, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 248, 195, 78, 123, 99, 68, 221, 98, 46, 159, 95, 69, 40, 228, 72, 71, - 41, 249, 133, 148, 116, 176, 220, 132, 98, 101, 217, 179, 182, 243, 98, 201, 210, 208, 27, 86, - 235, 118, 179, 126, 171, 226, 227, 107, 170, 154, 198, 128, 46, 52, 171, 193, 65, 174, 175, - 212, 114, 27, 32, 159, 141, 140, 202, 91, 162, 76, 4, 42, 4, 168, 23, 200, 0, 1, 95, 144, 53, - 55, 101, 132, 13, 152, 148, 61, 17, 105, 18, 153, 126, 48, 57, 175, 177, 106, 14, 165, 14, 11, - 232, 98, 66, 205, 84, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 87, 16, - 20, 165, 165, 21, 101, 73, 222, 144, 151, 253, 222, 178, 94, 179, 23, 67, 193, 231, 253, 190, - 41, 50, 24, 97, 203, 144, 231, 199, 48, 247, 186, 201, 28, 83, 239, 96, 19, 122, 56, 39, 210, - 242, 131, 85, 158, 36, 127, 78, 177, 184, 139, 254, 26, 213, 47, 137, 34, 110, 228, 222, 156, - 113, 162, 76, 4, 17, 4, 168, 23, 200, 0, 1, 95, 144, 105, 234, 107, 49, 239, 48, 93, 107, 153, - 187, 45, 76, 157, 153, 69, 111, 161, 8, 176, 42, 51, 117, 36, 95, 80, 119, 232, 0, 1, 57, 17, - 240, 10, 27, 222, 172, 138, 103, 139, 140, 166, 234, 164, 60, 147, 207, 253, 89, 151, 112, 41, - 207, 61, 37, 80, 228, 83, 3, 218, 93, 69, 223, 101, 176, 2, 36, 141, 79, 87, 137, 165, 171, - 110, 241, 3, 213, 214, 178, 196, 139, 80, 37, 1, 64, 243, 12, 243, 186, 69, 219, 231, 92, 68, - 164, 76, 4, 156, 81, 4, 168, 23, 200, 0, 1, 95, 144, 71, 117, 175, 139, 146, 29, 239, 216, 212, - 183, 100, 148, 71, 86, 22, 170, 36, 151, 134, 40, 17, 190, 66, 118, 71, 235, 168, 0, 1, 214, - 33, 186, 0, 143, 220, 132, 52, 119, 109, 105, 220, 0, 92, 167, 236, 65, 210, 74, 32, 214, 17, - 53, 18, 48, 105, 3, 204, 178, 38, 67, 44, 148, 79, 217, 78, 136, 174, 134, 5, 64, 165, 37, 36, - 227, 137, 48, 97, 85, 248, 91, 65, 89, 2, 200, 165, 214, 83, 117, 80, 181, 92, 251, 20, 164, - 204, 4, 160, 171, 6, 252, 35, 172, 0, 5, 22, 21, 43, 39, 176, 9, 139, 41, 224, 156, 220, 254, - 70, 143, 225, 165, 109, 47, 248, 101, 21, 174, 8, 33, 171, 13, 68, 20, 152, 0, 0, 1, 116, 38, - 20, 169, 30, 130, 70, 143, 175, 217, 157, 212, 34, 15, 79, 190, 40, 144, 23, 6, 222, 0, 147, - 57, 242, 102, 156, 48, 85, 178, 27, 43, 205, 103, 66, 136, 38, 37, 204, 194, 72, 250, 41, 183, - 50, 163, 216, 141, 248, 182, 1, 129, 247, 51, 167, 160, 170, 205, 255, 73, 112, 251, 80, 93, - 160, 72, 4, 6, 252, 35, 172, 0, 82, 8, 50, 190, 52, 59, 148, 248, 96, 18, 77, 196, 254, 226, - 120, 253, 203, 211, 140, 16, 45, 136, 212, 245, 1, 119, 241, 139, 48, 0, 1, 27, 3, 142, 179, - 151, 253, 138, 72, 94, 44, 12, 11, 19, 173, 109, 177, 218, 68, 31, 13, 207, 26, 161, 8, 238, - 215, 154, 199, 201, 249, 101, 226, 64, 16, 84, 235, 140, 53, 220, 145, 107, 162, 120, 78, 211, - 111, 214, 176, 185, 205, 68, 186, 139, 189, 135, 13, 190, 195, 22, 148, 11, 150, 114, 48, 164, - 76, 4, 160, 172, 6, 252, 35, 172, 0, 5, 22, 21, 99, 164, 112, 170, 102, 24, 227, 185, 224, 249, - 196, 137, 237, 165, 142, 211, 29, 97, 90, 241, 111, 218, 144, 236, 213, 104, 124, 0, 1, 179, - 22, 88, 178, 103, 80, 37, 118, 86, 85, 176, 157, 68, 63, 230, 81, 54, 4, 205, 249, 243, 174, - 159, 141, 234, 225, 130, 228, 175, 35, 110, 14, 22, 120, 27, 123, 152, 35, 74, 94, 141, 160, - 54, 180, 226, 59, 211, 2, 159, 84, 166, 1, 141, 121, 118, 232, 248, 189, 118, 139, 69, 15, 111, - 9, 164, 76, 4, 156, 82, 4, 168, 23, 200, 0, 1, 95, 144, 32, 14, 221, 23, 243, 4, 133, 168, 115, - 88, 120, 102, 25, 96, 205, 122, 154, 149, 115, 63, 24, 145, 228, 26, 68, 84, 180, 0, 1, 138, - 88, 238, 201, 25, 144, 115, 163, 140, 240, 241, 110, 173, 125, 231, 231, 31, 236, 73, 33, 208, - 236, 208, 197, 59, 101, 131, 215, 118, 22, 57, 39, 113, 2, 25, 86, 152, 202, 144, 170, 231, 87, - 210, 187, 34, 178, 200, 177, 37, 142, 152, 104, 44, 28, 243, 203, 212, 241, 72, 119, 148, 2, - 85, 68, 166, 76, 4, 6, 117, 115, 4, 168, 23, 200, 0, 1, 95, 144, 132, 165, 78, 84, 156, 167, - 157, 175, 194, 145, 128, 212, 150, 96, 242, 59, 17, 66, 17, 185, 19, 184, 151, 60, 215, 56, 20, - 0, 0, 141, 93, 144, 121, 105, 172, 187, 89, 189, 32, 111, 183, 90, 24, 27, 122, 179, 217, 63, - 162, 247, 72, 139, 43, 93, 102, 39, 115, 52, 33, 224, 157, 140, 170, 95, 120, 239, 136, 75, 29, - 219, 217, 244, 173, 140, 241, 222, 90, 85, 104, 89, 132, 45, 151, 142, 55, 189, 87, 9, 232, 77, - 181, 159, 112, 166, 76, 4, 6, 117, 116, 4, 168, 23, 200, 0, 1, 95, 144, 183, 179, 105, 14, 250, - 107, 63, 8, 212, 236, 40, 159, 246, 85, 196, 183, 187, 21, 238, 57, 16, 24, 239, 58, 212, 124, - 232, 0, 1, 84, 194, 0, 101, 65, 211, 80, 82, 36, 76, 222, 245, 194, 229, 112, 73, 127, 152, 95, - 112, 24, 16, 121, 253, 220, 193, 189, 174, 233, 170, 87, 81, 219, 143, 135, 147, 245, 21, 224, - 108, 241, 243, 95, 34, 252, 83, 229, 210, 112, 191, 137, 229, 174, 167, 155, 226, 231, 149, - 185, 20, 223, 219, 90, 94, 166, 76, 4, 6, 117, 117, 4, 168, 23, 200, 0, 1, 95, 144, 144, 189, - 23, 22, 103, 27, 139, 244, 133, 254, 136, 216, 166, 5, 180, 121, 157, 251, 78, 177, 14, 109, - 183, 44, 83, 131, 36, 0, 0, 17, 246, 55, 148, 59, 56, 8, 176, 245, 109, 202, 134, 84, 89, 155, - 2, 94, 41, 81, 253, 55, 242, 37, 247, 126, 121, 139, 190, 156, 28, 171, 208, 1, 226, 75, 24, - 247, 242, 98, 56, 159, 177, 10, 144, 157, 89, 136, 27, 153, 92, 189, 101, 167, 177, 238, 187, - 116, 32, 95, 178, 102, 150, 111, 59, 164, 204, 4, 104, 213, 4, 168, 23, 200, 0, 1, 95, 144, - 135, 100, 179, 96, 7, 104, 9, 187, 164, 99, 92, 66, 129, 195, 244, 76, 22, 119, 208, 19, 1, 21, - 201, 213, 8, 49, 253, 16, 0, 1, 116, 107, 97, 41, 190, 159, 45, 130, 239, 213, 55, 43, 89, 0, - 101, 225, 184, 9, 235, 166, 122, 52, 161, 80, 160, 48, 130, 39, 204, 71, 125, 49, 121, 2, 198, - 136, 12, 180, 164, 67, 154, 249, 148, 218, 113, 209, 242, 121, 189, 18, 54, 106, 182, 61, 51, - 11, 255, 139, 123, 173, 134, 128, 136, 100, 166, 76, 4, 6, 117, 118, 4, 168, 23, 200, 0, 1, 95, - 144, 34, 206, 147, 9, 65, 101, 21, 27, 59, 136, 228, 247, 192, 51, 3, 224, 126, 34, 61, 128, - 14, 4, 168, 23, 200, 0, 1, 95, 144, 180, 140, 202, 191, 174, 56, 252, 247, 107, 50, 131, 79, 4, - 163, 141, 9, 79, 67, 42, 129, 46, 45, 213, 98, 203, 214, 96, 0, 0, 152, 213, 197, 80, 134, 61, - 153, 40, 16, 162, 192, 5, 73, 211, 113, 209, 201, 173, 71, 139, 75, 186, 130, 155, 190, 181, - 232, 129, 227, 208, 118, 94, 143, 60, 156, 27, 173, 9, 104, 242, 190, 121, 84, 112, 196, 21, - 202, 229, 180, 49, 17, 58, 85, 103, 180, 10, 10, 249, 93, 203, 197, 149, 107, 111, 166, 76, 4, - 6, 85, 15, 4, 168, 23, 200, 0, 1, 95, 144, 119, 18, 7, 16, 244, 191, 51, 131, 82, 125, 251, 96, - 122, 59, 7, 183, 116, 39, 103, 103, 45, 235, 108, 133, 16, 173, 60, 0, 1, 160, 217, 89, 181, 3, - 10, 133, 181, 54, 86, 182, 119, 238, 155, 225, 27, 226, 254, 121, 115, 185, 212, 219, 174, 49, - 73, 230, 244, 119, 11, 104, 73, 115, 64, 75, 134, 236, 91, 50, 70, 149, 60, 208, 221, 2, 127, - 137, 87, 13, 70, 136, 157, 198, 17, 14, 37, 12, 84, 182, 175, 125, 76, 220, 38, 166, 76, 4, 6, - 85, 16, 4, 168, 23, 200, 0, 1, 95, 144, 59, 1, 235, 210, 65, 159, 109, 57, 219, 106, 180, 71, - 30, 99, 130, 132, 135, 22, 100, 31, 47, 36, 211, 126, 175, 6, 224, 0, 1, 249, 65, 245, 75, 244, - 66, 76, 249, 87, 252, 196, 179, 121, 42, 237, 222, 184, 217, 101, 53, 133, 39, 58, 94, 240, - 222, 13, 193, 204, 165, 246, 194, 157, 118, 138, 179, 42, 136, 97, 36, 24, 94, 209, 176, 92, - 14, 11, 148, 202, 172, 234, 33, 145, 195, 38, 125, 46, 24, 174, 245, 93, 158, 201, 19, 166, 76, - 4, 6, 85, 17, 4, 168, 23, 200, 0, 1, 95, 144, 54, 45, 177, 228, 131, 11, 242, 196, 1, 215, 249, - 244, 80, 52, 245, 230, 225, 196, 106, 11, 45, 102, 10, 192, 54, 195, 32, 0, 1, 130, 161, 33, - 218, 254, 22, 229, 30, 85, 245, 98, 83, 37, 71, 213, 96, 115, 246, 61, 47, 124, 210, 206, 121, - 247, 139, 61, 64, 182, 119, 44, 140, 90, 4, 178, 43, 45, 104, 115, 116, 56, 136, 181, 34, 156, - 180, 132, 17, 118, 2, 236, 244, 206, 23, 105, 229, 173, 252, 18, 84, 102, 222, 75, 52, 162, 72, - 5, 5, 4, 227, 178, 146, 0, 82, 8, 200, 197, 41, 222, 64, 143, 84, 241, 116, 139, 40, 103, 126, - 82, 83, 89, 217, 53, 125, 130, 2, 30, 25, 224, 201, 186, 178, 64, 0, 0, 0, 238, 240, 227, 195, - 24, 212, 41, 85, 81, 141, 113, 246, 180, 27, 246, 131, 214, 21, 46, 199, 17, 44, 2, 169, 87, - 48, 143, 169, 0, 79, 27, 234, 110, 29, 116, 83, 30, 67, 203, 107, 129, 160, 242, 68, 117, 147, - 0, 134, 176, 17, 147, 108, 207, 43, 228, 157, 209, 193, 181, 110, 67, 163, 169, 96, 166, 76, 4, - 6, 85, 18, 4, 168, 23, 200, 0, 1, 95, 144, 7, 112, 160, 177, 219, 5, 164, 71, 206, 203, 84, 74, - 216, 191, 69, 2, 27, 13, 51, 158, 41, 118, 163, 246, 14, 14, 64, 0, 0, 163, 130, 121, 99, 73, - 107, 37, 0, 126, 140, 177, 200, 8, 156, 140, 157, 150, 38, 24, 221, 88, 29, 237, 235, 160, 42, - 145, 137, 41, 144, 28, 201, 102, 237, 228, 246, 78, 175, 29, 7, 4, 205, 73, 146, 87, 98, 45, - 254, 144, 219, 24, 137, 95, 48, 211, 214, 41, 48, 227, 165, 31, 2, 68, 34, 164, 76, 4, 236, - 163, 4, 168, 23, 200, 0, 1, 95, 144, 175, 154, 35, 36, 169, 196, 186, 177, 13, 93, 12, 7, 9, - 83, 26, 77, 159, 28, 67, 132, 68, 199, 227, 17, 172, 241, 216, 0, 1, 6, 1, 157, 220, 138, 63, - 181, 70, 244, 119, 43, 147, 155, 148, 176, 58, 213, 145, 85, 140, 147, 23, 121, 145, 172, 152, - 253, 53, 247, 240, 146, 95, 69, 137, 224, 183, 154, 134, 100, 181, 199, 195, 202, 82, 213, 205, - 113, 72, 250, 130, 34, 166, 242, 154, 24, 21, 153, 9, 149, 170, 138, 28, 157, 114, 166, 76, 4, - 6, 85, 19, 4, 168, 23, 200, 0, 1, 95, 144, 125, 125, 155, 115, 175, 17, 187, 209, 41, 87, 71, - 144, 231, 230, 95, 112, 223, 66, 183, 135, 43, 41, 4, 56, 224, 18, 24, 0, 0, 157, 225, 195, 32, - 187, 218, 230, 235, 191, 136, 227, 34, 43, 104, 8, 185, 16, 27, 220, 48, 27, 155, 201, 156, - 187, 109, 156, 64, 63, 10, 240, 75, 166, 230, 209, 200, 128, 153, 168, 157, 250, 66, 33, 35, - 125, 154, 171, 109, 170, 154, 233, 1, 55, 217, 91, 125, 4, 60, 131, 171, 11, 92, 250, 104, 162, - 72, 4, 6, 4, 168, 23, 200, 0, 85, 240, 41, 16, 84, 58, 243, 154, 186, 12, 208, 157, 187, 45, - 80, 32, 11, 62, 128, 10, 99, 210, 8, 154, 174, 183, 16, 190, 0, 0, 69, 78, 49, 53, 90, 52, 51, - 55, 71, 1, 190, 56, 162, 31, 211, 7, 49, 147, 217, 11, 145, 209, 129, 247, 63, 189, 49, 27, - 245, 133, 132, 235, 235, 3, 58, 0, 50, 52, 53, 177, 253, 78, 13, 128, 254, 236, 2, 134, 27, 59, - 188, 26, 185, 39, 172, 123, 158, 212, 100, 105, 56, 254, 101, 26, 143, 78, 162, 45, 124, 165, - 63, 202, 109, 127, 166, 76, 4, 6, 85, 20, 4, 168, 23, 200, 0, 1, 95, 144, 50, 238, 160, 102, - 236, 194, 206, 119, 154, 74, 26, 138, 173, 128, 2, 32, 191, 229, 40, 38, 40, 26, 40, 176, 161, - 120, 212, 0, 1, 201, 71, 232, 187, 95, 35, 197, 218, 71, 208, 27, 41, 8, 78, 195, 45, 30, 49, - 228, 49, 15, 132, 10, 191, 144, 217, 53, 31, 180, 216, 4, 5, 227, 165, 47, 46, 134, 49, 113, - 207, 255, 132, 41, 118, 230, 93, 243, 170, 124, 31, 156, 2, 70, 228, 80, 218, 172, 86, 10, 55, - 240, 154, 158, 61, 166, 76, 4, 6, 85, 21, 4, 168, 23, 200, 0, 1, 95, 144, 229, 3, 120, 178, - 228, 25, 68, 105, 106, 176, 194, 255, 233, 62, 152, 242, 196, 43, 61, 232, 43, 31, 176, 202, - 27, 125, 36, 0, 0, 211, 155, 100, 241, 136, 59, 85, 251, 145, 241, 7, 31, 168, 52, 79, 38, 7, - 189, 248, 156, 242, 201, 156, 57, 89, 6, 149, 107, 54, 227, 167, 129, 197, 123, 61, 106, 201, - 76, 36, 104, 14, 1, 187, 148, 212, 112, 199, 126, 150, 196, 134, 151, 67, 8, 189, 218, 228, 97, - 123, 125, 84, 83, 110, 49, 164, 72, 4, 1, 217, 4, 168, 23, 200, 0, 86, 34, 251, 177, 183, 60, - 79, 11, 218, 79, 103, 220, 162, 102, 206, 110, 244, 47, 82, 15, 187, 152, 58, 54, 95, 114, 228, - 25, 196, 0, 0, 28, 144, 77, 221, 100, 181, 52, 120, 143, 165, 20, 103, 121, 36, 182, 55, 204, - 151, 98, 197, 73, 56, 102, 154, 156, 178, 82, 142, 108, 65, 253, 205, 52, 110, 205, 199, 34, - 185, 122, 7, 165, 21, 109, 162, 185, 46, 82, 104, 71, 72, 225, 69, 163, 227, 130, 131, 216, 1, - 111, 184, 160, 65, 4, 48, 166, 76, 4, 6, 85, 22, 4, 168, 23, 200, 0, 1, 95, 144, 244, 34, 169, - 248, 105, 68, 132, 190, 132, 34, 191, 191, 136, 198, 108, 46, 173, 189, 97, 77, 42, 1, 228, 90, - 13, 213, 248, 0, 0, 227, 206, 160, 107, 39, 168, 89, 92, 89, 183, 146, 90, 237, 138, 106, 120, - 59, 215, 104, 213, 96, 210, 245, 206, 135, 166, 249, 48, 9, 10, 130, 223, 198, 161, 42, 99, - 227, 96, 58, 201, 117, 198, 133, 96, 174, 141, 230, 178, 144, 44, 222, 211, 68, 213, 48, 153, - 169, 100, 17, 127, 96, 90, 91, 28, 166, 204, 3, 2, 179, 197, 4, 168, 23, 200, 0, 1, 95, 144, - 198, 9, 71, 109, 168, 216, 65, 90, 143, 109, 91, 182, 179, 0, 59, 84, 66, 228, 194, 52, 55, - 242, 164, 5, 129, 60, 0, 1, 111, 192, 197, 121, 154, 38, 91, 67, 6, 65, 204, 83, 77, 242, 219, - 220, 31, 66, 224, 162, 19, 205, 76, 103, 131, 110, 132, 25, 178, 122, 190, 206, 81, 203, 56, - 216, 170, 83, 4, 160, 236, 176, 243, 169, 76, 202, 44, 222, 185, 148, 222, 71, 4, 253, 210, - 255, 106, 120, 209, 253, 22, 235, 150, 42, 166, 76, 4, 6, 85, 23, 4, 168, 23, 200, 0, 1, 95, - 144, 205, 243, 86, 193, 4, 168, 23, 200, 0, 1, 95, 144, 55, 115, 25, 73, 178, 229, 197, 159, - 142, 156, 175, 31, 214, 207, 172, 55, 27, 158, 239, 149, 42, 173, 11, 10, 110, 198, 136, 0, 0, - 48, 32, 229, 195, 78, 60, 199, 221, 252, 62, 219, 145, 208, 125, 134, 53, 193, 152, 81, 124, - 138, 129, 178, 46, 34, 85, 119, 142, 133, 132, 191, 187, 219, 23, 253, 176, 67, 5, 45, 17, 106, - 218, 237, 1, 80, 147, 165, 169, 210, 2, 125, 64, 59, 51, 222, 38, 243, 20, 133, 97, 5, 61, 128, - 98, 166, 76, 4, 1, 222, 194, 4, 168, 23, 200, 0, 1, 95, 144, 201, 197, 174, 108, 140, 168, 124, - 95, 10, 0, 144, 4, 109, 243, 213, 101, 29, 35, 53, 224, 42, 211, 48, 14, 216, 145, 84, 0, 1, - 174, 23, 58, 115, 175, 166, 107, 67, 251, 229, 45, 94, 102, 61, 8, 195, 108, 245, 101, 216, - 116, 110, 243, 153, 36, 232, 232, 69, 218, 196, 2, 91, 191, 219, 1, 75, 85, 217, 248, 85, 152, - 192, 104, 206, 235, 17, 150, 15, 174, 3, 116, 27, 60, 226, 156, 202, 229, 111, 250, 136, 36, - 57, 202, 8, 166, 76, 4, 1, 222, 195, 4, 168, 23, 200, 0, 1, 95, 144, 164, 109, 248, 116, 252, - 230, 73, 149, 49, 251, 109, 51, 156, 21, 137, 213, 11, 124, 254, 154, 43, 72, 74, 229, 38, 6, - 148, 0, 1, 117, 156, 202, 89, 100, 207, 131, 187, 100, 132, 183, 204, 176, 74, 128, 209, 204, - 243, 137, 116, 225, 53, 143, 123, 62, 59, 82, 160, 30, 254, 75, 121, 45, 172, 83, 205, 174, - 177, 84, 239, 9, 235, 4, 6, 196, 235, 42, 249, 163, 241, 57, 130, 2, 218, 251, 75, 92, 209, - 150, 39, 89, 21, 160, 25, 166, 76, 4, 1, 222, 196, 4, 168, 23, 200, 0, 1, 95, 144, 89, 54, 44, - 72, 95, 209, 234, 91, 107, 87, 101, 255, 61, 188, 205, 91, 200, 112, 229, 157, 46, 137, 177, - 11, 97, 241, 56, 0, 0, 40, 34, 171, 12, 79, 118, 241, 177, 202, 249, 90, 152, 11, 3, 7, 181, 0, - 151, 40, 74, 75, 197, 123, 236, 35, 74, 151, 200, 227, 221, 54, 239, 1, 1, 235, 106, 39, 241, - 236, 186, 245, 197, 80, 254, 176, 118, 170, 243, 214, 5, 153, 110, 89, 14, 162, 237, 92, 229, - 93, 114, 71, 225, 204, 59, 166, 76, 4, 1, 222, 197, 4, 168, 23, 200, 0, 1, 95, 144, 222, 204, - 235, 0, 247, 93, 12, 236, 183, 40, 153, 110, 149, 111, 207, 148, 100, 83, 55, 167, 46, 207, - 118, 19, 5, 242, 28, 0, 1, 145, 61, 172, 97, 24, 210, 107, 239, 141, 51, 97, 15, 88, 103, 243, - 86, 90, 130, 126, 9, 123, 225, 50, 59, 214, 93, 63, 72, 124, 147, 124, 154, 12, 185, 134, 8, - 255, 79, 51, 160, 197, 46, 238, 212, 17, 253, 166, 177, 81, 178, 216, 75, 119, 50, 78, 55, 3, - 26, 147, 239, 213, 145, 90, 101, 166, 76, 4, 1, 222, 198, 4, 168, 23, 200, 0, 1, 95, 144, 138, - 234, 170, 79, 80, 83, 150, 210, 75, 120, 59, 166, 134, 148, 177, 11, 26, 63, 11, 206, 47, 83, - 77, 240, 236, 53, 64, 0, 0, 39, 56, 61, 217, 4, 102, 93, 205, 211, 158, 143, 243, 146, 80, 176, - 248, 215, 245, 192, 106, 40, 157, 183, 157, 197, 204, 167, 200, 171, 53, 243, 123, 204, 157, - 32, 171, 159, 151, 40, 107, 83, 100, 200, 87, 67, 124, 194, 212, 191, 73, 186, 184, 82, 231, - 176, 60, 111, 140, 122, 142, 157, 157, 91, 127, 166, 76, 4, 1, 222, 199, 4, 168, 23, 200, 0, 1, - 95, 144, 145, 173, 193, 242, 37, 183, 173, 113, 158, 186, 108, 181, 119, 119, 69, 53, 154, 77, - 4, 188, 47, 183, 243, 134, 156, 166, 176, 0, 1, 19, 208, 61, 75, 94, 12, 99, 186, 205, 25, 156, - 122, 115, 104, 180, 5, 137, 205, 116, 127, 119, 75, 42, 158, 228, 208, 22, 19, 251, 140, 47, - 213, 252, 209, 35, 78, 243, 84, 198, 55, 198, 60, 13, 107, 81, 100, 133, 101, 143, 12, 54, 56, - 220, 134, 111, 110, 211, 4, 237, 38, 125, 148, 70, 65, 166, 76, 4, 1, 222, 200, 4, 168, 23, - 200, 0, 1, 95, 144, 155, 114, 128, 137, 29, 193, 159, 211, 162, 231, 84, 203, 144, 7, 32, 56, - 32, 101, 4, 3, 49, 244, 111, 114, 143, 122, 152, 0, 1, 199, 98, 203, 50, 188, 34, 108, 111, - 102, 87, 114, 7, 70, 121, 44, 232, 180, 31, 13, 183, 202, 20, 186, 40, 242, 96, 7, 100, 55, - 144, 190, 43, 168, 225, 40, 38, 29, 18, 112, 225, 159, 220, 16, 63, 114, 234, 147, 124, 1, 100, - 54, 18, 89, 64, 187, 234, 51, 143, 130, 23, 117, 174, 231, 19, 166, 76, 4, 1, 222, 201, 4, 168, - 23, 200, 0, 1, 95, 144, 217, 107, 111, 139, 164, 190, 181, 126, 2, 26, 63, 54, 55, 230, 131, - 175, 111, 138, 75, 135, 50, 178, 200, 181, 151, 16, 244, 0, 0, 100, 145, 106, 59, 86, 63, 33, - 63, 106, 102, 169, 116, 208, 228, 157, 161, 236, 170, 182, 156, 163, 126, 2, 13, 136, 171, 16, - 50, 182, 95, 83, 124, 103, 148, 180, 95, 92, 174, 255, 203, 146, 70, 61, 115, 189, 130, 64, - 121, 42, 135, 155, 206, 5, 232, 225, 175, 72, 66, 184, 40, 116, 65, 72, 75, 166, 76, 4, 1, 222, - 202, 4, 168, 23, 200, 0, 1, 95, 144, 162, 128, 123, 142, 226, 212, 105, 19, 175, 50, 120, 3, - 135, 3, 241, 0, 40, 235, 180, 151, 52, 60, 114, 222, 215, 148, 4, 0, 0, 38, 175, 8, 2, 218, 93, - 192, 245, 187, 72, 52, 125, 254, 176, 114, 86, 153, 136, 192, 61, 72, 151, 67, 105, 166, 18, 2, - 12, 239, 78, 0, 180, 122, 19, 178, 3, 77, 129, 67, 190, 20, 78, 166, 91, 240, 40, 36, 140, 158, - 207, 46, 139, 131, 136, 157, 237, 11, 11, 28, 27, 77, 123, 0, 43, 166, 76, 4, 1, 222, 203, 4, - 168, 23, 200, 0, 1, 95, 144, 236, 51, 222, 213, 148, 218, 73, 133, 131, 171, 237, 164, 244, - 110, 96, 63, 41, 110, 46, 249, 52, 119, 38, 213, 91, 39, 28, 0, 0, 189, 173, 226, 43, 68, 51, - 207, 173, 120, 129, 212, 9, 215, 164, 200, 71, 255, 58, 254, 180, 122, 126, 49, 180, 65, 233, - 138, 217, 201, 129, 117, 150, 7, 44, 111, 210, 54, 87, 227, 224, 175, 222, 86, 3, 32, 134, 250, - 230, 23, 178, 236, 39, 179, 253, 7, 25, 46, 110, 96, 97, 236, 216, 138, 117, 166, 76, 4, 1, - 222, 204, 4, 168, 23, 200, 0, 1, 95, 144, 68, 209, 104, 28, 173, 104, 128, 16, 135, 52, 245, - 221, 201, 157, 14, 130, 228, 217, 175, 3, 54, 79, 163, 62, 125, 172, 100, 0, 0, 13, 30, 41, 80, - 110, 131, 229, 217, 158, 254, 15, 94, 152, 231, 62, 144, 154, 177, 200, 22, 201, 14, 1, 70, 34, - 83, 183, 13, 8, 245, 222, 20, 242, 242, 29, 92, 223, 226, 47, 224, 97, 8, 57, 78, 67, 52, 63, - 135, 11, 120, 246, 37, 155, 166, 120, 192, 249, 185, 234, 197, 130, 11, 72, 14, 162, 72, 4, 15, - 11, 164, 59, 116, 0, 82, 8, 150, 170, 54, 109, 14, 141, 233, 246, 193, 105, 58, 175, 232, 101, - 101, 131, 31, 21, 126, 86, 1, 142, 18, 150, 227, 96, 176, 0, 1, 7, 171, 193, 233, 14, 66, 3, - 123, 5, 120, 80, 113, 147, 78, 144, 57, 45, 22, 144, 129, 198, 188, 3, 167, 182, 244, 249, 119, - 71, 220, 200, 130, 151, 145, 59, 74, 236, 156, 253, 231, 237, 5, 51, 146, 154, 121, 116, 204, - 7, 127, 237, 65, 155, 161, 13, 1, 195, 169, 57, 147, 95, 146, 6, 79, 164, 76, 4, 127, 36, 4, - 168, 23, 200, 0, 1, 95, 144, 216, 60, 194, 19, 235, 115, 178, 81, 115, 61, 201, 165, 240, 218, - 196, 105, 238, 98, 155, 54, 15, 76, 111, 59, 119, 165, 60, 0, 0, 232, 103, 107, 15, 152, 45, - 120, 134, 124, 214, 175, 234, 248, 143, 75, 102, 46, 222, 147, 241, 157, 172, 148, 150, 107, - 253, 23, 154, 64, 41, 30, 235, 86, 17, 225, 187, 79, 3, 219, 20, 42, 162, 229, 247, 192, 168, - 118, 238, 174, 129, 152, 192, 12, 48, 192, 76, 172, 72, 242, 221, 219, 85, 215, 110, 166, 76, - 4, 1, 222, 205, 4, 168, 23, 200, 0, 1, 95, 144, 74, 63, 186, 51, 23, 70, 70, 147, 84, 144, 97, - 1, 0, 10, 144, 4, 115, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 22, 115, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 22, 97, 1, 9, 96, 0, 80, 96, 0, 133, 129, 82, 96, - 32, 1, 144, 129, 82, 96, 32, 1, 96, 0, 32, 96, 0, 80, 96, 1, 1, 96, 0, 80, 84, 97, 1, 9, 96, 0, - 80, 96, 0, 134, 129, 82, 96, 32, 1, 144, 129, 82, 96, 32, 1, 96, 0, 32, 96, 0, 80, 96, 2, 1, - 96, 0, 80, 96, 64, 81, 128, 130, 128, 84, 96, 1, 129, 96, 1, 22, 21, 97, 1, 0, 2, 3, 22, 96, 2, - 144, 4, 128, 21, 97, 16, 118, 87, 128, 96, 31, 16, 97, 16, 75, 87, 97, 1, 0, 128, 131, 84, 4, - 2, 131, 82, 145, 96, 32, 1, 145, 97, 16, 118, 86, 91, 130, 1, 145, 144, 96, 0, 82, 96, 32, 96, - 0, 32, 144, 91, 129, 84, 129, 82, 144, 96, 1, 1, 144, 96, 32, 1, 128, 131, 17, 97, 16, 89, 87, - 130, 144, 3, 96, 31, 22, 130, 1, 145, 91, 80, 80, 145, 80, 80, 96, 0, 96, 64, 81, 128, 131, 3, - 129, 133, 135, 97, 133, 2, 90, 3, 241, 146, 80, 80, 80, 80, 127, 231, 201, 87, 192, 110, 154, - 102, 44, 26, 108, 119, 54, 97, 121, 245, 183, 2, 185, 118, 81, 220, 40, 238, 231, 213, 191, 29, - 255, 110, 64, 187, 74, 51, 132, 97, 1, 9, 96, 0, 80, 96, 0, 135, 129, 82, 96, 32, 1, 144, 129, - 82, 96, 32, 1, 96, 0, 32, 96, 0, 80, 96, 1, 1, 96, 0, 80, 84, 97, 1, 9, 96, 0, 80, 96, 0, 136, - 129, 82, 96, 32, 1, 144, 129, 82, 96, 32, 1, 96, 0, 32, 96, 0, 80, 96, 0, 1, 96, 0, 144, 84, - 144, 97, 1, 0, 10, 144, 4, 115, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 22, 97, 1, 9, 96, 0, 80, 96, 0, 137, 129, 82, 96, 32, - 1, 144, 129, 82, 96, 32, 1, 96, 0, 32, 96, 0, 80, 96, 2, 1, 96, 0, 80, 96, 64, 81, 128, 134, - 115, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 22, 129, 82, 96, 32, 1, 133, 129, 82, 96, 32, 1, 132, 129, 82, 96, 32, 1, 131, 115, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 22, 129, 82, 96, 32, 1, 128, 96, 32, 1, 130, 129, 3, 130, 82, 131, 129, 129, 84, 96, 1, - 129, 96, 1, 22, 21, 97, 1, 0, 2, 3, 22, 96, 2, 144, 4, 129, 82, 96, 32, 1, 145, 80, 128, 84, - 96, 1, 129, 96, 1, 22, 21, 97, 1, 0, 2, 3, 22, 96, 2, 144, 4, 128, 21, 97, 18, 0, 87, 128, 96, - 31, 16, 97, 17, 213, 87, 97, 1, 0, 128, 131, 84, 4, 2, 131, 82, 145, 96, 32, 1, 145, 97, 18, 0, - 86, 91, 130, 1, 145, 144, 96, 0, 82, 96, 32, 96, 0, 32, 144, 91, 129, 84, 129, 82, 144, 96, 1, - 1, 144, 96, 32, 1, 128, 131, 17, 97, 17, 227, 87, 130, 144, 3, 96, 31, 22, 130, 1, 145, 91, 80, - 80, 150, 80, 80, 80, 80, 80, 80, 80, 96, 64, 81, 128, 145, 3, 144, 161, 97, 1, 9, 96, 0, 80, - 96, 0, 132, 129, 82, 96, 32, 1, 144, 129, 82, 96, 32, 1, 96, 0, 32, 96, 0, 96, 0, 130, 1, 96, - 0, 97, 1, 0, 10, 129, 84, 144, 115, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 2, 25, 22, 144, 85, 96, 1, 130, 1, 96, 0, 80, 96, 0, - 144, 85, 96, 2, 130, 1, 96, 0, 80, 128, 84, 96, 1, 129, 96, 1, 22, 21, 97, 1, 0, 2, 3, 22, 96, - 2, 144, 4, 96, 0, 130, 85, 128, 96, 31, 16, 97, 18, 137, 87, 80, 97, 18, 198, 86, 91, 96, 31, - 1, 96, 32, 144, 4, 144, 96, 0, 82, 96, 32, 96, 0, 32, 144, 129, 1, 144, 97, 18, 197, 145, 144, - 97, 18, 167, 86, 91, 128, 130, 17, 21, 97, 18, 193, 87, 96, 0, 129, 129, 80, 96, 0, 144, 85, - 80, 96, 1, 1, 97, 18, 167, 86, 91, 80, 144, 86, 91, 91, 80, 80, 80, 96, 1, 145, 80, 80, 97, 18, - 214, 86, 91, 91, 80, 91, 145, 144, 80, 86, 91, 96, 0, 96, 0, 96, 0, 96, 0, 97, 1, 2, 96, 0, 80, - 96, 0, 51, 115, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 22, 129, 82, 96, 32, 1, 144, 129, 82, 96, 32, 1, 96, 0, 32, 96, 0, 80, 84, - 146, 80, 96, 0, 131, 20, 21, 97, 19, 36, 87, 97, 21, 53, 86, 91, 97, 1, 3, 96, 0, 80, 96, 0, - 134, 129, 82, 96, 32, 1, 144, 129, 82, 96, 32, 1, 96, 0, 32, 96, 0, 80, 145, 80, 96, 0, 130, - 96, 0, 1, 96, 0, 80, 84, 20, 21, 97, 19, 253, 87, 96, 0, 96, 0, 80, 84, 130, 96, 0, 1, 96, 0, - 80, 129, 144, 85, 80, 96, 0, 130, 96, 1, 1, 96, 0, 80, 129, 144, 85, 80, 97, 1, 4, 96, 0, 80, - 128, 84, 128, 145, 144, 96, 1, 1, 144, 144, 129, 84, 129, 131, 85, 129, 129, 21, 17, 97, 19, - 195, 87, 129, 131, 96, 0, 82, 96, 32, 96, 0, 32, 145, 130, 1, 145, 1, 97, 19, 194, 145, 144, - 97, 19, 164, 86, 91, 128, 130, 17, 21, 97, 19, 190, 87, 96, 0, 129, 129, 80, 96, 0, 144, 85, - 80, 96, 1, 1, 97, 19, 164, 86, 91, 80, 144, 86, 91, 91, 80, 80, 80, 130, 96, 2, 1, 96, 0, 80, - 129, 144, 85, 80, 132, 97, 1, 4, 96, 0, 80, 131, 96, 2, 1, 96, 0, 80, 84, 129, 84, 129, 16, 21, - 97, 0, 2, 87, 144, 96, 0, 82, 96, 32, 96, 0, 32, 144, 1, 96, 0, 91, 80, 129, 144, 85, 80, 91, - 130, 96, 2, 10, 144, 80, 96, 0, 129, 131, 96, 1, 1, 96, 0, 80, 84, 22, 20, 21, 97, 21, 52, 87, - 127, 225, 197, 45, 198, 59, 113, 154, 222, 130, 232, 190, 169, 76, 196, 26, 13, 93, 40, 228, - 170, 245, 54, 173, 181, 233, 204, 204, 159, 248, 193, 174, 218, 51, 134, 96, 64, 81, 128, 131, - 115, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 22, 129, 82, 96, 32, 1, 130, 129, 82, 96, 32, 1, 146, 80, 80, 80, 96, 64, 81, 128, - 145, 3, 144, 161, 96, 1, 130, 96, 0, 1, 96, 0, 80, 84, 17, 21, 21, 97, 21, 7, 87, 97, 1, 4, 96, - 0, 80, 97, 1, 3, 96, 0, 80, 96, 0, 135, 129, 82, 96, 32, 1, 144, 129, 82, 96, 32, 1, 96, 0, 32, - 96, 0, 80, 96, 2, 1, 96, 0, 80, 84, 129, 84, 129, 16, 21, 97, 0, 2, 87, 144, 96, 0, 82, 96, 32, - 96, 0, 32, 144, 1, 96, 0, 91, 80, 96, 0, 144, 85, 97, 1, 3, 96, 0, 80, 96, 0, 134, 129, 82, 96, - 32, 1, 144, 129, 82, 96, 32, 1, 96, 0, 32, 96, 0, 96, 0, 130, 1, 96, 0, 80, 96, 0, 144, 85, 96, - 1, 130, 1, 96, 0, 80, 96, 0, 144, 85, 96, 2, 130, 1, 96, 0, 80, 96, 0, 144, 85, 80, 80, 96, 1, - 147, 80, 97, 21, 53, 86, 97, 21, 51, 86, 91, 129, 96, 0, 1, 96, 0, 129, 129, 80, 84, 128, 146, - 145, 144, 96, 1, 144, 3, 145, 144, 80, 85, 80, 128, 130, 96, 1, 1, 96, 0, 130, 130, 130, 80, - 84, 23, 146, 80, 80, 129, 144, 85, 80, 91, 91, 91, 80, 80, 80, 145, 144, 80, 86, 91, 96, 0, 96, - 0, 96, 1, 144, 80, 91, 96, 1, 96, 0, 80, 84, 129, 16, 21, 97, 22, 210, 87, 91, 96, 1, 96, 0, - 80, 84, 129, 16, 128, 21, 97, 21, 128, 87, 80, 96, 0, 96, 2, 96, 0, 80, 130, 97, 1, 0, 129, 16, - 21, 97, 0, 2, 87, 144, 144, 1, 96, 0, 91, 80, 84, 20, 21, 91, 21, 97, 21, 178, 178, 2, 222, - 227, 194, 193, 10, 242, 207, 65, 50, 206, 172, 119, 197, 103, 161, 87, 59, 166, 76, 4, 5, 142, - 146, 4, 168, 23, 200, 0, 1, 95, 144, 66, 127, 65, 195, 166, 183, 156, 166, 67, 33, 1, 149, 163, - 60, 219, 170, 113, 65, 119, 170, 20, 5, 253, 150, 218, 206, 128, 0, 0, 92, 36, 172, 52, 225, - 117, 251, 227, 223, 186, 253, 22, 13, 196, 62, 158, 208, 83, 94, 247, 85, 153, 141, 251, 54, - 43, 92, 7, 233, 184, 236, 191, 206, 119, 16, 252, 175, 28, 186, 68, 79, 58, 39, 31, 155, 47, - 137, 54, 54, 246, 171, 89, 17, 233, 76, 225, 192, 98, 246, 34, 92, 154, 193, 22, 164, 204, 4, - 68, 147, 4, 168, 23, 200, 0, 1, 134, 160, 151, 88, 195, 133, 37, 37, 181, 38, 40, 202, 170, 86, - 249, 119, 10, 19, 253, 249, 187, 143, 1, 160, 67, 165, 148, 101, 215, 128, 0, 1, 48, 218, 6, - 63, 102, 76, 220, 86, 101, 83, 88, 184, 125, 82, 103, 175, 201, 102, 98, 149, 241, 53, 223, 69, - 142, 249, 119, 27, 45, 19, 176, 233, 117, 235, 165, 239, 69, 203, 126, 140, 152, 218, 87, 72, - 56, 179, 188, 21, 238, 238, 244, 36, 11, 26, 113, 244, 36, 149, 143, 230, 168, 68, 6, 7, 166, - 76, 4, 5, 142, 147, 4, 168, 23, 200, 0, 1, 95, 144, 229, 49, 180, 239, 51, 228, 148, 55, 211, - 161, 205, 7, 52, 203, 67, 93, 42, 227, 36, 28, 19, 135, 180, 230, 88, 176, 92, 0, 0, 118, 46, - 130, 89, 96, 43, 209, 193, 55, 245, 84, 114, 18, 105, 114, 84, 140, 189, 43, 214, 99, 97, 27, - 96, 76, 185, 108, 233, 54, 113, 22, 174, 111, 242, 90, 112, 242, 250, 165, 172, 197, 243, 166, - 124, 75, 172, 75, 24, 36, 81, 49, 113, 164, 204, 235, 77, 11, 148, 210, 234, 195, 155, 234, 61, - 164, 76, 4, 83, 132, 4, 168, 23, 200, 0, 1, 95, 144, 62, 121, 161, 48, 197, 86, 240, 111, 27, - 34, 101, 80, 254, 3, 190, 40, 48, 117, 175, 175, 19, 125, 254, 145, 147, 8, 192, 0, 0, 153, - 134, 236, 214, 112, 134, 188, 247, 155, 117, 169, 133, 198, 160, 199, 215, 19, 97, 158, 241, - 57, 76, 64, 218, 77, 245, 198, 12, 214, 88, 205, 124, 218, 58, 202, 194, 152, 138, 70, 95, 72, - 103, 5, 178, 136, 105, 218, 241, 106, 120, 80, 143, 50, 198, 226, 9, 74, 249, 85, 63, 57, 0, - 151, 103, 166, 76, 4, 5, 142, 148, 4, 168, 23, 200, 0, 1, 95, 144, 85, 91, 225, 239, 1, 54, 22, - 38, 154, 171, 171, 24, 51, 92, 46, 196, 7, 2, 181, 237, 17, 94, 134, 78, 237, 40, 120, 0, 1, - 211, 217, 254, 16, 106, 65, 188, 140, 74, 38, 199, 117, 51, 28, 227, 145, 154, 97, 55, 8, 135, - 196, 39, 67, 62, 235, 55, 34, 52, 179, 230, 56, 104, 231, 245, 251, 0, 247, 169, 188, 193, 172, - 54, 35, 117, 31, 16, 21, 50, 175, 232, 173, 225, 116, 48, 14, 222, 104, 183, 149, 69, 46, 73, - 0, 164, 76, 4, 83, 133, 4, 168, 23, 200, 0, 1, 95, 144, 139, 97, 65, 98, 255, 41, 207, 213, 71, - 129, 64, 79, 68, 149, 82, 83, 28, 3, 5, 54, 19, 9, 141, 201, 242, 14, 104, 0, 1, 138, 16, 32, - 188, 217, 228, 64, 207, 90, 91, 42, 15, 176, 71, 124, 64, 196, 17, 118, 121, 172, 53, 70, 168, - 52, 252, 238, 32, 162, 9, 243, 178, 35, 63, 173, 124, 27, 142, 147, 2, 98, 193, 89, 105, 141, - 75, 208, 126, 140, 75, 189, 138, 254, 186, 25, 239, 28, 241, 192, 206, 82, 120, 23, 95, 166, - 76, 4, 5, 142, 149, 4, 168, 23, 200, 0, 1, 95, 144, 60, 118, 234, 179, 78, 217, 239, 51, 169, - 137, 23, 90, 134, 221, 128, 171, 128, 137, 52, 196, 18, 193, 169, 235, 1, 197, 172, 0, 1, 101, - 77, 0, 144, 150, 1, 27, 118, 177, 22, 224, 77, 191, 160, 98, 172, 164, 111, 189, 92, 38, 14, - 138, 68, 43, 43, 56, 195, 12, 216, 58, 190, 208, 0, 179, 33, 171, 77, 23, 13, 27, 100, 253, 71, - 5, 99, 145, 45, 205, 77, 73, 85, 226, 40, 14, 224, 251, 77, 156, 8, 108, 161, 7, 124, 164, 76, - 4, 211, 128, 4, 168, 23, 200, 0, 1, 95, 144, 175, 63, 217, 215, 181, 210, 6, 98, 95, 68, 94, - 51, 217, 84, 47, 127, 48, 139, 221, 78, 68, 182, 93, 71, 8, 19, 144, 0, 1, 54, 177, 66, 156, 1, - 132, 131, 37, 110, 150, 44, 204, 214, 75, 30, 255, 83, 199, 100, 16, 79, 33, 43, 81, 70, 213, - 163, 230, 51, 117, 29, 217, 195, 20, 140, 116, 243, 167, 62, 53, 154, 190, 239, 225, 65, 177, - 153, 28, 148, 48, 76, 134, 116, 166, 165, 188, 150, 58, 164, 32, 249, 52, 174, 36, 166, 76, 4, - 5, 142, 150, 4, 168, 23, 200, 0, 1, 95, 144, 91, 85, 166, 101, 104, 215, 14, 194, 159, 5, 157, - 161, 54, 136, 79, 42, 128, 71, 103, 242, 19, 207, 227, 45, 40, 246, 76, 0, 0, 26, 58, 71, 249, - 17, 75, 203, 162, 140, 131, 248, 25, 207, 233, 138, 147, 153, 244, 192, 217, 255, 107, 188, 95, - 169, 242, 109, 137, 206, 84, 18, 193, 63, 217, 105, 175, 5, 77, 39, 10, 46, 202, 108, 110, 111, - 91, 214, 197, 72, 199, 73, 196, 129, 185, 50, 63, 83, 60, 165, 105, 32, 193, 124, 77, 162, 72, - 4, 9, 11, 164, 59, 116, 0, 82, 8, 137, 65, 6, 108, 18, 15, 105, 68, 249, 175, 94, 215, 124, - 136, 129, 61, 41, 143, 200, 50, 3, 221, 60, 64, 146, 123, 208, 0, 0, 21, 245, 65, 183, 201, - 250, 185, 214, 128, 93, 113, 118, 143, 201, 174, 201, 171, 17, 10, 128, 41, 166, 75, 96, 0, - 195, 171, 39, 106, 210, 99, 124, 75, 33, 0, 55, 12, 239, 12, 180, 136, 184, 17, 167, 57, 138, - 102, 242, 27, 81, 141, 45, 170, 69, 61, 152, 160, 145, 201, 16, 91, 24, 4, 57, 164, 76, 4, 83, - 134, 4, 168, 23, 200, 0, 1, 95, 144, 87, 120, 82, 17, 78, 174, 44, 154, 204, 102, 25, 181, 27, - 156, 154, 242, 94, 99, 68, 224, 18, 125, 167, 106, 90, 154, 200, 0, 1, 178, 125, 187, 150, 122, - 122, 150, 201, 78, 166, 191, 114, 210, 44, 73, 27, 151, 87, 72, 81, 21, 232, 155, 100, 181, 25, - 147, 55, 18, 252, 103, 79, 82, 2, 73, 124, 90, 41, 61, 155, 77, 35, 171, 228, 96, 206, 191, 16, - 87, 6, 9, 41, 100, 111, 253, 139, 1, 116, 46, 141, 127, 141, 147, 9, 162, 76, 4, 2, 4, 168, 23, - 200, 0, 1, 95, 144, 110, 79, 36, 162, 151, 196, 150, 174, 245, 212, 4, 114, 90, 155, 189, 76, - 194, 58, 199, 149, 2, 82, 76, 70, 18, 7, 0, 0, 1, 153, 20, 6, 5, 217, 221, 64, 56, 27, 169, 69, - 108, 8, 236, 131, 137, 39, 76, 7, 221, 176, 176, 111, 208, 15, 73, 234, 42, 88, 103, 183, 142, - 10, 185, 233, 134, 35, 181, 172, 245, 114, 102, 214, 64, 252, 101, 120, 172, 159, 89, 18, 161, - 48, 13, 121, 12, 190, 133, 225, 28, 154, 44, 9, 27, 166, 76, 4, 5, 142, 151, 4, 168, 23, 200, - 0, 1, 95, 144, 0, 219, 205, 16, 116, 76, 190, 2, 104, 24, 250, 235, 148, 61, 22, 116, 156, 69, - 167, 238, 18, 99, 77, 119, 67, 36, 244, 0, 0, 88, 173, 64, 111, 145, 152, 221, 16, 168, 224, - 165, 5, 110, 255, 74, 244, 222, 145, 193, 187, 225, 171, 217, 245, 137, 173, 108, 213, 1, 9, - 102, 183, 31, 80, 27, 182, 249, 187, 26, 11, 105, 1, 215, 78, 87, 139, 253, 78, 207, 28, 248, - 133, 245, 39, 98, 55, 142, 149, 239, 236, 89, 36, 197, 28, 160, 200, 3, 11, 164, 59, 116, 0, - 82, 8, 126, 209, 228, 105, 252, 179, 238, 25, 192, 54, 109, 130, 158, 41, 20, 81, 190, 99, 142, - 89, 132, 205, 142, 197, 0, 124, 0, 1, 109, 49, 51, 29, 122, 31, 186, 181, 114, 165, 210, 77, - 43, 138, 119, 11, 8, 36, 251, 57, 105, 51, 7, 242, 30, 245, 197, 124, 33, 158, 87, 126, 159, - 45, 38, 96, 115, 232, 74, 181, 239, 196, 0, 113, 103, 147, 45, 138, 143, 9, 181, 34, 109, 109, - 50, 108, 58, 137, 98, 216, 50, 19, 114, 66, 160, 72, 4, 11, 164, 59, 116, 81, 138, 139, 212, - 53, 154, 96, 216, 199, 234, 48, 201, 3, 26, 109, 126, 246, 110, 252, 100, 86, 190, 146, 210, - 43, 193, 176, 46, 23, 231, 38, 108, 164, 76, 0, 100, 134, 4, 168, 23, 200, 0, 1, 212, 192, 34, - 91, 195, 175, 252, 29, 163, 155, 211, 203, 33, 0, 199, 74, 65, 198, 35, 16, 209, 225, 170, 100, - 196, 59, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 181, 96, 100, 105, 243, 23, 1, 141, 33, 245, 4, - 182, 225, 81, 142, 84, 178, 63, 167, 97, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 96, 36, 106, 190, - 106, 195, 133, 68, 44, 252, 57, 192, 152, 142, 126, 226, 121, 226, 24, 169, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 60, 82, 120, 52, 1, 199, 106, - 51, 175, 207, 125, 248, 255, 90, 81, 250, 222, 244, 30, 108, 191, 147, 53, 184, 221, 95, 153, - 93, 122, 97, 66, 70, 106, 72, 184, 130, 210, 253, 22, 165, 113, 247, 51, 78, 58, 226, 212, 224, - 76, 135, 198, 12, 39, 212, 4, 204, 57, 68, 165, 65, 204, 114, 64, 103, 174, 156, 32, 126, 125, - 166, 76, 4, 2, 104, 29, 4, 168, 23, 200, 0, 1, 95, 144, 27, 174, 131, 106, 98, 115, 65, 237, - 78, 195, 137, 68, 207, 241, 36, 205, 167, 184, 31, 85, 138, 232, 112, 172, 246, 79, 56, 0, 0, - 100, 58, 227, 211, 192, 173, 147, 31, 86, 133, 190, 82, 101, 81, 195, 217, 208, 195, 36, 111, - 8, 249, 44, 63, 215, 102, 97, 47, 212, 193, 142, 88, 39, 153, 121, 11, 161, 149, 7, 179, 223, - 234, 4, 184, 88, 157, 80, 155, 215, 176, 75, 129, 107, 52, 28, 56, 198, 244, 165, 7, 196, 132, - 62, 74, 166, 76, 4, 2, 104, 30, 4, 168, 23, 200, 0, 1, 95, 144, 233, 12, 37, 192, 251, 241, 99, - 180, 101, 215, 31, 134, 90, 21, 123, 47, 255, 86, 226, 72, 6, 244, 177, 108, 240, 212, 88, 64, - 0, 174, 241, 116, 112, 68, 155, 231, 171, 35, 64, 134, 68, 219, 183, 162, 198, 251, 69, 16, 63, - 128, 115, 68, 84, 101, 103, 30, 238, 151, 112, 142, 112, 37, 247, 226, 200, 209, 227, 156, 228, - 200, 187, 210, 139, 78, 74, 252, 98, 148, 2, 143, 223, 110, 192, 2, 196, 50, 78, 160, 118, 251, - 69, 147, 0, 166, 76, 4, 2, 104, 31, 4, 168, 23, 200, 0, 1, 95, 144, 31, 87, 248, 38, 202, 245, - 148, 247, 168, 55, 217, 252, 9, 36, 86, 135, 10, 40, 147, 101, 2, 180, 237, 151, 197, 233, 0, - 0, 1, 165, 233, 131, 151, 196, 164, 118, 139, 103, 216, 61, 52, 235, 96, 192, 210, 121, 59, 66, - 248, 227, 192, 183, 171, 54, 46, 72, 14, 183, 34, 53, 97, 17, 143, 117, 9, 50, 206, 57, 156, - 105, 150, 193, 58, 67, 238, 148, 201, 232, 83, 161, 22, 59, 123, 219, 8, 219, 60, 66, 29, 55, - 8, 248, 66, 166, 76, 4, 2, 104, 32, 4, 168, 23, 200, 0, 1, 95, 144, 31, 87, 248, 38, 202, 245, - 148, 247, 168, 55, 217, 252, 9, 36, 86, 135, 10, 40, 147, 101, 2, 170, 32, 79, 131, 168, 8, 0, - 1, 145, 4, 49, 36, 105, 10, 80, 39, 148, 19, 145, 69, 241, 227, 126, 56, 140, 180, 97, 44, 73, - 113, 28, 184, 173, 161, 246, 61, 220, 63, 155, 16, 11, 131, 81, 252, 184, 138, 197, 100, 122, - 138, 221, 193, 31, 168, 173, 249, 132, 68, 154, 175, 225, 107, 247, 244, 23, 110, 0, 5, 54, - 192, 181, 49, 162, 76, 4, 4, 4, 168, 23, 200, 0, 1, 216, 168, 108, 138, 125, 186, 124, 18, 15, - 151, 135, 88, 178, 79, 252, 89, 80, 33, 103, 82, 65, 42, 2, 198, 138, 240, 187, 20, 0, 0, 0, - 27, 137, 58, 195, 210, 26, 115, 89, 159, 1, 118, 196, 127, 92, 181, 184, 249, 214, 111, 95, 75, - 174, 215, 131, 177, 226, 37, 75, 209, 245, 11, 137, 15, 99, 126, 100, 34, 0, 183, 209, 242, 38, - 85, 151, 140, 194, 214, 53, 158, 176, 238, 247, 216, 250, 137, 175, 202, 5, 8, 249, 66, 39, 49, - 101, 166, 76, 4, 5, 96, 122, 4, 168, 23, 200, 0, 1, 95, 144, 238, 149, 110, 78, 97, 112, 64, - 129, 186, 75, 198, 63, 105, 9, 206, 153, 216, 9, 13, 8, 14, 56, 239, 188, 240, 210, 108, 0, 0, - 191, 217, 21, 157, 128, 186, 39, 116, 65, 135, 221, 46, 82, 246, 79, 150, 132, 155, 146, 20, - 77, 61, 177, 210, 170, 227, 220, 177, 125, 209, 214, 173, 172, 125, 95, 94, 234, 228, 157, 123, - 33, 26, 50, 96, 105, 125, 66, 222, 249, 128, 251, 192, 100, 177, 176, 191, 80, 16, 50, 108, - 255, 231, 171, 55, 164, 76, 4, 205, 189, 4, 168, 23, 200, 0, 1, 95, 144, 243, 53, 48, 221, 112, - 178, 131, 169, 152, 239, 140, 210, 113, 43, 64, 80, 223, 50, 10, 108, 72, 254, 135, 221, 114, - 222, 32, 0, 1, 109, 121, 187, 30, 206, 189, 146, 185, 76, 95, 142, 223, 30, 184, 66, 242, 167, - 165, 126, 70, 150, 162, 84, 15, 231, 27, 51, 53, 42, 10, 236, 129, 134, 210, 31, 128, 140, 212, - 156, 49, 46, 144, 193, 77, 6, 29, 26, 166, 208, 70, 239, 70, 0, 230, 199, 226, 96, 35, 69, 108, - 79, 150, 102, 1, 164, 72, 4, 21, 206, 4, 168, 23, 200, 0, 86, 34, 251, 177, 183, 60, 79, 11, - 218, 79, 103, 220, 162, 102, 206, 110, 244, 47, 82, 15, 187, 152, 5, 93, 143, 234, 106, 194, - 200, 0, 0, 88, 240, 118, 17, 94, 58, 199, 138, 24, 156, 125, 169, 27, 86, 14, 161, 193, 52, - 101, 11, 155, 208, 255, 112, 144, 175, 108, 251, 167, 1, 230, 192, 215, 208, 168, 47, 200, 246, - 31, 195, 19, 206, 105, 156, 154, 161, 48, 126, 229, 233, 78, 32, 58, 216, 174, 207, 176, 165, - 92, 60, 18, 114, 250, 100, 162, 72, 4, 168, 6, 252, 35, 172, 0, 82, 8, 50, 190, 52, 59, 148, - 248, 96, 18, 77, 196, 254, 226, 120, 253, 203, 211, 140, 16, 45, 136, 16, 55, 103, 230, 246, - 102, 188, 0, 1, 154, 56, 240, 104, 197, 111, 187, 127, 183, 30, 168, 214, 116, 0, 29, 46, 1, - 223, 170, 167, 111, 145, 239, 244, 51, 232, 38, 156, 21, 0, 197, 242, 111, 26, 201, 249, 122, - 47, 139, 209, 0, 140, 18, 238, 250, 18, 162, 68, 10, 141, 251, 95, 76, 95, 219, 10, 43, 120, - 125, 115, 43, 159, 21, 82, 162, 72, 4, 165, 6, 252, 35, 172, 0, 82, 8, 50, 190, 52, 59, 148, - 248, 96, 18, 77, 196, 254, 226, 120, 253, 203, 211, 140, 16, 45, 136, 14, 11, 34, 215, 113, - 244, 244, 0, 1, 148, 36, 63, 210, 172, 56, 82, 247, 184, 88, 100, 110, 228, 125, 71, 107, 25, - 117, 217, 96, 61, 163, 115, 34, 27, 159, 206, 68, 77, 161, 243, 39, 208, 146, 216, 170, 252, - 118, 239, 37, 239, 102, 96, 221, 186, 141, 173, 114, 232, 16, 156, 40, 167, 154, 36, 162, 121, - 93, 11, 56, 66, 104, 139, 20, 164, 72, 4, 1, 178, 6, 252, 35, 172, 0, 82, 8, 50, 190, 52, 59, - 148, 248, 96, 18, 77, 196, 254, 226, 120, 253, 203, 211, 140, 16, 45, 136, 16, 104, 8, 56, 71, - 196, 104, 0, 0, 186, 244, 16, 67, 93, 79, 14, 77, 101, 220, 227, 158, 16, 18, 18, 239, 122, 54, - 170, 79, 133, 195, 28, 27, 233, 84, 31, 123, 57, 48, 7, 40, 194, 234, 149, 205, 75, 241, 142, - 38, 3, 85, 174, 24, 78, 87, 250, 8, 82, 72, 206, 192, 3, 232, 72, 222, 241, 89, 49, 208, 175, - 226, 201, 90, 164, 72, 4, 1, 199, 6, 252, 35, 172, 0, 82, 8, 50, 190, 52, 59, 148, 248, 96, 18, - 77, 196, 254, 226, 120, 253, 203, 211, 140, 16, 45, 136, 15, 44, 107, 235, 202, 81, 76, 0, 1, - 128, 249, 122, 41, 144, 205, 147, 222, 152, 4, 225, 50, 161, 222, 245, 13, 94, 89, 128, 19, - 195, 189, 123, 52, 251, 94, 210, 216, 85, 234, 217, 37, 101, 151, 47, 15, 122, 117, 10, 217, - 48, 57, 121, 100, 28, 87, 38, 179, 39, 250, 186, 109, 124, 21, 131, 239, 97, 13, 146, 190, 129, - 235, 207, 25, 164, 72, 4, 2, 76, 6, 252, 35, 172, 0, 82, 8, 73, 248, 28, 140, 232, 183, 184, - 141, 132, 208, 22, 220, 138, 50, 247, 198, 195, 204, 189, 142, 72, 243, 194, 109, 2, 154, 148, - 169, 18, 124, 28, 88, 56, 96, 164, 76, 4, 57, 92, 4, 168, 23, 200, 0, 1, 95, 144, 243, 155, - 220, 90, 109, 194, 34, 242, 66, 212, 209, 116, 252, 150, 144, 254, 123, 24, 64, 92, 156, 182, - 214, 181, 86, 24, 252, 0, 1, 103, 216, 239, 67, 22, 41, 84, 209, 143, 93, 196, 59, 180, 13, 51, - 129, 152, 254, 200, 115, 241, 54, 62, 82, 133, 233, 230, 151, 213, 43, 223, 102, 170, 159, 114, - 122, 179, 25, 247, 222, 27, 24, 153, 18, 176, 191, 69, 98, 241, 12, 232, 190, 59, 178, 78, 197, - 141, 174, 27, 204, 148, 41, 229, 110, 166, 76, 4, 5, 62, 23, 4, 168, 23, 200, 0, 1, 95, 144, 4, - 243, 164, 93, 252, 40, 74, 53, 116, 18, 235, 227, 234, 3, 245, 61, 117, 173, 150, 80, 136, 46, - 102, 67, 236, 73, 240, 0, 0, 161, 36, 26, 69, 99, 200, 124, 213, 153, 153, 99, 6, 91, 51, 62, - 70, 227, 22, 24, 54, 41, 41, 236, 191, 242, 178, 237, 173, 77, 2, 1, 154, 175, 65, 87, 133, - 113, 46, 239, 117, 97, 199, 162, 76, 226, 105, 183, 185, 29, 24, 22, 28, 129, 224, 42, 183, - 191, 206, 213, 176, 150, 178, 88, 110, 164, 76, 4, 57, 93, 4, 168, 23, 200, 0, 1, 95, 144, 106, - 140, 194, 51, 225, 79, 135, 73, 131, 187, 47, 55, 209, 222, 112, 111, 105, 16, 239, 11, 151, - 121, 19, 174, 2, 184, 164, 0, 1, 234, 127, 120, 184, 119, 15, 135, 81, 173, 200, 40, 37, 32, - 179, 42, 102, 228, 83, 114, 196, 212, 171, 6, 206, 193, 224, 1, 202, 2, 212, 158, 107, 36, 190, - 236, 246, 233, 252, 126, 177, 46, 221, 204, 63, 34, 60, 60, 174, 171, 233, 144, 7, 86, 46, 35, - 189, 13, 204, 153, 43, 119, 193, 168, 119, 164, 76, 4, 200, 175, 4, 168, 23, 200, 0, 1, 95, - 144, 30, 66, 111, 28, 153, 12, 8, 119, 131, 198, 51, 147, 17, 187, 121, 246, 66, 60, 230, 146, - 68, 177, 238, 198, 22, 47, 0, 0, 0, 202, 248, 84, 48, 94, 58, 159, 73, 56, 24, 154, 235, 39, - 128, 216, 210, 167, 89, 222, 198, 207, 19, 26, 40, 22, 172, 184, 153, 227, 0, 48, 106, 142, - 132, 42, 141, 181, 36, 224, 225, 30, 104, 134, 170, 30, 101, 60, 132, 162, 159, 28, 206, 208, - 122, 139, 247, 143, 94, 160, 80, 153, 129, 111, 117, 164, 76, 4, 57, 94, 4, 168, 23, 200, 0, 1, - 95, 144, 116, 196, 204, 101, 84, 205, 248, 119, 192, 116, 129, 170, 244, 236, 192, 153, 209, - 182, 215, 128, 85, 190, 236, 71, 90, 65, 228, 0, 0, 41, 28, 188, 118, 58, 187, 246, 95, 107, - 36, 148, 223, 41, 251, 44, 72, 136, 206, 93, 92, 50, 24, 129, 62, 77, 184, 236, 6, 195, 92, 26, - 242, 153, 77, 185, 97, 39, 162, 83, 13, 176, 86, 24, 69, 201, 255, 134, 17, 232, 144, 131, 243, - 210, 158, 127, 82, 186, 0, 242, 111, 130, 234, 165, 43, 160, 72, 4, 4, 168, 23, 200, 0, 82, 8, - 18, 10, 39, 11, 188, 0, 150, 68, 227, 95, 11, 182, 171, 19, 249, 91, 129, 153, 196, 173, 4, - 216, 95, 40, 207, 39, 64, 0, 0, 217, 191, 79, 121, 187, 26, 235, 113, 215, 82, 125, 218, 221, - 60, 238, 111, 89, 172, 217, 212, 70, 59, 219, 126, 222, 37, 255, 186, 163, 132, 167, 119, 230, - 41, 68, 164, 144, 142, 66, 8, 49, 137, 185, 14, 98, 83, 245, 74, 237, 57, 88, 21, 236, 48, 23, - 161, 45, 51, 19, 39, 219, 253, 237, 74, 164, 76, 4, 57, 95, 4, 168, 23, 200, 0, 1, 95, 144, 29, - 129, 100, 243, 180, 30, 158, 69, 209, 68, 83, 96, 237, 174, 151, 10, 15, 121, 156, 163, 143, - 150, 50, 246, 234, 7, 140, 0, 0, 239, 215, 227, 2, 149, 178, 252, 178, 91, 213, 161, 69, 189, - 207, 99, 243, 204, 187, 54, 175, 44, 145, 167, 205, 49, 192, 172, 164, 167, 244, 244, 97, 238, - 102, 205, 255, 123, 11, 168, 103, 247, 48, 187, 6, 59, 136, 233, 180, 135, 219, 139, 229, 200, - 155, 6, 181, 65, 33, 196, 80, 217, 7, 82, 73, 164, 76, 4, 57, 96, 4, 168, 23, 200, 0, 1, 95, - 144, 192, 38, 86, 191, 187, 192, 157, 200, 115, 224, 96, 225, 211, 210, 94, 84, 65, 93, 162, - 205, 138, 124, 20, 82, 192, 194, 40, 0, 0, 48, 219, 40, 12, 45, 154, 83, 168, 73, 137, 89, 95, - 227, 233, 40, 226, 64, 129, 52, 136, 72, 37, 3, 133, 106, 93, 110, 221, 148, 142, 189, 15, 135, - 99, 235, 108, 197, 129, 8, 200, 214, 89, 58, 99, 208, 99, 1, 106, 15, 5, 56, 58, 1, 164, 189, - 106, 118, 165, 178, 23, 249, 94, 212, 87, 164, 76, 4, 57, 97, 4, 168, 23, 200, 0, 1, 95, 144, - 182, 211, 212, 214, 243, 226, 133, 82, 97, 27, 184, 121, 195, 24, 171, 236, 75, 150, 188, 236, - 113, 225, 3, 52, 124, 10, 128, 0, 1, 144, 223, 80, 71, 227, 254, 119, 155, 18, 218, 4, 226, - 152, 38, 47, 243, 200, 140, 231, 60, 24, 122, 143, 93, 55, 98, 230, 249, 205, 158, 18, 152, - 195, 213, 119, 123, 15, 132, 209, 6, 162, 2, 0, 73, 4, 123, 34, 83, 8, 52, 198, 176, 20, 200, - 84, 64, 159, 68, 56, 13, 170, 46, 207, 74, 166, 76, 4, 5, 62, 24, 4, 168, 23, 200, 0, 1, 95, - 144, 233, 130, 67, 239, 79, 188, 26, 178, 172, 171, 188, 157, 108, 118, 16, 153, 78, 48, 10, - 214, 117, 144, 72, 110, 216, 131, 120, 0, 0, 158, 25, 43, 232, 166, 235, 145, 178, 103, 133, - 131, 91, 104, 68, 114, 209, 187, 172, 150, 24, 203, 93, 149, 233, 73, 200, 1, 91, 56, 14, 228, - 121, 196, 95, 120, 110, 156, 35, 207, 229, 175, 193, 232, 34, 171, 130, 228, 54, 187, 22, 22, - 207, 232, 170, 174, 123, 35, 7, 64, 130, 135, 27, 176, 52, 164, 76, 4, 57, 98, 4, 168, 23, 200, - 0, 1, 95, 144, 204, 199, 31, 134, 123, 234, 63, 150, 26, 191, 212, 239, 217, 138, 119, 250, - 189, 80, 204, 32, 117, 241, 231, 9, 139, 36, 192, 0, 1, 51, 64, 137, 188, 97, 152, 61, 166, - 203, 62, 95, 110, 30, 109, 32, 226, 38, 100, 19, 13, 94, 240, 172, 44, 43, 140, 49, 133, 18, - 171, 157, 20, 11, 149, 172, 39, 95, 98, 25, 147, 134, 237, 110, 97, 186, 253, 127, 181, 51, 53, - 140, 2, 111, 193, 229, 132, 187, 159, 163, 174, 54, 133, 236, 96, 166, 76, 4, 5, 62, 25, 4, - 168, 23, 200, 0, 1, 95, 144, 203, 58, 67, 248, 186, 156, 137, 216, 217, 65, 110, 143, 231, 122, - 63, 26, 136, 65, 121, 251, 104, 156, 112, 106, 185, 244, 48, 0, 1, 43, 92, 190, 224, 93, 6, 90, - 55, 99, 241, 141, 125, 67, 115, 238, 210, 137, 92, 66, 136, 175, 123, 228, 112, 159, 26, 175, - 51, 214, 248, 135, 80, 171, 204, 67, 72, 39, 206, 87, 52, 245, 160, 202, 194, 57, 24, 40, 119, - 117, 208, 67, 138, 154, 208, 138, 120, 91, 245, 237, 33, 207, 120, 220, 102, 164, 204, 0, 165, - 61, 4, 168, 23, 200, 0, 4, 147, 224, 173, 98, 245, 106, 3, 51, 75, 100, 126, 85, 219, 219, 91, - 134, 66, 194, 70, 5, 168, 1, 1, 62, 212, 134, 121, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 76, 70, - 247, 244, 255, 0, 206, 217, 243, 101, 14, 89, 73, 108, 73, 129, 243, 182, 206, 177, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 86, 244, 88, 145, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 192, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 160, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 28, 74, 16, 176, 159, 84, 194, 162, 96, 27, 113, 231, 100, - 65, 36, 239, 239, 210, 205, 123, 0, 0, 0, 0, 0, 0, 0, 217, 62, 192, 171, 59, 83, 52, 154, 111, - 131, 168, 0, 188, 175, 247, 182, 19, 190, 143, 223, 132, 123, 106, 154, 132, 78, 218, 176, 190, - 174, 225, 36, 192, 137, 195, 121, 174, 235, 19, 23, 101, 117, 17, 108, 74, 32, 73, 1, 226, 72, - 76, 143, 46, 230, 247, 48, 115, 119, 103, 190, 154, 104, 126, 42, 184, 52, 168, 65, 89, 206, - 78, 51, 167, 30, 247, 22, 162, 72, 4, 117, 11, 164, 59, 116, 0, 82, 8, 22, 153, 51, 58, 78, 70, - 9, 62, 21, 169, 75, 74, 33, 59, 136, 235, 138, 150, 56, 52, 14, 20, 210, 127, 61, 132, 216, 0, - 1, 5, 164, 176, 22, 19, 131, 202, 72, 104, 144, 134, 63, 246, 65, 122, 76, 201, 224, 41, 45, - 184, 107, 104, 237, 24, 107, 173, 28, 49, 56, 58, 75, 53, 210, 177, 215, 94, 232, 145, 240, - 194, 108, 229, 28, 70, 109, 136, 183, 156, 77, 9, 133, 145, 160, 143, 139, 98, 101, 145, 94, 2, - 68, 154, 121, 162, 72, 4, 143, 4, 168, 23, 200, 0, 86, 34, 251, 177, 183, 60, 79, 11, 218, 79, - 103, 220, 162, 102, 206, 110, 244, 47, 82, 15, 187, 152, 1, 132, 104, 183, 229, 91, 60, 0, 0, - 207, 243, 27, 85, 146, 68, 110, 106, 53, 84, 168, 48, 34, 142, 7, 125, 155, 96, 70, 232, 99, - 165, 7, 180, 81, 91, 0, 161, 95, 2, 64, 102, 248, 15, 142, 118, 187, 30, 208, 197, 101, 226, - 119, 171, 104, 56, 123, 33, 23, 36, 231, 131, 201, 27, 72, 118, 240, 33, 189, 223, 87, 164, 55, - 125, 162, 12, 0, 198, 4, 168, 23, 200, 0, 45, 198, 192, 96, 96, 96, 64, 82, 96, 64, 81, 97, 17, - 13, 56, 3, 128, 97, 17, 13, 131, 57, 129, 1, 96, 64, 144, 129, 82, 129, 81, 96, 128, 81, 96, - 160, 81, 145, 144, 147, 1, 128, 81, 96, 1, 144, 129, 1, 129, 85, 51, 96, 1, 96, 160, 96, 2, 10, - 3, 22, 96, 3, 129, 144, 85, 96, 0, 144, 129, 82, 97, 1, 2, 96, 32, 82, 147, 132, 32, 85, 146, - 145, 129, 144, 132, 144, 132, 144, 91, 130, 81, 129, 16, 21, 97, 0, 219, 87, 130, 129, 129, 81, - 129, 16, 21, 97, 0, 2, 87, 144, 96, 32, 1, 144, 96, 32, 2, 1, 81, 96, 1, 96, 160, 96, 2, 10, 3, - 22, 96, 2, 96, 0, 80, 130, 96, 2, 1, 97, 1, 0, 129, 16, 21, 97, 0, 2, 87, 1, 96, 0, 145, 144, - 85, 131, 81, 96, 2, 131, 1, 145, 97, 1, 2, 145, 134, 144, 133, 144, 129, 16, 21, 97, 0, 2, 87, - 144, 96, 32, 1, 144, 96, 32, 2, 1, 81, 96, 1, 96, 160, 96, 2, 10, 3, 22, 129, 82, 96, 32, 1, - 144, 129, 82, 96, 32, 1, 96, 0, 32, 96, 0, 80, 129, 144, 85, 80, 96, 1, 1, 97, 0, 85, 86, 91, - 129, 96, 0, 96, 0, 80, 129, 144, 85, 80, 80, 80, 80, 128, 97, 1, 5, 96, 0, 80, 129, 144, 85, - 80, 97, 0, 255, 98, 1, 81, 128, 66, 4, 144, 86, 91, 97, 1, 7, 85, 80, 80, 80, 80, 97, 15, 248, - 128, 97, 1, 21, 96, 0, 57, 96, 0, 243, 96, 96, 96, 64, 82, 54, 21, 97, 0, 185, 87, 96, 224, 96, - 2, 10, 96, 0, 53, 4, 99, 23, 56, 37, 217, 129, 20, 97, 1, 11, 87, 128, 99, 47, 84, 191, 110, - 20, 97, 1, 95, 87, 128, 99, 65, 35, 203, 107, 20, 97, 1, 135, 87, 128, 99, 92, 82, 194, 245, - 20, 97, 1, 144, 87, 128, 99, 112, 101, 203, 72, 20, 97, 1, 186, 87, 128, 99, 116, 108, 145, - 113, 20, 97, 1, 231, 87, 128, 99, 121, 122, 246, 39, 20, 97, 1, 240, 87, 128, 99, 178, 13, 48, - 169, 20, 97, 2, 3, 87, 128, 99, 182, 29, 39, 246, 20, 97, 2, 48, 87, 128, 99, 183, 92, 125, - 198, 20, 97, 2, 81, 87, 128, 99, 186, 81, 166, 223, 20, 97, 2, 128, 87, 128, 99, 194, 207, 115, - 38, 20, 97, 2, 173, 87, 128, 99, 203, 240, 176, 192, 20, 97, 2, 235, 87, 128, 99, 240, 13, 75, - 93, 20, 97, 3, 24, 87, 128, 99, 241, 115, 109, 134, 20, 97, 3, 74, 87, 91, 97, 3, 84, 96, 0, - 52, 17, 21, 97, 1, 9, 87, 96, 64, 128, 81, 96, 1, 96, 160, 96, 2, 10, 3, 51, 22, 129, 82, 52, - 96, 32, 130, 1, 82, 129, 81, 127, 225, 255, 252, 196, 146, 61, 4, 181, 89, 244, 210, 154, 139, - 252, 108, 218, 4, 235, 91, 13, 60, 70, 7, 81, 194, 64, 44, 92, 92, 201, 16, 156, 146, 145, 129, - 144, 3, 144, 145, 1, 144, 161, 91, 86, 91, 97, 3, 84, 96, 4, 53, 96, 0, 96, 0, 54, 96, 64, 81, - 128, 131, 131, 128, 130, 132, 55, 130, 1, 145, 80, 80, 146, 80, 80, 80, 96, 64, 81, 128, 145, - 3, 144, 32, 97, 6, 74, 129, 91, 96, 1, 96, 160, 96, 2, 10, 3, 51, 22, 96, 0, 144, 129, 82, 97, - 1, 2, 96, 32, 82, 96, 64, 129, 32, 84, 129, 128, 130, 129, 20, 21, 97, 12, 102, 87, 97, 13, - 191, 86, 91, 97, 3, 86, 96, 4, 53, 91, 96, 1, 96, 160, 96, 2, 10, 3, 129, 22, 96, 0, 144, 129, - 82, 97, 1, 2, 96, 32, 82, 96, 64, 129, 32, 84, 17, 91, 145, 144, 80, 86, 91, 97, 3, 86, 96, 1, - 84, 129, 86, 91, 97, 3, 84, 96, 0, 54, 96, 64, 81, 128, 131, 131, 128, 130, 132, 55, 130, 1, - 145, 80, 80, 146, 80, 80, 80, 96, 64, 81, 128, 145, 3, 144, 32, 97, 7, 139, 129, 97, 1, 54, 86, - 91, 97, 3, 84, 96, 4, 53, 96, 0, 54, 96, 64, 81, 128, 131, 131, 128, 130, 132, 55, 130, 1, 145, - 80, 80, 146, 80, 80, 80, 96, 64, 81, 128, 145, 3, 144, 32, 97, 5, 198, 129, 97, 1, 54, 86, 91, - 97, 3, 86, 96, 0, 84, 129, 86, 91, 97, 3, 86, 96, 4, 53, 91, 96, 0, 129, 97, 10, 39, 129, 97, - 1, 54, 86, 91, 97, 3, 84, 96, 4, 53, 96, 0, 54, 96, 64, 81, 128, 131, 131, 128, 130, 132, 55, - 130, 1, 145, 80, 80, 146, 80, 80, 80, 96, 64, 81, 128, 145, 3, 144, 32, 97, 7, 127, 129, 97, 1, - 54, 86, 91, 97, 3, 86, 96, 4, 128, 53, 144, 96, 36, 128, 53, 145, 96, 68, 53, 145, 130, 1, 145, - 1, 53, 96, 0, 97, 7, 170, 51, 97, 1, 102, 86, 91, 97, 3, 84, 96, 4, 53, 96, 1, 96, 160, 96, 2, - 10, 3, 51, 22, 96, 0, 144, 129, 82, 97, 1, 2, 96, 32, 82, 96, 64, 129, 32, 84, 144, 128, 130, - 129, 20, 21, 97, 3, 104, 87, 97, 3, 231, 86, 91, 97, 3, 84, 96, 4, 53, 96, 0, 54, 96, 64, 81, - 128, 131, 131, 128, 130, 132, 55, 130, 1, 145, 80, 80, 146, 80, 80, 80, 96, 64, 81, 128, 145, - 3, 144, 32, 97, 7, 8, 129, 97, 1, 54, 86, 91, 97, 3, 86, 96, 4, 53, 96, 36, 53, 96, 0, 130, - 129, 82, 97, 1, 3, 96, 32, 144, 129, 82, 96, 64, 128, 131, 32, 96, 1, 96, 160, 96, 2, 10, 3, - 133, 22, 132, 82, 97, 1, 2, 144, 146, 82, 130, 32, 84, 130, 129, 129, 20, 21, 97, 7, 97, 87, - 97, 7, 118, 86, 91, 97, 3, 84, 96, 4, 53, 96, 0, 54, 96, 64, 81, 128, 131, 131, 128, 130, 132, - 55, 130, 1, 145, 80, 80, 146, 80, 80, 80, 96, 64, 81, 128, 145, 3, 144, 32, 97, 7, 153, 129, - 97, 1, 54, 86, 91, 97, 3, 84, 96, 4, 53, 96, 36, 53, 96, 0, 96, 0, 54, 96, 64, 81, 128, 131, - 131, 128, 130, 132, 55, 130, 1, 145, 80, 80, 146, 80, 80, 80, 96, 64, 81, 128, 145, 3, 144, 32, - 97, 4, 114, 129, 97, 1, 54, 86, 91, 97, 3, 86, 97, 1, 5, 84, 129, 86, 91, 0, 91, 96, 64, 128, - 81, 145, 130, 82, 81, 144, 129, 144, 3, 96, 32, 1, 144, 243, 91, 80, 80, 96, 0, 130, 129, 82, - 97, 1, 3, 96, 32, 82, 96, 64, 129, 32, 96, 1, 129, 1, 84, 96, 2, 132, 144, 10, 146, 144, 131, - 22, 17, 21, 97, 3, 231, 87, 128, 84, 96, 1, 130, 129, 1, 128, 84, 146, 144, 145, 1, 131, 85, - 144, 131, 144, 3, 144, 85, 96, 64, 128, 81, 96, 1, 96, 27, 41, 85, 19, 203, 58, 130, 83, 28, - 231, 152, 31, 0, 30, 153, 171, 45, 18, 106, 42, 150, 32, 135, 37, 220, 148, 109, 43, 188, 78, - 221, 19, 162, 179, 188, 173, 133, 12, 85, 24, 246, 154, 112, 185, 214, 239, 187, 168, 43, 80, - 64, 86, 102, 245, 180, 130, 61, 187, 33, 49, 117, 166, 76, 4, 2, 60, 72, 4, 168, 23, 200, 0, 1, - 95, 144, 98, 158, 53, 224, 164, 129, 216, 33, 41, 63, 53, 78, 13, 210, 126, 67, 163, 84, 208, - 97, 41, 184, 114, 119, 196, 231, 182, 0, 0, 111, 152, 241, 231, 58, 201, 2, 185, 56, 253, 47, - 48, 146, 191, 233, 248, 121, 140, 105, 220, 157, 139, 126, 90, 55, 221, 187, 22, 48, 251, 36, - 53, 138, 23, 125, 21, 94, 121, 187, 189, 132, 213, 34, 20, 192, 188, 214, 129, 95, 36, 32, 85, - 251, 12, 81, 206, 62, 114, 0, 27, 202, 200, 216, 84, 164, 76, 4, 191, 245, 4, 168, 23, 200, 0, - 1, 95, 144, 16, 237, 140, 204, 6, 231, 200, 237, 144, 161, 60, 144, 222, 67, 143, 223, 171, - 179, 49, 141, 68, 180, 252, 188, 37, 124, 186, 0, 0, 137, 10, 148, 80, 222, 49, 119, 53, 140, - 212, 102, 241, 41, 37, 191, 127, 23, 87, 103, 174, 172, 89, 147, 203, 111, 213, 2, 199, 23, - 163, 12, 131, 249, 43, 77, 118, 69, 79, 69, 173, 132, 230, 249, 86, 141, 113, 174, 71, 79, 79, - 139, 66, 237, 181, 141, 220, 243, 73, 113, 3, 190, 58, 1, 86, 166, 76, 4, 2, 60, 73, 4, 168, - 23, 200, 0, 1, 95, 144, 154, 118, 106, 200, 27, 238, 240, 41, 249, 27, 96, 246, 124, 170, 43, - 50, 153, 88, 134, 160, 14, 49, 96, 69, 8, 106, 48, 0, 1, 180, 213, 254, 174, 190, 109, 2, 203, - 202, 189, 2, 162, 43, 227, 245, 217, 236, 4, 133, 115, 93, 132, 51, 108, 86, 197, 231, 233, 78, - 166, 1, 234, 102, 234, 177, 0, 11, 239, 110, 184, 255, 6, 120, 133, 130, 41, 94, 240, 234, 64, - 12, 134, 18, 80, 101, 233, 180, 156, 169, 12, 151, 212, 158, 115, 162, 76, 4, 28, 4, 168, 23, - 200, 0, 1, 216, 168, 186, 48, 194, 16, 39, 187, 32, 154, 238, 153, 66, 57, 145, 85, 14, 16, 7, - 1, 0, 109, 15, 67, 252, 44, 4, 238, 0, 0, 1, 100, 194, 18, 132, 213, 11, 216, 195, 195, 142, - 164, 25, 1, 165, 36, 126, 163, 75, 85, 98, 133, 38, 179, 123, 107, 227, 191, 189, 89, 240, 158, - 14, 48, 164, 1, 1, 88, 123, 173, 154, 133, 141, 199, 214, 243, 217, 82, 204, 68, 124, 92, 242, - 31, 190, 115, 2, 253, 239, 106, 44, 14, 93, 164, 71, 166, 76, 4, 2, 60, 74, 4, 168, 23, 200, 0, - 1, 95, 144, 31, 87, 248, 38, 202, 245, 148, 247, 168, 55, 217, 252, 9, 36, 86, 135, 10, 40, - 147, 101, 2, 130, 83, 164, 66, 72, 8, 0, 1, 70, 158, 66, 200, 188, 220, 183, 132, 135, 164, 30, - 43, 135, 177, 230, 71, 133, 188, 76, 99, 136, 63, 222, 200, 186, 9, 171, 224, 157, 59, 194, - 217, 108, 225, 224, 134, 58, 236, 190, 114, 15, 222, 108, 106, 0, 238, 134, 64, 214, 198, 46, - 27, 155, 51, 229, 229, 2, 8, 208, 12, 200, 85, 160, 32, 162, 72, 4, 4, 11, 164, 59, 116, 0, 82, - 8, 28, 56, 127, 31, 51, 134, 184, 222, 236, 97, 207, 65, 31, 206, 224, 36, 176, 163, 123, 167, - 49, 245, 196, 237, 39, 104, 0, 0, 0, 81, 176, 55, 113, 48, 52, 75, 209, 16, 195, 62, 246, 88, - 47, 22, 157, 75, 52, 90, 144, 77, 242, 211, 124, 24, 15, 210, 246, 233, 225, 216, 66, 174, 10, - 24, 1, 165, 131, 23, 145, 240, 26, 173, 57, 231, 102, 198, 77, 89, 18, 27, 51, 148, 164, 79, - 107, 147, 195, 59, 87, 53, 71, 31, 3, 160, 72, 4, 4, 168, 23, 200, 0, 82, 8, 162, 227, 23, 198, - 174, 246, 75, 100, 79, 55, 176, 243, 133, 224, 240, 81, 93, 83, 2, 75, 19, 70, 16, 120, 22, - 114, 189, 192, 0, 241, 180, 189, 85, 188, 109, 103, 187, 106, 5, 107, 66, 52, 108, 23, 1, 187, - 254, 212, 205, 1, 45, 61, 58, 97, 222, 8, 46, 134, 23, 143, 208, 55, 253, 158, 120, 11, 250, - 111, 52, 5, 67, 72, 230, 247, 250, 68, 219, 51, 229, 121, 197, 47, 44, 249, 205, 31, 103, 237, - 254, 218, 131, 27, 3, 162, 72, 4, 1, 4, 168, 23, 200, 0, 82, 8, 50, 114, 240, 24, 205, 181, - 102, 13, 42, 112, 123, 23, 223, 74, 78, 146, 45, 12, 62, 168, 78, 154, 0, 120, 192, 166, 0, 0, - 1, 137, 219, 173, 107, 110, 23, 153, 165, 126, 106, 201, 139, 138, 91, 66, 27, 184, 119, 213, - 0, 46, 23, 125, 133, 21, 141, 155, 39, 190, 225, 197, 50, 197, 28, 18, 91, 248, 212, 169, 228, - 251, 208, 34, 137, 227, 178, 87, 20, 188, 240, 87, 125, 122, 221, 216, 100, 90, 151, 136, 49, - 17, 46, 29, 30, 162, 76, 4, 11, 4, 168, 23, 200, 0, 1, 216, 168, 10, 73, 98, 90, 219, 103, 242, - 164, 69, 141, 217, 149, 169, 72, 51, 26, 45, 122, 11, 137, 13, 224, 182, 179, 167, 100, 0, 0, - 1, 5, 94, 152, 100, 46, 145, 78, 33, 136, 187, 198, 114, 149, 180, 222, 226, 50, 28, 112, 162, - 190, 242, 41, 83, 25, 116, 18, 210, 63, 61, 204, 14, 202, 211, 109, 173, 6, 127, 138, 138, 92, - 178, 20, 38, 210, 23, 121, 177, 224, 240, 232, 185, 221, 213, 188, 89, 209, 10, 218, 42, 107, - 87, 54, 45, 164, 76, 4, 77, 9, 4, 168, 23, 200, 0, 1, 95, 144, 219, 24, 104, 242, 182, 138, - 194, 245, 58, 41, 253, 89, 84, 106, 116, 3, 119, 3, 24, 119, 14, 0, 137, 153, 193, 122, 36, 0, - 1, 47, 119, 254, 198, 84, 12, 9, 241, 32, 197, 246, 243, 59, 19, 251, 201, 224, 168, 18, 238, - 124, 126, 147, 18, 74, 253, 242, 63, 173, 112, 140, 247, 142, 167, 175, 75, 201, 210, 145, 190, - 157, 44, 125, 142, 198, 128, 216, 198, 101, 141, 85, 133, 0, 103, 31, 120, 215, 12, 131, 117, - 71, 206, 171, 85, 164, 76, 4, 77, 10, 4, 168, 23, 200, 0, 1, 95, 144, 207, 18, 252, 120, 137, - 166, 48, 217, 26, 25, 175, 5, 223, 217, 54, 194, 12, 95, 156, 136, 13, 232, 22, 194, 25, 170, - 103, 128, 1, 18, 118, 72, 85, 52, 64, 75, 155, 57, 248, 223, 103, 187, 42, 0, 118, 225, 143, - 89, 91, 214, 202, 183, 0, 160, 131, 182, 252, 228, 64, 164, 17, 38, 78, 161, 52, 207, 85, 102, - 145, 238, 229, 102, 124, 172, 47, 215, 237, 139, 170, 174, 2, 238, 117, 59, 214, 168, 7, 161, - 39, 48, 109, 136, 33, 164, 76, 4, 77, 11, 4, 168, 23, 200, 0, 1, 95, 144, 165, 6, 179, 128, 98, - 48, 254, 93, 219, 147, 116, 231, 74, 75, 71, 92, 54, 4, 14, 211, 13, 227, 106, 41, 206, 172, - 140, 0, 1, 207, 0, 149, 169, 88, 174, 135, 205, 26, 151, 239, 187, 30, 252, 232, 131, 185, 165, - 218, 205, 21, 77, 93, 148, 66, 187, 89, 132, 208, 89, 198, 82, 214, 56, 247, 187, 220, 109, - 204, 107, 67, 196, 108, 91, 102, 252, 41, 231, 178, 211, 115, 137, 57, 215, 46, 204, 21, 123, - 185, 55, 185, 239, 82, 84, 166, 76, 4, 5, 1, 18, 4, 168, 23, 200, 0, 1, 95, 144, 206, 168, 249, - 84, 175, 211, 255, 122, 191, 49, 182, 229, 84, 170, 24, 43, 104, 234, 147, 239, 14, 215, 112, - 48, 120, 33, 188, 0, 0, 173, 86, 144, 160, 67, 202, 55, 213, 179, 73, 174, 237, 116, 16, 32, - 129, 1, 146, 144, 157, 171, 102, 238, 78, 62, 100, 6, 149, 180, 155, 227, 192, 35, 202, 137, - 67, 233, 8, 253, 230, 136, 139, 155, 197, 109, 203, 253, 54, 42, 46, 195, 218, 171, 180, 37, - 113, 186, 229, 185, 26, 100, 197, 235, 65, 164, 204, 0, 104, 11, 4, 168, 23, 200, 0, 3, 208, - 144, 173, 98, 245, 106, 3, 51, 75, 100, 126, 85, 219, 219, 91, 134, 66, 194, 70, 5, 168, 1, 1, - 62, 212, 134, 121, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 33, 180, 194, 71, 66, 238, 87, 240, 5, - 0, 125, 246, 59, 57, 8, 53, 98, 121, 94, 13, 0, 0, 0, 0, 0, 0, 0, 81, 128, 145, 3, 144, 32, 97, - 4, 251, 129, 97, 18, 219, 86, 91, 21, 97, 6, 103, 87, 97, 5, 9, 131, 97, 9, 165, 86, 91, 21, - 97, 5, 20, 87, 80, 97, 6, 105, 86, 91, 97, 1, 2, 96, 0, 80, 96, 0, 133, 115, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 22, 129, - 82, 96, 32, 1, 144, 129, 82, 96, 32, 1, 96, 0, 32, 96, 0, 80, 84, 145, 80, 96, 0, 130, 20, 21, - 97, 5, 86, 87, 80, 97, 6, 105, 86, 91, 97, 5, 94, 97, 23, 119, 86, 91, 130, 115, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 22, 96, 2, - 96, 0, 80, 131, 97, 1, 0, 129, 16, 21, 97, 0, 2, 87, 144, 144, 1, 96, 0, 91, 80, 129, 144, 85, - 80, 96, 0, 97, 1, 2, 96, 0, 80, 96, 0, 134, 115, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 22, 129, 82, 96, 32, 1, 144, 129, 82, - 96, 32, 1, 96, 0, 32, 96, 0, 80, 129, 144, 85, 80, 129, 97, 1, 2, 96, 0, 80, 96, 0, 133, 115, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 22, 129, 82, 96, 32, 1, 144, 129, 82, 96, 32, 1, 96, 0, 32, 96, 0, 80, 129, 144, 85, 80, - 127, 181, 50, 7, 59, 56, 200, 49, 69, 227, 229, 19, 83, 119, 160, 139, 249, 170, 181, 91, 192, - 253, 124, 17, 121, 205, 79, 185, 149, 210, 165, 21, 156, 132, 132, 96, 64, 81, 128, 131, 115, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 22, 129, 82, 96, 32, 1, 130, 115, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 22, 129, 82, 96, 32, 1, 146, 80, 80, 80, 96, 64, - 81, 128, 145, 3, 144, 161, 91, 80, 91, 80, 80, 80, 86, 91, 96, 0, 54, 67, 96, 64, 81, 128, 132, - 132, 128, 130, 132, 55, 130, 1, 145, 80, 80, 130, 129, 82, 96, 32, 1, 147, 80, 80, 80, 80, 96, - 64, 81, 128, 145, 3, 144, 32, 97, 6, 157, 129, 97, 18, 219, 86, 91, 21, 97, 7, 191, 87, 97, 6, - 171, 130, 97, 9, 165, 86, 91, 21, 97, 6, 182, 87, 80, 97, 7, 193, 86, 91, 97, 6, 190, 97, 23, - 119, 86, 91, 96, 250, 96, 1, 96, 0, 80, 84, 16, 21, 21, 97, 6, 215, 87, 97, 6, 213, 97, 21, 61, - 86, 91, 80, 91, 96, 250, 96, 1, 96, 0, 80, 84, 16, 21, 21, 97, 6, 236, 87, 80, 97, 7, 193, 86, - 91, 96, 1, 96, 0, 129, 129, 80, 84, 128, 146, 145, 144, 96, 1, 1, 145, 144, 80, 85, 80, 129, - 115, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 22, 96, 2, 96, 0, 80, 96, 1, 96, 0, 80, 84, 97, 1, 0, 129, 16, 21, 97, 0, 2, 87, 144, - 144, 1, 96, 0, 91, 80, 129, 144, 85, 80, 96, 1, 96, 0, 80, 84, 97, 1, 2, 96, 0, 80, 96, 0, 132, - 115, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 22, 129, 82, 96, 32, 1, 144, 129, 82, 96, 32, 1, 96, 0, 32, 96, 0, 80, 129, 144, 85, - 80, 127, 153, 74, 147, 102, 70, 254, 135, 255, 228, 241, 228, 105, 211, 214, 170, 65, 125, 107, - 133, 85, 152, 57, 127, 50, 61, 229, 180, 73, 247, 101, 240, 195, 130, 96, 64, 81, 128, 130, - 115, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 22, 129, 82, 96, 32, 1, 145, 80, 80, 96, 64, 81, 128, 145, 3, 144, 161, 91, 80, 91, - 80, 86, 91, 96, 0, 96, 0, 54, 67, 96, 64, 81, 128, 132, 132, 128, 130, 132, 55, 130, 1, 145, - 80, 80, 130, 129, 82, 96, 32, 1, 147, 80, 80, 80, 80, 96, 64, 81, 128, 145, 3, 144, 32, 97, 7, - 245, 129, 97, 18, 219, 86, 91, 21, 97, 9, 9, 87, 97, 1, 2, 96, 0, 80, 96, 0, 132, 115, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 22, 129, 82, 96, 32, 1, 144, 129, 82, 96, 32, 1, 96, 0, 32, 96, 0, 80, 84, 145, 80, 96, 0, 130, - 20, 21, 97, 8, 60, 87, 80, 97, 9, 11, 86, 91, 96, 1, 96, 1, 96, 0, 80, 84, 3, 96, 0, 96, 0, 80, - 84, 17, 21, 97, 8, 87, 87, 80, 97, 9, 11, 86, 91, 96, 0, 96, 2, 96, 0, 80, 131, 97, 1, 0, 129, - 16, 21, 97, 0, 2, 87, 144, 144, 1, 96, 0, 91, 80, 129, 144, 85, 80, 96, 0, 97, 1, 2, 96, 0, 80, - 96, 0, 133, 115, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 22, 129, 82, 96, 32, 1, 144, 129, 82, 96, 32, 1, 96, 0, 32, 96, 0, 80, - 129, 144, 85, 80, 97, 8, 178, 97, 23, 119, 86, 91, 97, 8, 186, 97, 21, 61, 86, 91, 80, 127, 88, - 97, 144, 118, 173, 245, 187, 9, 67, 209, 0, 239, 136, 213, 45, 124, 63, 214, 145, 177, 157, 58, - 144, 113, 181, 85, 182, 81, 251, 244, 24, 218, 131, 96, 64, 81, 128, 130, 115, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 22, 129, - 82, 96, 32, 1, 145, 80, 80, 96, 64, 81, 128, 145, 3, 144, 161, 91, 80, 91, 80, 80, 86, 91, 96, - 0, 54, 67, 96, 64, 81, 128, 132, 132, 128, 130, 132, 55, 130, 1, 145, 80, 80, 130, 129, 82, 96, - 32, 1, 147, 80, 80, 80, 80, 96, 64, 81, 128, 145, 3, 144, 32, 97, 9, 62, 129, 97, 18, 219, 86, - 91, 21, 97, 9, 160, 87, 96, 1, 96, 0, 80, 84, 130, 17, 21, 97, 9, 86, 87, 80, 97, 9, 162, 86, - 91, 129, 96, 0, 96, 0, 80, 129, 144, 85, 80, 97, 9, 104, 97, 23, 119, 86, 91, 127, 172, 189, - 176, 132, 199, 33, 51, 42, 197, 159, 155, 142, 57, 33, 150, 201, 235, 14, 73, 50, 134, 45, 168, - 235, 155, 234, 240, 218, 212, 245, 80, 218, 130, 96, 64, 81, 128, 130, 129, 82, 96, 32, 1, 145, - 80, 80, 96, 64, 81, 128, 145, 3, 144, 161, 91, 80, 91, 80, 86, 91, 96, 0, 96, 0, 97, 1, 2, 96, - 0, 80, 96, 0, 132, 115, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 22, 129, 82, 96, 32, 1, 144, 129, 82, 96, 32, 1, 96, 0, 32, 96, - 0, 80, 84, 17, 144, 80, 97, 9, 226, 86, 91, 145, 144, 80, 86, 91, 96, 0, 96, 0, 96, 0, 96, 0, - 97, 1, 3, 96, 0, 80, 96, 0, 135, 129, 82, 96, 32, 1, 144, 129, 82, 96, 32, 1, 96, 0, 32, 96, 0, - 80, 146, 80, 97, 1, 2, 96, 0, 80, 96, 0, 134, 115, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 22, 129, 82, 96, 32, 1, 144, 129, 82, - 96, 32, 1, 96, 0, 32, 96, 0, 80, 84, 145, 80, 96, 0, 130, 20, 21, 97, 10, 80, 87, 96, 0, 147, - 80, 97, 10, 127, 86, 91, 129, 96, 2, 10, 144, 80, 96, 0, 129, 132, 96, 1, 1, 96, 0, 80, 84, 22, - 20, 21, 97, 10, 117, 87, 96, 0, 147, 80, 97, 10, 127, 86, 97, 10, 126, 86, 91, 96, 1, 147, 80, - 97, 10, 127, 86, 91, 91, 80, 80, 80, 146, 145, 80, 80, 86, 91, 96, 0, 96, 0, 80, 84, 129, 86, - 91, 96, 1, 96, 0, 80, 84, 129, 86, 91, 96, 0, 54, 67, 96, 64, 81, 128, 132, 132, 128, 130, 132, - 55, 130, 1, 145, 80, 80, 130, 129, 82, 96, 32, 1, 147, 80, 80, 80, 80, 96, 64, 81, 128, 145, 3, - 144, 32, 97, 10, 201, 129, 97, 18, 219, 86, 91, 21, 97, 10, 218, 87, 129, 97, 1, 5, 96, 144, - 96, 32, 1, 128, 131, 17, 97, 11, 192, 87, 130, 144, 3, 96, 31, 22, 130, 1, 145, 91, 80, 80, - 150, 80, 80, 80, 80, 80, 80, 80, 96, 64, 81, 128, 145, 3, 144, 161, 96, 0, 131, 129, 82, 97, 1, - 8, 96, 32, 82, 96, 64, 129, 32, 128, 84, 96, 1, 96, 160, 96, 2, 10, 3, 25, 22, 129, 85, 96, 1, - 129, 129, 1, 131, 144, 85, 96, 2, 130, 129, 1, 128, 84, 133, 130, 85, 147, 148, 147, 144, 146, - 129, 22, 21, 97, 1, 0, 2, 96, 0, 25, 1, 22, 4, 96, 31, 129, 144, 16, 97, 12, 72, 87, 80, 91, - 80, 80, 80, 96, 1, 145, 80, 80, 97, 1, 130, 86, 91, 96, 31, 1, 96, 32, 144, 4, 144, 96, 0, 82, - 96, 32, 96, 0, 32, 144, 129, 1, 144, 97, 12, 59, 145, 144, 97, 9, 81, 86, 91, 96, 0, 133, 129, - 82, 97, 1, 3, 96, 32, 82, 96, 64, 129, 32, 128, 84, 144, 147, 80, 20, 21, 97, 12, 238, 87, 96, - 0, 128, 84, 131, 85, 96, 1, 131, 129, 1, 145, 144, 145, 85, 97, 1, 4, 128, 84, 145, 130, 1, - 128, 130, 85, 130, 128, 21, 130, 144, 17, 97, 12, 189, 87, 129, 131, 96, 0, 82, 96, 32, 96, 0, - 32, 145, 130, 1, 145, 1, 97, 12, 189, 145, 144, 97, 9, 81, 86, 91, 80, 80, 80, 96, 2, 131, 1, - 129, 144, 85, 97, 1, 4, 128, 84, 135, 146, 144, 129, 16, 21, 97, 0, 2, 87, 96, 0, 145, 144, - 145, 82, 96, 0, 128, 81, 96, 32, 97, 15, 216, 131, 57, 129, 81, 145, 82, 1, 85, 91, 80, 96, 1, - 129, 1, 84, 96, 2, 131, 144, 10, 144, 129, 22, 96, 0, 20, 21, 97, 13, 191, 87, 96, 64, 128, 81, - 96, 1, 96, 160, 96, 2, 10, 3, 51, 22, 129, 82, 96, 32, 129, 1, 135, 144, 82, 129, 81, 127, 225, - 197, 45, 198, 59, 113, 154, 222, 130, 232, 190, 169, 76, 196, 26, 13, 93, 40, 228, 170, 245, - 54, 173, 181, 233, 204, 204, 159, 248, 193, 174, 218, 146, 145, 129, 144, 3, 144, 145, 1, 144, - 161, 129, 84, 96, 1, 144, 17, 97, 13, 172, 87, 96, 0, 133, 129, 82, 97, 1, 3, 96, 32, 82, 96, - 64, 144, 32, 96, 2, 1, 84, 97, 1, 4, 128, 84, 144, 145, 144, 129, 16, 21, 97, 0, 2, 87, 96, 64, - 96, 0, 144, 129, 32, 96, 0, 128, 81, 96, 32, 97, 15, 216, 131, 57, 129, 81, 145, 82, 146, 144, - 146, 1, 129, 144, 85, 128, 130, 85, 96, 1, 130, 129, 1, 130, 144, 85, 96, 2, 146, 144, 146, 1, - 85, 148, 80, 97, 13, 191, 144, 80, 86, 91, 129, 84, 96, 0, 25, 1, 130, 85, 96, 1, 130, 1, 128, - 84, 130, 23, 144, 85, 91, 80, 80, 80, 145, 144, 80, 86, 91, 91, 96, 1, 128, 84, 17, 128, 21, - 97, 13, 234, 87, 80, 96, 1, 84, 96, 2, 144, 97, 1, 0, 129, 16, 21, 97, 0, 2, 87, 1, 84, 96, 0, - 20, 91, 21, 97, 13, 254, 87, 96, 1, 128, 84, 96, 0, 25, 1, 144, 85, 97, 13, 200, 86, 91, 96, 1, - 84, 129, 16, 128, 21, 97, 14, 33, 87, 80, 96, 1, 84, 96, 2, 144, 97, 1, 0, 129, 16, 21, 97, 0, - 2, 87, 1, 84, 96, 0, 20, 21, 91, 128, 21, 97, 14, 59, 87, 80, 96, 2, 129, 97, 1, 0, 129, 16, - 21, 97, 0, 2, 87, 1, 84, 96, 0, 20, 91, 21, 97, 14, 156, 87, 96, 1, 84, 96, 2, 144, 97, 1, 0, - 129, 16, 21, 97, 0, 2, 87, 129, 1, 84, 144, 130, 97, 1, 0, 129, 16, 21, 97, 0, 2, 87, 144, 144, - 1, 96, 0, 80, 85, 128, 97, 1, 2, 96, 0, 96, 2, 131, 97, 1, 0, 129, 16, 21, 97, 0, 2, 87, 129, - 1, 84, 130, 82, 96, 32, 146, 144, 146, 82, 96, 64, 129, 32, 146, 144, 146, 85, 96, 1, 84, 97, - 1, 0, 129, 16, 21, 97, 0, 2, 87, 1, 85, 91, 97, 6, 18, 86, 91, 21, 97, 1, 130, 87, 97, 1, 7, - 84, 97, 14, 183, 91, 98, 1, 81, 128, 66, 4, 144, 86, 91, 17, 21, 97, 14, 208, 87, 96, 0, 97, 1, - 6, 85, 97, 14, 203, 97, 14, 174, 86, 91, 97, 1, 7, 85, 91, 97, 1, 6, 84, 128, 131, 1, 16, 128, - 21, 144, 97, 14, 237, 87, 80, 97, 1, 5, 84, 97, 1, 6, 84, 131, 1, 17, 21, 91, 21, 97, 15, 3, - 87, 80, 97, 1, 6, 128, 84, 130, 1, 144, 85, 96, 1, 97, 1, 130, 86, 91, 80, 96, 0, 97, 1, 130, - 86, 91, 97, 5, 193, 97, 1, 4, 84, 96, 0, 91, 129, 129, 16, 21, 97, 15, 174, 87, 97, 1, 4, 128, - 84, 130, 144, 129, 16, 21, 97, 0, 2, 87, 96, 0, 145, 130, 82, 96, 0, 128, 81, 96, 32, 97, 15, - 216, 131, 57, 129, 81, 145, 82, 1, 84, 20, 97, 15, 136, 87, 97, 1, 4, 128, 84, 97, 1, 3, 145, - 96, 0, 145, 132, 144, 129, 16, 21, 97, 0, 2, 87, 96, 0, 128, 81, 96, 32, 97, 15, 216, 131, 57, - 129, 81, 145, 82, 1, 84, 130, 82, 80, 96, 32, 145, 144, 145, 82, 96, 64, 129, 32, 129, 129, 85, - 96, 1, 129, 1, 130, 144, 85, 96, 2, 1, 85, 91, 96, 1, 1, 97, 15, 21, 86, 91, 96, 31, 1, 96, 32, - 144, 4, 144, 96, 0, 82, 96, 32, 96, 0, 32, 144, 129, 1, 144, 97, 5, 55, 145, 144, 97, 9, 81, - 86, 91, 97, 1, 4, 128, 84, 96, 0, 128, 131, 85, 145, 144, 145, 82, 97, 4, 109, 144, 96, 0, 128, - 81, 96, 32, 97, 15, 216, 131, 57, 129, 81, 145, 82, 144, 129, 1, 144, 97, 9, 81, 86, 76, 11, - 230, 2, 0, 250, 162, 5, 89, 48, 140, 183, 181, 161, 187, 50, 85, 193, 108, 177, 202, 185, 31, - 82, 91, 90, 231, 160, 61, 2, 250, 190, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 96, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 28, 113, 175, - 85, 103, 212, 73, 217, 22, 125, 82, 92, 66, 253, 67, 60, 60, 185, 219, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 148, 252, 40, 117, 103, 6, 150, 130, 60, 247, 33, 206, 55, 210, 236, 103, 246, 109, - 205, 163, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 177, 156, 201, 26, 34, 135, 235, 175, 51, 3, 64, - 132, 200, 56, 49, 215, 20, 84, 212, 247, 0, 44, 53, 200, 213, 46, 164, 196, 99, 138, 4, 252, - 58, 0, 98, 12, 34, 71, 206, 60, 223, 159, 200, 125, 182, 91, 112, 78, 141, 247, 27, 247, 11, - 159, 168, 115, 121, 215, 110, 242, 123, 215, 111, 110, 68, 78, 48, 128, 160, 21, 239, 107, 110, - 48, 49, 28, 184, 138, 112, 228, 85, 170, 159, 205, 17, 164, 204, 4, 125, 139, 4, 168, 23, 200, - 0, 3, 208, 144, 252, 56, 148, 142, 231, 213, 248, 181, 189, 57, 199, 38, 80, 171, 26, 172, 119, - 46, 120, 92, 8, 3, 106, 34, 208, 234, 62, 0, 0, 1, 112, 194, 239, 40, 123, 221, 230, 239, 220, - 185, 78, 207, 2, 17, 3, 200, 134, 77, 128, 224, 230, 207, 193, 230, 165, 193, 213, 132, 83, - 240, 220, 172, 220, 88, 195, 121, 161, 124, 134, 21, 4, 188, 136, 108, 98, 231, 11, 204, 122, - 11, 129, 191, 190, 137, 145, 187, 33, 254, 188, 8, 10, 126, 34, 90, 166, 76, 4, 1, 233, 56, 4, - 168, 23, 200, 0, 1, 95, 144, 31, 87, 248, 38, 202, 245, 148, 247, 168, 55, 217, 252, 9, 36, 86, - 135, 10, 40, 147, 101, 2, 62, 88, 199, 98, 33, 68, 0, 1, 189, 84, 20, 21, 91, 21, 97, 21, 146, - 87, 128, 128, 96, 1, 1, 145, 80, 80, 97, 21, 84, 86, 91, 91, 96, 1, 96, 1, 96, 0, 80, 84, 17, - 128, 21, 97, 21, 196, 87, 80, 96, 0, 96, 2, 96, 0, 80, 96, 1, 96, 0, 80, 84, 97, 1, 0, 129, 16, - 21, 97, 0, 2, 87, 144, 144, 1, 96, 0, 91, 80, 84, 20, 91, 21, 97, 21, 227, 87, 96, 1, 96, 0, - 129, 129, 80, 84, 128, 146, 145, 144, 96, 1, 144, 3, 145, 144, 80, 85, 80, 97, 21, 147, 86, 91, - 96, 1, 96, 0, 80, 84, 129, 16, 128, 21, 97, 22, 20, 87, 80, 96, 0, 96, 2, 96, 0, 80, 96, 1, 96, - 0, 80, 84, 97, 1, 0, 129, 16, 21, 97, 0, 2, 87, 144, 144, 1, 96, 0, 91, 80, 84, 20, 21, 91, - 128, 21, 97, 22, 55, 87, 80, 96, 0, 96, 2, 96, 0, 80, 130, 97, 1, 0, 129, 16, 21, 97, 0, 2, 87, - 144, 144, 1, 96, 0, 91, 80, 84, 20, 91, 21, 97, 22, 205, 87, 96, 2, 96, 0, 80, 96, 1, 96, 0, - 80, 84, 97, 1, 0, 129, 16, 21, 97, 0, 2, 87, 144, 144, 1, 96, 0, 91, 80, 84, 96, 2, 96, 0, 80, - 130, 97, 1, 0, 129, 16, 21, 97, 0, 2, 87, 144, 144, 1, 96, 0, 91, 80, 129, 144, 85, 80, 128, - 97, 1, 2, 96, 0, 80, 96, 0, 96, 2, 96, 0, 80, 132, 97, 1, 0, 129, 16, 21, 97, 0, 2, 87, 144, - 144, 1, 96, 0, 91, 80, 84, 129, 82, 96, 32, 1, 144, 129, 82, 96, 32, 1, 96, 0, 32, 96, 0, 80, - 129, 144, 85, 80, 96, 0, 96, 2, 96, 0, 80, 96, 1, 96, 0, 80, 84, 97, 1, 0, 129, 16, 21, 97, 0, - 2, 87, 144, 144, 1, 96, 0, 91, 80, 129, 144, 85, 80, 91, 97, 21, 70, 86, 91, 91, 80, 144, 86, - 91, 96, 0, 97, 22, 226, 51, 97, 9, 165, 86, 91, 21, 97, 23, 113, 87, 97, 1, 7, 96, 0, 80, 84, - 97, 22, 246, 97, 25, 128, 86, 91, 17, 21, 97, 23, 27, 87, 96, 0, 97, 1, 6, 96, 0, 80, 129, 144, - 85, 80, 97, 23, 16, 97, 25, 128, 86, 91, 97, 1, 7, 96, 0, 80, 129, 144, 85, 80, 91, 97, 1, 6, - 96, 0, 80, 84, 130, 97, 1, 6, 96, 0, 80, 84, 1, 16, 21, 128, 21, 97, 23, 71, 87, 80, 97, 1, 5, - 96, 0, 80, 84, 130, 97, 1, 6, 96, 0, 80, 84, 1, 17, 21, 91, 21, 97, 23, 104, 87, 129, 97, 1, 6, - 96, 0, 130, 130, 130, 80, 84, 1, 146, 80, 80, 129, 144, 85, 80, 96, 1, 144, 80, 97, 23, 114, - 86, 91, 96, 0, 144, 80, 97, 23, 114, 86, 91, 91, 145, 144, 80, 86, 91, 96, 0, 96, 0, 97, 1, 4, - 96, 0, 80, 128, 84, 144, 80, 145, 80, 96, 0, 144, 80, 91, 129, 129, 16, 21, 97, 24, 120, 87, - 97, 1, 9, 96, 0, 80, 96, 0, 97, 1, 4, 96, 0, 80, 131, 129, 84, 129, 16, 21, 97, 0, 2, 87, 144, - 96, 0, 82, 96, 32, 96, 0, 32, 144, 1, 96, 0, 91, 80, 84, 129, 82, 96, 32, 1, 144, 129, 82, 96, - 32, 1, 96, 0, 32, 96, 0, 96, 0, 130, 1, 96, 0, 97, 1, 0, 10, 129, 84, 144, 115, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 2, 25, 22, - 144, 85, 96, 1, 130, 1, 96, 0, 80, 96, 0, 144, 85, 96, 2, 130, 1, 96, 0, 80, 128, 84, 96, 1, - 129, 96, 1, 22, 21, 97, 1, 0, 2, 3, 22, 96, 2, 144, 4, 96, 0, 130, 85, 128, 96, 31, 16, 97, 24, - 42, 87, 80, 97, 24, 103, 86, 91, 96, 31, 1, 96, 32, 144, 4, 144, 96, 0, 82, 96, 32, 96, 0, 32, - 144, 129, 1, 144, 97, 24, 102, 145, 144, 97, 24, 72, 86, 91, 128, 130, 17, 21, 97, 24, 98, 87, - 96, 0, 129, 129, 80, 96, 0, 144, 85, 80, 96, 1, 1, 97, 24, 72, 86, 91, 80, 144, 86, 91, 91, 80, - 80, 80, 91, 128, 96, 1, 1, 144, 80, 128, 80, 97, 23, 140, 86, 91, 97, 24, 128, 97, 24, 133, 86, - 91, 91, 80, 80, 86, 91, 96, 0, 96, 0, 97, 1, 4, 96, 0, 80, 128, 84, 144, 80, 145, 80, 96, 0, - 144, 80, 91, 129, 129, 16, 21, 97, 25, 56, 87, 96, 0, 96, 1, 2, 97, 1, 4, 96, 0, 80, 130, 129, - 84, 129, 16, 21, 97, 0, 2, 87, 144, 96, 0, 82, 96, 32, 96, 0, 32, 144, 1, 96, 0, 91, 80, 84, - 20, 21, 21, 97, 25, 42, 87, 97, 1, 3, 96, 0, 80, 96, 0, 97, 1, 4, 96, 0, 80, 131, 129, 84, 129, - 16, 21, 97, 0, 2, 87, 144, 96, 0, 82, 96, 32, 96, 0, 32, 144, 1, 96, 0, 91, 80, 84, 129, 82, - 96, 32, 1, 144, 129, 82, 96, 32, 1, 96, 0, 32, 96, 0, 96, 0, 130, 1, 96, 0, 80, 96, 0, 144, 85, - 96, 1, 130, 1, 96, 0, 80, 96, 0, 144, 85, 96, 2, 130, 1, 96, 0, 80, 96, 0, 144, 85, 80, 80, 91, - 91, 128, 96, 1, 1, 144, 80, 128, 80, 97, 24, 154, 86, 91, 97, 1, 4, 96, 0, 80, 128, 84, 96, 0, - 130, 85, 144, 96, 0, 82, 96, 32, 96, 0, 32, 144, 129, 1, 144, 97, 25, 121, 145, 144, 97, 25, - 91, 86, 91, 128, 130, 17, 21, 97, 25, 117, 87, 96, 0, 129, 129, 80, 96, 0, 144, 85, 80, 96, 1, - 1, 97, 25, 91, 86, 91, 80, 144, 86, 91, 91, 80, 91, 80, 80, 86, 91, 96, 0, 98, 1, 81, 128, 66, - 4, 144, 80, 97, 25, 143, 86, 91, 144, 86, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 96, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 82, 183, 210, 220, 200, 12, 210, 228, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 22, 160, 108, 72, 251, 156, 119, 219, 100, 72, 196, 152, 187, 21, 154, 106, 98, 235, 235, 179, - 0, 40, 222, 119, 120, 63, 122, 114, 89, 116, 126, 254, 167, 243, 150, 215, 68, 240, 35, 49, 1, - 215, 230, 8, 23, 223, 226, 210, 136, 85, 76, 128, 19, 200, 130, 171, 54, 63, 183, 35, 184, 5, - 35, 227, 11, 120, 74, 148, 166, 215, 65, 90, 252, 35, 143, 195, 195, 178, 7, 178, 53, 237, 254, - 12, 86, 164, 72, 4, 1, 220, 6, 252, 35, 172, 0, 82, 8, 50, 190, 52, 59, 148, 248, 96, 18, 77, - 196, 254, 226, 120, 253, 203, 211, 140, 16, 45, 136, 85, 169, 198, 103, 1, 172, 172, 0, 0, 115, - 90, 249, 180, 1, 10, 135, 32, 207, 0, 24, 6, 195, 61, 207, 87, 167, 243, 213, 7, 174, 171, 81, - 1, 224, 41, 26, 138, 227, 194, 76, 137, 189, 37, 3, 48, 96, 241, 87, 115, 52, 184, 123, 234, - 91, 138, 102, 195, 176, 95, 175, 26, 39, 212, 23, 11, 205, 151, 97, 122, 142, 39, 136, 34, 162, - 200, 4, 190, 15, 34, 77, 74, 0, 82, 8, 193, 63, 38, 51, 130, 130, 5, 187, 197, 124, 61, 198, - 119, 71, 224, 220, 108, 185, 163, 133, 4, 219, 208, 239, 240, 97, 18, 0, 0, 0, 215, 156, 112, - 188, 49, 230, 189, 123, 206, 220, 223, 252, 33, 104, 60, 75, 85, 72, 163, 94, 168, 132, 247, - 45, 81, 111, 86, 210, 14, 150, 168, 207, 96, 229, 24, 33, 197, 233, 222, 203, 247, 18, 22, 151, - 148, 129, 54, 150, 40, 180, 165, 244, 122, 241, 43, 111, 131, 71, 173, 135, 182, 111, 136, 49, - 162, 200, 4, 1, 11, 164, 59, 116, 0, 82, 8, 113, 128, 235, 57, 166, 38, 73, 56, 253, 179, 239, - 253, 115, 65, 196, 114, 124, 56, 33, 83, 64, 81, 128, 145, 3, 144, 32, 97, 11, 14, 129, 97, 18, - 219, 86, 91, 21, 97, 11, 32, 87, 96, 0, 97, 1, 6, 96, 0, 80, 129, 144, 85, 80, 91, 80, 91, 86, - 91, 97, 1, 5, 96, 0, 80, 84, 129, 86, 91, 97, 1, 6, 96, 0, 80, 84, 129, 86, 91, 97, 1, 7, 96, - 0, 80, 84, 129, 86, 91, 97, 1, 8, 96, 0, 80, 84, 129, 86, 91, 96, 0, 54, 67, 96, 64, 81, 128, - 132, 132, 128, 130, 132, 55, 130, 1, 145, 80, 80, 130, 129, 82, 96, 32, 1, 147, 80, 80, 80, 80, - 96, 64, 81, 128, 145, 3, 144, 32, 97, 11, 123, 129, 97, 18, 219, 86, 91, 21, 97, 11, 153, 87, - 129, 115, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 22, 255, 91, 80, 91, 80, 86, 91, 96, 0, 97, 11, 169, 51, 97, 9, 165, 86, 91, 21, - 97, 15, 5, 87, 97, 11, 183, 132, 97, 22, 215, 86, 91, 21, 97, 12, 160, 87, 127, 146, 202, 58, - 128, 133, 62, 102, 99, 250, 49, 250, 16, 185, 146, 37, 241, 141, 73, 2, 147, 155, 76, 83, 169, - 202, 174, 144, 67, 246, 239, 208, 4, 51, 133, 135, 134, 134, 96, 64, 81, 128, 134, 115, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 22, 129, 82, 96, 32, 1, 133, 129, 82, 96, 32, 1, 132, 115, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 22, 129, 82, 96, 32, 1, 128, - 96, 32, 1, 130, 129, 3, 130, 82, 132, 132, 130, 129, 129, 82, 96, 32, 1, 146, 80, 128, 130, - 132, 55, 130, 1, 145, 80, 80, 150, 80, 80, 80, 80, 80, 80, 80, 96, 64, 81, 128, 145, 3, 144, - 161, 132, 115, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 22, 132, 132, 132, 96, 64, 81, 128, 131, 131, 128, 130, 132, 55, 130, 1, - 145, 80, 80, 146, 80, 80, 80, 96, 0, 96, 64, 81, 128, 131, 3, 129, 133, 135, 97, 133, 2, 90, 3, - 241, 146, 80, 80, 80, 80, 96, 0, 96, 1, 2, 144, 80, 97, 15, 6, 86, 91, 96, 0, 54, 67, 96, 64, - 81, 128, 132, 132, 128, 130, 132, 55, 130, 1, 145, 80, 80, 130, 129, 82, 96, 32, 1, 147, 80, - 80, 80, 80, 96, 64, 81, 128, 145, 3, 144, 32, 144, 80, 128, 80, 97, 12, 211, 129, 97, 15, 14, - 86, 91, 21, 128, 21, 97, 13, 51, 87, 80, 96, 0, 97, 1, 9, 96, 0, 80, 96, 0, 131, 129, 82, 96, - 32, 1, 144, 129, 82, 96, 32, 1, 96, 0, 32, 96, 0, 80, 96, 0, 1, 96, 0, 144, 84, 144, 97, 1, 0, - 10, 144, 4, 115, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 22, 115, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 22, 20, 91, 21, 97, 15, 4, 87, 132, 97, 1, 9, 96, 0, - 80, 96, 0, 131, 129, 82, 96, 32, 1, 144, 129, 82, 96, 32, 1, 96, 0, 32, 96, 0, 80, 96, 0, 1, - 96, 0, 97, 1, 0, 10, 129, 84, 129, 115, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 2, 25, 22, 144, 131, 2, 23, 144, 85, 80, 131, 97, - 1, 9, 96, 0, 80, 96, 0, 131, 129, 82, 96, 32, 1, 144, 129, 82, 96, 32, 1, 96, 0, 32, 96, 0, 80, - 96, 1, 1, 96, 0, 80, 129, 144, 85, 80, 130, 130, 97, 1, 9, 96, 0, 80, 96, 0, 132, 129, 82, 96, - 32, 1, 144, 129, 82, 96, 32, 1, 96, 0, 32, 96, 0, 80, 96, 2, 1, 96, 0, 80, 145, 144, 130, 128, - 84, 96, 1, 129, 96, 1, 22, 21, 97, 1, 0, 2, 3, 22, 96, 2, 144, 4, 144, 96, 0, 82, 96, 32, 96, - 0, 32, 144, 96, 31, 1, 96, 32, 144, 4, 129, 1, 146, 130, 96, 31, 16, 97, 14, 8, 87, 128, 53, - 96, 255, 25, 22, 131, 128, 1, 23, 133, 85, 97, 14, 57, 86, 91, 130, 128, 1, 96, 1, 1, 133, 85, - 130, 21, 97, 14, 57, 87, 145, 130, 1, 91, 130, 129, 17, 21, 97, 14, 56, 87, 130, 53, 130, 96, - 0, 80, 85, 145, 96, 32, 1, 145, 144, 96, 1, 1, 144, 97, 14, 26, 86, 91, 91, 80, 144, 80, 97, - 14, 100, 145, 144, 97, 14, 70, 86, 91, 128, 130, 17, 21, 97, 14, 96, 87, 96, 0, 129, 129, 80, - 96, 0, 144, 85, 80, 96, 1, 1, 97, 14, 70, 86, 91, 80, 144, 86, 91, 80, 80, 127, 23, 51, 203, - 181, 54, 89, 215, 19, 183, 149, 128, 247, 159, 63, 159, 242, 21, 247, 138, 124, 122, 164, 88, - 144, 243, 184, 159, 197, 205, 223, 191, 50, 129, 51, 134, 136, 135, 135, 96, 64, 81, 128, 135, - 129, 82, 96, 32, 1, 134, 115, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 22, 129, 82, 96, 32, 1, 133, 129, 82, 96, 32, 1, 132, 115, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 22, 129, 82, 96, 32, 1, 128, 96, 32, 1, 130, 129, 3, 130, 82, 132, 132, 130, 129, 129, 82, - 96, 32, 1, 146, 80, 128, 130, 132, 55, 130, 1, 145, 80, 80, 151, 80, 80, 80, 80, 80, 80, 80, - 80, 96, 64, 81, 128, 145, 3, 144, 161, 91, 91, 91, 148, 147, 80, 80, 80, 80, 86, 91, 96, 0, - 129, 97, 15, 26, 129, 97, 18, 219, 86, 91, 21, 97, 18, 212, 87, 96, 0, 97, 1, 9, 96, 0, 80, 96, - 0, 133, 129, 82, 96, 32, 1, 144, 129, 82, 96, 32, 1, 96, 0, 32, 96, 0, 80, 96, 0, 1, 96, 0, - 144, 84, 144, 97, 1, 0, 10, 144, 4, 115, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 22, 115, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 22, 20, 21, 21, 97, 18, 211, 87, - 97, 1, 9, 96, 0, 80, 96, 0, 132, 129, 82, 96, 32, 1, 144, 129, 82, 96, 32, 1, 96, 0, 32, 96, 0, - 80, 96, 0, 1, 96, 0, 144, 84, 144, 97, 1, 0, 10, 144, 4, 115, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 22, 115, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 22, 97, 1, - 9, 96, 0, 80, 96, 0, 133, 129, 82, 96, 32, 1, 144, 129, 82, 96, 32, 1, 96, 0, 32, 96, 0, 80, - 96, 1, 1, 96, 0, 80, 84, 97, 1, 9, 96, 0, 80, 96, 0, 134, 129, 82, 96, 32, 1, 144, 129, 82, 96, - 32, 1, 96, 0, 32, 96, 0, 80, 96, 2, 1, 96, 0, 80, 96, 64, 81, 128, 130, 128, 84, 96, 1, 129, - 96, 1, 22, 21, 97, 1, 0, 2, 3, 22, 96, 2, 144, 4, 128, 21, 97, 16, 118, 87, 128, 96, 31, 16, - 97, 16, 75, 87, 97, 1, 0, 128, 131, 84, 4, 2, 131, 82, 145, 96, 32, 1, 145, 97, 16, 118, 86, - 91, 130, 1, 145, 144, 96, 0, 82, 96, 32, 96, 0, 32, 144, 91, 129, 84, 129, 82, 144, 96, 1, 1, - 144, 96, 32, 1, 128, 131, 17, 97, 16, 89, 87, 130, 144, 3, 96, 31, 22, 130, 1, 145, 91, 80, 80, - 145, 80, 80, 96, 0, 96, 64, 81, 128, 131, 3, 129, 133, 135, 97, 133, 2, 90, 3, 241, 146, 80, - 80, 80, 80, 127, 231, 201, 87, 192, 110, 154, 102, 44, 26, 108, 119, 54, 97, 121, 245, 183, 2, - 185, 118, 81, 220, 40, 238, 231, 213, 191, 29, 255, 110, 64, 187, 74, 51, 132, 97, 1, 9, 96, 0, - 80, 96, 0, 135, 129, 82, 96, 32, 1, 144, 129, 82, 96, 32, 1, 96, 0, 32, 96, 0, 80, 96, 1, 1, - 96, 0, 80, 84, 97, 1, 9, 96, 0, 80, 96, 0, 136, 129, 82, 96, 32, 1, 144, 129, 31, 4, 168, 23, - 200, 0, 86, 34, 251, 177, 183, 60, 79, 11, 218, 79, 103, 220, 162, 102, 206, 110, 244, 47, 82, - 15, 187, 152, 15, 139, 131, 200, 158, 12, 60, 0, 0, 91, 135, 109, 177, 179, 166, 167, 25, 225, - 78, 143, 125, 242, 29, 230, 96, 205, 225, 195, 83, 46, 133, 253, 128, 113, 50, 237, 112, 132, - 197, 98, 206, 21, 133, 70, 90, 243, 171, 109, 64, 109, 156, 20, 236, 103, 119, 115, 228, 152, - 12, 17, 187, 76, 15, 191, 67, 100, 91, 46, 155, 121, 99, 151, 97, 160, 12, 0, 4, 168, 23, 200, - 0, 45, 198, 192, 96, 96, 96, 64, 82, 96, 2, 97, 1, 8, 96, 0, 80, 85, 96, 64, 81, 97, 27, 81, - 56, 3, 128, 97, 27, 81, 131, 57, 129, 1, 96, 64, 82, 128, 128, 81, 130, 1, 145, 144, 96, 32, 1, - 128, 81, 144, 96, 32, 1, 144, 145, 144, 128, 81, 144, 96, 32, 1, 144, 145, 144, 80, 80, 91, - 128, 91, 131, 131, 91, 96, 0, 96, 1, 131, 81, 1, 96, 1, 96, 0, 80, 129, 144, 85, 80, 51, 115, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 22, 96, 2, 96, 0, 80, 96, 1, 97, 1, 0, 129, 16, 21, 97, 0, 2, 87, 144, 144, 1, 96, 0, 91, - 80, 129, 144, 85, 80, 96, 1, 97, 1, 2, 96, 0, 80, 96, 0, 51, 115, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 22, 129, 82, 96, 32, 1, - 144, 129, 82, 96, 32, 1, 96, 0, 32, 96, 0, 80, 129, 144, 85, 80, 96, 0, 144, 80, 91, 130, 81, - 129, 16, 21, 97, 1, 110, 87, 130, 129, 129, 81, 129, 16, 21, 97, 0, 2, 87, 144, 96, 32, 1, 144, - 96, 32, 2, 1, 81, 115, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 22, 96, 2, 96, 0, 80, 130, 96, 2, 1, 97, 1, 0, 129, 16, 21, 97, - 0, 2, 87, 144, 144, 1, 96, 0, 91, 80, 129, 144, 85, 80, 128, 96, 2, 1, 97, 1, 2, 96, 0, 80, 96, - 0, 133, 132, 129, 81, 129, 16, 21, 97, 0, 2, 87, 144, 96, 32, 1, 144, 96, 32, 2, 1, 81, 115, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 22, 129, 82, 96, 32, 1, 144, 129, 82, 96, 32, 1, 96, 0, 32, 96, 0, 80, 129, 144, 85, 80, - 91, 128, 96, 1, 1, 144, 80, 128, 80, 97, 0, 194, 86, 91, 129, 96, 0, 96, 0, 80, 129, 144, 85, - 80, 91, 80, 80, 80, 128, 97, 1, 5, 96, 0, 80, 129, 144, 85, 80, 97, 1, 143, 97, 1, 173, 86, 91, - 97, 1, 7, 96, 0, 80, 129, 144, 85, 80, 91, 80, 91, 80, 80, 80, 97, 25, 146, 128, 97, 1, 191, - 96, 0, 57, 96, 0, 243, 91, 96, 0, 98, 1, 81, 128, 66, 4, 144, 80, 97, 1, 188, 86, 91, 144, 86, - 96, 96, 96, 64, 82, 54, 21, 97, 0, 248, 87, 96, 0, 53, 124, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 144, 4, 128, 99, 23, 56, 37, 217, 20, 97, 1, - 96, 87, 128, 99, 47, 84, 191, 110, 20, 97, 1, 120, 87, 128, 99, 65, 35, 203, 107, 20, 97, 1, - 164, 87, 128, 99, 82, 55, 80, 147, 20, 97, 1, 199, 87, 128, 99, 84, 253, 77, 80, 20, 97, 1, - 234, 87, 128, 99, 92, 82, 194, 245, 20, 97, 2, 13, 87, 128, 99, 101, 144, 16, 231, 20, 97, 2, - 28, 87, 128, 99, 112, 101, 203, 72, 20, 97, 2, 63, 87, 128, 99, 116, 108, 145, 113, 20, 97, 2, - 87, 87, 128, 99, 121, 122, 246, 39, 20, 97, 2, 122, 87, 128, 99, 178, 13, 48, 169, 20, 97, 2, - 166, 87, 128, 99, 182, 29, 39, 246, 20, 97, 2, 190, 87, 128, 99, 183, 92, 125, 198, 20, 97, 3, - 7, 87, 128, 99, 186, 81, 166, 223, 20, 97, 3, 31, 87, 128, 99, 194, 207, 115, 38, 20, 97, 3, - 55, 87, 128, 99, 203, 240, 176, 192, 20, 97, 3, 108, 87, 128, 99, 240, 13, 75, 93, 20, 97, 3, - 132, 87, 128, 99, 241, 115, 109, 134, 20, 97, 3, 165, 87, 97, 0, 248, 86, 91, 97, 1, 94, 91, - 96, 0, 52, 17, 21, 97, 1, 91, 87, 127, 225, 255, 252, 196, 146, 61, 4, 181, 89, 244, 210, 154, - 139, 252, 108, 218, 4, 235, 91, 13, 60, 70, 7, 81, 194, 64, 44, 92, 92, 201, 16, 156, 51, 52, - 96, 64, 81, 128, 131, 115, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 22, 129, 82, 96, 32, 1, 130, 129, 82, 96, 32, 1, 146, 80, - 80, 80, 96, 64, 81, 128, 145, 3, 144, 161, 91, 91, 86, 91, 0, 91, 97, 1, 118, 96, 4, 128, 128, - 53, 144, 96, 32, 1, 144, 145, 144, 80, 80, 97, 7, 196, 86, 91, 0, 91, 97, 1, 142, 96, 4, 128, - 128, 53, 144, 96, 32, 1, 144, 145, 144, 80, 80, 97, 9, 165, 86, 91, 96, 64, 81, 128, 130, 129, - 82, 96, 32, 1, 145, 80, 80, 96, 64, 81, 128, 145, 3, 144, 243, 91, 97, 1, 177, 96, 4, 128, 80, - 80, 97, 10, 145, 86, 91, 96, 64, 81, 128, 130, 129, 82, 96, 32, 1, 145, 80, 80, 96, 64, 81, - 128, 145, 3, 144, 243, 91, 97, 1, 212, 96, 4, 128, 80, 80, 97, 11, 56, 86, 91, 96, 64, 81, 128, - 130, 129, 82, 96, 32, 1, 145, 80, 80, 96, 64, 81, 128, 145, 3, 144, 243, 91, 97, 1, 247, 96, 4, - 128, 80, 80, 97, 11, 66, 86, 91, 96, 64, 81, 128, 130, 129, 82, 96, 32, 1, 145, 80, 80, 96, 64, - 81, 128, 145, 3, 144, 243, 91, 97, 2, 26, 96, 4, 128, 80, 80, 97, 10, 223, 86, 91, 0, 91, 97, - 2, 41, 96, 4, 128, 80, 80, 97, 11, 46, 86, 91, 96, 64, 81, 128, 130, 129, 82, 96, 32, 1, 145, - 80, 80, 96, 64, 81, 128, 145, 3, 144, 243, 91, 97, 2, 85, 96, 4, 128, 128, 53, 144, 96, 32, 1, - 144, 145, 144, 80, 80, 97, 6, 110, 86, 91, 0, 91, 97, 2, 100, 96, 4, 128, 80, 80, 97, 10, 136, - 86, 91, 96, 64, 81, 128, 130, 129, 82, 96, 32, 1, 145, 80, 80, 96, 64, 81, 128, 145, 3, 144, - 243, 91, 97, 2, 144, 96, 4, 128, 128, 53, 144, 96, 32, 1, 144, 145, 144, 80, 80, 97, 15, 14, - 86, 91, 96, 64, 81, 128, 130, 129, 82, 96, 32, 1, 145, 80, 80, 96, 64, 81, 128, 145, 3, 144, - 243, 91, 97, 2, 188, 96, 4, 128, 128, 53, 144, 96, 32, 1, 144, 145, 144, 80, 80, 97, 10, 154, - 86, 91, 0, 91, 97, 2, 241, 96, 4, 128, 128, 53, 144, 96, 32, 1, 144, 145, 144, 128, 53, 144, - 96, 32, 1, 144, 145, 144, 128, 53, 144, 96, 32, 1, 144, 130, 1, 128, 53, 144, 96, 32, 1, 145, - 144, 145, 146, 144, 80, 80, 97, 11, 158, 86, 91, 96, 64, 81, 128, 130, 129, 82, 96, 32, 1, 145, - 80, 80, 96, 64, 81, 128, 145, 3, 144, 243, 91, 97, 3, 29, 96, 4, 128, 128, 53, 144, 96, 32, 1, - 144, 145, 144, 80, 80, 97, 3, 200, 86, 91, 0, 91, 97, 3, 53, 96, 4, 128, 128, 53, 144, 96, 32, - 1, 144, 145, 144, 80, 80, 97, 9, 15, 86, 91, 0, 91, 97, 3, 86, 96, 4, 128, 128, 53, 144, 96, - 32, 1, 144, 145, 144, 128, 53, 144, 96, 32, 1, 144, 145, 144, 80, 80, 97, 9, 231, 86, 91, 96, - 64, 81, 128, 130, 129, 82, 96, 32, 1, 145, 80, 80, 96, 64, 81, 128, 145, 3, 144, 243, 91, 97, - 3, 130, 96, 4, 128, 128, 53, 144, 96, 32, 1, 144, 145, 144, 80, 80, 97, 11, 76, 86, 91, 0, 91, - 97, 3, 163, 96, 4, 128, 128, 53, 144, 96, 32, 1, 144, 145, 144, 128, 53, 144, 96, 32, 1, 144, - 145, 144, 80, 80, 97, 4, 202, 86, 91, 0, 91, 97, 3, 178, 96, 4, 128, 80, 80, 97, 11, 36, 86, - 91, 96, 64, 81, 128, 130, 1, 5, 96, 0, 80, 96, 0, 133, 115, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 22, 129, 82, 96, 32, 1, 144, - 129, 82, 96, 32, 1, 96, 0, 32, 96, 0, 80, 129, 144, 85, 80, 97, 6, 147, 97, 13, 187, 86, 91, - 97, 6, 155, 97, 12, 36, 86, 91, 127, 88, 97, 144, 118, 173, 245, 187, 9, 67, 209, 0, 239, 136, - 213, 45, 124, 63, 214, 145, 177, 157, 58, 144, 113, 181, 85, 182, 81, 251, 244, 24, 218, 131, - 96, 64, 81, 128, 130, 115, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 22, 129, 82, 96, 32, 1, 145, 80, 80, 96, 64, 81, 128, 145, - 3, 144, 161, 91, 80, 91, 80, 80, 86, 91, 96, 0, 54, 96, 64, 81, 128, 131, 131, 128, 130, 132, - 55, 130, 1, 145, 80, 80, 146, 80, 80, 80, 96, 64, 81, 128, 145, 3, 144, 32, 97, 7, 22, 129, 97, - 7, 249, 86, 91, 21, 97, 7, 120, 87, 96, 4, 96, 0, 80, 84, 130, 17, 21, 97, 7, 46, 87, 80, 97, - 7, 122, 86, 91, 129, 96, 3, 96, 0, 80, 129, 144, 85, 80, 97, 7, 64, 97, 13, 187, 86, 91, 127, - 172, 189, 176, 132, 199, 33, 51, 42, 197, 159, 155, 142, 57, 33, 150, 201, 235, 14, 73, 50, - 134, 45, 168, 235, 155, 234, 240, 218, 212, 245, 80, 218, 130, 96, 64, 81, 128, 130, 129, 82, - 96, 32, 1, 145, 80, 80, 96, 64, 81, 128, 145, 3, 144, 161, 91, 80, 91, 80, 86, 91, 96, 0, 96, - 0, 54, 96, 64, 81, 128, 131, 131, 128, 130, 132, 55, 130, 1, 145, 80, 80, 146, 80, 80, 80, 96, - 64, 81, 128, 145, 3, 144, 32, 97, 7, 166, 129, 97, 7, 249, 86, 91, 21, 97, 7, 224, 87, 130, 96, - 0, 96, 0, 97, 1, 0, 10, 129, 84, 129, 115, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 2, 25, 22, 144, 131, 2, 23, 144, 85, 80, 96, - 1, 145, 80, 80, 97, 7, 226, 86, 91, 80, 91, 145, 144, 80, 86, 91, 96, 3, 96, 0, 80, 84, 129, - 86, 91, 96, 4, 96, 0, 80, 84, 129, 86, 91, 96, 0, 96, 0, 96, 0, 96, 0, 97, 1, 5, 96, 0, 80, 96, - 0, 51, 115, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 22, 129, 82, 96, 32, 1, 144, 129, 82, 96, 32, 1, 96, 0, 32, 96, 0, 80, 84, - 146, 80, 96, 0, 131, 20, 21, 97, 8, 66, 87, 97, 10, 77, 86, 91, 96, 1, 96, 0, 80, 96, 0, 134, - 129, 82, 96, 32, 1, 144, 129, 82, 96, 32, 1, 96, 0, 32, 96, 0, 80, 145, 80, 96, 0, 130, 96, 0, - 1, 96, 0, 80, 84, 20, 21, 97, 9, 24, 87, 96, 3, 96, 0, 80, 84, 130, 96, 0, 1, 96, 0, 80, 129, - 144, 85, 80, 96, 0, 130, 96, 1, 1, 96, 0, 80, 129, 144, 85, 80, 96, 2, 96, 0, 80, 128, 84, 128, - 145, 144, 96, 1, 1, 144, 144, 129, 84, 129, 131, 85, 129, 129, 21, 17, 97, 8, 223, 87, 129, - 131, 96, 0, 82, 96, 32, 96, 0, 32, 145, 130, 1, 145, 1, 97, 8, 222, 145, 144, 97, 8, 192, 86, - 91, 128, 130, 17, 21, 97, 8, 218, 87, 96, 0, 129, 129, 80, 96, 0, 144, 85, 80, 96, 1, 1, 97, 8, - 192, 86, 91, 80, 144, 86, 91, 91, 80, 80, 80, 130, 96, 2, 1, 96, 0, 80, 129, 144, 85, 80, 132, - 96, 2, 96, 0, 80, 131, 96, 2, 1, 96, 0, 80, 84, 129, 84, 129, 16, 21, 97, 0, 2, 87, 144, 96, 0, - 82, 96, 32, 96, 0, 32, 144, 1, 96, 0, 91, 80, 129, 144, 85, 80, 91, 130, 96, 2, 10, 144, 80, - 96, 0, 129, 131, 96, 1, 1, 96, 0, 80, 84, 22, 20, 21, 97, 10, 76, 87, 127, 225, 197, 45, 198, - 59, 113, 154, 222, 130, 232, 190, 169, 76, 196, 26, 13, 93, 40, 228, 170, 245, 54, 173, 181, - 233, 204, 204, 159, 248, 193, 174, 218, 51, 134, 96, 64, 81, 128, 131, 115, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 22, 129, 82, - 96, 32, 1, 130, 129, 82, 96, 32, 1, 146, 80, 80, 80, 96, 64, 81, 128, 145, 3, 144, 161, 96, 1, - 130, 96, 0, 1, 96, 0, 80, 84, 17, 21, 21, 97, 10, 31, 87, 96, 2, 96, 0, 80, 96, 1, 96, 0, 80, - 96, 0, 135, 129, 82, 96, 32, 1, 144, 129, 82, 96, 32, 1, 96, 0, 32, 96, 0, 80, 96, 2, 1, 96, 0, - 80, 84, 129, 84, 129, 16, 21, 97, 0, 2, 87, 144, 96, 0, 82, 96, 32, 96, 0, 32, 144, 1, 96, 0, - 91, 80, 96, 0, 144, 85, 96, 1, 96, 0, 80, 96, 0, 134, 129, 82, 96, 32, 1, 144, 129, 82, 96, 32, - 1, 96, 0, 32, 96, 0, 96, 0, 130, 1, 96, 0, 80, 96, 0, 144, 85, 96, 1, 130, 1, 96, 0, 80, 96, 0, - 144, 85, 96, 2, 130, 1, 96, 0, 80, 96, 0, 144, 85, 80, 80, 96, 1, 147, 80, 97, 10, 77, 86, 97, - 10, 75, 86, 91, 129, 96, 0, 1, 96, 0, 129, 129, 80, 84, 128, 146, 145, 144, 96, 1, 144, 3, 145, - 144, 80, 85, 80, 128, 130, 96, 1, 1, 96, 0, 130, 130, 130, 80, 84, 23, 146, 80, 80, 129, 144, - 85, 80, 91, 91, 91, 80, 80, 80, 145, 144, 80, 86, 91, 96, 0, 96, 0, 97, 1, 5, 96, 0, 80, 96, 0, - 132, 115, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 22, 129, 82, 96, 32, 1, 144, 129, 82, 96, 32, 1, 96, 0, 32, 96, 0, 80, 84, 17, - 144, 80, 97, 10, 146, 86, 91, 145, 144, 80, 86, 91, 96, 0, 96, 0, 96, 0, 97, 1, 5, 96, 0, 80, - 96, 0, 51, 115, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 22, 129, 82, 96, 32, 1, 144, 129, 82, 96, 32, 1, 96, 0, 32, 96, 0, 80, 84, - 146, 80, 96, 0, 131, 20, 21, 97, 10, 222, 87, 97, 11, 146, 86, 91, 130, 96, 2, 10, 145, 80, 96, - 1, 96, 0, 80, 96, 0, 133, 129, 82, 96, 32, 1, 144, 129, 82, 96, 32, 1, 96, 0, 32, 96, 0, 80, - 144, 80, 96, 0, 130, 130, 96, 1, 1, 96, 0, 80, 84, 22, 17, 21, 97, 11, 145, 87, 128, 96, 0, 1, - 96, 0, 129, 129, 80, 84, 128, 146, 145, 144, 96, 1, 1, 145, 144, 80, 85, 80, 129, 129, 96, 1, - 1, 96, 0, 130, 130, 130, 80, 84, 3, 146, 80, 80, 129, 144, 85, 80, 127, 199, 251, 100, 126, 89, - 177, 128, 71, 48, 154, 161, 90, 173, 65, 142, 93, 124, 169, 109, 23, 58, 215, 4, 241, 3, 26, - 44, 61, 117, 145, 115, 75, 51, 133, 96, 64, 81, 128, 131, 115, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 22, 129, 82, 96, 32, 1, - 130, 129, 82, 96, 32, 1, 146, 80, 80, 80, 96, 64, 81, 128, 145, 3, 144, 161, 91, 91, 80, 80, - 80, 80, 86, 91, 96, 0, 96, 0, 96, 0, 96, 0, 96, 1, 96, 0, 80, 96, 0, 135, 129, 82, 96, 32, 1, - 144, 129, 82, 96, 32, 1, 96, 0, 32, 96, 0, 80, 146, 80, 97, 1, 5, 96, 0, 80, 96, 0, 134, 115, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 22, 129, 82, 96, 32, 1, 144, 129, 82, 96, 32, 1, 96, 0, 32, 96, 0, 80, 84, 145, 80, 96, 0, - 130, 20, 21, 97, 12, 0, 87, 96, 0, 147, 80, 97, 12, 27, 86, 91, 129, 96, 2, 10, 144, 80, 96, 0, - 129, 132, 96, 1, 1, 96, 0, 80, 84, 22, 20, 21, 147, 80, 97, 12, 27, 86, 91, 80, 80, 80, 146, - 145, 80, 80, 86, 91, 96, 0, 96, 1, 144, 80, 91, 96, 4, 96, 0, 80, 84, 129, 16, 21, 97, 13, 183, - 87, 91, 96, 4, 96, 0, 80, 84, 162, 204, 4, 9, 11, 164, 59, 116, 0, 1, 95, 144, 19, 101, 206, - 242, 247, 156, 10, 192, 230, 15, 5, 241, 15, 148, 34, 82, 122, 203, 71, 216, 2, 222, 91, 52, - 134, 187, 192, 160, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 76, - 252, 103, 100, 56, 10, 30, 241, 60, 95, 234, 185, 56, 72, 226, 247, 111, 47, 43, 90, 188, 185, - 238, 57, 101, 222, 47, 38, 74, 127, 243, 187, 155, 34, 219, 154, 113, 209, 201, 231, 30, 167, - 120, 37, 239, 30, 251, 109, 121, 37, 42, 223, 29, 110, 44, 120, 246, 18, 143, 146, 165, 75, 52, - 166, 76, 4, 3, 29, 124, 11, 164, 59, 116, 0, 1, 95, 144, 33, 54, 229, 192, 42, 5, 30, 155, 226, - 195, 198, 192, 127, 1, 144, 32, 2, 81, 147, 14, 34, 213, 222, 149, 67, 236, 184, 0, 1, 21, 196, - 181, 100, 146, 124, 22, 20, 40, 7, 40, 196, 132, 166, 157, 164, 52, 188, 55, 112, 87, 153, 146, - 185, 189, 73, 187, 125, 223, 189, 171, 137, 84, 159, 118, 67, 223, 72, 133, 206, 207, 156, 44, - 0, 155, 27, 193, 205, 130, 81, 246, 17, 99, 244, 71, 250, 163, 63, 234, 25, 244, 122, 73, 114, - 164, 204, 4, 60, 48, 11, 164, 59, 116, 0, 3, 208, 144, 90, 0, 75, 76, 190, 203, 76, 72, 174, 6, - 219, 13, 46, 249, 181, 208, 86, 205, 182, 199, 9, 194, 7, 190, 200, 132, 26, 32, 0, 0, 35, 33, - 118, 74, 2, 2, 200, 231, 250, 109, 153, 45, 147, 33, 204, 27, 55, 36, 73, 139, 135, 81, 230, - 20, 7, 22, 180, 5, 42, 27, 182, 180, 45, 204, 222, 29, 202, 231, 70, 45, 10, 144, 146, 93, 125, - 46, 254, 129, 164, 22, 16, 241, 123, 48, 12, 29, 229, 197, 101, 255, 27, 164, 56, 8, 166, 76, - 4, 3, 29, 125, 11, 164, 59, 116, 0, 1, 95, 144, 159, 212, 224, 13, 70, 38, 118, 186, 42, 151, - 52, 208, 156, 143, 70, 190, 38, 44, 83, 99, 16, 200, 106, 240, 61, 44, 128, 0, 1, 82, 16, 211, - 133, 108, 134, 233, 188, 92, 68, 124, 33, 15, 63, 141, 118, 5, 21, 176, 0, 183, 83, 70, 209, - 247, 91, 193, 27, 217, 189, 50, 135, 64, 232, 206, 50, 185, 77, 147, 40, 243, 37, 78, 50, 222, - 150, 66, 154, 122, 159, 214, 11, 217, 210, 245, 218, 163, 32, 2, 87, 214, 220, 141, 17, 166, - 76, 4, 3, 29, 126, 11, 164, 59, 116, 0, 1, 95, 144, 82, 25, 18, 118, 88, 144, 105, 247, 15, 14, - 70, 188, 204, 113, 141, 121, 61, 26, 161, 208, 36, 90, 104, 143, 199, 35, 248, 0, 1, 165, 5, - 177, 200, 42, 248, 213, 78, 131, 158, 107, 62, 45, 96, 31, 255, 180, 131, 27, 163, 32, 138, - 250, 107, 54, 4, 206, 208, 71, 211, 39, 138, 239, 163, 221, 147, 111, 113, 132, 209, 94, 96, - 171, 236, 244, 243, 3, 6, 106, 173, 2, 91, 65, 208, 100, 168, 154, 35, 51, 124, 201, 68, 56, - 92, 160, 76, 4, 11, 164, 59, 116, 0, 1, 95, 144, 53, 55, 101, 132, 13, 152, 148, 61, 17, 105, - 18, 153, 126, 48, 57, 175, 177, 106, 14, 165, 1, 45, 251, 12, 181, 232, 128, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 163, 161, 30, 212, 248, 163, 75, 79, 133, 121, - 13, 131, 16, 59, 249, 107, 60, 133, 15, 79, 230, 0, 130, 231, 41, 209, 50, 52, 86, 236, 214, - 68, 255, 120, 77, 30, 88, 164, 212, 94, 146, 30, 133, 123, 225, 23, 35, 42, 26, 151, 184, 46, - 146, 87, 23, 109, 48, 158, 192, 249, 232, 0, 169, 31, 162, 76, 4, 5, 11, 164, 59, 116, 0, 1, - 216, 168, 50, 164, 74, 13, 121, 102, 68, 132, 6, 149, 221, 79, 209, 204, 55, 197, 104, 117, - 152, 74, 2, 173, 218, 32, 180, 147, 204, 154, 1, 216, 31, 46, 234, 123, 196, 132, 88, 71, 4, - 177, 88, 199, 147, 159, 102, 15, 243, 221, 163, 143, 31, 233, 11, 250, 109, 183, 72, 175, 90, - 217, 65, 54, 67, 177, 73, 228, 123, 157, 215, 7, 196, 116, 190, 38, 104, 213, 19, 195, 193, 93, - 229, 89, 179, 161, 251, 187, 235, 57, 166, 34, 58, 163, 64, 166, 76, 4, 3, 29, 127, 11, 164, - 59, 116, 0, 1, 95, 144, 252, 139, 196, 237, 134, 223, 115, 0, 28, 230, 8, 47, 61, 185, 20, 215, - 199, 169, 166, 204, 33, 5, 31, 17, 237, 209, 88, 0, 1, 51, 53, 13, 156, 182, 18, 75, 161, 110, - 135, 184, 102, 156, 16, 111, 10, 203, 169, 73, 78, 99, 124, 34, 215, 154, 165, 200, 127, 156, - 216, 98, 71, 16, 59, 20, 115, 148, 126, 201, 157, 107, 5, 74, 54, 225, 127, 192, 51, 127, 145, - 169, 90, 172, 22, 159, 196, 86, 239, 246, 245, 63, 245, 90, 115, 166, 76, 4, 3, 29, 128, 11, - 164, 59, 116, 0, 1, 95, 144, 178, 143, 231, 185, 129, 89, 130, 255, 32, 63, 117, 28, 229, 80, - 17, 29, 117, 45, 135, 149, 40, 68, 156, 229, 161, 60, 196, 0, 1, 226, 210, 220, 220, 12, 244, - 96, 27, 10, 169, 193, 157, 148, 61, 47, 125, 227, 73, 117, 191, 212, 85, 153, 80, 176, 187, 85, - 222, 181, 54, 76, 154, 119, 197, 30, 42, 84, 47, 141, 50, 246, 208, 43, 165, 250, 168, 195, - 131, 253, 105, 212, 213, 176, 210, 105, 43, 156, 78, 232, 64, 224, 237, 234, 32, 166, 76, 4, 3, - 29, 129, 11, 164, 59, 116, 0, 1, 95, 144, 66, 135, 122, 235, 124, 22, 202, 205, 219, 235, 8, - 43, 103, 243, 123, 202, 85, 72, 219, 245, 38, 211, 145, 139, 17, 16, 184, 0, 0, 34, 88, 151, - 108, 146, 248, 21, 1, 244, 141, 1, 122, 238, 202, 187, 96, 216, 252, 129, 87, 200, 126, 95, - 103, 164, 74, 83, 60, 219, 76, 209, 195, 156, 59, 83, 179, 32, 176, 136, 218, 53, 198, 196, - 159, 157, 170, 37, 107, 10, 73, 153, 238, 165, 177, 241, 70, 134, 183, 55, 227, 227, 91, 166, - 0, 162, 204, 4, 11, 11, 164, 59, 116, 0, 1, 95, 144, 232, 166, 197, 156, 80, 238, 171, 90, 102, - 201, 6, 251, 175, 69, 168, 94, 119, 18, 140, 194, 2, 181, 227, 175, 22, 177, 136, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 57, 100, 0, 189, 144, 195, 159, 229, - 37, 166, 238, 216, 132, 104, 15, 199, 12, 20, 211, 237, 184, 152, 174, 205, 1, 37, 118, 174, - 204, 10, 31, 95, 87, 212, 221, 6, 89, 231, 102, 6, 74, 8, 236, 224, 204, 7, 43, 191, 122, 61, - 33, 212, 94, 188, 93, 59, 32, 192, 50, 187, 228, 6, 78, 45, 162, 200, 4, 2, 11, 164, 59, 116, - 0, 86, 34, 251, 177, 183, 60, 79, 11, 218, 79, 103, 220, 162, 102, 206, 110, 244, 47, 82, 15, - 187, 152, 9, 31, 111, 37, 204, 54, 211, 128, 0, 0, 168, 14, 51, 105, 135, 245, 16, 125, 161, - 38, 2, 206, 123, 54, 211, 248, 228, 185, 232, 76, 92, 184, 98, 213, 171, 17, 96, 181, 170, 204, - 226, 87, 48, 160, 15, 75, 241, 212, 6, 107, 46, 143, 141, 193, 232, 106, 243, 175, 211, 49, - 226, 92, 131, 62, 153, 194, 220, 120, 170, 1, 222, 120, 136, 48, 162, 12, 0, 89, 46, 144, 237, - 208, 0, 18, 79, 128, 96, 96, 96, 64, 82, 96, 0, 128, 84, 96, 1, 96, 160, 96, 2, 10, 3, 25, 22, - 51, 23, 129, 85, 96, 1, 129, 144, 85, 96, 6, 129, 144, 85, 96, 7, 85, 96, 10, 128, 84, 97, 255, - 255, 25, 22, 144, 85, 97, 7, 144, 128, 97, 0, 60, 96, 0, 57, 96, 0, 243, 96, 96, 96, 64, 82, - 54, 21, 97, 0, 229, 87, 96, 224, 96, 2, 10, 96, 0, 53, 4, 99, 2, 208, 93, 63, 129, 20, 97, 0, - 231, 87, 128, 99, 15, 141, 67, 203, 20, 97, 0, 249, 87, 128, 99, 20, 19, 71, 148, 20, 97, 1, 8, - 87, 128, 99, 27, 250, 184, 198, 20, 97, 1, 223, 87, 128, 99, 40, 216, 250, 231, 20, 97, 2, 71, - 87, 128, 99, 44, 161, 81, 34, 20, 97, 2, 80, 87, 128, 99, 49, 4, 64, 55, 20, 97, 3, 80, 87, - 128, 99, 66, 173, 23, 33, 20, 88, 39, 33, 196, 71, 109, 8, 26, 19, 124, 231, 65, 6, 187, 4, - 165, 46, 162, 72, 0, 12, 11, 164, 59, 116, 0, 117, 48, 21, 108, 55, 143, 147, 47, 94, 235, 149, - 239, 218, 186, 72, 11, 204, 71, 154, 246, 21, 136, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 1, 250, 219, 76, 179, 231, 150, 162, 223, 54, 143, 26, 200, 234, 247, 117, 171, - 127, 36, 202, 122, 207, 172, 134, 252, 21, 186, 42, 48, 136, 33, 43, 166, 41, 236, 68, 90, 99, - 36, 244, 118, 204, 56, 219, 87, 98, 16, 223, 242, 128, 117, 185, 10, 228, 46, 136, 37, 105, 53, - 58, 168, 16, 231, 35, 25, 166, 204, 4, 2, 195, 98, 11, 164, 59, 116, 0, 1, 95, 144, 43, 92, 65, - 43, 206, 84, 209, 124, 200, 5, 249, 40, 225, 223, 244, 164, 47, 243, 112, 199, 5, 55, 77, 98, - 170, 230, 255, 200, 0, 0, 71, 54, 237, 196, 96, 36, 181, 81, 229, 92, 3, 221, 247, 186, 186, - 244, 159, 133, 69, 189, 14, 208, 219, 227, 42, 219, 132, 67, 19, 27, 205, 104, 21, 77, 67, 50, - 181, 176, 113, 204, 12, 163, 240, 167, 111, 182, 159, 74, 190, 186, 96, 223, 229, 161, 22, 35, - 28, 0, 128, 102, 196, 206, 63, 125, 160, 12, 0, 11, 164, 59, 116, 0, 6, 147, 31, 96, 96, 96, - 64, 82, 96, 0, 128, 84, 96, 1, 96, 160, 96, 2, 10, 3, 25, 22, 51, 23, 144, 85, 97, 4, 58, 128, - 97, 0, 36, 96, 0, 57, 96, 0, 243, 96, 96, 96, 64, 82, 54, 21, 97, 0, 130, 87, 96, 224, 96, 2, - 10, 96, 0, 53, 4, 99, 7, 151, 60, 207, 129, 20, 97, 0, 132, 87, 128, 99, 9, 97, 120, 30, 20, - 97, 0, 141, 87, 128, 99, 30, 1, 4, 57, 20, 97, 0, 181, 87, 128, 99, 65, 62, 243, 180, 20, 97, - 0, 211, 87, 128, 99, 65, 192, 225, 181, 20, 97, 0, 241, 87, 128, 99, 88, 217, 250, 4, 20, 97, - 1, 26, 87, 128, 99, 163, 231, 108, 15, 20, 97, 1, 63, 87, 128, 99, 173, 122, 103, 47, 20, 97, - 2, 29, 87, 128, 99, 197, 126, 82, 202, 20, 97, 2, 38, 87, 128, 99, 244, 189, 255, 244, 20, 97, - 2, 76, 87, 91, 0, 91, 97, 2, 112, 96, 1, 84, 129, 86, 91, 97, 0, 130, 96, 4, 53, 96, 36, 53, - 96, 68, 53, 96, 0, 84, 96, 1, 96, 160, 96, 2, 10, 3, 144, 129, 22, 51, 144, 145, 22, 20, 97, 3, - 95, 87, 97, 0, 2, 86, 91, 97, 2, 112, 96, 4, 53, 96, 0, 129, 129, 82, 96, 3, 96, 32, 82, 96, - 64, 144, 32, 96, 1, 1, 84, 91, 145, 144, 80, 86, 91, 97, 2, 112, 96, 4, 53, 91, 96, 0, 129, - 129, 82, 96, 3, 96, 32, 82, 96, 64, 144, 32, 84, 96, 255, 22, 97, 0, 206, 86, 91, 97, 0, 130, - 96, 0, 84, 96, 1, 96, 160, 96, 2, 10, 3, 144, 129, 22, 51, 144, 145, 22, 20, 21, 97, 3, 93, 87, - 96, 0, 84, 96, 1, 96, 160, 96, 2, 10, 3, 22, 255, 91, 97, 0, 130, 96, 4, 53, 96, 36, 53, 96, 0, - 84, 96, 1, 96, 160, 96, 2, 10, 3, 144, 129, 22, 51, 144, 145, 22, 20, 97, 3, 79, 87, 97, 0, 2, - 86, 91, 97, 0, 130, 96, 2, 128, 84, 52, 1, 144, 85, 51, 96, 1, 96, 160, 96, 2, 10, 3, 22, 96, - 0, 144, 129, 82, 96, 4, 96, 32, 82, 96, 64, 129, 32, 84, 144, 129, 20, 97, 1, 215, 87, 96, 0, - 129, 129, 82, 96, 3, 96, 32, 144, 129, 82, 96, 64, 145, 130, 144, 32, 96, 1, 129, 129, 1, 128, - 84, 52, 144, 129, 1, 144, 145, 85, 96, 2, 146, 144, 146, 1, 84, 132, 81, 145, 130, 82, 146, - 129, 1, 145, 144, 145, 82, 130, 81, 132, 147, 96, 1, 96, 160, 96, 2, 10, 3, 147, 144, 147, 22, - 146, 127, 221, 144, 101, 111, 211, 252, 230, 39, 169, 241, 17, 18, 53, 118, 20, 93, 101, 21, - 142, 124, 250, 228, 185, 28, 251, 109, 214, 247, 36, 65, 130, 116, 146, 130, 144, 3, 1, 144, - 163, 91, 96, 64, 128, 81, 51, 96, 1, 96, 160, 96, 2, 10, 3, 22, 129, 82, 52, 96, 32, 130, 1, - 82, 129, 81, 127, 136, 165, 150, 109, 55, 11, 153, 25, 178, 15, 62, 44, 19, 255, 101, 112, 111, - 25, 106, 78, 50, 204, 44, 18, 191, 87, 8, 143, 136, 82, 88, 116, 146, 145, 129, 144, 3, 144, - 145, 1, 144, 161, 80, 86, 91, 97, 2, 112, 96, 2, 84, 129, 86, 91, 97, 2, 130, 96, 4, 53, 96, 0, - 129, 129, 82, 96, 3, 96, 32, 82, 96, 64, 144, 32, 96, 2, 1, 84, 96, 1, 96, 160, 96, 2, 10, 3, - 22, 97, 0, 206, 86, 91, 97, 2, 112, 96, 4, 53, 96, 1, 96, 160, 96, 2, 10, 3, 129, 22, 96, 0, - 144, 129, 82, 96, 4, 96, 32, 82, 96, 64, 144, 32, 84, 97, 0, 206, 86, 91, 96, 64, 128, 81, 145, - 130, 82, 81, 144, 129, 144, 3, 96, 32, 1, 144, 243, 91, 96, 64, 128, 81, 96, 1, 96, 160, 96, 2, - 10, 3, 146, 144, 146, 22, 130, 82, 81, 144, 129, 144, 3, 96, 32, 1, 144, 243, 91, 21, 21, 97, - 3, 88, 87, 96, 0, 130, 129, 82, 96, 3, 96, 32, 129, 129, 82, 96, 64, 128, 132, 32, 128, 84, 96, - 255, 25, 22, 96, 1, 144, 129, 23, 130, 85, 96, 2, 145, 144, 145, 1, 128, 84, 115, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 25, - 22, 135, 23, 128, 130, 85, 96, 1, 96, 160, 96, 2, 10, 3, 144, 129, 22, 135, 82, 96, 4, 133, 82, - 131, 135, 32, 137, 144, 85, 130, 84, 131, 1, 144, 146, 85, 135, 134, 82, 147, 131, 82, 146, 84, - 129, 81, 133, 129, 82, 146, 131, 1, 148, 144, 148, 82, 128, 81, 134, 148, 144, 147, 22, 146, - 127, 221, 144, 101, 111, 211, 252, 230, 39, 169, 241, 17, 18, 53, 118, 20, 93, 101, 21, 142, - 124, 250, 228, 185, 28, 251, 109, 214, 247, 36, 65, 130, 116, 146, 129, 144, 3, 144, 145, 1, - 144, 163, 80, 80, 86, 91, 97, 2, 159, 130, 97, 0, 218, 86, 91, 97, 0, 2, 86, 91, 86, 91, 97, 3, - 104, 131, 97, 0, 218, 86, 91, 21, 21, 97, 3, 115, 87, 97, 0, 2, 86, 91, 96, 0, 131, 129, 82, - 96, 3, 96, 32, 82, 96, 64, 144, 32, 96, 1, 1, 84, 129, 144, 16, 97, 3, 88, 87, 96, 64, 96, 0, - 129, 129, 32, 96, 1, 1, 128, 84, 132, 144, 3, 144, 85, 144, 81, 96, 1, 96, 160, 96, 2, 10, 3, - 132, 22, 145, 144, 131, 144, 130, 129, 129, 129, 133, 136, 131, 241, 147, 80, 80, 80, 80, 80, - 130, 96, 3, 96, 0, 80, 96, 0, 133, 129, 82, 96, 32, 1, 144, 129, 82, 96, 32, 1, 96, 0, 32, 96, - 0, 80, 96, 2, 1, 96, 0, 144, 84, 144, 97, 1, 0, 10, 144, 4, 96, 1, 96, 160, 96, 2, 10, 3, 22, - 96, 1, 96, 160, 96, 2, 10, 3, 22, 127, 221, 144, 101, 111, 211, 252, 230, 39, 169, 241, 17, 18, - 53, 118, 20, 93, 101, 21, 142, 124, 250, 228, 185, 28, 251, 109, 214, 247, 36, 65, 130, 116, - 96, 2, 132, 96, 64, 81, 128, 131, 129, 82, 96, 32, 1, 130, 129, 82, 96, 32, 1, 146, 80, 80, 80, - 96, 64, 81, 128, 145, 3, 144, 163, 97, 0, 2, 86, 1, 110, 122, 26, 229, 14, 122, 176, 242, 155, - 157, 235, 74, 62, 39, 254, 232, 106, 197, 199, 1, 243, 202, 240, 23, 0, 44, 254, 31, 63, 26, - 239, 76, 29, 63, 208, 68, 197, 74, 255, 59, 142, 245, 71, 140, 77, 241, 103, 198, 116, 110, 10, - 224, 68, 8, 221, 146, 82, 65, 161, 9, 116, 64, 13, 109, 164, 204, 0, 17, 195, 12, 27, 113, 8, - 0, 3, 208, 144, 154, 240, 153, 145, 173, 99, 129, 78, 83, 255, 193, 188, 207, 33, 62, 231, 64, - 39, 96, 139, 1, 62, 212, 134, 121, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 179, 8, 152, 132, 250, - 151, 9, 34, 230, 192, 153, 232, 24, 168, 22, 75, 208, 212, 2, 210, 0, 0, 0, 0, 0, 0, 2, 23, - 144, 85, 80, 91, 97, 2, 31, 128, 97, 0, 64, 96, 0, 57, 96, 0, 243, 0, 96, 96, 96, 64, 82, 54, - 21, 97, 0, 72, 87, 96, 0, 53, 124, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 144, 4, 128, 99, 65, 192, 225, 181, 20, 97, 0, 171, 87, 128, 99, - 229, 34, 83, 129, 20, 97, 0, 184, 87, 97, 0, 72, 86, 91, 97, 0, 169, 91, 96, 0, 52, 17, 21, 97, - 0, 166, 87, 96, 88, 51, 115, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 22, 127, 144, 137, 8, 9, 198, 84, 241, 29, 110, 114, 162, - 143, 166, 1, 73, 119, 10, 13, 17, 236, 108, 146, 49, 157, 108, 235, 43, 176, 164, 234, 26, 21, - 52, 96, 64, 81, 128, 130, 129, 82, 96, 32, 1, 145, 80, 80, 96, 64, 81, 128, 145, 3, 144, 163, - 91, 91, 86, 91, 0, 91, 97, 0, 182, 96, 4, 80, 97, 0, 197, 86, 91, 0, 91, 97, 0, 195, 96, 4, 80, - 97, 1, 89, 86, 91, 0, 91, 96, 0, 96, 0, 144, 84, 144, 97, 1, 0, 10, 144, 4, 115, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 22, 115, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 22, 51, 115, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 22, 20, 21, 97, 1, 86, 87, 96, 0, 96, 0, 144, 84, 144, 97, 1, 0, 10, - 144, 4, 115, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 22, 115, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 22, 255, 91, 91, 86, 91, 96, 0, 96, 0, 144, 84, 144, 97, 1, - 0, 10, 144, 4, 115, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 22, 115, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 22, 51, 115, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 22, 20, 21, 97, 2, 28, 87, 96, 0, - 96, 0, 144, 84, 144, 97, 1, 0, 10, 144, 4, 115, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 22, 115, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 22, 96, 0, 48, 115, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 22, 49, 96, 64, 81, 128, 144, 80, 96, 0, 96, 64, 81, 128, 131, 3, 129, 133, 136, 136, 241, 147, - 80, 80, 80, 80, 80, 91, 91, 86, 0, 74, 174, 171, 118, 216, 172, 174, 232, 97, 204, 233, 104, - 117, 10, 49, 177, 228, 143, 84, 231, 230, 104, 247, 182, 34, 202, 208, 134, 87, 205, 44, 219, - 39, 111, 207, 47, 212, 8, 232, 242, 226, 83, 201, 223, 167, 227, 28, 66, 112, 116, 217, 222, - 79, 252, 243, 67, 43, 120, 11, 64, 149, 246, 153, 60, 166, 76, 4, 2, 133, 195, 11, 164, 59, - 116, 0, 1, 95, 144, 53, 21, 159, 117, 50, 111, 231, 226, 156, 223, 197, 108, 190, 145, 118, - 175, 45, 124, 216, 211, 20, 34, 250, 235, 167, 110, 64, 0, 0, 148, 171, 64, 14, 106, 128, 188, - 50, 124, 89, 16, 117, 122, 48, 247, 58, 244, 98, 77, 101, 96, 91, 72, 79, 226, 206, 207, 99, - 17, 111, 0, 7, 106, 212, 54, 207, 2, 24, 241, 151, 133, 68, 32, 95, 162, 188, 146, 105, 99, - 175, 42, 85, 188, 24, 129, 44, 158, 212, 50, 177, 168, 216, 3, 26, 166, 72, 4, 2, 191, 19, 11, - 164, 59, 116, 0, 195, 80, 252, 27, 191, 236, 179, 98, 175, 82, 197, 127, 227, 7, 97, 153, 117, - 195, 212, 156, 189, 66, 4, 100, 167, 216, 88, 167, 81, 0, 0, 181, 232, 60, 127, 113, 207, 77, - 119, 164, 108, 152, 159, 113, 116, 134, 81, 96, 6, 36, 198, 247, 144, 157, 40, 181, 233, 137, - 41, 245, 45, 29, 64, 218, 226, 25, 172, 192, 138, 192, 150, 54, 22, 139, 73, 158, 125, 223, 94, - 11, 211, 92, 21, 161, 253, 45, 140, 46, 1, 27, 149, 44, 178, 75, 10, 166, 76, 4, 2, 133, 196, - 12, 199, 255, 57, 190, 1, 95, 144, 19, 83, 58, 239, 29, 185, 133, 147, 214, 183, 166, 5, 61, - 77, 227, 145, 90, 93, 65, 252, 20, 17, 246, 194, 92, 149, 12, 0, 0, 134, 11, 144, 171, 98, 98, - 105, 62, 107, 52, 119, 6, 2, 234, 229, 47, 72, 157, 116, 160, 255, 78, 19, 118, 39, 2, 159, - 116, 6, 251, 77, 137, 101, 156, 172, 154, 41, 248, 176, 155, 45, 50, 228, 114, 102, 129, 74, - 85, 171, 28, 135, 45, 180, 226, 96, 141, 249, 97, 172, 136, 142, 120, 47, 112, 166, 76, 4, 2, - 133, 197, 12, 199, 255, 57, 190, 1, 95, 144, 186, 76, 173, 0, 219, 226, 136, 181, 102, 244, - 131, 179, 19, 92, 111, 91, 240, 107, 210, 191, 20, 15, 94, 191, 9, 122, 104, 0, 1, 194, 238, - 81, 200, 230, 9, 252, 247, 145, 168, 208, 117, 244, 156, 103, 252, 178, 33, 225, 173, 94, 179, - 53, 151, 147, 193, 80, 226, 180, 58, 76, 78, 209, 145, 57, 77, 174, 167, 0, 82, 102, 38, 85, - 10, 105, 156, 164, 107, 230, 72, 71, 225, 227, 208, 224, 138, 126, 122, 214, 133, 83, 5, 124, - 18, 164, 204, 4, 43, 124, 12, 172, 216, 203, 150, 3, 208, 144, 168, 8, 13, 61, 85, 195, 201, - 119, 3, 9, 58, 134, 164, 204, 2, 184, 24, 54, 158, 209, 9, 193, 117, 4, 87, 197, 76, 152, 0, 0, - 208, 218, 177, 197, 174, 44, 208, 38, 106, 127, 119, 43, 85, 115, 107, 129, 186, 187, 128, 95, - 138, 51, 111, 241, 183, 225, 8, 231, 86, 221, 151, 181, 112, 219, 23, 104, 207, 126, 20, 140, - 182, 116, 223, 26, 80, 238, 63, 119, 19, 44, 33, 160, 156, 194, 0, 232, 200, 128, 112, 251, - 158, 202, 109, 1, 166, 76, 4, 2, 133, 198, 12, 167, 70, 233, 205, 1, 95, 144, 223, 136, 210, - 207, 69, 14, 17, 52, 224, 205, 121, 76, 59, 137, 214, 72, 195, 38, 159, 252, 20, 14, 186, 18, - 184, 126, 192, 0, 1, 36, 213, 210, 255, 189, 248, 53, 230, 252, 135, 199, 87, 28, 150, 135, 80, - 7, 105, 243, 136, 65, 211, 41, 81, 158, 251, 177, 104, 134, 61, 23, 0, 234, 163, 52, 57, 240, - 81, 230, 91, 4, 148, 216, 124, 218, 140, 150, 128, 49, 149, 121, 242, 78, 74, 127, 189, 39, - 146, 186, 55, 175, 31, 98, 37, 166, 76, 4, 2, 133, 199, 12, 167, 70, 233, 205, 1, 95, 144, 71, - 186, 228, 121, 95, 164, 167, 74, 171, 149, 222, 193, 69, 124, 86, 234, 236, 162, 216, 176, 20, - 3, 53, 223, 144, 21, 136, 0, 1, 28, 80, 58, 62, 128, 253, 40, 187, 121, 220, 188, 231, 254, - 125, 207, 79, 36, 216, 97, 93, 118, 240, 248, 245, 152, 65, 226, 222, 154, 2, 8, 168, 218, 186, - 78, 170, 203, 8, 227, 42, 2, 195, 80, 140, 97, 239, 152, 68, 180, 100, 187, 95, 163, 3, 84, - 207, 116, 115, 253, 202, 2, 186, 13, 31, 166, 76, 4, 2, 133, 200, 12, 167, 70, 233, 205, 1, 95, - 144, 156, 199, 46, 191, 61, 170, 241, 44, 114, 228, 134, 5, 225, 230, 123, 71, 201, 90, 25, 17, - 19, 233, 168, 103, 200, 232, 160, 0, 0, 163, 93, 164, 2, 245, 63, 19, 157, 128, 90, 149, 14, - 43, 124, 38, 25, 68, 164, 100, 110, 54, 58, 222, 9, 155, 244, 210, 178, 94, 218, 168, 183, 157, - 205, 214, 186, 136, 105, 9, 131, 96, 19, 210, 56, 13, 75, 115, 167, 199, 1, 167, 247, 0, 192, - 17, 153, 174, 114, 248, 184, 19, 176, 12, 125, 164, 72, 3, 20, 39, 12, 160, 214, 113, 224, 82, - 8, 20, 88, 196, 59, 8, 88, 167, 3, 197, 123, 116, 50, 190, 38, 96, 156, 71, 162, 11, 164, 59, - 116, 0, 1, 95, 144, 189, 112, 29, 0, 82, 60, 70, 140, 148, 179, 185, 24, 201, 66, 191, 162, 70, - 159, 249, 46, 14, 173, 49, 225, 3, 157, 164, 0, 0, 154, 85, 2, 108, 161, 24, 116, 201, 84, 214, - 11, 15, 190, 69, 21, 138, 220, 89, 140, 69, 169, 90, 85, 43, 5, 135, 21, 142, 228, 232, 112, - 202, 82, 220, 187, 53, 162, 56, 83, 166, 203, 71, 107, 214, 17, 33, 42, 145, 58, 236, 225, 193, - 243, 203, 13, 94, 37, 41, 250, 103, 107, 44, 140, 6, 166, 76, 4, 2, 71, 163, 11, 164, 59, 116, - 0, 1, 95, 144, 127, 209, 192, 89, 127, 125, 235, 71, 78, 216, 219, 246, 107, 71, 145, 32, 210, - 56, 155, 88, 14, 137, 165, 217, 13, 229, 200, 0, 1, 235, 236, 80, 45, 76, 174, 161, 80, 209, - 90, 174, 16, 235, 182, 100, 72, 147, 247, 188, 159, 40, 149, 221, 234, 191, 89, 28, 61, 105, - 178, 120, 124, 59, 17, 5, 236, 199, 237, 56, 236, 127, 238, 105, 17, 94, 49, 166, 57, 110, 12, - 222, 196, 112, 47, 148, 38, 192, 123, 107, 177, 169, 43, 181, 104, 164, 204, 4, 32, 44, 11, - 164, 59, 116, 0, 3, 208, 144, 181, 96, 100, 105, 243, 23, 1, 141, 33, 245, 4, 182, 225, 81, - 142, 84, 178, 63, 167, 97, 2, 181, 227, 175, 22, 177, 136, 0, 0, 0, 23, 193, 145, 126, 117, 19, - 221, 35, 114, 255, 169, 72, 124, 219, 5, 66, 59, 17, 180, 3, 101, 95, 0, 79, 168, 125, 26, 187, - 167, 75, 235, 206, 223, 241, 130, 210, 168, 175, 181, 146, 125, 44, 175, 40, 80, 3, 13, 153, - 221, 194, 221, 95, 252, 208, 43, 54, 252, 105, 247, 248, 7, 155, 96, 51, 166, 76, 4, 2, 71, - 164, 11, 164, 59, 116, 0, 1, 95, 144, 221, 116, 87, 100, 158, 226, 239, 221, 60, 103, 73, 225, - 224, 47, 56, 78, 99, 48, 220, 185, 14, 131, 64, 58, 218, 155, 160, 0, 0, 109, 137, 95, 36, 182, - 253, 255, 159, 255, 65, 224, 231, 142, 167, 98, 108, 247, 181, 98, 123, 127, 28, 206, 51, 91, - 159, 169, 129, 35, 115, 222, 0, 151, 54, 4, 66, 65, 17, 4, 252, 250, 17, 82, 177, 84, 212, 66, - 220, 78, 186, 49, 199, 57, 91, 3, 249, 53, 150, 176, 238, 111, 57, 156, 60, 166, 76, 4, 2, 71, - 165, 11, 164, 59, 116, 0, 1, 95, 144, 122, 43, 174, 155, 33, 14, 173, 22, 251, 220, 63, 239, - 224, 229, 153, 82, 182, 19, 85, 103, 14, 110, 226, 101, 155, 164, 180, 0, 0, 69, 109, 185, 152, - 14, 197, 52, 103, 197, 127, 115, 159, 113, 22, 192, 239, 178, 93, 177, 170, 226, 19, 99, 159, - 91, 143, 140, 92, 144, 153, 179, 3, 200, 201, 219, 177, 223, 140, 17, 197, 5, 88, 162, 60, 12, - 98, 117, 31, 31, 55, 216, 105, 151, 242, 109, 195, 230, 57, 65, 63, 58, 18, 68, 54, 166, 76, 4, - 2, 71, 166, 11, 164, 59, 116, 0, 1, 95, 144, 107, 93, 169, 89, 120, 109, 128, 28, 27, 237, 218, - 88, 248, 160, 113, 164, 15, 153, 47, 3, 14, 107, 206, 219, 57, 175, 152, 0, 1, 27, 18, 61, 222, - 93, 27, 187, 165, 238, 0, 228, 168, 23, 164, 147, 62, 53, 90, 145, 1, 213, 114, 102, 13, 207, - 44, 178, 147, 52, 10, 89, 8, 126, 1, 64, 25, 124, 51, 5, 217, 29, 14, 172, 242, 79, 11, 173, - 220, 158, 105, 36, 166, 99, 182, 25, 191, 191, 83, 119, 184, 11, 42, 111, 96, 166, 76, 4, 2, - 71, 167, 11, 164, 59, 116, 0, 1, 95, 144, 24, 98, 237, 173, 13, 108, 222, 118, 136, 144, 27, - 182, 250, 194, 228, 44, 10, 210, 109, 198, 14, 96, 19, 89, 162, 211, 124, 0, 1, 94, 197, 115, - 136, 185, 25, 84, 107, 242, 32, 66, 55, 166, 187, 109, 112, 51, 127, 167, 109, 76, 111, 21, - 205, 68, 8, 184, 16, 241, 162, 230, 1, 29, 210, 191, 81, 153, 19, 93, 239, 251, 89, 170, 6, 46, - 76, 201, 241, 43, 116, 197, 126, 164, 141, 175, 119, 90, 83, 176, 82, 176, 158, 225, 71, 166, - 76, 4, 2, 71, 168, 11, 164, 59, 116, 0, 1, 95, 144, 110, 28, 99, 103, 18, 103, 114, 13, 29, 97, - 89, 96, 118, 162, 195, 181, 165, 191, 50, 174, 14, 82, 244, 87, 52, 106, 160, 0, 0, 11, 16, - 200, 232, 27, 202, 186, 172, 84, 227, 252, 127, 220, 44, 101, 59, 79, 97, 166, 187, 216, 65, - 79, 33, 54, 113, 82, 40, 108, 101, 232, 120, 6, 230, 71, 5, 40, 125, 23, 36, 83, 133, 173, 28, - 39, 187, 87, 46, 39, 78, 18, 239, 54, 74, 165, 200, 184, 168, 61, 41, 117, 146, 238, 6, 166, - 76, 4, 2, 71, 169, 11, 164, 59, 116, 0, 1, 95, 144, 58, 80, 196, 186, 255, 178, 184, 82, 85, - 23, 240, 94, 245, 195, 43, 155, 206, 66, 24, 207, 14, 81, 180, 216, 32, 192, 220, 0, 0, 39, - 139, 5, 230, 78, 213, 207, 243, 106, 118, 177, 162, 186, 96, 53, 251, 70, 106, 162, 136, 129, - 99, 97, 169, 29, 117, 81, 232, 212, 186, 207, 70, 135, 27, 219, 18, 2, 113, 153, 121, 194, 82, - 29, 188, 55, 78, 227, 35, 160, 91, 136, 199, 95, 84, 41, 57, 251, 14, 214, 118, 200, 111, 173, - 29, 162, 72, 4, 30, 11, 164, 59, 116, 0, 86, 34, 251, 177, 183, 60, 79, 11, 218, 79, 103, 220, - 162, 102, 206, 110, 244, 47, 82, 15, 187, 152, 27, 197, 54, 90, 23, 47, 132, 0, 1, 73, 218, - 222, 37, 60, 11, 229, 55, 40, 101, 185, 17, 87, 15, 247, 202, 3, 151, 230, 115, 78, 188, 2, 17, - 238, 226, 227, 197, 67, 160, 49, 57, 211, 222, 249, 157, 126, 147, 15, 132, 156, 123, 202, 189, - 125, 89, 9, 199, 86, 7, 240, 239, 90, 73, 216, 254, 84, 86, 186, 166, 232, 22, 144, 36, 166, - 76, 4, 2, 71, 170, 11, 164, 59, 116, 0, 1, 95, 144, 166, 104, 97, 67, 192, 118, 115, 25, 106, - 35, 218, 146, 153, 246, 252, 186, 180, 195, 202, 111, 14, 74, 92, 193, 71, 42, 164, 0, 0, 222, - 20, 222, 218, 43, 136, 132, 42, 147, 2, 195, 161, 166, 226, 4, 215, 128, 203, 171, 131, 150, - 79, 32, 249, 253, 134, 91, 162, 220, 141, 153, 162, 202, 178, 49, 124, 159, 201, 122, 164, 3, - 162, 96, 2, 37, 220, 143, 134, 228, 89, 206, 211, 20, 183, 212, 13, 89, 186, 209, 121, 45, 85, - 139, 113, 166, 76, 4, 2, 71, 171, 11, 164, 59, 116, 0, 1, 95, 144, 165, 251, 62, 67, 153, 232, - 241, 134, 67, 249, 105, 208, 206, 101, 204, 53, 47, 194, 82, 198, 126, 2, 23, 72, 194, 118, - 240, 0, 1, 255, 146, 144, 129, 195, 33, 203, 221, 24, 103, 106, 156, 226, 135, 182, 150, 183, - 18, 242, 208, 29, 122, 211, 252, 132, 60, 238, 168, 149, 101, 180, 147, 53, 35, 129, 77, 108, - 227, 60, 11, 150, 229, 79, 63, 193, 218, 192, 197, 236, 53, 107, 151, 195, 92, 125, 190, 167, - 77, 245, 169, 124, 169, 127, 67, 166, 76, 4, 2, 71, 172, 11, 164, 59, 116, 0, 1, 95, 144, 50, - 155, 141, 174, 71, 112, 4, 98, 5, 76, 219, 210, 112, 214, 58, 135, 95, 95, 173, 144, 99, 211, - 101, 46, 114, 56, 48, 0, 0, 162, 195, 99, 205, 231, 184, 75, 198, 94, 10, 146, 172, 99, 55, - 184, 35, 232, 23, 40, 158, 128, 114, 144, 163, 231, 71, 198, 107, 245, 39, 46, 114, 11, 25, - 242, 147, 208, 32, 215, 3, 183, 230, 90, 174, 23, 135, 93, 74, 69, 251, 24, 148, 97, 181, 138, - 125, 136, 157, 197, 171, 99, 7, 138, 105, 166, 76, 4, 2, 71, 173, 11, 164, 59, 116, 0, 1, 95, - 144, 112, 120, 104, 234, 59, 251, 115, 0, 113, 6, 207, 211, 15, 103, 143, 219, 148, 209, 33, - 115, 52, 232, 74, 128, 108, 40, 176, 0, 1, 231, 251, 106, 180, 129, 43, 201, 61, 163, 47, 235, - 0, 22, 21, 178, 236, 107, 220, 215, 28, 145, 142, 17, 147, 76, 118, 77, 198, 201, 209, 216, - 168, 99, 38, 228, 161, 122, 12, 205, 94, 4, 53, 227, 137, 108, 250, 131, 91, 84, 255, 111, 220, - 159, 207, 42, 253, 221, 33, 241, 74, 52, 226, 205, 52, 164, 76, 0, 10, 249, 11, 164, 59, 116, - 0, 1, 212, 192, 17, 88, 195, 201, 167, 14, 133, 66, 248, 165, 234, 135, 94, 4, 81, 153, 188, - 246, 174, 138, 239, 164, 177, 180, 236, 27, 35, 165, 55, 85, 119, 86, 11, 94, 16, 64, 132, 186, - 67, 194, 14, 164, 135, 92, 130, 193, 138, 87, 63, 157, 210, 121, 164, 76, 0, 1, 151, 11, 164, - 59, 116, 0, 1, 95, 144, 51, 153, 1, 34, 99, 139, 145, 50, 202, 41, 199, 35, 189, 240, 55, 241, - 168, 145, 167, 12, 67, 44, 237, 4, 98, 101, 116, 67, 111, 105, 110, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 101, 93, 124, 191, 199, 213, 189, 39, 59, - 121, 28, 11, 109, 61, 197, 44, 9, 194, 47, 238, 48, 154, 57, 81, 245, 185, 153, 129, 29, 181, - 92, 31, 232, 126, 92, 179, 242, 76, 58, 176, 25, 105, 21, 163, 145, 48, 71, 2, 67, 92, 38, 177, - 174, 101, 173, 52, 136, 204, 40, 70, 41, 165, 95, 2, 166, 76, 4, 2, 24, 98, 11, 164, 59, 116, - 0, 1, 95, 144, 57, 102, 203, 113, 37, 118, 91, 237, 36, 50, 131, 11, 101, 218, 68, 173, 205, - 116, 121, 238, 17, 31, 136, 112, 146, 152, 176, 0, 1, 79, 72, 189, 62, 91, 89, 177, 151, 175, - 55, 203, 46, 57, 212, 130, 167, 123, 116, 72, 201, 51, 5, 235, 132, 142, 222, 8, 203, 4, 240, - 176, 139, 112, 253, 215, 168, 181, 48, 96, 76, 97, 55, 30, 37, 61, 164, 212, 112, 67, 33, 227, - 109, 209, 82, 14, 180, 98, 12, 185, 158, 78, 124, 42, 82, 166, 76, 4, 2, 24, 99, 11, 164, 59, - 116, 0, 1, 95, 144, 241, 103, 142, 36, 240, 94, 215, 162, 167, 173, 237, 141, 198, 110, 225, - 166, 97, 50, 90, 16, 16, 77, 35, 223, 89, 130, 240, 0, 0, 199, 206, 7, 22, 136, 19, 202, 27, - 246, 85, 76, 31, 179, 219, 26, 136, 192, 251, 202, 243, 119, 99, 180, 112, 191, 19, 172, 130, - 212, 225, 69, 196, 163, 101, 47, 51, 252, 219, 251, 123, 193, 45, 102, 166, 2, 143, 214, 156, - 155, 186, 32, 230, 32, 121, 36, 135, 4, 120, 38, 176, 46, 147, 36, 84, 162, 76, 0, 15, 11, 164, - 59, 116, 0, 2, 76, 37, 62, 231, 25, 47, 6, 159, 58, 177, 96, 119, 248, 67, 242, 244, 105, 189, - 50, 126, 32, 108, 169, 5, 156, 187, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 81, 216, 120, 45, 130, - 37, 132, 65, 7, 142, 87, 20, 29, 170, 143, 253, 218, 248, 245, 125, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 11, 214, 1, 182, 134, 241, 167, - 82, 167, 204, 82, 67, 217, 74, 71, 237, 77, 52, 53, 1, 30, 88, 94, 64, 250, 21, 83, 28, 65, - 191, 55, 163, 61, 167, 139, 179, 78, 112, 134, 228, 177, 179, 145, 48, 158, 83, 155, 34, 205, - 213, 223, 203, 47, 52, 78, 39, 170, 58, 216, 192, 141, 80, 159, 148, 92, 241, 3, 166, 76, 4, 1, - 32, 112, 11, 164, 59, 116, 0, 1, 95, 144, 191, 221, 152, 32, 160, 66, 131, 178, 137, 149, 121, - 86, 53, 59, 95, 60, 31, 102, 94, 207, 14, 67, 120, 71, 220, 6, 12, 128, 0, 192, 27, 239, 13, - 182, 127, 244, 156, 208, 132, 229, 105, 118, 160, 107, 144, 73, 253, 181, 31, 121, 210, 8, 36, - 135, 65, 0, 219, 210, 44, 126, 236, 227, 29, 211, 235, 20, 161, 140, 250, 139, 82, 73, 100, - 121, 126, 69, 233, 229, 164, 33, 4, 206, 231, 40, 189, 33, 14, 224, 231, 161, 240, 80, 23, 166, - 76, 4, 1, 32, 113, 11, 164, 59, 116, 0, 1, 95, 144, 226, 90, 188, 255, 54, 141, 55, 253, 222, - 220, 215, 247, 61, 224, 190, 160, 54, 42, 5, 127, 139, 42, 77, 180, 251, 86, 0, 0, 0, 77, 53, - 141, 71, 161, 163, 141, 166, 157, 102, 78, 254, 122, 45, 239, 91, 228, 204, 33, 70, 150, 87, - 36, 126, 132, 103, 59, 164, 62, 77, 234, 167, 143, 134, 203, 255, 246, 181, 240, 59, 157, 122, - 167, 188, 210, 250, 186, 165, 215, 102, 56, 245, 42, 123, 188, 58, 141, 47, 16, 229, 137, 230, - 151, 63, 166, 76, 4, 1, 32, 114, 11, 164, 59, 116, 0, 1, 95, 144, 230, 109, 192, 150, 19, 208, - 242, 195, 13, 93, 171, 82, 29, 162, 40, 110, 95, 199, 80, 36, 8, 97, 96, 135, 120, 84, 80, 0, - 0, 81, 10, 94, 21, 194, 4, 169, 12, 75, 130, 176, 221, 116, 34, 135, 115, 175, 235, 151, 196, - 241, 96, 211, 121, 222, 14, 147, 135, 158, 129, 164, 168, 171, 120, 230, 234, 111, 118, 220, - 16, 240, 77, 26, 210, 9, 0, 162, 145, 137, 209, 243, 213, 105, 248, 247, 12, 140, 224, 98, 33, - 95, 163, 11, 52, 166, 76, 4, 1, 32, 115, 11, 164, 59, 116, 0, 1, 95, 144, 241, 20, 85, 176, - 234, 89, 91, 108, 181, 1, 188, 242, 200, 62, 28, 190, 99, 50, 60, 170, 3, 117, 176, 44, 154, - 35, 136, 32, 1, 236, 220, 248, 194, 204, 65, 255, 146, 157, 226, 146, 48, 255, 167, 106, 143, - 7, 212, 63, 166, 189, 189, 71, 216, 216, 107, 178, 59, 186, 245, 230, 68, 89, 158, 101, 130, 7, - 114, 13, 129, 176, 213, 136, 17, 96, 127, 1, 173, 143, 105, 187, 52, 76, 161, 84, 122, 77, 92, - 8, 187, 205, 68, 147, 222, 162, 76, 4, 10, 11, 164, 59, 116, 0, 45, 198, 192, 41, 53, 170, 10, - 45, 47, 187, 121, 22, 34, 194, 158, 177, 193, 23, 182, 91, 122, 144, 133, 29, 149, 7, 104, 229, - 208, 43, 241, 63, 136, 127, 173, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 135, 138, 206, 66, 9, 43, - 127, 26, 225, 242, 141, 22, 193, 39, 43, 26, 168, 12, 164, 103, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 21, 142, 70, 9, 19, 208, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 227, 33, 60, 71, - 48, 226, 247, 30, 47, 3, 130, 151, 209, 48, 255, 255, 41, 123, 203, 234, 212, 3, 252, 170, 41, - 149, 33, 227, 121, 176, 150, 182, 80, 120, 204, 42, 93, 124, 38, 133, 104, 26, 128, 130, 245, - 245, 54, 65, 151, 133, 60, 6, 254, 218, 22, 34, 189, 174, 135, 139, 142, 107, 182, 43, 166, 76, - 4, 2, 24, 100, 11, 164, 59, 116, 0, 1, 95, 144, 183, 53, 120, 36, 50, 112, 73, 33, 34, 160, - 154, 82, 24, 173, 89, 219, 223, 139, 122, 181, 16, 70, 141, 31, 163, 79, 160, 0, 1, 164, 215, - 144, 42, 193, 162, 104, 187, 110, 177, 34, 91, 217, 201, 70, 196, 47, 145, 61, 44, 193, 67, 7, - 81, 221, 254, 177, 172, 10, 52, 203, 106, 29, 7, 246, 66, 82, 175, 130, 44, 71, 202, 208, 111, - 207, 46, 29, 100, 201, 173, 75, 51, 118, 15, 163, 98, 84, 150, 177, 158, 62, 223, 229, 88, 162, - 72, 4, 15, 11, 164, 59, 116, 0, 86, 34, 251, 177, 183, 60, 79, 11, 218, 79, 103, 220, 162, 102, - 206, 110, 244, 47, 82, 15, 187, 152, 17, 27, 205, 120, 101, 149, 16, 0, 0, 250, 157, 235, 25, - 211, 50, 187, 144, 179, 43, 56, 9, 62, 14, 252, 195, 51, 1, 131, 154, 195, 204, 126, 190, 202, - 142, 122, 249, 204, 95, 129, 67, 88, 75, 235, 155, 30, 115, 140, 50, 57, 19, 22, 149, 135, 174, - 79, 165, 209, 218, 56, 182, 165, 223, 82, 225, 101, 144, 245, 120, 21, 90, 211, 31, 164, 204, - 0, 28, 215, 12, 27, 113, 8, 0, 3, 208, 144, 154, 240, 153, 145, 173, 99, 129, 78, 83, 255, 193, - 188, 207, 33, 62, 231, 64, 39, 96, 139, 1, 62, 212, 134, 121, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 156, 78, 168, 210, 93, 97, 80, 168, 237, 40, 72, 252, 116, 81, 88, 170, 217, 38, 191, 141, - 0, 0, 0, 0, 0, 0, 0, 56, 75, 2, 158, 134, 139, 89, 157, 110, 73, 111, 174, 91, 26, 242, 237, - 242, 18, 54, 120, 178, 135, 77, 200, 14, 161, 190, 115, 5, 26, 109, 204, 145, 59, 209, 62, 7, - 99, 7, 59, 148, 169, 66, 224, 94, 98, 10, 235, 61, 141, 185, 181, 174, 88, 102, 4, 241, 92, 24, - 164, 76, 4, 220, 200, 11, 164, 59, 116, 0, 1, 95, 144, 86, 75, 55, 11, 236, 74, 119, 177, 97, - 228, 2, 241, 214, 251, 233, 204, 48, 32, 49, 218, 19, 6, 163, 93, 214, 22, 208, 0, 1, 175, 20, - 93, 200, 252, 22, 18, 155, 146, 179, 142, 111, 10, 151, 73, 137, 72, 159, 248, 18, 52, 94, 200, - 122, 76, 133, 90, 49, 18, 134, 152, 249, 105, 222, 0, 159, 63, 87, 20, 17, 106, 247, 141, 234, - 116, 187, 80, 105, 177, 184, 108, 51, 132, 156, 126, 12, 253, 43, 128, 81, 153, 218, 6, 123, - 164, 76, 4, 220, 201, 11, 164, 59, 116, 0, 1, 95, 144, 245, 119, 113, 240, 227, 22, 196, 58, - 146, 16, 148, 161, 180, 159, 205, 184, 108, 187, 210, 94, 19, 26, 240, 72, 110, 169, 44, 0, 0, - 207, 18, 223, 219, 52, 122, 252, 236, 116, 95, 97, 49, 243, 197, 150, 229, 91, 181, 63, 225, - 189, 149, 120, 77, 60, 151, 139, 13, 148, 146, 47, 114, 147, 177, 54, 41, 128, 238, 17, 39, - 190, 124, 74, 143, 111, 168, 232, 203, 195, 159, 21, 196, 200, 192, 251, 133, 18, 236, 163, - 210, 76, 203, 10, 89, 164, 72, 3, 7, 189, 11, 164, 59, 116, 0, 82, 8, 48, 58, 78, 1, 98, 120, - 12, 142, 136, 75, 148, 137, 228, 149, 237, 87, 198, 94, 31, 104, 18, 12, 134, 44, 242, 0, 1, - 15, 191, 83, 34, 67, 37, 58, 219, 14, 147, 198, 224, 45, 91, 153, 61, 169, 19, 66, 73, 227, - 144, 13, 0, 136, 247, 160, 169, 25, 90, 165, 171, 223, 76, 8, 251, 94, 222, 93, 249, 54, 174, - 82, 249, 177, 231, 105, 114, 237, 53, 116, 129, 180, 221, 77, 73, 141, 24, 135, 134, 112, 236, - 240, 64, 164, 76, 4, 220, 202, 11, 164, 59, 116, 0, 1, 95, 144, 239, 18, 60, 89, 195, 119, 203, - 171, 160, 162, 148, 77, 111, 50, 81, 201, 205, 71, 204, 62, 20, 8, 214, 250, 11, 198, 244, 0, - 1, 4, 241, 215, 188, 209, 78, 230, 87, 251, 167, 0, 99, 40, 196, 105, 217, 39, 162, 35, 82, - 239, 195, 193, 228, 177, 244, 134, 138, 55, 33, 153, 200, 33, 125, 200, 69, 43, 244, 158, 245, - 188, 1, 186, 179, 21, 127, 55, 200, 128, 129, 198, 166, 52, 158, 108, 42, 205, 157, 251, 180, - 126, 216, 24, 55, 164, 76, 4, 220, 203, 11, 164, 59, 116, 0, 1, 95, 144, 153, 75, 174, 83, 188, - 163, 228, 28, 143, 77, 83, 255, 4, 236, 212, 82, 155, 12, 215, 72, 21, 70, 130, 83, 228, 223, - 200, 0, 0, 20, 190, 96, 237, 168, 153, 62, 149, 62, 44, 67, 104, 99, 12, 242, 39, 110, 76, 146, - 204, 208, 53, 197, 68, 55, 236, 109, 12, 167, 129, 170, 145, 35, 178, 26, 236, 37, 167, 61, - 226, 46, 251, 117, 239, 236, 43, 215, 153, 77, 173, 135, 7, 211, 11, 67, 249, 174, 161, 197, - 133, 194, 190, 40, 92, 164, 76, 4, 220, 204, 11, 164, 59, 116, 0, 1, 95, 144, 154, 110, 176, - 187, 190, 80, 40, 22, 68, 215, 167, 131, 145, 71, 245, 152, 25, 80, 106, 192, 22, 89, 118, 10, - 173, 215, 148, 0, 1, 122, 87, 21, 137, 99, 24, 106, 94, 137, 7, 200, 64, 159, 13, 238, 245, 63, - 171, 178, 88, 80, 99, 206, 43, 145, 9, 12, 9, 179, 79, 177, 53, 54, 52, 44, 25, 197, 255, 89, - 249, 66, 38, 238, 97, 131, 38, 104, 63, 141, 64, 54, 208, 122, 154, 158, 21, 253, 163, 157, - 226, 148, 185, 110, 70, 164, 76, 4, 220, 205, 11, 164, 59, 116, 0, 1, 95, 144, 147, 16, 22, - 131, 18, 234, 24, 249, 91, 236, 238, 74, 143, 15, 248, 40, 77, 26, 141, 14, 22, 179, 181, 137, - 19, 7, 20, 0, 0, 31, 172, 118, 188, 234, 21, 43, 112, 64, 99, 193, 167, 44, 47, 7, 148, 87, - 192, 222, 247, 48, 116, 158, 175, 148, 116, 164, 23, 132, 221, 163, 154, 172, 145, 107, 226, - 228, 199, 97, 126, 55, 13, 8, 77, 199, 190, 91, 145, 6, 235, 222, 189, 150, 128, 35, 79, 51, - 118, 59, 54, 145, 117, 70, 106, 164, 76, 4, 220, 206, 11, 164, 59, 116, 0, 1, 95, 144, 215, 81, - 64, 133, 245, 72, 211, 72, 244, 177, 124, 233, 203, 161, 141, 172, 243, 174, 240, 23, 28, 4, - 45, 123, 43, 84, 88, 0, 1, 215, 206, 151, 154, 6, 209, 110, 130, 249, 30, 76, 47, 253, 35, 28, - 108, 214, 45, 180, 67, 51, 71, 158, 198, 214, 60, 191, 243, 217, 168, 236, 3, 59, 165, 217, - 155, 213, 84, 203, 47, 59, 18, 178, 107, 226, 192, 74, 194, 53, 66, 13, 46, 158, 27, 207, 75, - 236, 27, 193, 244, 174, 143, 146, 63, 164, 76, 4, 220, 207, 11, 164, 59, 116, 0, 1, 95, 144, - 103, 197, 233, 131, 143, 209, 167, 177, 245, 145, 44, 56, 110, 228, 239, 205, 199, 222, 34, - 163, 28, 58, 178, 126, 241, 150, 144, 0, 1, 125, 227, 39, 122, 8, 201, 143, 145, 94, 245, 113, - 125, 59, 193, 252, 41, 250, 48, 30, 254, 74, 198, 118, 76, 50, 4, 248, 253, 107, 238, 246, 36, - 171, 122, 182, 177, 70, 84, 74, 194, 147, 228, 47, 161, 136, 7, 49, 35, 171, 149, 186, 90, 38, - 25, 172, 224, 174, 116, 207, 178, 180, 156, 39, 7, 164, 76, 4, 220, 208, 11, 164, 59, 116, 0, - 1, 95, 144, 29, 172, 14, 181, 168, 86, 253, 16, 59, 137, 168, 213, 168, 14, 124, 222, 166, 93, - 114, 83, 28, 104, 156, 106, 16, 171, 4, 0, 0, 25, 113, 17, 244, 35, 117, 137, 14, 186, 53, 39, - 125, 109, 204, 42, 66, 243, 62, 255, 238, 206, 16, 107, 100, 217, 191, 87, 123, 222, 164, 107, - 171, 117, 155, 147, 218, 20, 228, 122, 129, 226, 247, 150, 19, 0, 190, 118, 80, 223, 133, 25, - 137, 42, 88, 60, 206, 60, 75, 145, 50, 223, 156, 22, 47, 164, 76, 4, 220, 209, 11, 164, 59, - 116, 0, 1, 95, 144, 242, 160, 112, 32, 33, 96, 181, 26, 69, 43, 80, 16, 230, 169, 211, 24, 223, - 247, 194, 235, 29, 182, 0, 69, 46, 217, 132, 0, 0, 10, 127, 208, 183, 90, 3, 201, 215, 233, - 194, 217, 192, 160, 86, 72, 109, 100, 50, 100, 150, 72, 127, 91, 111, 90, 148, 184, 195, 142, - 178, 14, 73, 69, 164, 110, 206, 7, 92, 125, 53, 137, 154, 33, 213, 64, 108, 169, 209, 16, 124, - 24, 91, 40, 167, 61, 65, 240, 180, 194, 31, 191, 206, 157, 96, 164, 76, 4, 220, 210, 11, 164, - 59, 116, 0, 1, 95, 144, 209, 48, 177, 98, 28, 16, 243, 52, 250, 186, 115, 1, 190, 56, 46, 111, - 50, 61, 98, 17, 31, 104, 161, 236, 134, 210, 244, 0, 1, 54, 240, 159, 199, 141, 187, 231, 146, - 215, 49, 151, 190, 246, 230, 1, 191, 24, 215, 40, 117, 98, 150, 233, 148, 136, 138, 71, 31, 26, - 144, 67, 38, 81, 156, 160, 220, 104, 130, 42, 213, 174, 72, 200, 125, 65, 59, 116, 110, 120, - 227, 254, 210, 16, 148, 18, 23, 25, 43, 128, 21, 102, 122, 44, 55, 164, 76, 4, 220, 211, 11, - 164, 59, 116, 0, 1, 95, 144, 209, 56, 57, 172, 167, 243, 73, 225, 243, 47, 123, 85, 254, 158, - 228, 92, 57, 10, 46, 4, 34, 118, 139, 196, 91, 30, 192, 0, 1, 174, 144, 131, 5, 193, 161, 142, - 72, 248, 112, 186, 107, 176, 197, 7, 111, 107, 100, 84, 18, 165, 62, 75, 43, 80, 229, 65, 101, - 122, 206, 224, 242, 12, 27, 210, 107, 122, 247, 239, 222, 202, 80, 52, 114, 250, 221, 228, 102, - 91, 10, 9, 191, 104, 29, 78, 128, 81, 18, 7, 61, 148, 253, 74, 80, 164, 204, 0, 22, 56, 12, 27, - 113, 8, 0, 3, 208, 144, 154, 240, 153, 145, 173, 99, 129, 78, 83, 255, 193, 188, 207, 33, 62, - 231, 64, 39, 96, 139, 1, 62, 212, 134, 121, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 46, 100, 128, - 196, 55, 4, 16, 10, 127, 96, 246, 0, 109, 148, 175, 245, 165, 128, 31, 73, 0, 0, 0, 0, 0, 0, 0, - 127, 160, 219, 99, 197, 62, 244, 31, 152, 168, 65, 179, 87, 64, 41, 235, 210, 226, 157, 21, - 255, 96, 92, 12, 207, 102, 137, 171, 217, 25, 214, 26, 140, 138, 207, 31, 128, 140, 221, 82, - 232, 63, 126, 54, 241, 43, 80, 8, 8, 196, 89, 96, 77, 4, 162, 76, 4, 10, 11, 164, 59, 116, 0, - 1, 95, 144, 30, 148, 223, 164, 160, 154, 1, 50, 45, 64, 189, 207, 75, 95, 170, 105, 33, 36, - 235, 245, 111, 10, 83, 245, 17, 85, 64, 0, 0, 79, 176, 56, 251, 199, 107, 19, 109, 168, 95, 49, - 202, 171, 76, 81, 171, 1, 51, 130, 175, 225, 144, 245, 105, 191, 191, 24, 35, 251, 30, 91, 19, - 255, 193, 142, 203, 50, 75, 48, 148, 54, 86, 115, 159, 150, 215, 44, 156, 19, 65, 137, 47, 204, - 60, 101, 226, 8, 100, 164, 245, 176, 217, 127, 39, 164, 76, 0, 79, 242, 10, 122, 53, 130, 0, 7, - 161, 32, 51, 117, 238, 48, 66, 139, 42, 113, 196, 40, 175, 165, 232, 158, 66, 121, 5, 249, 95, - 126, 125, 36, 42, 229, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 1, 118, 183, 248, 207, 104, 188, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 64, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 34, 18, 32, 138, 152, 141, 85, 167, 154, 200, 138, 178, - 106, 201, 20, 170, 94, 94, 220, 156, 63, 15, 115, 122, 235, 223, 50, 85, 14, 45, 58, 97, 201, - 17, 202, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 1, 132, 29, 157, 10, 0, 82, 225, 66, 22, 41, 122, 183, 212, 6, 122, 215, 141, 153, 161, 187, - 219, 113, 85, 195, 33, 241, 167, 78, 109, 23, 143, 185, 32, 177, 45, 51, 162, 41, 40, 226, 165, - 101, 81, 246, 3, 184, 216, 37, 115, 174, 156, 59, 203, 87, 146, 57, 210, 10, 175, 23, 220, 67, - 66, 18, 166, 76, 4, 1, 221, 99, 11, 164, 59, 116, 0, 1, 95, 144, 119, 218, 147, 90, 255, 162, - 241, 153, 54, 53, 157, 21, 202, 247, 85, 18, 180, 110, 119, 82, 14, 58, 69, 255, 196, 190, 108, - 0, 1, 158, 218, 69, 85, 29, 179, 37, 30, 57, 67, 205, 189, 31, 185, 100, 227, 163, 161, 180, - 243, 102, 19, 107, 49, 14, 102, 218, 10, 8, 177, 133, 19, 113, 65, 184, 135, 55, 98, 159, 170, - 108, 121, 220, 53, 57, 32, 68, 198, 39, 233, 99, 83, 155, 228, 0, 225, 191, 95, 169, 160, 220, - 101, 203, 68, 162, 72, 4, 18, 11, 164, 59, 116, 0, 86, 34, 251, 177, 183, 60, 79, 11, 218, 79, - 103, 220, 162, 102, 206, 110, 244, 47, 82, 15, 187, 152, 7, 11, 253, 107, 58, 217, 232, 0, 1, - 135, 37, 139, 85, 228, 147, 232, 71, 17, 133, 179, 77, 0, 144, 136, 1, 49, 22, 39, 104, 35, 2, - 195, 205, 59, 107, 159, 168, 180, 51, 4, 210, 225, 155, 238, 208, 18, 175, 226, 254, 15, 71, - 141, 49, 48, 25, 202, 152, 118, 70, 48, 14, 36, 247, 195, 113, 164, 122, 114, 113, 128, 188, - 162, 123, 166, 76, 4, 1, 17, 195, 11, 164, 59, 116, 0, 1, 95, 144, 187, 179, 60, 230, 25, 145, - 204, 66, 114, 213, 129, 181, 207, 250, 119, 244, 190, 9, 242, 108, 14, 58, 104, 106, 20, 128, - 140, 0, 0, 149, 186, 143, 77, 118, 171, 254, 229, 77, 234, 124, 46, 47, 26, 228, 31, 160, 116, - 86, 117, 143, 161, 207, 185, 229, 34, 7, 155, 229, 17, 83, 98, 171, 224, 96, 19, 250, 135, 205, - 151, 204, 201, 139, 47, 18, 107, 21, 172, 114, 47, 195, 139, 172, 8, 208, 159, 93, 102, 118, - 129, 0, 148, 55, 51, 164, 204, 0, 6, 151, 12, 27, 113, 8, 0, 3, 208, 144, 154, 240, 153, 145, - 173, 99, 129, 78, 83, 255, 193, 188, 207, 33, 62, 231, 64, 39, 96, 139, 1, 62, 212, 134, 121, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 241, 85, 14, 247, 231, 214, 198, 154, 210, 241, 194, 30, - 25, 210, 163, 137, 232, 37, 39, 172, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 86, 167, 194, 114, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 192, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 127, - 179, 184, 119, 242, 216, 93, 146, 212, 23, 39, 100, 234, 92, 214, 137, 130, 203, 229, 62, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 240, 209, 128, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, - 2, 201, 67, 53, 31, 192, 158, 129, 26, 190, 62, 113, 156, 42, 178, 88, 82, 90, 160, 53, 176, - 100, 236, 170, 114, 135, 250, 48, 130, 210, 159, 190, 36, 24, 148, 17, 224, 170, 155, 171, 208, - 222, 94, 141, 40, 134, 73, 138, 186, 206, 158, 160, 109, 26, 252, 240, 37, 45, 145, 70, 74, - 235, 148, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 27, 1, 86, 89, 10, 167, 209, 186, 72, 102, 179, 118, 57, 67, 95, 13, 226, 123, 92, - 246, 179, 254, 2, 109, 107, 61, 188, 107, 128, 138, 32, 100, 55, 168, 123, 199, 246, 60, 97, - 221, 46, 92, 18, 3, 94, 153, 75, 0, 8, 107, 225, 60, 95, 63, 173, 129, 75, 70, 22, 8, 67, 209, - 109, 121, 131, 57, 166, 76, 4, 1, 17, 196, 11, 164, 59, 116, 0, 1, 95, 144, 96, 253, 241, 219, - 101, 69, 16, 160, 23, 83, 192, 219, 141, 144, 187, 25, 136, 10, 208, 29, 69, 252, 25, 46, 112, - 208, 68, 0, 0, 76, 86, 215, 207, 237, 72, 179, 153, 41, 110, 112, 69, 82, 90, 96, 240, 39, 216, - 167, 34, 29, 18, 235, 145, 229, 126, 247, 67, 181, 230, 180, 41, 201, 20, 158, 241, 77, 151, - 114, 228, 40, 207, 70, 209, 137, 193, 89, 179, 113, 197, 181, 171, 110, 74, 180, 123, 50, 83, - 115, 93, 225, 32, 93, 98, 164, 204, 4, 24, 206, 11, 164, 59, 116, 0, 3, 208, 144, 124, 247, - 190, 90, 164, 163, 43, 218, 154, 101, 2, 30, 147, 62, 97, 74, 127, 180, 137, 67, 5, 218, 205, - 19, 202, 158, 48, 0, 0, 0, 76, 140, 137, 129, 156, 32, 183, 178, 14, 168, 85, 21, 126, 64, 246, - 114, 207, 174, 192, 254, 164, 60, 197, 108, 236, 215, 198, 54, 98, 160, 132, 156, 188, 79, 90, - 153, 96, 240, 192, 176, 169, 66, 33, 175, 21, 148, 236, 10, 182, 127, 35, 99, 17, 27, 53, 126, - 103, 4, 205, 1, 164, 21, 131, 1, 162, 76, 0, 7, 11, 164, 59, 116, 0, 2, 71, 160, 121, 22, 211, - 52, 174, 110, 149, 21, 80, 127, 119, 245, 234, 5, 224, 85, 255, 127, 96, 223, 182, 29, 39, 246, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 180, 101, 214, 220, 105, 76, 148, 91, 25, 8, 180, 81, 131, - 80, 108, 216, 38, 96, 79, 59, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 54, 53, 201, 173, 197, 222, 160, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 235, 174, 64, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 179, - 131, 255, 108, 160, 63, 214, 161, 255, 206, 127, 58, 39, 193, 110, 237, 225, 94, 231, 192, 157, - 216, 185, 50, 189, 82, 58, 133, 60, 168, 240, 182, 18, 117, 164, 68, 134, 176, 157, 226, 122, - 103, 172, 226, 191, 68, 237, 217, 151, 38, 226, 215, 97, 13, 133, 172, 243, 227, 190, 201, 135, - 222, 46, 66, 137, 4, 113, 255, 213, 254, 6, 203, 243, 236, 253, 58, 78, 75, 201, 24, 34, 21, - 240, 157, 71, 87, 132, 103, 197, 46, 13, 167, 124, 67, 93, 95, 32, 195, 160, 191, 141, 191, - 237, 230, 5, 36, 203, 74, 102, 27, 33, 133, 92, 115, 118, 40, 232, 157, 33, 128, 78, 159, 34, - 26, 130, 127, 117, 30, 163, 126, 24, 36, 77, 7, 33, 54, 67, 98, 240, 86, 75, 64, 190, 17, 49, - 49, 18, 18, 117, 90, 111, 204, 46, 171, 97, 61, 223, 184, 135, 15, 33, 177, 129, 109, 65, 128, - 194, 46, 67, 209, 184, 77, 162, 192, 83, 53, 157, 118, 137, 196, 197, 42, 56, 208, 192, 61, - 133, 138, 167, 38, 255, 50, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 27, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 27, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 27, 1, 173, 9, 255, 174, 231, 43, 147, 31, 101, 2, 255, 163, - 217, 147, 74, 89, 77, 68, 62, 200, 251, 161, 173, 3, 220, 163, 182, 231, 71, 89, 241, 49, 119, - 137, 130, 71, 247, 49, 117, 82, 47, 105, 35, 20, 76, 9, 240, 24, 209, 197, 236, 117, 164, 122, - 125, 87, 89, 123, 231, 125, 160, 29, 19, 30, 164, 204, 0, 15, 117, 12, 27, 113, 8, 0, 4, 147, - 224, 154, 240, 153, 145, 173, 99, 129, 78, 83, 255, 193, 188, 207, 33, 62, 231, 64, 39, 96, - 139, 1, 62, 212, 134, 121, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 61, 241, 7, 71, 254, 241, 91, - 208, 218, 46, 9, 127, 170, 175, 233, 219, 35, 25, 165, 75, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 86, 158, 110, 35, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 192, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 160, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 36, 12, 33, 162, 238, 10, 15, 113, 186, 155, 227, 251, 244, 10, 172, 106, 46, 68, - 39, 62, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 54, - 168, 79, 64, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 6, 143, 175, 222, 69, 132, 152, 14, 215, 123, 48, 74, 160, 156, 58, 34, 247, 162, - 122, 59, 245, 22, 192, 226, 203, 130, 97, 17, 58, 52, 194, 116, 42, 0, 116, 99, 104, 21, 201, - 6, 10, 250, 85, 173, 54, 113, 204, 161, 181, 235, 132, 97, 84, 57, 177, 142, 82, 46, 166, 140, - 144, 76, 203, 34, 250, 9, 151, 250, 183, 68, 158, 142, 179, 168, 120, 252, 138, 217, 174, 15, - 239, 106, 88, 125, 84, 229, 142, 224, 93, 182, 75, 28, 105, 143, 203, 242, 112, 46, 79, 188, - 189, 96, 17, 159, 62, 127, 216, 80, 150, 173, 67, 167, 220, 121, 6, 245, 50, 158, 175, 196, - 205, 92, 19, 85, 95, 82, 48, 213, 138, 233, 141, 144, 240, 7, 28, 139, 83, 95, 226, 39, 79, - 208, 100, 6, 123, 35, 106, 182, 83, 206, 209, 68, 66, 157, 44, 10, 59, 22, 124, 147, 253, 120, - 9, 202, 6, 36, 137, 18, 77, 241, 10, 27, 136, 112, 213, 130, 246, 247, 222, 55, 233, 74, 50, - 86, 58, 239, 29, 91, 148, 194, 232, 16, 95, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 28, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 28, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 27, 0, 40, 81, 169, 119, 71, 183, 80, 81, 141, - 130, 118, 94, 212, 201, 159, 138, 98, 9, 78, 91, 101, 64, 6, 220, 144, 146, 125, 196, 230, 149, - 218, 104, 138, 143, 201, 127, 123, 184, 83, 151, 223, 176, 250, 6, 89, 25, 63, 38, 234, 138, - 111, 196, 210, 71, 121, 105, 55, 236, 132, 166, 242, 185, 85, 101, 164, 72, 4, 101, 84, 11, - 164, 59, 116, 0, 160, 40, 69, 6, 11, 92, 238, 25, 6, 97, 250, 39, 209, 225, 137, 244, 49, 247, - 178, 181, 34, 117, 68, 221, 109, 103, 73, 214, 97, 0, 1, 134, 198, 62, 211, 109, 75, 125, 238, - 100, 112, 228, 24, 184, 17, 218, 95, 71, 138, 195, 56, 104, 250, 218, 39, 139, 201, 220, 131, - 219, 193, 79, 201, 10, 30, 67, 125, 131, 121, 218, 193, 178, 185, 72, 250, 40, 174, 110, 74, - 159, 38, 102, 62, 198, 71, 141, 100, 203, 122, 144, 35, 144, 101, 47, 109, 162, 76, 0, 44, 11, - 164, 59, 116, 0, 5, 87, 48, 204, 243, 172, 249, 115, 234, 242, 167, 24, 172, 95, 39, 3, 95, 95, - 92, 159, 87, 82, 232, 202, 109, 86, 220, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 113, 234, 208, - 238, 39, 107, 70, 120, 251, 86, 50, 11, 36, 96, 114, 42, 216, 130, 158, 4, 0, 155, 132, 44, - 208, 109, 103, 179, 133, 70, 212, 41, 148, 11, 236, 67, 105, 125, 57, 77, 68, 133, 170, 220, - 198, 180, 189, 89, 194, 96, 233, 64, 35, 126, 176, 54, 141, 179, 199, 51, 238, 66, 31, 32, 236, - 244, 10, 80, 68, 224, 26, 122, 213, 81, 2, 121, 160, 49, 131, 174, 170, 103, 89, 246, 58, 164, - 76, 4, 3, 138, 11, 164, 59, 116, 0, 7, 161, 32, 47, 170, 49, 111, 196, 98, 78, 195, 154, 220, - 46, 247, 181, 48, 17, 36, 207, 182, 135, 119, 26, 94, 39, 238, 241, 62, 0, 0, 131, 231, 139, - 49, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 1, 1, 35, 95, 199, 174, 10, 147, 37, 249, 109, 202, 251, 108, 102, 202, 113, 46, 209, 230, - 97, 194, 163, 96, 78, 176, 11, 159, 95, 120, 20, 204, 39, 44, 69, 101, 152, 159, 110, 54, 92, - 162, 42, 152, 33, 75, 12, 254, 129, 46, 132, 35, 119, 57, 196, 91, 34, 135, 121, 253, 203, 211, - 140, 16, 45, 136, 18, 105, 34, 0, 81, 127, 220, 0, 1, 5, 34, 151, 63, 62, 16, 21, 220, 63, 31, - 188, 162, 134, 96, 141, 34, 149, 129, 103, 96, 43, 204, 169, 21, 169, 67, 254, 71, 40, 132, - 172, 163, 190, 210, 27, 104, 121, 57, 101, 244, 223, 77, 86, 62, 176, 94, 9, 35, 121, 30, 182, - 16, 25, 192, 79, 137, 50, 62, 231, 133, 4, 138, 57, 121, 164, 72, 4, 41, 105, 13, 248, 71, 88, - 0, 195, 80, 73, 209, 5, 179, 1, 128, 115, 11, 42, 209, 195, 164, 127, 182, 58, 236, 75, 23, - 254, 236, 9, 61, 180, 167, 43, 81, 124, 0, 0, 233, 90, 227, 43, 16, 253, 26, 177, 113, 230, - 123, 135, 254, 18, 203, 93, 141, 33, 214, 106, 59, 82, 162, 22, 161, 101, 210, 148, 175, 182, - 201, 122, 150, 116, 50, 157, 49, 55, 172, 33, 194, 225, 146, 27, 225, 148, 198, 32, 138, 181, - 75, 73, 153, 228, 151, 149, 138, 244, 93, 251, 230, 227, 155, 84, 164, 200, 4, 41, 106, 13, - 248, 71, 88, 0, 195, 80, 141, 114, 235, 142, 188, 56, 118, 193, 92, 141, 248, 199, 147, 39, - 248, 181, 213, 252, 92, 31, 2, 55, 182, 105, 68, 235, 185, 224, 0, 1, 179, 119, 169, 250, 61, - 199, 102, 153, 210, 30, 88, 25, 164, 51, 76, 72, 196, 230, 96, 38, 107, 51, 186, 221, 85, 114, - 124, 169, 208, 140, 227, 149, 188, 151, 179, 156, 250, 194, 223, 141, 87, 130, 242, 98, 184, - 40, 174, 180, 207, 67, 74, 0, 58, 189, 56, 92, 202, 137, 198, 166, 239, 214, 244, 14, 164, 200, - 4, 41, 107, 13, 248, 71, 88, 0, 195, 80, 237, 185, 212, 250, 86, 155, 35, 187, 8, 168, 178, - 157, 223, 227, 53, 160, 75, 34, 71, 8, 2, 161, 106, 172, 174, 96, 126, 64, 0, 1, 76, 133, 175, - 250, 246, 139, 197, 43, 49, 147, 59, 131, 121, 178, 66, 202, 147, 108, 255, 250, 41, 5, 148, - 42, 183, 150, 96, 235, 244, 105, 154, 80, 144, 119, 29, 13, 83, 69, 34, 65, 146, 160, 110, 31, - 15, 170, 221, 112, 109, 33, 237, 134, 8, 75, 167, 66, 51, 182, 144, 5, 14, 233, 154, 41, 164, - 72, 4, 41, 108, 13, 248, 71, 88, 0, 195, 80, 0, 140, 251, 117, 181, 158, 156, 78, 60, 154, 73, - 101, 255, 76, 9, 58, 220, 219, 214, 65, 116, 233, 18, 125, 235, 59, 204, 0, 0, 133, 19, 17, - 102, 10, 63, 197, 107, 194, 222, 49, 157, 136, 35, 213, 250, 202, 144, 234, 231, 69, 185, 171, - 10, 253, 255, 39, 49, 67, 212, 249, 94, 104, 36, 34, 191, 247, 81, 215, 30, 79, 121, 9, 70, - 183, 9, 139, 75, 159, 19, 247, 156, 165, 53, 9, 99, 17, 80, 149, 120, 112, 233, 82, 7, 160, 72, - 4, 13, 248, 71, 88, 0, 82, 8, 50, 190, 52, 59, 148, 248, 96, 18, 77, 196, 254, 226, 120, 253, - 203, 211, 140, 16, 45, 136, 113, 43, 43, 111, 236, 225, 204, 0, 1, 179, 246, 37, 195, 63, 134, - 107, 127, 36, 195, 78, 171, 221, 45, 207, 73, 99, 35, 139, 226, 205, 93, 23, 29, 55, 87, 34, - 196, 137, 136, 188, 74, 181, 193, 253, 192, 39, 220, 162, 181, 64, 255, 119, 26, 5, 231, 194, - 246, 182, 97, 62, 189, 60, 84, 101, 117, 187, 52, 139, 79, 16, 78, 204, 10, 164, 76, 4, 178, - 88, 11, 164, 59, 116, 0, 1, 95, 144, 245, 119, 113, 240, 227, 22, 196, 58, 146, 16, 148, 161, - 180, 159, 205, 184, 108, 187, 210, 94, 13, 255, 115, 29, 3, 63, 52, 0, 0, 218, 225, 165, 85, - 38, 52, 9, 186, 172, 238, 3, 0, 196, 206, 111, 241, 23, 23, 0, 45, 22, 36, 93, 102, 134, 72, - 40, 5, 88, 100, 71, 15, 225, 156, 102, 144, 6, 223, 230, 203, 91, 187, 148, 34, 148, 253, 250, - 66, 77, 209, 226, 199, 206, 203, 241, 130, 132, 99, 181, 100, 175, 155, 197, 7, 164, 76, 4, - 178, 89, 11, 164, 59, 116, 0, 1, 95, 144, 225, 69, 132, 207, 191, 69, 170, 61, 204, 31, 223, - 91, 33, 36, 237, 5, 99, 158, 46, 174, 15, 72, 39, 41, 242, 10, 116, 0, 0, 193, 131, 48, 172, - 78, 113, 203, 8, 67, 203, 209, 10, 250, 34, 104, 177, 15, 229, 180, 65, 166, 207, 104, 30, 36, - 92, 102, 109, 79, 146, 197, 179, 7, 58, 22, 3, 85, 16, 106, 43, 134, 142, 218, 41, 66, 226, - 182, 52, 219, 23, 115, 239, 174, 131, 226, 51, 30, 238, 121, 86, 152, 69, 231, 60, 164, 76, 4, - 178, 90, 11, 164, 59, 116, 0, 1, 95, 144, 48, 49, 197, 155, 236, 121, 23, 236, 155, 249, 67, - 121, 138, 34, 85, 199, 10, 115, 193, 151, 15, 192, 246, 175, 144, 106, 232, 0, 0, 184, 112, - 198, 199, 148, 208, 205, 43, 92, 81, 29, 196, 156, 221, 66, 67, 95, 31, 1, 71, 107, 125, 64, - 109, 45, 79, 184, 127, 97, 79, 177, 87, 248, 59, 19, 22, 251, 249, 151, 94, 37, 19, 83, 124, - 201, 102, 218, 208, 109, 105, 195, 31, 124, 39, 28, 143, 6, 183, 82, 105, 175, 69, 169, 95, - 166, 76, 4, 1, 133, 159, 11, 164, 59, 116, 0, 1, 95, 144, 128, 108, 78, 32, 204, 145, 61, 123, - 112, 245, 183, 226, 76, 107, 161, 91, 22, 197, 106, 92, 20, 205, 207, 104, 204, 147, 88, 0, 0, - 230, 91, 196, 144, 104, 22, 249, 81, 16, 248, 105, 242, 202, 0, 109, 86, 108, 48, 161, 128, - 209, 45, 173, 138, 52, 213, 188, 200, 16, 223, 69, 62, 184, 175, 18, 35, 246, 158, 143, 83, - 210, 215, 48, 245, 123, 246, 123, 204, 52, 78, 47, 124, 44, 103, 54, 217, 143, 133, 185, 243, - 112, 117, 30, 28, 160, 72, 4, 11, 164, 59, 116, 0, 82, 8, 87, 167, 25, 24, 92, 240, 30, 143, - 198, 137, 178, 135, 151, 149, 230, 84, 216, 215, 93, 120, 13, 224, 182, 179, 167, 100, 0, 0, 0, - 0, 85, 114, 112, 116, 56, 231, 216, 138, 47, 231, 61, 103, 112, 222, 188, 200, 255, 174, 83, - 42, 70, 95, 223, 159, 239, 247, 208, 38, 23, 77, 147, 147, 25, 81, 43, 44, 123, 28, 90, 234, - 22, 195, 250, 24, 93, 154, 116, 23, 55, 72, 251, 195, 182, 208, 127, 171, 221, 164, 112, 48, - 43, 240, 103, 164, 76, 4, 178, 91, 11, 164, 59, 116, 0, 1, 95, 144, 182, 53, 50, 166, 198, 144, - 69, 34, 249, 170, 125, 191, 109, 118, 59, 110, 209, 133, 37, 88, 18, 217, 92, 203, 25, 53, 252, - 0, 1, 218, 58, 45, 158, 169, 176, 6, 6, 47, 125, 52, 19, 15, 34, 45, 123, 139, 233, 214, 103, - 176, 128, 203, 139, 255, 94, 186, 252, 181, 58, 204, 89, 40, 1, 240, 63, 216, 216, 25, 19, 156, - 187, 216, 8, 91, 229, 187, 10, 255, 187, 71, 130, 227, 89, 238, 218, 220, 75, 1, 47, 247, 89, - 194, 49, 162, 204, 3, 7, 11, 164, 59, 116, 0, 1, 248, 220, 253, 96, 30, 209, 54, 184, 73, 204, - 112, 118, 14, 150, 255, 176, 213, 172, 184, 107, 0, 181, 206, 14, 177, 84, 249, 0, 0, 0, 33, - 39, 145, 243, 183, 152, 35, 247, 95, 74, 49, 4, 231, 245, 146, 155, 11, 44, 97, 55, 232, 111, - 37, 130, 130, 39, 169, 41, 44, 46, 59, 249, 78, 174, 99, 219, 72, 238, 71, 175, 238, 123, 139, - 215, 117, 155, 202, 87, 0, 241, 31, 95, 123, 170, 76, 82, 58, 74, 15, 220, 41, 172, 99, 87, - 164, 76, 4, 178, 92, 11, 164, 59, 116, 0, 1, 95, 144, 242, 160, 112, 32, 33, 96, 181, 26, 69, - 43, 80, 16, 230, 169, 211, 24, 223, 247, 194, 235, 28, 110, 13, 113, 30, 214, 152, 0, 0, 107, - 133, 43, 111, 68, 247, 220, 25, 128, 139, 81, 168, 254, 228, 137, 74, 53, 231, 28, 239, 136, - 142, 42, 143, 195, 7, 194, 49, 82, 77, 226, 184, 201, 14, 28, 152, 40, 62, 128, 208, 159, 98, - 19, 79, 231, 71, 230, 117, 82, 19, 57, 183, 30, 41, 28, 71, 28, 124, 110, 31, 151, 103, 107, - 69, 164, 76, 4, 178, 93, 11, 164, 59, 116, 0, 1, 95, 144, 135, 168, 141, 241, 45, 207, 188, 52, - 27, 100, 205, 163, 243, 125, 249, 4, 140, 150, 128, 131, 42, 14, 40, 83, 225, 164, 232, 0, 1, - 83, 128, 102, 39, 122, 65, 229, 41, 240, 90, 202, 49, 240, 103, 241, 78, 160, 158, 229, 208, - 129, 102, 68, 220, 45, 117, 67, 76, 15, 102, 132, 197, 217, 237, 200, 103, 34, 84, 67, 177, 62, - 159, 133, 77, 164, 204, 4, 7, 177, 11, 164, 59, 116, 0, 1, 95, 144, 77, 103, 244, 240, 219, 69, - 179, 181, 163, 66, 75, 57, 237, 141, 151, 47, 113, 3, 129, 213, 2, 208, 65, 215, 5, 162, 198, - 0, 0, 1, 118, 37, 117, 85, 11, 119, 43, 56, 243, 122, 99, 112, 155, 161, 44, 99, 37, 169, 115, - 65, 72, 219, 166, 136, 181, 198, 27, 15, 10, 213, 247, 93, 122, 115, 97, 165, 105, 75, 15, 52, - 188, 238, 138, 13, 153, 247, 214, 57, 110, 75, 52, 177, 244, 60, 25, 175, 79, 163, 92, 210, 40, - 17, 146, 109, 162, 76, 4, 4, 11, 164, 59, 116, 0, 1, 95, 144, 110, 79, 36, 162, 151, 196, 150, - 174, 245, 212, 4, 114, 90, 155, 189, 76, 194, 58, 199, 149, 1, 140, 170, 37, 198, 155, 84, 0, - 1, 44, 53, 34, 231, 243, 243, 133, 156, 78, 227, 25, 248, 203, 185, 171, 251, 32, 182, 206, - 151, 81, 49, 117, 194, 42, 131, 10, 89, 228, 80, 251, 84, 48, 171, 113, 245, 93, 195, 159, 150, - 6, 230, 72, 247, 57, 182, 49, 182, 73, 72, 127, 29, 159, 77, 165, 248, 35, 195, 118, 235, 157, - 203, 35, 96, 160, 76, 4, 11, 164, 59, 116, 0, 3, 13, 64, 18, 10, 39, 11, 188, 0, 150, 68, 227, - 95, 11, 182, 171, 19, 249, 91, 129, 153, 196, 173, 42, 190, 91, 174, 116, 52, 0, 0, 1, 38, 214, - 213, 239, 128, 156, 76, 124, 238, 159, 247, 143, 130, 32, 237, 139, 233, 54, 74, 243, 152, 200, - 68, 252, 144, 132, 169, 159, 12, 113, 142, 250, 156, 109, 230, 37, 202, 161, 54, 123, 46, 136, - 167, 217, 178, 33, 225, 111, 255, 199, 165, 26, 182, 204, 253, 9, 78, 192, 97, 29, 111, 207, - 218, 65, 166, 76, 4, 1, 95, 136, 11, 164, 59, 116, 0, 1, 95, 144, 182, 55, 211, 198, 35, 81, - 64, 81, 154, 149, 27, 127, 178, 174, 17, 129, 38, 62, 18, 111, 36, 94, 31, 100, 147, 5, 92, 0, - 1, 225, 152, 5, 188, 224, 132, 78, 103, 189, 0, 241, 169, 222, 19, 17, 237, 84, 82, 150, 81, - 212, 170, 64, 132, 53, 67, 244, 234, 123, 187, 127, 203, 227, 50, 187, 133, 232, 121, 158, 177, - 151, 54, 105, 239, 227, 37, 147, 88, 199, 156, 228, 147, 17, 100, 99, 196, 44, 6, 181, 199, - 145, 53, 9, 81, 166, 76, 4, 1, 95, 137, 11, 164, 59, 116, 0, 1, 95, 144, 231, 82, 63, 120, 174, - 158, 219, 129, 220, 123, 42, 156, 167, 24, 186, 55, 118, 193, 79, 46, 49, 183, 206, 237, 244, - 110, 236, 0, 1, 6, 252, 103, 75, 49, 20, 99, 250, 113, 156, 248, 67, 132, 124, 57, 249, 136, - 39, 119, 139, 116, 229, 43, 247, 62, 145, 4, 169, 156, 9, 240, 66, 106, 124, 220, 102, 111, 10, - 205, 60, 58, 38, 219, 161, 215, 190, 61, 205, 131, 161, 233, 134, 213, 90, 117, 84, 122, 192, - 156, 176, 182, 93, 211, 74, 166, 76, 4, 1, 95, 138, 11, 164, 59, 116, 0, 1, 95, 144, 184, 7, - 207, 190, 10, 129, 204, 79, 60, 238, 176, 132, 245, 162, 230, 179, 72, 202, 10, 183, 32, 103, - 161, 211, 34, 68, 216, 0, 0, 55, 52, 60, 27, 38, 173, 101, 110, 145, 93, 133, 213, 30, 242, 80, - 185, 240, 166, 5, 134, 40, 186, 78, 157, 159, 244, 163, 50, 248, 79, 107, 201, 195, 222, 215, - 49, 144, 246, 149, 212, 86, 79, 7, 72, 253, 235, 112, 97, 63, 137, 60, 64, 197, 119, 211, 26, - 215, 125, 58, 218, 71, 38, 46, 82, 164, 72, 4, 12, 88, 13, 248, 71, 88, 0, 82, 8, 50, 190, 52, - 59, 148, 248, 96, 18, 77, 196, 254, 226, 120, 253, 203, 211, 140, 16, 45, 136, 16, 57, 182, - 188, 187, 201, 104, 0, 1, 141, 137, 164, 1, 227, 65, 144, 136, 32, 24, 112, 127, 11, 97, 33, - 68, 50, 133, 134, 186, 161, 244, 159, 7, 253, 120, 184, 133, 233, 143, 138, 163, 218, 115, 181, - 109, 60, 182, 87, 218, 225, 105, 108, 152, 82, 211, 21, 2, 164, 238, 176, 25, 233, 102, 102, - 59, 105, 81, 255, 20, 188, 27, 4, 57, 164, 72, 4, 1, 5, 13, 248, 71, 88, 0, 82, 8, 50, 190, 52, - 59, 148, 248, 96, 18, 77, 196, 254, 226, 120, 253, 203, 211, 140, 16, 45, 136, 67, 231, 196, - 178, 62, 232, 204, 0, 1, 50, 33, 78, 49, 175, 181, 85, 153, 141, 143, 153, 202, 109, 164, 212, - 92, 82, 119, 121, 3, 161, 51, 90, 98, 225, 203, 228, 4, 80, 24, 21, 47, 171, 107, 78, 60, 245, - 72, 13, 249, 11, 76, 30, 252, 45, 65, 99, 180, 208, 236, 152, 89, 145, 117, 233, 163, 15, 120, - 221, 7, 233, 194, 49, 108, 162, 72, 4, 187, 13, 248, 71, 88, 0, 82, 8, 50, 190, 52, 59, 148, - 248, 96, 18, 77, 196, 254, 226, 120, 253, 203, 211, 140, 16, 45, 136, 36, 89, 165, 109, 246, - 154, 156, 0, 0, 172, 204, 89, 105, 25, 125, 173, 106, 102, 40, 0, 254, 242, 137, 131, 134, 215, - 240, 1, 26, 102, 123, 103, 237, 153, 91, 119, 171, 171, 92, 169, 22, 144, 252, 119, 31, 212, - 44, 124, 254, 149, 162, 160, 21, 121, 88, 237, 33, 136, 196, 46, 178, 128, 111, 254, 141, 12, - 114, 163, 166, 18, 48, 33, 1, 162, 72, 4, 14, 13, 248, 71, 88, 0, 82, 8, 50, 190, 52, 59, 148, - 248, 96, 18, 77, 196, 254, 226, 120, 253, 203, 211, 140, 16, 45, 136, 17, 74, 39, 168, 34, 3, - 100, 0, 1, 114, 41, 22, 16, 247, 17, 196, 9, 31, 181, 120, 185, 213, 84, 64, 142, 181, 206, - 219, 193, 146, 41, 13, 75, 109, 67, 229, 192, 55, 44, 208, 214, 22, 208, 56, 47, 125, 104, 155, - 112, 126, 161, 99, 153, 68, 89, 77, 68, 217, 76, 145, 47, 148, 110, 47, 235, 133, 66, 235, 147, - 89, 153, 180, 31, 164, 72, 4, 1, 164, 13, 248, 71, 88, 0, 82, 8, 50, 190, 52, 59, 148, 248, 96, - 18, 77, 196, 254, 226, 120, 253, 203, 211, 140, 16, 45, 136, 49, 179, 84, 247, 88, 4, 44, 0, 1, - 196, 208, 184, 173, 191, 134, 3, 102, 92, 18, 218, 108, 124, 201, 65, 88, 214, 82, 109, 232, - 239, 172, 154, 80, 218, 139, 130, 85, 90, 79, 9, 25, 83, 166, 136, 187, 120, 26, 32, 123, 99, - 235, 105, 65, 251, 29, 148, 172, 85, 133, 180, 169, 244, 80, 94, 188, 33, 172, 98, 86, 168, 32, - 184, 29, 162, 72, 4, 246, 13, 248, 71, 88, 0, 82, 8, 50, 190, 52, 59, 148, 248, 96, 18, 77, - 196, 254, 226, 120, 253, 203, 211, 140, 16, 45, 136, 36, 252, 198, 146, 8, 26, 244, 0, 1, 73, - 77, 42, 31, 244, 15, 178, 114, 138, 199, 199, 203, 234, 0, 48, 138, 199, 156, 9, 6, 248, 154, - 38, 22, 76, 115, 207, 57, 170, 178, 39, 222, 151, 125, 235, 123, 48, 107, 135, 244, 181, 17, - 128, 230, 229, 147, 144, 143, 89, 217, 129, 23, 73, 210, 245, 203, 216, 85, 132, 183, 223, 120, - 145, 20, 164, 72, 4, 1, 251, 13, 248, 71, 88, 0, 82, 8, 50, 190, 52, 59, 148, 248, 96, 18, 77, - 196, 254, 226, 120, 253, 203, 211, 140, 16, 45, 136, 56, 24, 50, 253, 132, 187, 68, 0, 1, 115, - 43, 36, 204, 107, 56, 247, 114, 42, 41, 44, 47, 187, 54, 254, 237, 185, 22, 232, 254, 250, 57, - 79, 111, 141, 55, 120, 96, 28, 189, 201, 63, 83, 16, 20, 139, 100, 6, 105, 141, 170, 224, 211, - 96, 50, 100, 7, 232, 159, 58, 165, 142, 214, 146, 198, 74, 203, 54, 255, 202, 44, 106, 224, 73, - 164, 200, 4, 7, 152, 13, 248, 71, 88, 0, 82, 8, 50, 190, 52, 59, 148, 248, 96, 18, 77, 196, - 254, 226, 120, 253, 203, 211, 140, 16, 45, 136, 2, 208, 61, 93, 15, 6, 91, 64, 0, 1, 193, 158, - 222, 246, 87, 59, 203, 229, 190, 30, 98, 58, 27, 135, 155, 2, 172, 245, 189, 56, 128, 117, 175, - 38, 184, 172, 72, 181, 215, 50, 61, 218, 126, 5, 11, 189, 40, 141, 90, 103, 80, 158, 127, 250, - 46, 84, 198, 228, 191, 52, 194, 201, 148, 244, 209, 29, 248, 149, 228, 18, 192, 193, 142, 105, - 164, 72, 4, 1, 133, 13, 248, 71, 88, 0, 82, 8, 51, 97, 18, 141, 86, 91, 21, 91, 21, 97, 18, 47, - 87, 97, 0, 2, 86, 91, 96, 0, 52, 17, 21, 97, 18, 61, 87, 97, 0, 2, 86, 91, 127, 195, 87, 137, - 204, 255, 118, 39, 29, 192, 239, 166, 191, 222, 47, 77, 74, 50, 205, 72, 221, 134, 39, 143, - 117, 248, 100, 140, 176, 104, 200, 110, 59, 96, 64, 81, 128, 144, 80, 96, 64, 81, 128, 145, 3, - 144, 161, 91, 86, 91, 97, 1, 9, 96, 2, 144, 84, 144, 97, 1, 0, 10, 144, 4, 96, 255, 22, 129, - 86, 91, 97, 1, 7, 96, 0, 80, 84, 129, 86, 91, 96, 0, 96, 0, 97, 1, 2, 96, 0, 80, 96, 0, 132, - 115, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 22, 129, 82, 96, 32, 1, 144, 129, 82, 96, 32, 1, 96, 0, 32, 96, 0, 80, 84, 17, 144, - 80, 97, 18, 202, 86, 91, 145, 144, 80, 86, 91, 96, 0, 96, 0, 96, 0, 144, 84, 144, 97, 1, 0, 10, - 144, 4, 115, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 22, 115, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 22, 51, 115, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 22, 20, 21, 97, 20, 17, 87, 97, 1, 2, - 96, 0, 80, 96, 0, 131, 115, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 22, 129, 82, 96, 32, 1, 144, 129, 82, 96, 32, 1, 96, 0, 32, - 96, 0, 80, 84, 144, 80, 96, 0, 129, 20, 21, 97, 19, 104, 87, 97, 20, 18, 86, 91, 96, 0, 96, 2, - 96, 0, 80, 130, 97, 1, 0, 129, 16, 21, 97, 0, 2, 87, 144, 144, 1, 96, 0, 91, 80, 129, 144, 85, - 80, 96, 0, 97, 1, 2, 96, 0, 80, 96, 0, 132, 115, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 22, 129, 82, 96, 32, 1, 144, 129, 82, - 96, 32, 1, 96, 0, 32, 96, 0, 80, 129, 144, 85, 80, 97, 19, 195, 97, 22, 34, 86, 91, 127, 106, - 55, 255, 197, 41, 144, 4, 93, 52, 105, 153, 104, 72, 171, 129, 100, 143, 64, 11, 22, 72, 62, - 228, 47, 43, 80, 83, 149, 150, 125, 178, 90, 130, 96, 64, 81, 128, 130, 115, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 22, 129, - 82, 96, 32, 1, 145, 80, 80, 96, 64, 81, 128, 145, 3, 144, 161, 91, 91, 80, 80, 86, 91, 96, 0, - 96, 0, 144, 84, 144, 97, 1, 0, 10, 144, 4, 115, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 22, 115, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 22, 51, 115, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 22, - 20, 21, 97, 21, 126, 87, 97, 20, 117, 129, 97, 18, 141, 86, 91, 21, 97, 20, 127, 87, 97, 21, - 127, 86, 91, 96, 250, 96, 1, 96, 0, 80, 84, 16, 21, 21, 97, 20, 151, 87, 97, 20, 150, 97, 22, - 34, 86, 91, 91, 96, 250, 96, 1, 96, 0, 80, 84, 16, 21, 21, 97, 20, 171, 87, 97, 0, 2, 86, 91, - 96, 1, 96, 0, 129, 129, 80, 84, 128, 146, 145, 144, 96, 1, 1, 145, 144, 80, 85, 80, 128, 115, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 22, 96, 2, 96, 0, 80, 96, 1, 96, 0, 80, 84, 97, 1, 0, 129, 16, 21, 97, 0, 2, 87, 144, 144, - 1, 96, 0, 91, 80, 129, 144, 85, 80, 96, 1, 96, 0, 80, 84, 97, 1, 2, 96, 0, 80, 96, 0, 131, 115, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 22, 129, 82, 96, 32, 1, 144, 129, 82, 96, 32, 1, 96, 0, 32, 96, 0, 80, 129, 144, 85, 80, - 127, 135, 104, 53, 165, 177, 225, 234, 82, 14, 237, 35, 221, 101, 60, 244, 112, 186, 127, 171, - 189, 26, 16, 75, 245, 183, 75, 144, 103, 231, 93, 190, 78, 129, 96, 64, 81, 128, 130, 115, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 22, 129, 82, 96, 32, 1, 145, 80, 80, 96, 64, 81, 128, 145, 3, 144, 161, 91, 91, 80, 86, 91, 96, - 0, 96, 1, 144, 80, 91, 96, 1, 96, 0, 80, 84, 129, 96, 255, 22, 17, 21, 21, 97, 21, 223, 87, 96, - 0, 97, 1, 2, 96, 0, 80, 96, 0, 96, 2, 96, 0, 80, 132, 97, 1, 0, 129, 16, 21, 97, 0, 2, 87, 144, - 144, 1, 96, 0, 91, 80, 84, 129, 82, 96, 32, 1, 144, 129, 82, 96, 32, 1, 96, 0, 32, 96, 0, 80, - 129, 144, 85, 80, 91, 128, 128, 96, 1, 1, 145, 80, 80, 97, 21, 137, 86, 91, 96, 2, 96, 0, 80, - 128, 97, 1, 0, 1, 144, 97, 22, 18, 145, 144, 97, 21, 244, 86, 91, 128, 130, 17, 21, 97, 22, 14, - 87, 96, 0, 129, 129, 80, 96, 0, 144, 85, 80, 96, 1, 1, 97, 21, 244, 86, 91, 80, 144, 86, 91, - 80, 96, 0, 96, 1, 96, 0, 80, 129, 144, 85, 80, 91, 80, 86, 91, 96, 0, 96, 1, 144, 80, 91, 96, - 1, 96, 0, 80, 84, 129, 16, 21, 97, 23, 181, 87, 91, 96, 1, 96, 0, 80, 84, 129, 16, 128, 21, 97, - 22, 99, 87, 80, 96, 0, 96, 2, 96, 0, 80, 130, 97, 1, 0, 129, 16, 21, 97, 0, 2, 87, 144, 144, 1, - 96, 0, 91, 80, 84, 20, 21, 91, 21, 97, 22, 117, 87, 128, 128, 96, 1, 1, 145, 80, 80, 97, 22, - 55, 86, 91, 91, 96, 1, 96, 1, 96, 0, 80, 84, 17, 128, 21, 97, 22, 167, 87, 80, 96, 0, 96, 2, - 96, 0, 80, 96, 1, 96, 0, 80, 84, 97, 1, 0, 129, 16, 21, 97, 0, 2, 87, 144, 144, 1, 96, 0, 91, - 80, 84, 20, 91, 21, 97, 22, 198, 87, 96, 1, 96, 0, 129, 129, 80, 84, 128, 146, 145, 144, 96, 1, - 144, 3, 145, 144, 80, 85, 80, 97, 22, 118, 86, 91, 96, 1, 96, 0, 80, 84, 129, 16, 128, 21, 97, - 22, 247, 87, 80, 96, 0, 96, 2, 96, 0, 80, 96, 1, 96, 0, 80, 84, 97, 1, 0, 129, 16, 21, 97, 0, - 2, 87, 144, 144, 1, 96, 0, 91, 80, 84, 20, 21, 91, 128, 21, 97, 23, 26, 87, 80, 96, 0, 96, 2, - 96, 0, 80, 130, 97, 1, 0, 129, 16, 21, 97, 0, 2, 87, 144, 144, 1, 96, 0, 91, 80, 84, 20, 91, - 21, 97, 23, 176, 87, 96, 2, 96, 0, 80, 96, 1, 96, 0, 80, 84, 97, 1, 0, 129, 16, 21, 97, 0, 2, - 87, 144, 144, 1, 96, 0, 91, 80, 84, 96, 2, 96, 0, 80, 130, 97, 1, 0, 129, 16, 21, 97, 0, 2, 87, - 144, 144, 1, 96, 0, 91, 80, 129, 144, 85, 80, 128, 97, 1, 2, 96, 0, 80, 96, 0, 96, 2, 96, 0, - 80, 132, 97, 1, 0, 129, 16, 21, 97, 0, 2, 87, 144, 144, 1, 96, 0, 91, 80, 84, 129, 82, 96, 32, - 1, 144, 129, 82, 96, 32, 1, 96, 0, 32, 96, 0, 80, 129, 144, 85, 80, 96, 0, 96, 2, 96, 0, 80, - 96, 1, 96, 0, 80, 84, 97, 1, 0, 129, 16, 21, 97, 0, 2, 87, 144, 144, 1, 96, 0, 91, 80, 129, - 144, 85, 80, 91, 97, 22, 41, 86, 91, 91, 80, 86, 0, 249, 106, 95, 91, 28, 229, 36, 37, 63, 55, - 110, 42, 105, 114, 43, 250, 111, 10, 24, 8, 233, 6, 28, 91, 183, 243, 215, 130, 111, 49, 186, - 251, 42, 156, 79, 19, 156, 244, 214, 2, 245, 90, 218, 127, 0, 225, 93, 67, 135, 109, 89, 153, - 240, 39, 128, 36, 234, 211, 88, 70, 193, 212, 145, 10, 162, 204, 0, 226, 12, 27, 113, 8, 0, 4, - 147, 116, 94, 208, 139, 67, 66, 36, 106, 62, 207, 86, 192, 94, 2, 162, 12, 0, 24, 12, 146, 90, - 147, 37, 20, 119, 109, 96, 96, 96, 64, 82, 96, 64, 81, 96, 128, 128, 97, 17, 147, 131, 57, 96, - 224, 96, 64, 82, 144, 81, 144, 81, 96, 160, 81, 96, 192, 81, 96, 0, 128, 84, 96, 1, 96, 160, - 96, 2, 10, 3, 25, 22, 51, 23, 144, 85, 96, 1, 132, 129, 85, 96, 2, 132, 144, 85, 96, 3, 131, - 144, 85, 96, 7, 128, 84, 145, 130, 1, 128, 130, 85, 130, 128, 21, 130, 144, 17, 97, 0, 148, 87, - 96, 2, 2, 129, 96, 2, 2, 131, 96, 0, 82, 96, 32, 96, 0, 32, 145, 130, 1, 145, 1, 97, 0, 148, - 145, 144, 91, 128, 130, 17, 21, 97, 1, 118, 87, 128, 84, 96, 1, 96, 168, 96, 2, 10, 3, 25, 22, - 129, 85, 96, 0, 96, 1, 145, 144, 145, 1, 144, 129, 85, 97, 0, 110, 86, 91, 80, 80, 97, 1, 64, - 96, 64, 82, 96, 0, 96, 224, 129, 129, 82, 97, 1, 0, 130, 144, 82, 66, 97, 1, 32, 82, 130, 84, - 144, 147, 80, 129, 16, 21, 97, 0, 2, 87, 144, 129, 82, 127, 166, 108, 201, 40, 181, 237, 184, - 42, 249, 189, 73, 146, 41, 84, 21, 90, 183, 176, 148, 38, 148, 190, 164, 206, 68, 102, 29, 154, - 135, 54, 198, 136, 144, 80, 96, 0, 130, 1, 81, 129, 96, 0, 1, 96, 0, 97, 1, 0, 10, 129, 84, - 129, 96, 1, 96, 160, 96, 2, 10, 3, 2, 25, 22, 144, 131, 2, 23, 144, 85, 80, 96, 32, 130, 1, 81, - 129, 96, 0, 1, 96, 20, 97, 1, 0, 10, 129, 84, 129, 96, 255, 2, 25, 22, 144, 131, 2, 23, 144, - 85, 80, 96, 64, 130, 1, 81, 129, 96, 1, 1, 96, 0, 80, 85, 144, 80, 80, 128, 96, 1, 96, 160, 96, - 2, 10, 3, 22, 96, 0, 20, 21, 21, 97, 1, 100, 87, 128, 96, 0, 96, 0, 97, 1, 0, 10, 129, 84, 129, - 96, 1, 96, 160, 96, 2, 10, 3, 2, 25, 22, 144, 131, 2, 23, 144, 85, 80, 91, 80, 80, 80, 80, 97, - 16, 25, 128, 97, 1, 122, 96, 0, 57, 96, 0, 243, 91, 80, 144, 86, 96, 96, 96, 64, 82, 54, 21, - 97, 0, 185, 87, 96, 224, 96, 2, 10, 96, 0, 53, 4, 99, 1, 60, 240, 139, 129, 20, 97, 0, 187, 87, - 128, 99, 35, 126, 148, 146, 20, 97, 1, 40, 87, 128, 99, 57, 16, 104, 33, 20, 97, 2, 112, 87, - 128, 99, 64, 14, 57, 73, 20, 97, 2, 136, 87, 128, 99, 93, 175, 8, 202, 20, 97, 2, 145, 87, 128, - 99, 97, 87, 5, 192, 20, 97, 2, 252, 87, 128, 99, 105, 189, 52, 54, 20, 97, 3, 36, 87, 128, 99, - 129, 96, 240, 181, 20, 97, 3, 45, 87, 128, 99, 141, 165, 203, 91, 20, 97, 3, 54, 87, 128, 99, - 170, 2, 169, 15, 20, 97, 3, 72, 87, 128, 99, 177, 5, 13, 165, 20, 97, 3, 81, 87, 128, 99, 188, - 202, 31, 211, 20, 97, 4, 63, 87, 128, 99, 211, 192, 113, 91, 20, 97, 4, 103, 87, 128, 99, 236, - 235, 41, 69, 20, 97, 5, 20, 87, 128, 99, 242, 253, 227, 139, 20, 97, 5, 240, 87, 91, 0, 91, 97, - 6, 18, 96, 4, 53, 96, 4, 128, 84, 130, 144, 129, 16, 21, 97, 0, 2, 87, 144, 96, 0, 82, 96, 32, - 96, 0, 32, 144, 96, 10, 2, 1, 96, 0, 80, 96, 5, 129, 1, 84, 129, 84, 96, 1, 131, 1, 84, 96, 3, - 132, 1, 84, 96, 4, 133, 1, 84, 96, 6, 134, 1, 84, 96, 7, 135, 1, 84, 96, 1, 96, 160, 96, 2, 10, - 3, 149, 144, 149, 22, 151, 80, 146, 149, 96, 2, 1, 148, 145, 147, 96, 255, 130, 129, 22, 148, - 97, 1, 0, 144, 147, 4, 22, 146, 145, 144, 137, 86, 91, 96, 64, 128, 81, 96, 32, 96, 36, 128, - 53, 96, 4, 129, 129, 1, 53, 96, 31, 129, 1, 133, 144, 4, 133, 2, 134, 1, 133, 1, 144, 150, 82, - 133, 133, 82, 97, 6, 235, 149, 129, 53, 149, 145, 148, 96, 68, 148, 146, 147, 144, 146, 1, 145, - 129, 144, 132, 1, 131, 130, 128, 130, 132, 55, 80, 148, 150, 80, 80, 80, 80, 80, 80, 80, 96, 0, - 96, 0, 96, 4, 96, 0, 80, 132, 129, 84, 129, 16, 21, 97, 0, 2, 87, 80, 144, 82, 127, 138, 53, - 172, 251, 193, 95, 248, 26, 57, 174, 125, 52, 79, 215, 9, 242, 142, 134, 0, 180, 170, 140, 101, - 198, 182, 75, 254, 127, 227, 107, 209, 158, 96, 10, 132, 2, 144, 129, 1, 84, 96, 0, 128, 81, - 96, 32, 97, 15, 249, 131, 57, 129, 81, 145, 82, 144, 145, 1, 144, 66, 16, 128, 97, 1, 212, 87, - 80, 96, 4, 129, 1, 84, 96, 255, 22, 91, 128, 97, 2, 85, 87, 80, 128, 96, 0, 1, 96, 0, 144, 84, - 144, 97, 1, 0, 10, 144, 4, 96, 1, 96, 160, 96, 2, 10, 3, 22, 129, 96, 1, 1, 96, 0, 80, 84, 132, - 96, 64, 81, 128, 132, 96, 1, 96, 160, 96, 2, 10, 3, 22, 96, 96, 96, 2, 10, 2, 129, 82, 96, 20, - 1, 131, 129, 82, 96, 32, 1, 130, 128, 81, 144, 96, 32, 1, 144, 128, 131, 131, 130, 144, 96, 0, - 96, 4, 96, 32, 132, 96, 31, 1, 4, 96, 3, 2, 96, 15, 1, 241, 80, 144, 80, 1, 147, 80, 80, 80, - 80, 96, 64, 81, 128, 145, 3, 144, 32, 129, 96, 7, 1, 96, 0, 80, 84, 20, 21, 91, 128, 97, 2, - 102, 87, 80, 96, 1, 84, 96, 5, 130, 1, 84, 17, 21, 91, 21, 97, 14, 154, 87, 97, 0, 2, 86, 91, - 97, 6, 235, 96, 4, 53, 96, 6, 96, 32, 82, 96, 0, 144, 129, 82, 96, 64, 144, 32, 84, 129, 86, - 91, 97, 6, 235, 96, 5, 84, 129, 86, 91, 97, 6, 253, 96, 4, 53, 96, 7, 128, 84, 130, 144, 129, - 16, 21, 97, 0, 2, 87, 80, 96, 0, 82, 96, 2, 2, 96, 0, 128, 81, 96, 32, 97, 15, 217, 131, 57, - 129, 81, 145, 82, 129, 1, 84, 127, 166, 108, 201, 40, 181, 237, 184, 42, 249, 189, 73, 146, 41, - 84, 21, 90, 183, 176, 148, 38, 148, 190, 164, 206, 68, 102, 29, 154, 135, 54, 198, 137, 145, - 144, 145, 1, 84, 96, 1, 96, 160, 96, 2, 10, 3, 130, 22, 145, 96, 160, 96, 2, 10, 144, 4, 96, - 255, 22, 144, 131, 86, 91, 97, 0, 185, 96, 4, 53, 96, 36, 53, 96, 0, 128, 84, 129, 144, 96, 1, - 96, 160, 96, 2, 10, 3, 144, 129, 22, 51, 144, 145, 22, 20, 97, 7, 88, 87, 97, 0, 2, 86, 91, 97, - 6, 235, 96, 2, 84, 129, 86, 91, 97, 6, 235, 96, 1, 84, 129, 86, 91, 97, 7, 38, 96, 0, 84, 96, - 1, 96, 160, 96, 2, 10, 3, 22, 129, 86, 91, 97, 6, 235, 96, 3, 84, 129, 86, 91, 96, 64, 128, 81, - 96, 32, 96, 68, 53, 96, 4, 129, 129, 1, 53, 96, 31, 129, 1, 132, 144, 4, 132, 2, 133, 1, 132, - 1, 144, 149, 82, 132, 132, 82, 97, 6, 235, 148, 129, 53, 148, 96, 36, 128, 53, 149, 147, 148, - 96, 100, 148, 146, 147, 145, 1, 145, 129, 144, 132, 1, 131, 130, 128, 130, 132, 55, 80, 80, 96, - 64, 128, 81, 96, 32, 151, 53, 128, 138, 1, 53, 96, 31, 129, 1, 138, 144, 4, 138, 2, 131, 1, - 138, 1, 144, 147, 82, 130, 130, 82, 150, 152, 151, 96, 132, 151, 145, 150, 80, 96, 36, 144, - 145, 1, 148, 80, 144, 146, 80, 130, 145, 80, 132, 1, 131, 130, 128, 130, 132, 55, 80, 148, 150, - 80, 80, 80, 80, 80, 80, 80, 51, 96, 1, 96, 160, 96, 2, 10, 3, 22, 96, 0, 144, 129, 82, 96, 6, - 96, 32, 82, 96, 64, 129, 32, 84, 129, 144, 129, 20, 128, 97, 4, 53, 87, 80, 96, 64, 129, 32, - 84, 96, 7, 128, 84, 144, 145, 144, 129, 16, 21, 97, 0, 2, 87, 144, 130, 82, 96, 2, 2, 96, 0, - 128, 81, 96, 32, 97, 15, 217, 131, 57, 129, 81, 145, 82, 1, 84, 96, 160, 96, 2, 10, 144, 4, 96, - 255, 22, 21, 91, 21, 97, 10, 250, 87, 97, 0, 2, 86, 91, 97, 0, 185, 96, 4, 53, 96, 36, 53, 96, - 68, 53, 96, 0, 84, 96, 1, 96, 160, 0, 191, 184, 205, 227, 212, 214, 54, 31, 252, 91, 1, 37, - 235, 158, 78, 250, 150, 233, 164, 161, 38, 147, 245, 157, 104, 111, 134, 75, 238, 225, 120, 82, - 49, 68, 219, 49, 181, 1, 152, 230, 70, 106, 210, 32, 167, 68, 68, 57, 165, 161, 194, 243, 229, - 163, 156, 210, 171, 18, 42, 102, 9, 45, 11, 26, 166, 76, 4, 1, 23, 55, 11, 164, 59, 116, 0, 1, - 95, 144, 198, 94, 36, 34, 126, 64, 166, 160, 190, 92, 92, 241, 240, 241, 32, 194, 49, 14, 159, - 122, 50, 153, 153, 51, 139, 190, 24, 0, 1, 148, 191, 241, 167, 34, 189, 244, 88, 52, 38, 43, - 134, 170, 255, 215, 230, 114, 97, 17, 183, 179, 120, 31, 99, 141, 172, 151, 184, 21, 10, 208, - 179, 238, 109, 102, 58, 96, 5, 203, 147, 53, 168, 18, 18, 26, 222, 3, 13, 210, 78, 131, 3, 205, - 13, 30, 32, 56, 92, 97, 122, 215, 142, 218, 111, 166, 76, 4, 1, 23, 56, 11, 164, 59, 116, 0, 1, - 95, 144, 229, 49, 180, 239, 51, 228, 148, 55, 211, 161, 205, 7, 52, 203, 67, 93, 42, 227, 36, - 28, 56, 131, 9, 178, 108, 94, 44, 0, 1, 174, 163, 167, 172, 13, 45, 105, 145, 199, 146, 40, - 107, 162, 229, 222, 202, 182, 197, 116, 29, 185, 24, 234, 179, 225, 173, 102, 201, 215, 112, - 38, 208, 128, 196, 44, 69, 102, 118, 163, 118, 1, 203, 2, 20, 200, 184, 183, 85, 24, 212, 122, - 227, 184, 45, 84, 202, 50, 233, 179, 155, 0, 101, 151, 123, 162, 12, 0, 3, 11, 164, 59, 116, 0, - 6, 54, 196, 96, 96, 96, 64, 82, 96, 64, 81, 97, 4, 126, 56, 3, 128, 97, 4, 126, 131, 57, 129, - 1, 96, 64, 82, 128, 81, 96, 128, 81, 96, 160, 81, 96, 192, 81, 146, 147, 145, 130, 1, 146, 145, - 1, 144, 131, 96, 0, 20, 21, 97, 0, 59, 87, 98, 15, 66, 64, 147, 80, 91, 96, 1, 96, 160, 96, 2, - 10, 3, 51, 22, 96, 0, 144, 129, 82, 96, 3, 96, 32, 144, 129, 82, 96, 64, 130, 32, 134, 144, 85, - 132, 81, 130, 84, 131, 128, 82, 96, 2, 96, 1, 130, 22, 21, 97, 1, 0, 2, 96, 0, 25, 1, 144, 145, - 22, 4, 96, 31, 144, 129, 1, 131, 144, 4, 127, 41, 13, 236, 217, 84, 139, 98, 168, 214, 3, 69, - 169, 136, 56, 111, 200, 75, 166, 188, 149, 72, 64, 8, 246, 54, 47, 147, 22, 14, 243, 229, 99, - 144, 129, 1, 147, 144, 145, 144, 136, 1, 144, 131, 144, 16, 97, 0, 245, 87, 128, 81, 96, 255, - 25, 22, 131, 128, 1, 23, 133, 85, 91, 80, 97, 1, 37, 146, 145, 80, 91, 128, 130, 17, 21, 97, 1, - 126, 87, 96, 0, 129, 85, 96, 1, 1, 97, 0, 193, 86, 91, 80, 80, 96, 2, 128, 84, 96, 255, 25, 22, - 130, 23, 144, 85, 80, 80, 80, 80, 97, 2, 204, 128, 97, 1, 178, 96, 0, 57, 96, 0, 243, 91, 130, - 128, 1, 96, 1, 1, 133, 85, 130, 21, 97, 0, 185, 87, 145, 130, 1, 91, 130, 129, 17, 21, 97, 0, - 185, 87, 130, 81, 130, 96, 0, 80, 85, 145, 96, 32, 1, 145, 144, 96, 1, 1, 144, 97, 1, 7, 86, - 91, 80, 80, 129, 96, 1, 96, 0, 80, 144, 128, 81, 144, 96, 32, 1, 144, 130, 128, 84, 96, 1, 129, - 96, 1, 22, 21, 97, 1, 0, 2, 3, 22, 96, 2, 144, 4, 144, 96, 0, 82, 96, 32, 96, 0, 32, 144, 96, - 31, 1, 96, 32, 144, 4, 129, 1, 146, 130, 96, 31, 16, 97, 1, 130, 87, 128, 81, 96, 255, 25, 22, - 131, 128, 1, 23, 133, 85, 91, 80, 97, 0, 213, 146, 145, 80, 97, 0, 193, 86, 91, 80, 144, 86, - 91, 130, 128, 1, 96, 1, 1, 133, 85, 130, 21, 97, 1, 114, 87, 145, 130, 1, 91, 130, 129, 17, 21, - 97, 1, 114, 87, 130, 81, 130, 96, 0, 80, 85, 145, 96, 32, 1, 145, 144, 96, 1, 1, 144, 97, 1, - 148, 86, 96, 96, 96, 64, 82, 96, 224, 96, 2, 10, 96, 0, 53, 4, 99, 6, 253, 222, 3, 129, 20, 97, - 0, 71, 87, 128, 99, 49, 60, 229, 103, 20, 97, 0, 164, 87, 128, 99, 112, 160, 130, 49, 20, 97, - 0, 176, 87, 128, 99, 149, 216, 155, 65, 20, 97, 0, 200, 87, 128, 99, 169, 5, 156, 187, 20, 97, - 1, 35, 87, 91, 0, 91, 97, 1, 82, 96, 0, 128, 84, 96, 32, 96, 2, 96, 1, 131, 22, 21, 97, 1, 0, - 2, 96, 0, 25, 1, 144, 146, 22, 145, 144, 145, 4, 96, 31, 129, 1, 130, 144, 4, 144, 145, 2, 96, - 128, 144, 129, 1, 96, 64, 82, 96, 96, 130, 129, 82, 146, 145, 144, 130, 130, 128, 21, 97, 1, - 245, 87, 128, 96, 31, 16, 97, 1, 202, 87, 97, 1, 0, 128, 131, 84, 4, 2, 131, 82, 145, 96, 32, - 1, 145, 97, 1, 245, 86, 91, 97, 1, 192, 96, 2, 84, 96, 255, 22, 129, 86, 91, 97, 1, 192, 96, 4, - 53, 96, 3, 96, 32, 82, 96, 0, 144, 129, 82, 96, 64, 144, 32, 84, 129, 86, 91, 97, 1, 82, 96, 1, - 128, 84, 96, 32, 96, 31, 96, 2, 96, 0, 25, 97, 1, 0, 133, 135, 22, 21, 2, 1, 144, 147, 22, 146, - 144, 146, 4, 145, 130, 1, 129, 144, 4, 2, 96, 128, 144, 129, 1, 96, 64, 82, 96, 96, 130, 129, - 82, 146, 145, 144, 130, 130, 128, 21, 97, 1, 245, 87, 128, 96, 31, 16, 97, 1, 202, 87, 97, 1, - 0, 128, 131, 84, 4, 2, 131, 82, 145, 96, 32, 1, 145, 97, 1, 245, 86, 91, 97, 0, 69, 96, 4, 53, - 96, 36, 53, 96, 1, 96, 160, 96, 2, 10, 3, 51, 22, 96, 0, 144, 129, 82, 96, 3, 96, 32, 82, 96, - 64, 144, 32, 84, 129, 144, 16, 21, 97, 1, 253, 87, 97, 0, 2, 86, 91, 96, 64, 81, 128, 128, 96, - 32, 1, 130, 129, 3, 130, 82, 131, 129, 129, 81, 129, 82, 96, 32, 1, 145, 80, 128, 81, 144, 96, - 32, 1, 144, 128, 131, 131, 130, 144, 96, 0, 96, 4, 96, 32, 132, 96, 31, 1, 4, 96, 3, 2, 96, 15, - 1, 241, 80, 144, 80, 144, 129, 1, 144, 96, 31, 22, 128, 21, 97, 1, 178, 87, 128, 130, 3, 128, - 81, 96, 1, 131, 96, 32, 3, 97, 1, 0, 10, 3, 25, 22, 129, 82, 96, 32, 1, 145, 80, 91, 80, 146, - 80, 80, 80, 96, 64, 81, 128, 145, 3, 144, 243, 91, 96, 96, 144, 129, 82, 96, 32, 144, 243, 91, - 130, 1, 145, 144, 96, 0, 82, 96, 32, 96, 0, 32, 144, 91, 129, 84, 129, 82, 144, 96, 1, 1, 144, - 96, 32, 1, 128, 131, 17, 97, 1, 216, 87, 130, 144, 3, 96, 31, 22, 130, 1, 145, 91, 80, 80, 80, - 80, 80, 129, 86, 91, 96, 1, 96, 160, 96, 2, 10, 3, 130, 22, 96, 0, 144, 129, 82, 96, 64, 144, - 32, 84, 128, 130, 1, 16, 21, 97, 2, 31, 87, 97, 0, 2, 86, 91, 128, 96, 3, 96, 0, 80, 96, 0, 51, - 96, 1, 96, 160, 96, 2, 10, 3, 22, 129, 82, 96, 32, 1, 144, 129, 82, 96, 32, 1, 96, 0, 32, 96, - 0, 130, 130, 130, 80, 84, 3, 146, 80, 80, 129, 144, 85, 80, 128, 96, 3, 96, 0, 80, 96, 0, 132, - 96, 1, 96, 160, 96, 2, 10, 3, 22, 129, 82, 96, 32, 1, 144, 129, 82, 96, 32, 1, 96, 0, 32, 96, - 0, 130, 130, 130, 80, 84, 1, 146, 80, 80, 129, 144, 85, 80, 129, 96, 1, 96, 160, 96, 2, 10, 3, - 22, 51, 96, 1, 96, 160, 96, 2, 10, 3, 22, 127, 221, 242, 82, 173, 27, 226, 200, 155, 105, 194, - 176, 104, 252, 55, 141, 170, 149, 43, 167, 241, 99, 196, 161, 22, 40, 245, 90, 77, 245, 35, - 179, 239, 131, 96, 64, 81, 128, 130, 129, 82, 96, 32, 1, 145, 80, 80, 96, 64, 81, 128, 145, 3, - 144, 163, 80, 80, 86, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 2, 154, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 28, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 174, 51, 34, 132, 51, 184, 36, 23, 142, 253, 84, 101, 242, 186, - 139, 139, 122, 64, 24, 217, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 249, 206, 173, 44, 187, 144, - 93, 173, 192, 73, 14, 134, 173, 148, 1, 51, 58, 0, 111, 52, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 14, 159, 196, 240, 187, 201, 37, 138, 181, 165, 32, 83, 80, 61, 143, 59, 62, 70, 97, 11, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 92, 70, 50, 217, 160, 179, 101, 184, 57, 237, 94, 199, 160, 17, - 26, 126, 192, 192, 150, 137, 1, 124, 238, 255, 97, 39, 104, 74, 9, 68, 49, 78, 55, 100, 95, - 222, 94, 7, 249, 239, 97, 41, 224, 195, 217, 126, 237, 7, 178, 207, 192, 199, 41, 254, 151, 36, - 15, 199, 248, 148, 144, 201, 19, 141, 122, 83, 203, 6, 138, 173, 50, 254, 58, 154, 245, 208, - 106, 212, 93, 51, 183, 199, 254, 73, 78, 162, 204, 0, 8, 12, 27, 113, 8, 0, 3, 208, 144, 125, - 229, 171, 167, 222, 114, 137, 80, 201, 44, 87, 208, 142, 32, 212, 7, 113, 97, 241, 47, 1, 62, - 212, 134, 121, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 212, 233, 58, 75, 212, 35, 59, 42, 176, 70, - 144, 104, 247, 40, 108, 8, 9, 135, 119, 120, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 86, 121, 73, 115, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 192, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 73, - 12, 13, 209, 59, 254, 165, 134, 92, 169, 133, 41, 124, 242, 190, 211, 247, 123, 235, 93, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 173, 176, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, - 181, 15, 99, 14, 176, 128, 156, 24, 164, 208, 11, 214, 97, 16, 180, 132, 137, 229, 115, 159, - 162, 10, 34, 150, 148, 144, 146, 119, 73, 133, 182, 224, 36, 120, 40, 173, 76, 28, 0, 228, 161, - 53, 193, 236, 73, 214, 36, 2, 183, 88, 65, 149, 121, 94, 104, 53, 151, 248, 47, 116, 166, 118, - 124, 245, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 27, 0, 243, 247, 82, 42, 70, 20, 233, 164, 103, 125, 20, 94, 242, 0, 187, 118, 210, 9, - 97, 123, 199, 32, 99, 116, 254, 245, 108, 72, 205, 4, 170, 123, 9, 0, 211, 37, 42, 128, 44, - 109, 28, 168, 33, 111, 219, 132, 173, 146, 224, 83, 100, 144, 97, 226, 179, 127, 82, 185, 96, - 118, 18, 55, 26, 24, 164, 76, 0, 60, 189, 11, 164, 59, 116, 0, 3, 13, 64, 151, 25, 57, 167, - 123, 10, 64, 170, 104, 241, 38, 114, 38, 136, 240, 231, 147, 22, 33, 186, 125, 36, 42, 229, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 179, 99, 235, 164, - 26, 170, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 64, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 34, 18, 32, 188, 250, 105, 202, 162, 17, 56, 225, 143, 145, 244, 185, 211, 228, 122, - 205, 132, 14, 195, 222, 46, 36, 204, 87, 114, 106, 122, 207, 84, 222, 26, 78, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 6, 242, 100, 220, - 251, 71, 109, 232, 159, 56, 24, 16, 105, 40, 39, 74, 37, 132, 235, 126, 123, 27, 137, 39, 222, - 209, 27, 106, 95, 30, 11, 93, 147, 142, 61, 224, 217, 193, 36, 56, 199, 99, 69, 17, 152, 28, - 155, 7, 255, 156, 50, 99, 95, 209, 192, 163, 80, 27, 159, 111, 31, 93, 133, 17, 166, 76, 4, 1, - 15, 137, 11, 164, 59, 116, 0, 1, 95, 144, 149, 79, 163, 170, 235, 34, 114, 166, 23, 103, 218, - 137, 60, 157, 141, 253, 78, 222, 246, 77, 41, 172, 64, 32, 135, 40, 176, 0, 0, 94, 128, 35, - 199, 93, 7, 1, 125, 56, 243, 9, 120, 182, 238, 136, 110, 95, 245, 100, 112, 115, 211, 161, 222, - 207, 201, 130, 185, 12, 62, 219, 90, 13, 38, 154, 171, 231, 219, 129, 167, 116, 37, 39, 123, - 69, 14, 199, 6, 79, 184, 108, 13, 246, 109, 165, 188, 60, 247, 198, 250, 159, 102, 66, 79, 164, - 200, 3, 47, 211, 11, 164, 59, 116, 0, 82, 8, 196, 122, 170, 134, 0, 8, 190, 111, 101, 181, 140, - 108, 110, 2, 168, 78, 102, 110, 254, 49, 48, 162, 225, 230, 118, 212, 0, 1, 242, 73, 99, 212, - 167, 222, 160, 100, 162, 48, 169, 134, 151, 102, 36, 92, 53, 147, 96, 148, 113, 147, 58, 152, - 135, 245, 6, 220, 95, 176, 158, 241, 26, 222, 158, 36, 167, 208, 117, 187, 126, 128, 60, 65, - 158, 236, 26, 134, 67, 194, 185, 209, 159, 175, 149, 235, 80, 22, 197, 81, 255, 176, 225, 98, - 162, 200, 3, 60, 11, 164, 59, 116, 0, 82, 8, 196, 122, 170, 134, 0, 8, 190, 111, 101, 181, 140, - 108, 110, 2, 168, 78, 102, 110, 254, 49, 200, 106, 208, 183, 28, 240, 0, 0, 162, 215, 130, 81, - 209, 140, 132, 42, 34, 5, 241, 91, 122, 128, 124, 134, 160, 62, 178, 115, 112, 173, 76, 82, 4, - 64, 167, 176, 43, 12, 234, 97, 95, 236, 113, 43, 144, 101, 181, 125, 102, 204, 35, 248, 183, 6, - 46, 129, 30, 144, 133, 173, 181, 176, 108, 166, 140, 13, 59, 123, 10, 139, 108, 48, 164, 76, 0, - 13, 39, 11, 164, 59, 116, 0, 45, 198, 192, 199, 105, 107, 39, 131, 13, 216, 170, 72, 35, 161, - 203, 168, 68, 12, 39, 195, 106, 222, 196, 145, 183, 245, 237, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 90, 1, 133, 244, 242, 89, 234, 71, - 22, 164, 254, 246, 53, 173, 91, 114, 159, 244, 90, 197, 226, 28, 121, 118, 171, 112, 254, 250, - 145, 93, 71, 86, 149, 15, 235, 168, 198, 0, 124, 29, 208, 115, 235, 66, 229, 115, 80, 221, 224, - 241, 239, 29, 158, 132, 146, 67, 119, 55, 86, 169, 86, 36, 8, 27, 215, 66, 162, 204, 0, 96, 12, - 27, 113, 8, 0, 4, 147, 224, 125, 229, 171, 167, 222, 114, 137, 80, 201, 44, 87, 208, 142, 32, - 212, 7, 113, 97, 241, 47, 1, 101, 30, 114, 60, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 219, 247, 6, - 174, 65, 148, 170, 181, 252, 102, 90, 215, 100, 204, 64, 239, 153, 187, 18, 48, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 86, 121, 73, 221, 0, 0, 0, - 0, 0, 0, 0, 61, 82, 38, 24, 124, 56, 204, 21, 57, 138, 34, 113, 72, 250, 84, 181, 24, 162, 105, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 98, 90, - 0, 0, 239, 145, 45, 46, 87, 162, 215, 248, 117, 212, 67, 61, 26, 42, 97, 202, 170, 112, 14, 0, - 153, 64, 158, 190, 200, 26, 43, 223, 27, 216, 115, 230, 59, 187, 0, 32, 92, 35, 3, 24, 5, 202, - 244, 227, 111, 5, 216, 37, 15, 124, 75, 79, 139, 68, 114, 154, 173, 34, 199, 145, 109, 144, - 179, 14, 162, 76, 0, 243, 12, 90, 235, 78, 37, 2, 73, 240, 17, 88, 195, 201, 167, 14, 133, 216, - 53, 137, 114, 129, 14, 217, 132, 200, 230, 255, 207, 15, 216, 92, 137, 102, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 215, 158, 156, 72, 106, 113, 170, 253, 80, 206, 33, 144, 115, 192, 214, 121, - 235, 136, 112, 145, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 1, 49, 45, 0, 0, 27, 59, 19, 14, 114, 14, 82, 13, 33, 152, 182, 178, 113, 89, 228, - 152, 82, 95, 176, 0, 118, 141, 25, 131, 224, 35, 9, 19, 69, 151, 159, 19, 101, 208, 242, 110, - 243, 145, 75, 154, 1, 39, 74, 31, 208, 146, 150, 161, 109, 141, 166, 39, 131, 212, 168, 141, - 67, 19, 34, 112, 123, 208, 208, 95, 162, 76, 0, 244, 12, 90, 235, 78, 37, 2, 73, 240, 17, 88, - 195, 201, 167, 14, 133, 216, 53, 137, 114, 129, 14, 217, 132, 200, 230, 255, 207, 15, 216, 92, - 137, 102, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 105, 59, 164, 26, 157, 70, 192, 125, 145, 139, 2, - 115, 252, 159, 213, 190, 222, 38, 171, 55, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 76, 75, 64, 1, 114, 193, 136, 177, 15, 170, 164, 17, 96, - 233, 155, 99, 17, 163, 103, 111, 121, 137, 109, 0, 111, 130, 115, 230, 101, 148, 82, 47, 100, - 142, 64, 52, 104, 76, 93, 62, 22, 42, 227, 242, 67, 67, 199, 230, 78, 37, 132, 123, 107, 29, - 48, 169, 221, 64, 165, 92, 162, 7, 215, 226, 47, 75, 1, 61, 162, 76, 0, 245, 12, 90, 235, 78, - 37, 2, 73, 240, 17, 88, 195, 201, 167, 14, 133, 216, 53, 137, 114, 129, 14, 217, 132, 200, 230, - 255, 207, 15, 216, 92, 137, 102, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 222, 102, 7, 94, 241, 66, - 21, 241, 175, 105, 205, 195, 74, 207, 244, 189, 4, 1, 220, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 150, 237, 64, 0, 253, 62, 243, 193, 37, - 125, 58, 222, 41, 173, 100, 238, 49, 7, 253, 143, 78, 59, 102, 47, 146, 34, 93, 254, 56, 234, - 217, 28, 94, 150, 212, 59, 79, 127, 182, 215, 203, 144, 47, 248, 151, 168, 4, 171, 205, 228, - 116, 171, 84, 97, 12, 225, 20, 191, 101, 228, 162, 24, 145, 173, 223, 1, 189, 74, 162, 76, 0, - 246, 12, 90, 235, 78, 37, 2, 73, 240, 17, 88, 195, 201, 167, 14, 133, 216, 53, 137, 114, 129, - 14, 217, 132, 200, 230, 255, 207, 15, 216, 92, 137, 102, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 234, 217, 172, 106, 63, 1, 85, 222, 35, 27, 134, 203, 199, 29, 109, 7, 215, 89, 137, 227, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 76, 75, 64, 0, - 222, 109, 26, 58, 82, 31, 109, 241, 136, 134, 235, 1, 198, 201, 128, 222, 250, 77, 95, 88, 5, - 71, 128, 153, 31, 80, 149, 147, 155, 232, 127, 178, 225, 29, 199, 208, 229, 214, 87, 214, 105, - 168, 254, 142, 156, 91, 29, 45, 183, 255, 183, 170, 68, 194, 51, 78, 122, 207, 139, 238, 166, - 59, 173, 40, 162, 76, 0, 247, 12, 90, 235, 78, 37, 2, 73, 240, 17, 88, 195, 201, 167, 14, 133, - 216, 53, 137, 114, 129, 14, 217, 132, 200, 230, 255, 207, 15, 216, 92, 137, 102, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 103, 22, 162, 53, 227, 50, 72, 108, 110, 202, 230, 77, 102, 202, 89, 143, - 157, 33, 11, 196, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 122, 18, 0, 0, 145, 76, 97, 231, 59, 151, 192, 97, 66, 254, 19, 168, 82, 132, 94, 40, - 154, 139, 198, 183, 59, 94, 177, 30, 146, 168, 114, 146, 22, 73, 34, 180, 58, 136, 26, 63, 167, - 54, 137, 88, 166, 136, 144, 186, 11, 199, 142, 123, 75, 215, 78, 39, 211, 74, 187, 239, 202, - 58, 11, 187, 234, 110, 15, 120, 162, 76, 0, 248, 12, 90, 235, 78, 37, 2, 73, 240, 17, 88, 195, - 201, 167, 14, 133, 216, 53, 137, 114, 129, 14, 217, 132, 200, 230, 255, 207, 15, 216, 92, 137, - 102, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 130, 108, 26, 5, 192, 114, 170, 205, 191, 58, 240, 5, - 186, 8, 194, 48, 244, 236, 125, 63, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 25, 252, 213, 0, 0, 232, 226, 36, 45, 179, 208, 197, 139, 247, 81, 166, - 13, 128, 196, 241, 77, 124, 113, 116, 197, 117, 116, 15, 56, 89, 5, 1, 225, 43, 87, 23, 101, - 52, 67, 229, 71, 78, 110, 217, 201, 141, 191, 180, 205, 229, 200, 136, 234, 220, 184, 171, 191, - 223, 255, 203, 131, 79, 212, 215, 210, 134, 68, 95, 62, 162, 76, 0, 249, 12, 90, 235, 78, 37, - 2, 73, 240, 17, 88, 195, 201, 167, 14, 133, 216, 53, 137, 114, 129, 14, 217, 132, 200, 230, - 255, 207, 15, 216, 92, 137, 102, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 238, 243, 201, 215, 170, - 230, 104, 128, 99, 217, 15, 153, 40, 150, 106, 86, 39, 255, 160, 138, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 152, 150, 128, 0, 217, 17, 170, - 41, 2, 47, 216, 161, 169, 172, 143, 177, 29, 144, 73, 26, 58, 28, 187, 30, 107, 219, 22, 64, - 83, 229, 215, 180, 104, 173, 201, 0, 105, 191, 155, 6, 243, 161, 2, 18, 42, 239, 144, 108, 24, - 56, 16, 106, 138, 181, 169, 215, 86, 74, 250, 69, 204, 109, 243, 218, 190, 65, 0, 57, 162, 76, - 0, 250, 12, 90, 235, 78, 37, 2, 73, 240, 17, 88, 195, 201, 167, 14, 133, 216, 53, 137, 114, - 129, 14, 217, 132, 200, 230, 255, 207, 15, 216, 92, 137, 102, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 97, 201, 71, 238, 134, 67, 252, 27, 169, 137, 145, 65, 46, 236, 161, 245, 232, 211, 217, 29, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 76, 75, - 64, 0, 168, 31, 185, 35, 154, 182, 89, 51, 56, 71, 129, 18, 70, 243, 71, 173, 244, 157, 19, 24, - 173, 73, 207, 203, 12, 200, 141, 189, 227, 117, 185, 175, 178, 156, 207, 190, 107, 197, 85, - 207, 147, 238, 99, 3, 110, 74, 24, 154, 102, 18, 28, 236, 153, 250, 188, 65, 60, 88, 240, 131, - 134, 103, 195, 75, 162, 76, 0, 251, 12, 90, 235, 78, 37, 2, 73, 240, 17, 88, 195, 201, 167, 14, - 133, 216, 53, 137, 114, 129, 14, 217, 132, 200, 230, 255, 207, 15, 216, 92, 137, 102, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 60, 107, 2, 123, 147, 7, 73, 155, 83, 87, 16, 47, 171, 18, 135, 51, - 212, 29, 62, 49, 0, 0, 0, 0, 0, 0, 0, 208, 142, 32, 212, 7, 113, 97, 241, 47, 1, 101, 30, 114, - 60, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 221, 92, 211, 41, 110, 5, 221, 120, 222, 140, 209, 147, - 170, 87, 158, 119, 77, 177, 38, 121, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 86, 111, 4, 94, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 192, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 96, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 215, 161, 112, 69, 141, 23, 82, 45, 208, - 114, 62, 168, 253, 129, 36, 79, 28, 115, 55, 82, 206, 89, 35, 111, 132, 86, 212, 132, 245, 38, - 60, 68, 70, 116, 134, 64, 120, 154, 194, 75, 142, 197, 41, 115, 255, 7, 46, 122, 16, 146, 124, - 79, 204, 126, 1, 146, 209, 114, 234, 156, 197, 191, 127, 105, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 28, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 221, 92, 211, 41, 110, 5, 221, 120, 222, 140, 209, 147, 170, 87, 158, 119, 77, 177, 38, - 121, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 167, 204, 214, 185, 93, 150, 113, 161, 160, 222, 41, - 101, 31, 123, 131, 112, 17, 36, 226, 162, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 59, 43, 244, 14, - 0, 226, 171, 248, 105, 170, 94, 105, 122, 119, 115, 84, 12, 135, 55, 167, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 222, 77, 160, 152, 3, 94, 206, 58, 65, 60, 127, 246, 121, 195, 4, 154, 242, 201, - 155, 114, 1, 48, 254, 106, 143, 183, 161, 224, 39, 52, 101, 86, 27, 53, 205, 199, 104, 189, - 239, 206, 148, 58, 192, 47, 190, 185, 193, 78, 213, 188, 26, 34, 234, 187, 236, 239, 189, 37, - 143, 97, 234, 233, 127, 17, 226, 75, 175, 252, 95, 142, 225, 82, 70, 240, 71, 181, 60, 201, - 245, 252, 32, 93, 196, 168, 121, 164, 76, 0, 1, 49, 11, 164, 59, 116, 0, 3, 208, 144, 77, 99, - 135, 243, 185, 103, 218, 57, 177, 29, 225, 17, 21, 141, 73, 117, 76, 49, 152, 93, 240, 16, 38, - 74, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 34, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 160, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 37, 120, 83, - 177, 221, 142, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 196, 105, 224, 22, 25, 104, 241, 42, - 56, 228, 193, 161, 193, 83, 161, 130, 7, 224, 64, 200, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 196, - 105, 224, 22, 25, 104, 241, 42, 56, 228, 193, 161, 193, 83, 161, 130, 7, 224, 64, 200, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 34, 49, 50, - 117, 105, 97, 54, 74, 107, 114, 122, 78, 114, 51, 87, 49, 77, 84, 55, 68, 50, 75, 70, 104, 72, - 72, 118, 110, 82, 51, 89, 97, 51, 98, 70, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 222, 210, 150, 105, 178, 159, 78, 33, 17, 244, 237, 34, - 244, 190, 231, 208, 128, 179, 99, 10, 43, 222, 242, 37, 249, 200, 255, 109, 155, 23, 18, 76, - 147, 3, 39, 200, 142, 93, 66, 15, 66, 249, 181, 89, 254, 167, 65, 207, 248, 202, 118, 148, 164, - 124, 194, 251, 237, 118, 184, 152, 30, 76, 209, 35, 164, 76, 4, 1, 50, 11, 164, 59, 116, 0, 1, - 95, 144, 5, 101, 7, 16, 143, 230, 211, 1, 137, 236, 205, 108, 215, 244, 250, 213, 110, 238, - 246, 34, 37, 102, 144, 56, 165, 173, 128, 0, 0, 0, 2, 255, 0, 2, 255, 0, 46, 40, 143, 180, 0, - 118, 46, 90, 40, 6, 66, 66, 142, 40, 53, 148, 198, 21, 248, 9, 125, 98, 196, 73, 245, 6, 13, - 86, 111, 221, 174, 81, 224, 203, 242, 98, 253, 8, 68, 63, 136, 215, 192, 143, 241, 205, 37, - 128, 248, 152, 186, 177, 19, 133, 59, 118, 123, 68, 119, 183, 128, 221, 51, 127, 118, 100, 133, - 69, 25, 164, 76, 4, 192, 207, 11, 164, 59, 116, 0, 1, 95, 144, 196, 57, 87, 89, 226, 100, 105, - 186, 160, 230, 66, 27, 220, 29, 2, 50, 198, 244, 182, 195, 69, 101, 191, 237, 244, 204, 124, 0, - 1, 169, 249, 103, 162, 107, 240, 188, 142, 3, 39, 154, 72, 206, 52, 212, 218, 83, 86, 70, 39, - 193, 201, 160, 83, 197, 162, 12, 96, 17, 29, 70, 93, 207, 212, 212, 174, 90, 44, 155, 166, 171, - 251, 200, 28, 109, 148, 241, 128, 246, 122, 68, 207, 134, 171, 110, 93, 226, 72, 56, 198, 160, - 239, 235, 120, 162, 72, 4, 12, 11, 164, 59, 116, 0, 82, 8, 155, 10, 2, 142, 175, 222, 205, 227, - 175, 192, 253, 0, 183, 147, 112, 152, 56, 139, 124, 138, 62, 138, 115, 106, 224, 8, 191, 12, 1, - 84, 231, 225, 159, 129, 34, 193, 218, 17, 176, 55, 136, 117, 233, 159, 105, 138, 102, 25, 58, - 172, 49, 130, 82, 156, 163, 202, 188, 9, 199, 132, 233, 56, 35, 0, 22, 14, 228, 27, 177, 217, - 195, 190, 249, 238, 88, 183, 97, 122, 158, 43, 187, 217, 17, 190, 53, 254, 42, 19, 40, 108, - 211, 182, 119, 162, 204, 0, 19, 12, 27, 113, 8, 0, 3, 208, 144, 125, 229, 171, 167, 222, 114, - 137, 80, 201, 44, 87, 208, 142, 32, 212, 7, 113, 97, 241, 47, 1, 62, 212, 134, 121, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 11, 122, 228, 231, 185, 248, 113, 231, 12, 130, 41, 17, 196, 124, 5, - 12, 205, 80, 151, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 86, 111, 4, 111, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 192, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 1, 160, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 33, 38, 149, 134, 255, 97, - 148, 158, 57, 8, 139, 236, 247, 24, 39, 242, 41, 198, 158, 164, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 122, 18, 0, 0, 0, 0, 0, 0, 105, 165, - 51, 15, 30, 170, 46, 205, 116, 157, 128, 90, 83, 92, 217, 149, 8, 21, 93, 206, 149, 189, 82, - 41, 109, 253, 98, 89, 93, 204, 52, 41, 58, 160, 72, 4, 11, 164, 59, 116, 0, 82, 8, 177, 162, - 161, 233, 184, 172, 51, 253, 203, 33, 24, 183, 58, 135, 91, 9, 106, 237, 36, 173, 69, 95, 214, - 138, 23, 240, 96, 0, 1, 231, 80, 229, 241, 175, 76, 221, 118, 145, 157, 154, 73, 124, 109, 82, - 221, 138, 28, 209, 141, 194, 139, 191, 21, 194, 153, 26, 158, 154, 15, 100, 19, 164, 89, 238, - 199, 195, 253, 195, 138, 125, 93, 230, 34, 26, 104, 136, 81, 194, 9, 101, 219, 29, 16, 157, - 207, 27, 1, 189, 199, 127, 168, 48, 47, 162, 76, 0, 8, 11, 164, 59, 116, 0, 3, 13, 64, 53, 137, - 208, 90, 30, 196, 175, 159, 101, 176, 229, 85, 78, 100, 87, 7, 119, 94, 228, 60, 117, 9, 14, - 191, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 106, - 101, 102, 102, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 30, 132, 128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 205, 160, 173, 117, 66, 227, 11, 245, - 32, 101, 42, 5, 5, 110, 190, 1, 5, 199, 228, 154, 0, 146, 33, 50, 243, 132, 73, 176, 153, 213, - 44, 33, 104, 12, 124, 176, 70, 215, 148, 182, 38, 136, 191, 24, 155, 154, 107, 71, 215, 145, - 206, 180, 110, 94, 138, 156, 78, 44, 112, 8, 100, 213, 225, 212, 9, 133, 8, 38, 11, 18, 116, - 72, 250, 217, 214, 244, 190, 130, 12, 248, 234, 53, 140, 58, 59, 164, 72, 4, 68, 207, 11, 164, - 59, 116, 0, 160, 40, 51, 129, 81, 163, 171, 37, 83, 253, 207, 127, 14, 173, 156, 148, 37, 9, - 54, 142, 79, 28, 68, 177, 238, 198, 22, 47, 0, 0, 0, 58, 184, 148, 166, 188, 22, 241, 29, 173, - 2, 182, 22, 71, 228, 199, 74, 55, 225, 186, 244, 54, 31, 159, 233, 140, 123, 153, 101, 28, 5, - 220, 198, 75, 232, 49, 54, 134, 9, 172, 53, 102, 60, 147, 214, 120, 251, 233, 151, 246, 119, - 80, 216, 35, 251, 171, 112, 104, 37, 114, 195, 130, 222, 233, 112, 164, 76, 4, 160, 108, 11, - 164, 59, 116, 0, 1, 95, 144, 22, 254, 126, 249, 127, 3, 96, 121, 233, 248, 137, 119, 157, 239, - 194, 61, 4, 132, 161, 10, 14, 54, 156, 165, 133, 178, 240, 128, 0, 184, 59, 167, 205, 204, 127, - 69, 185, 58, 211, 151, 192, 80, 152, 251, 223, 94, 119, 142, 67, 12, 232, 31, 189, 202, 78, - 148, 208, 5, 140, 5, 183, 54, 212, 23, 175, 255, 161, 209, 0, 123, 106, 43, 138, 110, 6, 103, - 148, 145, 142, 56, 225, 199, 89, 158, 167, 225, 222, 78, 84, 36, 94, 34, 32, 164, 76, 4, 160, - 109, 11, 164, 59, 116, 0, 1, 95, 144, 7, 136, 56, 48, 76, 158, 230, 120, 32, 158, 160, 149, - 149, 135, 218, 155, 111, 49, 235, 255, 14, 21, 97, 15, 113, 150, 100, 128, 0, 58, 77, 36, 90, - 239, 197, 145, 125, 197, 42, 155, 65, 141, 227, 249, 145, 158, 68, 87, 242, 98, 189, 244, 34, - 3, 103, 217, 168, 239, 186, 215, 195, 89, 193, 118, 69, 161, 112, 178, 234, 3, 40, 112, 219, - 71, 163, 46, 225, 244, 35, 251, 146, 209, 141, 115, 46, 92, 65, 98, 169, 158, 99, 192, 19, 164, - 76, 4, 160, 110, 11, 164, 59, 116, 0, 1, 95, 144, 14, 220, 200, 112, 34, 17, 242, 133, 202, 9, - 14, 249, 25, 182, 239, 246, 194, 233, 46, 176, 139, 79, 229, 241, 138, 41, 208, 0, 0, 219, 133, - 103, 43, 130, 50, 239, 75, 90, 89, 170, 246, 89, 190, 216, 171, 88, 247, 76, 167, 197, 161, 56, - 132, 22, 244, 90, 218, 125, 186, 91, 214, 104, 195, 62, 174, 27, 62, 179, 86, 100, 142, 68, 47, - 246, 240, 198, 164, 99, 168, 200, 216, 208, 147, 240, 134, 210, 170, 158, 231, 67, 183, 140, - 12, 164, 76, 0, 48, 194, 11, 164, 59, 116, 0, 3, 13, 64, 233, 128, 120, 99, 108, 6, 177, 169, - 144, 251, 205, 87, 221, 212, 199, 106, 218, 48, 86, 6, 72, 9, 69, 223, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 9, 132, 70, 225, 145, 116, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 64, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 46, 81, 109, - 78, 104, 57, 103, 81, 66, 90, 121, 116, 86, 116, 116, 102, 76, 52, 57, 105, 120, 57, 112, 77, - 57, 53, 54, 90, 99, 118, 121, 104, 69, 53, 100, 49, 87, 82, 117, 76, 101, 89, 88, 70, 107, 65, - 89, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 126, 69, 10, 80, 73, 247, 45, 164, - 139, 44, 134, 142, 17, 69, 240, 158, 245, 98, 122, 54, 197, 94, 7, 44, 193, 130, 98, 234, 37, - 88, 187, 131, 146, 161, 248, 42, 8, 73, 156, 244, 243, 194, 236, 25, 157, 241, 221, 10, 20, - 170, 174, 32, 80, 151, 106, 120, 245, 232, 36, 125, 212, 126, 121, 107, 162, 72, 4, 178, 13, - 248, 71, 88, 0, 82, 8, 50, 190, 52, 59, 148, 248, 96, 18, 77, 196, 254, 226, 120, 253, 203, - 211, 140, 16, 45, 136, 68, 173, 116, 207, 121, 196, 64, 0, 0, 112, 190, 202, 196, 31, 43, 169, - 239, 43, 203, 216, 207, 155, 184, 109, 74, 11, 198, 99, 144, 60, 67, 170, 253, 211, 143, 159, - 216, 22, 126, 96, 213, 142, 205, 254, 46, 219, 14, 123, 235, 118, 41, 107, 194, 134, 208, 212, - 80, 21, 3, 18, 241, 176, 130, 97, 58, 124, 190, 109, 109, 150, 168, 127, 110, 162, 72, 4, 66, - 13, 248, 71, 88, 0, 82, 8, 50, 190, 52, 59, 148, 248, 96, 18, 77, 196, 254, 226, 120, 253, 203, - 211, 140, 16, 45, 136, 139, 75, 107, 250, 237, 191, 16, 0, 1, 152, 245, 215, 253, 201, 40, 199, - 145, 110, 133, 35, 54, 94, 50, 6, 243, 50, 242, 181, 69, 112, 30, 68, 94, 204, 197, 162, 11, - 203, 39, 24, 147, 241, 55, 213, 180, 131, 246, 185, 96, 194, 1, 220, 134, 186, 217, 230, 3, - 131, 215, 213, 245, 64, 237, 102, 84, 230, 65, 54, 105, 239, 181, 92, 88, 164, 72, 4, 9, 51, - 13, 248, 71, 88, 0, 82, 8, 50, 190, 52, 59, 148, 248, 96, 18, 77, 196, 254, 226, 120, 253, 203, - 211, 140, 16, 45, 136, 14, 16, 231, 24, 213, 43, 164, 0, 0, 139, 112, 162, 22, 164, 23, 223, - 197, 46, 247, 228, 197, 67, 104, 131, 16, 60, 102, 145, 20, 181, 143, 109, 60, 26, 6, 95, 215, - 11, 198, 37, 58, 175, 1, 167, 136, 148, 120, 173, 199, 181, 183, 79, 69, 92, 252, 85, 73, 203, - 63, 146, 0, 11, 29, 197, 88, 171, 36, 48, 141, 129, 26, 230, 28, 162, 72, 4, 123, 11, 164, 59, - 116, 0, 86, 34, 251, 177, 183, 60, 79, 11, 218, 79, 103, 220, 162, 102, 206, 110, 244, 47, 82, - 15, 187, 152, 14, 50, 225, 173, 88, 175, 80, 0, 0, 215, 184, 115, 145, 21, 51, 10, 11, 227, - 161, 147, 8, 7, 54, 192, 76, 219, 189, 220, 101, 156, 217, 148, 238, 18, 185, 198, 144, 95, 84, - 128, 39, 38, 177, 120, 201, 160, 201, 119, 49, 155, 249, 187, 145, 249, 89, 168, 19, 27, 121, - 187, 91, 84, 173, 172, 199, 235, 136, 199, 84, 242, 113, 199, 14, 164, 76, 0, 8, 22, 11, 164, - 59, 116, 0, 1, 95, 144, 178, 120, 228, 203, 32, 223, 191, 151, 231, 143, 39, 0, 31, 107, 21, - 40, 131, 2, 244, 215, 72, 166, 234, 59, 0, 0, 0, 0, 0, 0, 0, 1, 96, 160, 96, 2, 10, 3, 51, 22, - 129, 82, 96, 32, 129, 1, 134, 144, 82, 129, 81, 127, 199, 251, 100, 126, 89, 177, 128, 71, 48, - 154, 161, 90, 173, 65, 142, 93, 124, 169, 109, 23, 58, 215, 4, 241, 3, 26, 44, 61, 117, 145, - 115, 75, 146, 145, 129, 144, 3, 144, 145, 1, 144, 161, 91, 80, 80, 80, 80, 86, 91, 96, 1, 96, - 160, 96, 2, 10, 3, 131, 22, 96, 2, 131, 97, 1, 0, 129, 16, 21, 97, 0, 2, 87, 80, 131, 1, 129, - 144, 85, 96, 1, 96, 160, 96, 2, 10, 3, 133, 22, 96, 0, 129, 129, 82, 97, 1, 2, 96, 32, 144, - 129, 82, 96, 64, 128, 131, 32, 131, 144, 85, 132, 131, 82, 145, 130, 144, 32, 134, 144, 85, - 129, 81, 146, 131, 82, 130, 1, 146, 144, 146, 82, 129, 81, 127, 181, 50, 7, 59, 56, 200, 49, - 69, 227, 229, 19, 83, 119, 160, 139, 249, 170, 181, 91, 192, 253, 124, 17, 121, 205, 79, 185, - 149, 210, 165, 21, 156, 146, 145, 129, 144, 3, 144, 145, 1, 144, 161, 80, 91, 80, 80, 80, 86, - 91, 21, 97, 3, 203, 87, 97, 4, 100, 131, 97, 1, 98, 86, 91, 21, 97, 4, 111, 87, 80, 97, 4, 81, - 86, 91, 96, 1, 96, 160, 96, 2, 10, 3, 132, 22, 96, 0, 144, 129, 82, 97, 1, 2, 96, 32, 82, 96, - 64, 129, 32, 84, 146, 80, 130, 20, 21, 97, 4, 152, 87, 80, 97, 4, 81, 86, 91, 97, 3, 209, 91, - 97, 1, 4, 84, 96, 0, 91, 129, 129, 16, 21, 97, 14, 236, 87, 97, 1, 4, 128, 84, 97, 1, 8, 145, - 96, 0, 145, 132, 144, 129, 16, 21, 97, 0, 2, 87, 96, 0, 128, 81, 96, 32, 97, 15, 185, 131, 57, - 129, 81, 145, 82, 1, 84, 130, 82, 80, 96, 32, 145, 144, 145, 82, 96, 64, 129, 32, 128, 84, 96, - 1, 96, 160, 96, 2, 10, 3, 25, 22, 129, 85, 96, 1, 129, 129, 1, 131, 144, 85, 96, 2, 130, 129, - 1, 128, 84, 133, 130, 85, 147, 148, 147, 144, 146, 129, 22, 21, 97, 1, 0, 2, 96, 0, 25, 1, 22, - 4, 96, 31, 129, 144, 16, 97, 15, 113, 87, 80, 91, 80, 80, 80, 96, 1, 1, 97, 4, 163, 86, 91, 96, - 1, 128, 84, 129, 1, 144, 129, 144, 85, 96, 1, 96, 160, 96, 2, 10, 3, 131, 22, 144, 96, 2, 144, - 97, 1, 0, 129, 16, 21, 97, 0, 2, 87, 144, 144, 1, 96, 0, 80, 85, 96, 1, 84, 96, 1, 96, 160, 96, - 2, 10, 3, 131, 22, 96, 0, 129, 129, 82, 97, 1, 2, 96, 32, 144, 129, 82, 96, 64, 145, 130, 144, - 32, 147, 144, 147, 85, 128, 81, 145, 130, 82, 81, 127, 153, 74, 147, 102, 70, 254, 135, 255, - 228, 241, 228, 105, 211, 214, 170, 65, 125, 107, 133, 85, 152, 57, 127, 50, 61, 229, 180, 73, - 247, 101, 240, 195, 146, 145, 129, 144, 3, 144, 145, 1, 144, 161, 91, 80, 91, 80, 86, 91, 21, - 97, 5, 165, 87, 97, 5, 184, 130, 97, 1, 98, 86, 91, 21, 97, 5, 195, 87, 80, 97, 5, 167, 86, 91, - 97, 5, 203, 97, 4, 156, 86, 91, 96, 1, 84, 96, 250, 144, 16, 97, 5, 222, 87, 97, 5, 222, 97, 5, - 243, 86, 91, 96, 1, 84, 96, 250, 144, 16, 97, 5, 38, 87, 80, 97, 5, 167, 86, 91, 97, 6, 171, - 91, 96, 1, 91, 96, 1, 84, 129, 16, 21, 97, 5, 167, 87, 91, 96, 1, 84, 129, 16, 128, 21, 97, 6, - 33, 87, 80, 96, 2, 129, 97, 1, 0, 129, 16, 21, 97, 0, 2, 87, 1, 84, 96, 0, 20, 21, 91, 21, 97, - 13, 167, 87, 96, 1, 1, 97, 6, 1, 86, 91, 21, 97, 4, 81, 87, 96, 1, 96, 160, 96, 2, 10, 3, 131, - 22, 96, 0, 144, 129, 82, 97, 1, 2, 96, 32, 82, 96, 64, 129, 32, 84, 146, 80, 130, 20, 21, 97, - 6, 92, 87, 80, 97, 5, 165, 86, 91, 96, 1, 96, 1, 96, 0, 80, 84, 3, 96, 0, 96, 0, 80, 84, 17, - 21, 97, 6, 119, 87, 80, 97, 5, 165, 86, 91, 96, 0, 96, 2, 131, 97, 1, 0, 129, 16, 21, 97, 0, 2, - 87, 80, 131, 1, 129, 144, 85, 96, 1, 96, 160, 96, 2, 10, 3, 132, 22, 129, 82, 97, 1, 2, 96, 32, - 82, 96, 64, 129, 32, 85, 97, 5, 239, 97, 4, 156, 86, 91, 96, 64, 128, 81, 96, 1, 96, 160, 96, - 2, 10, 3, 133, 22, 129, 82, 144, 81, 127, 88, 97, 144, 118, 173, 245, 187, 9, 67, 209, 0, 239, - 136, 213, 45, 124, 63, 214, 145, 177, 157, 58, 144, 113, 181, 85, 182, 81, 251, 244, 24, 218, - 145, 129, 144, 3, 96, 32, 1, 144, 161, 80, 80, 80, 86, 91, 21, 97, 5, 165, 87, 96, 1, 84, 130, - 17, 21, 97, 7, 1, 87, 80, 97, 5, 167, 86, 91, 96, 0, 130, 144, 85, 97, 7, 14, 97, 4, 156, 86, - 91, 96, 64, 128, 81, 131, 129, 82, 144, 81, 127, 172, 189, 176, 132, 199, 33, 51, 42, 197, 159, - 155, 142, 57, 33, 150, 201, 235, 14, 73, 50, 134, 45, 168, 235, 155, 234, 240, 218, 212, 245, - 80, 218, 145, 129, 144, 3, 96, 32, 1, 144, 161, 80, 80, 86, 91, 80, 96, 1, 130, 1, 84, 96, 2, - 130, 144, 10, 144, 129, 22, 96, 0, 20, 21, 147, 80, 91, 80, 80, 80, 146, 145, 80, 80, 86, 91, - 21, 97, 5, 165, 87, 80, 97, 1, 5, 85, 86, 91, 21, 97, 5, 167, 87, 96, 0, 97, 1, 6, 85, 80, 86, - 91, 21, 97, 5, 165, 87, 129, 96, 1, 96, 160, 96, 2, 10, 3, 22, 255, 91, 21, 97, 9, 201, 87, 97, - 7, 162, 132, 96, 0, 97, 14, 129, 51, 97, 1, 98, 86, 91, 21, 97, 8, 94, 87, 127, 146, 202, 58, - 128, 133, 62, 102, 99, 250, 49, 250, 16, 185, 146, 37, 241, 141, 73, 2, 147, 155, 76, 83, 169, - 202, 174, 144, 67, 246, 239, 208, 4, 51, 133, 135, 134, 134, 96, 64, 81, 128, 134, 96, 1, 96, - 160, 96, 2, 10, 3, 22, 129, 82, 96, 32, 1, 133, 129, 82, 96, 32, 1, 132, 96, 1, 96, 160, 96, 2, - 10, 3, 22, 129, 82, 96, 32, 1, 128, 96, 32, 1, 130, 129, 3, 130, 82, 132, 132, 130, 129, 129, - 82, 96, 32, 1, 146, 80, 128, 130, 132, 55, 130, 1, 145, 80, 80, 150, 80, 80, 80, 80, 80, 80, - 80, 96, 64, 81, 128, 145, 3, 144, 161, 132, 96, 1, 96, 160, 96, 2, 10, 3, 22, 132, 132, 132, - 96, 64, 81, 128, 131, 131, 128, 130, 132, 55, 80, 80, 80, 144, 129, 1, 145, 80, 96, 0, 144, - 128, 131, 3, 129, 133, 135, 97, 133, 2, 90, 3, 241, 80, 96, 0, 147, 80, 97, 9, 201, 146, 80, - 80, 80, 86, 91, 96, 0, 54, 67, 96, 64, 81, 128, 132, 132, 128, 130, 132, 55, 80, 80, 80, 144, - 145, 1, 144, 129, 82, 96, 64, 81, 144, 129, 144, 3, 96, 32, 1, 144, 32, 145, 80, 97, 8, 142, - 144, 80, 129, 97, 1, 235, 86, 91, 21, 128, 21, 97, 8, 177, 87, 80, 96, 0, 129, 129, 82, 97, 1, - 8, 96, 32, 82, 96, 64, 129, 32, 84, 96, 1, 96, 160, 96, 2, 10, 3, 22, 20, 91, 21, 97, 9, 201, - 87, 96, 0, 129, 129, 82, 97, 1, 8, 96, 32, 144, 129, 82, 96, 64, 130, 32, 128, 84, 96, 1, 96, - 160, 96, 2, 10, 3, 25, 22, 136, 23, 129, 85, 96, 1, 129, 129, 1, 136, 144, 85, 96, 2, 145, 130, - 1, 128, 84, 129, 134, 82, 148, 132, 144, 32, 144, 148, 145, 130, 22, 21, 97, 1, 0, 2, 96, 0, - 25, 1, 144, 145, 22, 145, 144, 145, 4, 96, 31, 144, 129, 1, 146, 144, 146, 4, 129, 1, 145, 133, - 145, 144, 135, 144, 131, 144, 16, 97, 9, 209, 87, 96, 255, 25, 129, 53, 22, 131, 128, 1, 23, - 133, 85, 91, 80, 97, 9, 67, 146, 145, 80, 91, 128, 130, 17, 21, 97, 10, 1, 87, 96, 0, 129, 85, - 96, 1, 1, 97, 9, 47, 86, 91, 80, 80, 127, 23, 51, 203, 181, 54, 89, 215, 19, 183, 149, 128, - 247, 159, 63, 159, 242, 21, 247, 138, 124, 122, 164, 88, 144, 243, 184, 159, 197, 205, 223, - 191, 50, 129, 51, 134, 136, 135, 135, 96, 64, 81, 128, 135, 129, 0, 0, 0, 0, 0, 0, 0, 27, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, - 75, 151, 212, 253, 220, 42, 234, 76, 198, 27, 27, 125, 178, 22, 211, 184, 129, 70, 230, 184, - 52, 242, 184, 171, 254, 1, 204, 197, 172, 102, 208, 19, 47, 62, 14, 52, 175, 171, 197, 248, - 103, 114, 170, 216, 108, 145, 216, 163, 227, 136, 7, 190, 168, 170, 9, 217, 250, 225, 97, 151, - 71, 229, 235, 27, 164, 204, 3, 4, 243, 11, 164, 59, 116, 0, 30, 132, 128, 141, 78, 172, 16, - 214, 135, 69, 226, 168, 195, 106, 179, 60, 92, 57, 97, 237, 75, 113, 190, 35, 134, 242, 111, - 193, 0, 0, 201, 67, 229, 26, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 28, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 238, 197, 107, 250, 148, 149, 42, 231, 170, 85, 40, 70, - 101, 60, 219, 189, 106, 242, 247, 157, 243, 63, 244, 140, 24, 56, 156, 93, 89, 68, 171, 203, - 172, 223, 172, 29, 38, 204, 89, 240, 25, 98, 199, 115, 61, 63, 188, 183, 24, 234, 77, 238, 100, - 76, 117, 159, 131, 251, 110, 229, 71, 134, 26, 82, 164, 204, 3, 4, 244, 11, 164, 59, 116, 0, - 30, 132, 128, 141, 78, 172, 16, 214, 135, 69, 226, 168, 195, 106, 179, 60, 92, 57, 97, 237, 75, - 113, 190, 35, 134, 242, 111, 193, 0, 0, 201, 67, 229, 26, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 14, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 28, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 29, 0, 0, 0, 0, 248, 162, 81, 53, 59, 98, - 249, 7, 183, 211, 25, 43, 33, 96, 118, 99, 224, 164, 25, 1, 144, 246, 234, 241, 234, 57, 248, - 171, 163, 172, 61, 75, 176, 20, 176, 164, 114, 233, 244, 161, 255, 182, 96, 189, 214, 51, 108, - 201, 145, 182, 137, 131, 9, 234, 27, 221, 20, 92, 59, 165, 163, 27, 186, 25, 164, 204, 3, 4, - 245, 11, 164, 59, 116, 0, 30, 132, 128, 141, 78, 172, 16, 214, 135, 69, 226, 168, 195, 106, - 179, 60, 92, 57, 97, 237, 75, 113, 190, 35, 134, 242, 111, 193, 0, 0, 201, 67, 229, 26, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 15, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 28, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 64, 8, 8, - 8, 0, 125, 5, 45, 185, 115, 36, 242, 214, 114, 175, 92, 245, 100, 5, 198, 204, 31, 143, 160, 5, - 175, 248, 89, 215, 176, 169, 75, 187, 10, 243, 146, 36, 228, 179, 237, 251, 185, 188, 197, 108, - 132, 101, 230, 121, 143, 140, 15, 150, 72, 174, 50, 46, 209, 129, 185, 192, 66, 59, 177, 180, - 136, 104, 82, 50, 162, 12, 0, 22, 11, 164, 59, 116, 0, 4, 147, 224, 96, 96, 96, 64, 82, 97, 4, - 71, 128, 97, 0, 18, 96, 0, 57, 96, 0, 243, 96, 96, 96, 64, 82, 96, 0, 53, 124, 1, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 144, 4, 128, 99, 107, - 213, 8, 74, 20, 97, 0, 79, 87, 128, 99, 168, 136, 194, 205, 20, 97, 0, 114, 87, 128, 99, 243, - 254, 18, 201, 20, 97, 1, 68, 87, 97, 0, 77, 86, 91, 0, 91, 97, 0, 92, 96, 4, 128, 80, 80, 97, - 4, 50, 86, 91, 96, 64, 81, 128, 130, 129, 82, 96, 32, 1, 145, 80, 80, 96, 64, 81, 128, 145, 3, - 144, 243, 91, 97, 0, 136, 96, 4, 128, 128, 53, 144, 96, 32, 1, 144, 145, 144, 80, 80, 97, 1, - 154, 86, 91, 96, 64, 81, 128, 132, 115, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 22, 129, 82, 96, 32, 1, 128, 96, 32, 1, 131, 129, - 82, 96, 32, 1, 130, 129, 3, 130, 82, 132, 129, 129, 84, 96, 1, 129, 96, 1, 22, 21, 97, 1, 0, 2, - 3, 22, 96, 2, 144, 4, 129, 82, 96, 32, 1, 145, 80, 128, 84, 96, 1, 129, 96, 1, 22, 21, 97, 1, - 0, 2, 3, 22, 96, 2, 144, 4, 128, 21, 97, 1, 51, 87, 128, 96, 31, 16, 97, 1, 8, 87, 97, 1, 0, - 128, 131, 84, 4, 2, 131, 82, 145, 96, 32, 1, 145, 97, 1, 51, 86, 91, 130, 1, 145, 144, 96, 0, - 82, 96, 32, 96, 0, 32, 144, 91, 129, 84, 129, 82, 144, 96, 1, 1, 144, 96, 32, 1, 128, 131, 17, - 97, 1, 22, 87, 130, 144, 3, 96, 31, 22, 130, 1, 145, 91, 80, 80, 148, 80, 80, 80, 80, 80, 96, - 64, 81, 128, 145, 3, 144, 243, 91, 97, 1, 152, 96, 4, 128, 128, 53, 144, 96, 32, 1, 144, 130, - 1, 128, 53, 144, 96, 32, 1, 145, 145, 144, 128, 128, 96, 31, 1, 96, 32, 128, 145, 4, 2, 96, 32, - 1, 96, 64, 81, 144, 129, 1, 96, 64, 82, 128, 147, 146, 145, 144, 129, 129, 82, 96, 32, 1, 131, - 131, 128, 130, 132, 55, 130, 1, 145, 80, 80, 80, 80, 80, 80, 144, 144, 145, 144, 80, 80, 97, 1, - 249, 86, 91, 0, 91, 96, 0, 96, 0, 80, 129, 129, 84, 129, 16, 21, 97, 0, 2, 87, 144, 96, 0, 82, - 96, 32, 96, 0, 32, 144, 96, 3, 2, 1, 96, 0, 91, 145, 80, 144, 80, 128, 96, 0, 1, 96, 0, 144, - 84, 144, 97, 1, 0, 10, 144, 4, 115, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 22, 144, 128, 96, 1, 1, 96, 0, 80, 144, 128, 96, 2, 1, - 96, 0, 80, 84, 144, 80, 131, 86, 91, 96, 0, 96, 0, 96, 0, 80, 128, 84, 144, 80, 144, 80, 96, 0, - 96, 0, 80, 128, 84, 128, 145, 144, 96, 1, 1, 144, 144, 129, 84, 129, 131, 85, 129, 129, 21, 17, - 97, 2, 245, 87, 96, 3, 2, 129, 96, 3, 2, 131, 96, 0, 82, 96, 32, 96, 0, 32, 145, 130, 1, 145, - 1, 97, 2, 244, 145, 144, 97, 2, 65, 86, 91, 128, 130, 17, 21, 97, 2, 240, 87, 96, 0, 96, 0, - 130, 1, 96, 0, 97, 1, 0, 10, 129, 84, 144, 115, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 2, 25, 22, 144, 85, 96, 1, 130, 1, 96, - 0, 80, 128, 84, 96, 1, 129, 96, 1, 22, 21, 97, 1, 0, 2, 3, 22, 96, 2, 144, 4, 96, 0, 130, 85, - 128, 96, 31, 16, 97, 2, 158, 87, 80, 97, 2, 219, 86, 91, 96, 31, 1, 96, 32, 144, 4, 144, 96, 0, - 82, 96, 32, 96, 0, 32, 144, 129, 1, 144, 97, 2, 218, 145, 144, 97, 2, 188, 86, 91, 128, 130, - 17, 21, 97, 2, 214, 87, 96, 0, 129, 129, 80, 96, 0, 144, 85, 80, 96, 1, 1, 97, 2, 188, 86, 91, - 80, 144, 86, 91, 91, 80, 96, 2, 130, 1, 96, 0, 80, 96, 0, 144, 85, 80, 96, 1, 1, 97, 2, 65, 86, - 91, 80, 144, 86, 91, 91, 80, 80, 80, 2, 3, 22, 96, 2, 144, 4, 130, 128, 84, 96, 1, 129, 96, 1, - 22, 21, 97, 1, 0, 2, 3, 22, 96, 2, 144, 4, 144, 96, 0, 82, 96, 32, 96, 0, 32, 144, 96, 31, 1, - 96, 32, 144, 4, 129, 1, 146, 130, 96, 31, 16, 97, 11, 230, 87, 128, 84, 133, 85, 97, 12, 35, - 86, 91, 130, 128, 1, 96, 1, 1, 133, 85, 130, 21, 97, 12, 35, 87, 96, 0, 82, 96, 32, 96, 0, 32, - 145, 96, 31, 1, 96, 32, 144, 4, 130, 1, 91, 130, 129, 17, 21, 97, 12, 34, 87, 130, 84, 130, 85, - 145, 96, 1, 1, 145, 144, 96, 1, 1, 144, 97, 12, 7, 86, 91, 91, 80, 144, 80, 97, 12, 78, 145, - 144, 97, 12, 48, 86, 91, 128, 130, 17, 21, 97, 12, 74, 87, 96, 0, 129, 129, 80, 96, 0, 144, 85, - 80, 96, 1, 1, 97, 12, 48, 86, 91, 80, 144, 86, 91, 80, 80, 144, 80, 80, 96, 3, 96, 20, 129, - 129, 144, 84, 144, 97, 1, 0, 10, 144, 4, 103, 255, 255, 255, 255, 255, 255, 255, 255, 22, 128, - 146, 145, 144, 96, 1, 1, 145, 144, 97, 1, 0, 10, 129, 84, 129, 103, 255, 255, 255, 255, 255, - 255, 255, 255, 2, 25, 22, 144, 131, 2, 23, 144, 85, 80, 80, 96, 1, 96, 3, 96, 20, 144, 84, 144, - 97, 1, 0, 10, 144, 4, 103, 255, 255, 255, 255, 255, 255, 255, 255, 22, 3, 146, 80, 97, 12, 177, - 86, 91, 80, 80, 145, 144, 80, 86, 91, 96, 0, 96, 0, 96, 0, 131, 96, 64, 81, 128, 130, 128, 81, - 144, 96, 32, 1, 144, 128, 131, 131, 130, 144, 96, 0, 96, 4, 96, 32, 132, 96, 31, 1, 4, 96, 3, - 2, 96, 15, 1, 241, 80, 144, 80, 1, 145, 80, 80, 96, 64, 81, 128, 145, 3, 144, 32, 145, 80, 96, - 4, 96, 0, 80, 96, 0, 131, 129, 82, 96, 32, 1, 144, 129, 82, 96, 32, 1, 96, 0, 32, 96, 0, 80, - 144, 80, 129, 146, 80, 97, 13, 22, 86, 91, 80, 80, 145, 144, 80, 86, 0, 161, 114, 38, 248, 14, - 109, 160, 75, 155, 149, 133, 95, 71, 176, 109, 71, 212, 114, 83, 158, 219, 31, 11, 127, 33, - 207, 102, 123, 150, 180, 253, 85, 97, 112, 199, 225, 220, 148, 119, 145, 36, 229, 199, 15, 28, - 181, 185, 169, 188, 229, 132, 183, 80, 188, 138, 52, 170, 13, 244, 91, 3, 125, 67, 24, 164, 76, - 4, 1, 94, 11, 164, 59, 116, 0, 1, 95, 144, 19, 174, 91, 53, 248, 38, 81, 255, 50, 125, 65, 80, - 47, 10, 169, 13, 167, 109, 119, 126, 69, 135, 24, 116, 180, 181, 0, 0, 0, 224, 11, 133, 192, - 193, 216, 59, 102, 10, 154, 132, 20, 69, 142, 12, 120, 36, 71, 75, 23, 135, 74, 77, 73, 174, - 177, 88, 160, 76, 219, 37, 44, 36, 201, 207, 99, 45, 62, 226, 227, 253, 29, 176, 95, 235, 5, - 160, 18, 177, 48, 167, 69, 173, 11, 24, 171, 94, 252, 155, 11, 36, 120, 152, 114, 164, 72, 4, - 23, 115, 11, 164, 59, 116, 0, 160, 40, 235, 19, 37, 200, 217, 211, 234, 141, 116, 172, 17, 244, - 176, 15, 27, 35, 103, 104, 99, 25, 68, 177, 238, 198, 22, 47, 0, 0, 0, 179, 222, 152, 97, 106, - 117, 230, 182, 234, 187, 127, 15, 167, 67, 191, 190, 204, 248, 146, 241, 150, 253, 250, 214, - 71, 160, 73, 7, 255, 64, 202, 21, 81, 107, 251, 71, 163, 208, 177, 107, 151, 33, 10, 13, 69, - 149, 70, 25, 199, 151, 131, 194, 128, 202, 20, 197, 252, 28, 164, 53, 56, 73, 240, 12, 164, - 204, 3, 19, 1, 11, 164, 59, 116, 0, 1, 95, 144, 124, 80, 128, 152, 140, 109, 145, 208, 144, - 194, 61, 84, 116, 15, 133, 108, 105, 69, 11, 41, 88, 172, 152, 191, 152, 180, 0, 0, 170, 78, - 171, 43, 142, 102, 88, 3, 148, 138, 194, 176, 158, 236, 66, 185, 96, 138, 222, 63, 194, 252, - 148, 209, 165, 89, 28, 107, 170, 254, 11, 160, 57, 254, 167, 221, 14, 143, 183, 92, 136, 115, - 40, 180, 158, 93, 137, 237, 192, 85, 211, 172, 50, 67, 177, 120, 94, 143, 99, 111, 15, 31, 62, - 124, 164, 204, 3, 19, 2, 11, 164, 59, 116, 0, 1, 95, 144, 84, 206, 196, 38, 48, 124, 26, 202, - 166, 59, 37, 170, 72, 250, 222, 93, 249, 229, 212, 24, 58, 130, 122, 50, 218, 52, 0, 1, 103, - 102, 117, 100, 97, 217, 81, 148, 188, 124, 217, 3, 11, 61, 110, 136, 139, 31, 214, 244, 141, - 56, 237, 122, 26, 133, 170, 16, 107, 247, 163, 42, 163, 196, 213, 207, 233, 200, 252, 251, 87, - 87, 184, 232, 109, 134, 252, 20, 41, 118, 206, 112, 235, 37, 240, 45, 91, 75, 131, 220, 81, 66, - 90, 36, 164, 12, 0, 1, 104, 11, 164, 59, 116, 0, 45, 198, 192, 96, 96, 96, 64, 82, 91, 51, 96, - 1, 96, 0, 97, 1, 0, 10, 129, 84, 129, 115, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 2, 25, 22, 144, 131, 2, 23, 144, 85, 80, 91, - 97, 11, 26, 128, 97, 0, 63, 96, 0, 57, 96, 0, 243, 96, 96, 96, 64, 82, 54, 21, 97, 0, 160, 87, - 96, 0, 53, 124, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 144, 4, 128, 99, 2, 191, 97, 127, 20, 97, 0, 162, 87, 128, 99, 48, 84, 126, 76, 20, - 97, 0, 195, 87, 128, 99, 60, 2, 71, 35, 20, 97, 0, 249, 87, 128, 99, 104, 219, 95, 75, 20, 97, - 1, 17, 87, 128, 99, 156, 184, 162, 106, 20, 97, 1, 112, 87, 128, 99, 163, 236, 19, 141, 20, 97, - 1, 127, 87, 128, 99, 170, 171, 214, 176, 20, 97, 1, 208, 87, 128, 99, 173, 202, 24, 71, 20, 97, - 2, 9, 87, 128, 99, 204, 236, 110, 188, 20, 97, 2, 33, 87, 128, 99, 255, 255, 243, 5, 20, 97, 2, - 57, 87, 97, 0, 160, 86, 91, 0, 91, 97, 0, 193, 96, 4, 128, 128, 53, 144, 96, 32, 1, 144, 145, - 144, 128, 53, 144, 96, 32, 1, 144, 145, 144, 80, 80, 97, 6, 206, 86, 91, 0, 91, 97, 0, 217, 96, - 4, 128, 128, 53, 144, 96, 32, 1, 144, 145, 144, 80, 80, 97, 3, 180, 86, 91, 96, 64, 81, 128, - 130, 103, 255, 255, 255, 255, 255, 255, 255, 255, 22, 129, 82, 96, 32, 1, 145, 80, 80, 96, 64, - 81, 128, 145, 3, 144, 243, 91, 97, 1, 15, 96, 4, 128, 128, 53, 144, 96, 32, 1, 144, 145, 144, - 80, 80, 97, 4, 92, 86, 91, 0, 91, 97, 1, 110, 96, 4, 128, 128, 53, 144, 96, 32, 1, 144, 130, 1, - 128, 53, 144, 96, 32, 1, 145, 145, 144, 128, 128, 96, 31, 1, 96, 32, 128, 145, 4, 2, 96, 32, 1, - 96, 64, 81, 144, 129, 1, 96, 64, 82, 128, 147, 146, 145, 144, 129, 129, 82, 96, 32, 1, 131, - 131, 128, 130, 132, 55, 130, 1, 145, 80, 80, 80, 80, 80, 80, 144, 144, 145, 144, 128, 53, 144, - 96, 32, 1, 144, 145, 144, 80, 80, 97, 9, 32, 86, 91, 0, 91, 97, 1, 125, 96, 4, 128, 80, 80, 97, - 2, 190, 86, 91, 0, 91, 97, 1, 149, 96, 4, 128, 128, 53, 144, 96, 32, 1, 144, 145, 144, 80, 80, - 97, 3, 82, 86, 91, 96, 64, 81, 128, 132, 103, 255, 255, 255, 255, 255, 255, 255, 255, 22, 129, - 82, 96, 32, 1, 131, 103, 255, 255, 255, 255, 255, 255, 255, 255, 22, 129, 82, 96, 32, 1, 130, - 96, 255, 22, 129, 82, 96, 32, 1, 147, 80, 80, 80, 80, 96, 64, 81, 128, 145, 3, 144, 243, 91, - 97, 1, 221, 96, 4, 128, 80, 80, 97, 2, 152, 86, 91, 96, 64, 81, 128, 130, 115, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 22, 129, - 82, 96, 32, 1, 145, 80, 80, 96, 64, 81, 128, 145, 3, 144, 243, 91, 97, 2, 31, 96, 4, 128, 128, - 53, 144, 96, 32, 1, 144, 145, 144, 80, 80, 97, 4, 139, 86, 91, 0, 91, 97, 2, 55, 96, 4, 128, - 128, 53, 144, 96, 32, 1, 144, 145, 144, 80, 80, 97, 5, 219, 86, 91, 0, 91, 97, 2, 70, 96, 4, - 58, 219, 46, 224, 177, 135, 69, 145, 171, 91, 157, 78, 178, 62, 111, 56, 61, 243, 66, 127, 159, - 234, 158, 74, 152, 63, 8, 8, 164, 72, 4, 16, 156, 11, 164, 59, 116, 0, 82, 8, 0, 41, 181, 111, - 90, 223, 24, 49, 42, 75, 138, 111, 68, 70, 48, 254, 95, 158, 141, 255, 68, 181, 160, 49, 138, - 109, 120, 0, 1, 146, 73, 154, 110, 237, 115, 69, 114, 173, 21, 40, 136, 242, 132, 182, 163, - 138, 174, 231, 100, 28, 191, 8, 219, 165, 13, 172, 105, 82, 131, 171, 37, 11, 8, 255, 148, 81, - 66, 23, 198, 24, 233, 145, 189, 17, 167, 147, 240, 30, 176, 212, 29, 210, 177, 232, 1, 56, 211, - 224, 120, 92, 104, 111, 4, 164, 204, 4, 252, 78, 11, 164, 59, 116, 0, 1, 95, 144, 31, 87, 248, - 38, 202, 245, 148, 247, 168, 55, 217, 252, 9, 36, 86, 135, 10, 40, 147, 101, 13, 141, 114, 107, - 113, 119, 168, 0, 0, 0, 36, 45, 9, 178, 11, 25, 242, 56, 171, 219, 101, 211, 58, 76, 165, 92, - 237, 254, 161, 224, 79, 5, 210, 199, 144, 98, 107, 10, 222, 111, 186, 130, 11, 102, 108, 93, - 140, 170, 94, 70, 239, 62, 5, 180, 57, 171, 56, 24, 43, 187, 232, 182, 192, 30, 17, 109, 27, - 42, 193, 103, 231, 179, 53, 80, 164, 76, 4, 29, 181, 11, 164, 59, 116, 0, 1, 95, 144, 52, 211, - 79, 10, 99, 143, 105, 145, 228, 226, 83, 111, 65, 41, 93, 246, 233, 52, 166, 70, 57, 119, 44, - 88, 67, 255, 40, 0, 0, 186, 38, 45, 196, 168, 104, 172, 249, 121, 216, 201, 106, 120, 216, 140, - 235, 182, 88, 13, 72, 225, 179, 167, 77, 120, 198, 243, 81, 40, 35, 189, 99, 43, 183, 205, 81, - 229, 9, 177, 225, 218, 153, 255, 125, 105, 54, 157, 118, 237, 192, 33, 240, 173, 85, 115, 61, - 41, 223, 98, 112, 92, 61, 58, 36, 164, 204, 0, 14, 211, 16, 76, 83, 60, 0, 15, 66, 64, 17, 148, - 233, 102, 150, 84, 24, 199, 215, 58, 66, 204, 238, 178, 84, 216, 117, 134, 3, 86, 1, 213, 6, - 78, 209, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 96, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 2, 192, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 5, 32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 18, 85, 83, 68, 84, 95, 69, 84, 72, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 85, 83, 68, 84, 95, 66, 84, 67, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 66, 84, 67, 95, 69, 84, 72, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 69, 85, 82, 85, 83, 68, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 71, 66, 80, 85, 83, 68, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 85, 83, 68, 74, 80, 89, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 88, 65, 85, 85, 83, 68, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 88, 65, 71, 85, 83, 68, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 83, 80, 53, 48, 48, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 78, 65, 83, - 68, 65, 81, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 65, - 65, 80, 76, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 71, 79, 79, 71, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 77, 83, 70, 84, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 71, 77, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 71, 69, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 87, 77, 84, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 70, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 84, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 18, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 9, 20, 56, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 14, 235, 70, 96, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 76, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 17, 106, 252, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 23, 90, 232, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 32, 198, 148, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 70, 8, 80, 112, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 242, 145, 168, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 119, 109, 226, 16, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 127, 255, 255, 255, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 169, 199, 176, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 38, 225, - 78, 224, 0, 0, 0, 0, 0, 0, 0, 82, 96, 4, 1, 128, 130, 129, 82, 96, 32, 1, 145, 80, 80, 96, 0, - 96, 64, 81, 128, 131, 3, 129, 96, 0, 135, 97, 97, 218, 90, 3, 241, 146, 80, 80, 80, 80, 96, 1, - 96, 0, 144, 84, 144, 97, 1, 0, 10, 144, 4, 115, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 22, 115, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 22, 99, 82, 175, 188, 51, - 48, 96, 64, 81, 128, 128, 127, 115, 101, 116, 73, 116, 40, 117, 105, 110, 116, 50, 53, 54, 41, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 129, 82, 96, 32, 1, 80, 96, 14, 1, 144, - 80, 96, 64, 81, 128, 145, 3, 144, 32, 124, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 128, 145, 4, 2, 132, 96, 64, 81, 128, 130, 129, 82, 96, 32, - 1, 145, 80, 80, 96, 64, 81, 128, 145, 3, 144, 32, 134, 67, 1, 96, 1, 96, 20, 144, 84, 144, 97, - 1, 0, 10, 144, 4, 96, 255, 22, 96, 0, 96, 64, 81, 135, 124, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 129, 82, 96, 4, 1, 128, 135, 115, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 22, 129, 82, 96, 32, 1, 134, 129, 82, 96, 32, 1, 133, 129, 82, 96, 32, 1, 132, 129, 82, 96, 32, - 1, 131, 96, 255, 22, 129, 82, 96, 32, 1, 130, 129, 82, 96, 32, 1, 150, 80, 80, 80, 80, 80, 80, - 80, 96, 0, 96, 64, 81, 128, 131, 3, 129, 96, 0, 135, 97, 97, 218, 90, 3, 241, 21, 97, 0, 2, 87, - 80, 80, 80, 91, 80, 80, 86, 1, 223, 137, 57, 2, 109, 175, 148, 163, 185, 68, 128, 228, 128, - 253, 200, 45, 172, 225, 194, 134, 93, 220, 247, 86, 11, 244, 36, 157, 238, 240, 75, 162, 245, - 168, 57, 194, 185, 46, 157, 189, 4, 24, 137, 255, 173, 213, 68, 243, 178, 86, 219, 255, 96, - 215, 106, 178, 218, 92, 189, 220, 173, 174, 16, 16, 164, 72, 4, 1, 65, 11, 164, 59, 116, 0, 82, - 8, 50, 190, 52, 59, 148, 248, 96, 18, 77, 196, 254, 226, 120, 253, 203, 211, 140, 16, 45, 136, - 7, 5, 38, 133, 249, 14, 4, 0, 0, 146, 253, 246, 59, 231, 73, 232, 117, 8, 201, 61, 147, 131, - 96, 50, 228, 76, 81, 237, 72, 212, 144, 140, 103, 236, 238, 53, 72, 53, 126, 231, 118, 139, 27, - 222, 157, 170, 241, 67, 242, 100, 204, 164, 75, 203, 75, 78, 61, 131, 22, 10, 109, 190, 20, - 139, 245, 60, 32, 237, 229, 27, 151, 241, 14, 162, 72, 4, 84, 11, 164, 59, 116, 0, 82, 8, 50, - 190, 52, 59, 148, 248, 96, 18, 77, 196, 254, 226, 120, 253, 203, 211, 140, 16, 45, 136, 13, - 252, 244, 82, 20, 241, 68, 0, 0, 144, 39, 91, 142, 113, 228, 59, 135, 141, 211, 204, 68, 94, - 111, 118, 139, 140, 196, 50, 152, 192, 248, 164, 189, 17, 143, 147, 109, 192, 209, 205, 93, - 246, 195, 196, 64, 231, 128, 98, 231, 97, 114, 165, 39, 77, 133, 156, 196, 121, 116, 53, 139, - 126, 212, 31, 114, 102, 74, 201, 141, 245, 5, 61, 33, 164, 72, 4, 13, 211, 11, 164, 59, 116, 0, - 82, 8, 50, 190, 52, 59, 148, 248, 96, 18, 77, 196, 254, 226, 120, 253, 203, 211, 140, 16, 45, - 136, 1, 10, 171, 176, 226, 11, 0, 0, 0, 18, 227, 6, 136, 146, 136, 164, 143, 5, 172, 87, 103, - 11, 200, 159, 157, 7, 11, 29, 43, 164, 248, 160, 9, 166, 205, 32, 123, 55, 172, 147, 131, 32, - 166, 194, 24, 118, 107, 105, 222, 255, 8, 137, 60, 32, 97, 28, 23, 3, 125, 88, 32, 42, 35, 188, - 64, 174, 176, 101, 155, 81, 229, 34, 53, 162, 12, 0, 20, 11, 164, 59, 116, 0, 15, 66, 64, 96, - 96, 96, 64, 82, 96, 2, 97, 1, 8, 96, 0, 80, 85, 96, 64, 81, 97, 1, 86, 56, 3, 128, 97, 1, 86, - 131, 57, 129, 1, 96, 64, 82, 128, 81, 96, 128, 81, 96, 160, 81, 145, 144, 146, 1, 145, 144, - 128, 131, 131, 129, 81, 96, 1, 144, 129, 1, 129, 85, 96, 0, 144, 96, 1, 96, 160, 96, 2, 10, 3, - 50, 22, 144, 96, 2, 144, 96, 3, 131, 144, 85, 145, 131, 82, 80, 97, 1, 2, 96, 32, 82, 96, 64, - 130, 32, 85, 91, 130, 81, 129, 16, 21, 97, 0, 235, 87, 130, 129, 129, 81, 129, 16, 21, 97, 0, - 2, 87, 144, 96, 32, 1, 144, 96, 32, 2, 1, 81, 96, 1, 96, 160, 96, 2, 10, 3, 22, 96, 2, 96, 0, - 80, 130, 96, 2, 1, 97, 1, 0, 129, 16, 21, 97, 0, 2, 87, 144, 144, 1, 96, 0, 80, 129, 144, 85, - 80, 128, 96, 2, 1, 97, 1, 2, 96, 0, 80, 96, 0, 133, 132, 129, 81, 129, 16, 21, 97, 0, 2, 87, - 144, 96, 32, 1, 144, 96, 32, 2, 1, 81, 96, 1, 96, 160, 96, 2, 10, 3, 22, 129, 82, 96, 32, 1, - 144, 129, 82, 96, 32, 1, 96, 0, 32, 96, 0, 80, 129, 144, 85, 80, 96, 1, 1, 97, 0, 96, 86, 91, - 129, 96, 0, 96, 0, 80, 129, 144, 85, 80, 80, 80, 80, 128, 97, 1, 5, 96, 0, 80, 129, 144, 85, - 80, 97, 1, 15, 98, 1, 81, 128, 66, 4, 144, 86, 91, 97, 1, 7, 85, 80, 80, 80, 80, 96, 49, 128, - 97, 1, 37, 96, 0, 57, 96, 0, 243, 0, 54, 96, 0, 128, 55, 96, 32, 96, 0, 54, 96, 0, 52, 115, 39, - 57, 48, 210, 30, 1, 238, 37, 228, 194, 25, 182, 50, 89, 210, 20, 135, 34, 32, 162, 97, 35, 90, - 90, 3, 242, 21, 96, 1, 87, 96, 32, 96, 0, 243, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 96, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 138, 199, 35, 4, 137, 232, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 124, 68, 1, 174, 152, 241, 46, 246, 222, 57, 174, 36, 207, 159, 197, 31, 128, 235, 161, 107, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 167, 217, 250, 125, 14, 177, 24, 92, 103, 229, 77, 168, 60, - 46, 117, 219, 105, 227, 159, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 86, 42, 141, 203, 190, 238, - 247, 179, 96, 104, 93, 39, 48, 59, 214, 158, 9, 74, 204, 246, 1, 140, 248, 201, 133, 169, 101, - 33, 146, 68, 223, 146, 109, 185, 69, 171, 118, 177, 9, 227, 164, 57, 162, 51, 234, 43, 109, 93, - 18, 117, 235, 3, 137, 218, 35, 222, 53, 13, 166, 199, 201, 146, 65, 191, 126, 43, 163, 116, - 123, 154, 214, 239, 122, 120, 53, 32, 221, 81, 102, 195, 97, 1, 211, 209, 81, 162, 72, 4, 94, - 11, 164, 59, 116, 0, 82, 8, 50, 190, 52, 59, 148, 248, 96, 18, 77, 196, 254, 226, 120, 253, - 203, 211, 140, 16, 45, 136, 15, 35, 226, 29, 63, 111, 156, 0, 0, 117, 147, 232, 35, 151, 206, - 237, 147, 29, 87, 91, 243, 146, 238, 144, 169, 106, 15, 11, 41, 43, 149, 59, 93, 32, 212, 51, - 193, 200, 13, 31, 76, 112, 74, 124, 58, 235, 231, 54, 174, 113, 205, 150, 106, 237, 83, 236, - 35, 196, 44, 81, 181, 62, 143, 147, 140, 232, 79, 202, 160, 240, 186, 170, 125, 162, 72, 4, 27, - 11, 164, 59, 116, 0, 82, 8, 0, 0, 0, 0, 0, 0, 0, 192, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 64, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 192, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 200, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 91, 87, 111, 108, 102, 114, - 97, 109, 93, 32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 39, 65, 65, - 49, 50, 51, 32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, - 102, 108, 105, 103, 104, 116, 32, 108, 97, 110, 100, 101, 100, 39, 32, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 1, 61, 32, 39, 84, 114, 117, 101, 39, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 61, 32, 39, 70, 97, 108, 115, 101, 39, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 159, 40, 135, 126, 10, 185, 75, 223, 183, - 45, 53, 245, 220, 82, 51, 217, 155, 91, 204, 115, 222, 50, 164, 239, 154, 117, 133, 101, 254, - 206, 183, 85, 18, 148, 205, 24, 33, 178, 30, 72, 11, 67, 32, 205, 131, 192, 57, 244, 198, 175, - 240, 142, 1, 45, 134, 65, 202, 223, 126, 174, 177, 7, 230, 109, 162, 72, 4, 45, 11, 164, 59, - 116, 0, 82, 8, 196, 122, 170, 134, 0, 8, 190, 111, 101, 181, 140, 108, 110, 2, 168, 78, 102, - 110, 254, 49, 13, 105, 32, 88, 219, 45, 186, 32, 0, 50, 134, 166, 87, 154, 17, 53, 51, 222, - 187, 210, 156, 96, 88, 108, 212, 112, 97, 1, 71, 103, 46, 185, 47, 116, 112, 6, 170, 7, 142, 9, - 161, 250, 24, 193, 149, 102, 183, 106, 118, 192, 107, 50, 110, 22, 14, 16, 141, 21, 227, 89, - 180, 17, 38, 240, 87, 212, 175, 19, 123, 64, 249, 137, 109, 162, 200, 4, 25, 11, 164, 59, 116, - 0, 82, 8, 196, 122, 170, 134, 0, 8, 190, 111, 101, 181, 140, 108, 110, 2, 168, 78, 102, 110, - 254, 49, 6, 133, 88, 110, 54, 5, 234, 102, 128, 0, 116, 127, 134, 77, 35, 240, 45, 34, 224, 10, - 233, 95, 126, 18, 59, 110, 177, 244, 47, 80, 163, 9, 174, 222, 186, 66, 113, 209, 36, 76, 69, - 132, 133, 127, 151, 131, 126, 171, 156, 125, 92, 95, 179, 54, 149, 91, 95, 39, 38, 166, 242, 0, - 187, 49, 37, 161, 31, 187, 176, 25, 239, 169, 15, 8, 162, 12, 0, 6, 11, 164, 59, 116, 0, 15, - 66, 64, 96, 96, 96, 64, 82, 96, 64, 81, 97, 3, 70, 56, 3, 128, 97, 3, 70, 131, 57, 1, 96, 64, - 82, 96, 96, 128, 81, 96, 96, 1, 144, 96, 32, 1, 80, 91, 91, 51, 96, 0, 96, 0, 97, 1, 0, 10, - 129, 84, 129, 115, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 2, 25, 22, 144, 131, 2, 23, 144, 85, 80, 91, 128, 96, 1, 96, 0, 80, - 144, 128, 81, 144, 96, 32, 1, 144, 130, 128, 84, 130, 130, 85, 144, 96, 0, 82, 96, 32, 96, 0, - 32, 144, 96, 31, 1, 96, 32, 144, 4, 129, 1, 146, 130, 21, 96, 158, 87, 145, 130, 1, 91, 130, - 129, 17, 21, 96, 157, 87, 130, 81, 130, 96, 0, 80, 85, 145, 96, 32, 1, 145, 144, 96, 1, 1, 144, - 96, 129, 86, 91, 91, 80, 144, 80, 96, 197, 145, 144, 96, 169, 86, 91, 128, 130, 17, 21, 96, - 193, 87, 96, 0, 129, 129, 80, 96, 0, 144, 85, 80, 96, 1, 1, 96, 169, 86, 91, 80, 144, 86, 91, - 80, 80, 91, 80, 97, 2, 111, 128, 97, 0, 215, 96, 0, 57, 96, 0, 243, 96, 96, 96, 64, 82, 96, 0, - 53, 124, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 144, 4, 128, 99, 65, 192, 225, 181, 20, 97, 0, 68, 87, 128, 99, 207, 174, 50, 23, 20, 97, 0, - 81, 87, 97, 0, 66, 86, 91, 0, 91, 97, 0, 79, 96, 4, 80, 97, 0, 202, 86, 91, 0, 91, 97, 0, 92, - 96, 4, 80, 97, 1, 94, 86, 91, 96, 64, 81, 128, 128, 96, 32, 1, 130, 129, 3, 130, 82, 131, 129, - 129, 81, 129, 82, 96, 32, 1, 145, 80, 128, 81, 144, 96, 32, 1, 144, 128, 131, 131, 130, 144, - 96, 0, 96, 4, 96, 32, 132, 96, 31, 1, 4, 96, 3, 2, 96, 15, 1, 241, 80, 144, 80, 144, 129, 1, - 144, 96, 31, 22, 128, 21, 97, 0, 188, 87, 128, 130, 3, 128, 81, 96, 1, 131, 96, 32, 3, 97, 1, - 0, 10, 3, 25, 22, 129, 82, 96, 32, 1, 145, 80, 91, 80, 146, 80, 80, 80, 96, 64, 81, 128, 145, - 3, 144, 243, 91, 96, 0, 96, 0, 144, 84, 144, 97, 1, 0, 10, 144, 4, 115, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 22, 115, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 22, 51, 115, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 22, 20, 21, 97, 1, 91, 87, 96, 0, 96, 0, 144, 84, 144, 97, 1, 0, 10, 144, - 4, 115, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 22, 115, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 22, 255, 91, 91, 86, 91, 96, 32, 96, 64, 81, 144, 129, 1, 96, 64, - 82, 128, 96, 0, 129, 82, 96, 32, 1, 80, 127, 239, 222, 170, 245, 102, 247, 117, 29, 22, 161, - 44, 127, 168, 144, 158, 183, 65, 32, 244, 44, 186, 51, 77, 7, 221, 82, 70, 196, 143, 31, 186, - 129, 96, 1, 96, 0, 80, 96, 64, 81, 128, 128, 96, 32, 1, 130, 129, 3, 130, 82, 131, 129, 129, - 84, 129, 82, 96, 32, 1, 145, 80, 128, 84, 128, 21, 97, 1, 227, 87, 130, 1, 145, 144, 96, 0, 82, - 96, 32, 96, 0, 32, 144, 91, 129, 84, 129, 82, 144, 96, 1, 1, 144, 96, 32, 1, 128, 131, 17, 97, - 1, 198, 87, 130, 144, 3, 96, 31, 22, 130, 1, 145, 91, 80, 80, 146, 80, 80, 80, 96, 64, 81, 128, - 145, 3, 144, 161, 96, 2, 96, 0, 129, 129, 80, 84, 128, 146, 145, 144, 96, 1, 1, 145, 144, 80, - 85, 80, 96, 1, 96, 0, 53, 4, 69, 181, 47, 75, 162, 72, 4, 33, 11, 164, 59, 116, 0, 82, 8, 50, - 190, 52, 59, 148, 248, 96, 18, 77, 196, 254, 226, 120, 253, 203, 211, 140, 16, 45, 136, 14, 8, - 139, 102, 205, 199, 108, 0, 0, 93, 8, 17, 102, 86, 103, 149, 147, 101, 166, 22, 120, 77, 213, - 204, 21, 73, 208, 1, 247, 126, 178, 219, 95, 121, 203, 97, 204, 223, 45, 195, 191, 191, 191, - 150, 70, 240, 34, 108, 13, 104, 152, 111, 46, 86, 141, 140, 203, 190, 166, 72, 55, 66, 5, 72, - 20, 244, 124, 12, 208, 168, 161, 207, 96, 162, 12, 0, 175, 11, 164, 59, 116, 0, 5, 87, 48, 96, - 96, 96, 64, 82, 91, 51, 96, 0, 96, 0, 97, 1, 0, 10, 129, 84, 129, 115, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 2, 25, 22, 144, 131, - 2, 23, 144, 85, 80, 96, 0, 96, 2, 96, 0, 80, 129, 144, 85, 80, 91, 97, 2, 20, 128, 97, 0, 75, - 96, 0, 57, 96, 0, 243, 0, 96, 96, 96, 64, 82, 96, 0, 53, 124, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 144, 4, 128, 99, 16, 248, 17, 168, 20, - 97, 0, 101, 87, 128, 99, 78, 79, 130, 137, 20, 97, 0, 134, 87, 128, 99, 187, 212, 232, 201, 20, - 97, 0, 167, 87, 128, 99, 208, 227, 13, 176, 20, 97, 0, 200, 87, 128, 99, 248, 178, 203, 79, 20, - 97, 0, 213, 87, 97, 0, 99, 86, 91, 0, 91, 97, 0, 112, 96, 4, 80, 97, 0, 252, 86, 91, 96, 64, - 81, 128, 130, 129, 82, 96, 32, 1, 145, 80, 80, 96, 64, 81, 128, 145, 3, 144, 243, 91, 97, 0, - 145, 96, 4, 80, 97, 1, 14, 86, 91, 96, 64, 81, 128, 130, 129, 82, 96, 32, 1, 145, 80, 80, 96, - 64, 81, 128, 145, 3, 144, 243, 91, 97, 0, 178, 96, 4, 80, 97, 1, 5, 86, 91, 96, 64, 81, 128, - 130, 129, 82, 96, 32, 1, 145, 80, 80, 96, 64, 81, 128, 145, 3, 144, 243, 91, 97, 0, 211, 96, 4, - 80, 97, 1, 177, 86, 91, 0, 91, 97, 0, 230, 96, 4, 128, 53, 144, 96, 32, 1, 80, 97, 1, 23, 86, - 91, 96, 64, 81, 128, 130, 129, 82, 96, 32, 1, 145, 80, 80, 96, 64, 81, 128, 145, 3, 144, 243, - 91, 96, 1, 96, 0, 80, 84, 129, 86, 91, 96, 2, 96, 0, 80, 84, 129, 86, 91, 96, 3, 96, 0, 80, 84, - 129, 86, 91, 96, 0, 96, 0, 96, 0, 144, 84, 144, 97, 1, 0, 10, 144, 4, 115, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 22, 115, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 22, 51, 115, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 22, 20, 21, 21, 97, 1, 117, 87, 97, 1, 172, 86, 91, 96, 4, 96, 0, 80, 96, - 0, 131, 115, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 22, 129, 82, 96, 32, 1, 144, 129, 82, 96, 32, 1, 96, 0, 32, 96, 0, 80, 84, - 144, 80, 97, 1, 172, 86, 91, 145, 144, 80, 86, 91, 96, 2, 96, 0, 129, 129, 80, 84, 96, 1, 1, - 145, 144, 80, 129, 144, 85, 96, 1, 96, 0, 80, 129, 144, 85, 80, 66, 96, 3, 96, 0, 80, 129, 144, - 85, 80, 52, 96, 4, 96, 0, 80, 96, 0, 51, 115, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 22, 129, 82, 96, 32, 1, 144, 129, 82, 96, 32, - 1, 96, 0, 32, 96, 0, 130, 130, 130, 80, 84, 1, 146, 80, 80, 129, 144, 85, 80, 91, 86, 1, 225, - 155, 210, 34, 146, 81, 78, 41, 85, 99, 220, 53, 114, 108, 232, 136, 58, 227, 125, 203, 200, 43, - 61, 128, 243, 228, 13, 209, 58, 221, 244, 6, 75, 33, 83, 216, 52, 130, 17, 9, 181, 101, 206, - 115, 179, 166, 85, 163, 40, 40, 46, 107, 84, 3, 135, 116, 80, 37, 109, 91, 23, 176, 123, 84, - 164, 200, 4, 9, 227, 11, 164, 59, 116, 0, 82, 8, 124, 146, 101, 209, 175, 59, 217, 189, 78, - 196, 242, 18, 40, 97, 141, 33, 181, 250, 242, 97, 1, 48, 143, 218, 185, 160, 185, 0, 0, 1, 200, - 161, 224, 199, 77, 200, 48, 17, 97, 164, 112, 199, 26, 7, 189, 49, 239, 169, 150, 198, 137, 41, - 53, 116, 152, 191, 248, 74, 6, 78, 191, 91, 56, 7, 133, 107, 50, 131, 152, 204, 225, 94, 138, - 240, 40, 160, 73, 241, 233, 166, 66, 111, 48, 75, 0, 36, 196, 219, 68, 82, 135, 27, 48, 30, - 162, 72, 4, 60, 11, 164, 59, 116, 0, 82, 8, 251, 177, 183, 60, 79, 11, 218, 79, 103, 220, 162, - 102, 206, 110, 244, 47, 82, 15, 187, 152, 27, 205, 63, 35, 71, 40, 204, 0, 0, 2, 45, 120, 49, - 18, 175, 185, 231, 188, 109, 153, 191, 206, 154, 46, 199, 5, 90, 132, 41, 196, 174, 212, 107, - 68, 169, 39, 121, 72, 55, 167, 183, 83, 41, 52, 189, 228, 142, 230, 98, 75, 77, 62, 117, 133, - 99, 74, 194, 108, 180, 186, 92, 120, 56, 196, 65, 75, 7, 30, 75, 157, 102, 57, 28, 164, 76, 4, - 34, 30, 11, 164, 59, 116, 0, 1, 95, 144, 36, 86, 190, 158, 153, 113, 157, 211, 230, 31, 139, - 62, 120, 43, 185, 154, 110, 48, 158, 153, 41, 183, 122, 63, 161, 64, 198, 0, 1, 216, 204, 123, - 238, 182, 205, 81, 79, 74, 161, 255, 211, 109, 138, 104, 223, 100, 182, 242, 38, 229, 176, 1, - 96, 57, 231, 246, 193, 21, 92, 45, 151, 104, 172, 174, 245, 129, 55, 177, 8, 128, 239, 20, 59, - 89, 160, 0, 162, 90, 142, 19, 160, 202, 221, 204, 196, 83, 12, 119, 196, 207, 112, 142, 94, - 164, 204, 0, 1, 159, 16, 76, 83, 60, 0, 15, 66, 64, 17, 148, 233, 102, 150, 84, 24, 199, 215, - 58, 66, 204, 238, 178, 84, 216, 117, 134, 3, 86, 1, 213, 6, 78, 209, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 96, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 192, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 32, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 18, 69, 85, 82, 85, - 83, 68, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 71, 66, - 80, 85, 83, 68, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 85, 83, 68, 74, 80, 89, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 88, 65, 85, 85, 83, 68, 0, 0, 0 -] \ No newline at end of file From e622f38540976f80f0dfc2cbd2492d8dfac5149a Mon Sep 17 00:00:00 2001 From: Dan Cline <6798349+Rjected@users.noreply.github.com> Date: Fri, 15 Dec 2023 13:24:01 +0200 Subject: [PATCH 142/277] chore: remove RpcStateCacheArgs value parser (#5773) --- bin/reth/src/args/rpc_state_cache_args.rs | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/bin/reth/src/args/rpc_state_cache_args.rs b/bin/reth/src/args/rpc_state_cache_args.rs index 6396b2483a9f..df909dbbd4bd 100644 --- a/bin/reth/src/args/rpc_state_cache_args.rs +++ b/bin/reth/src/args/rpc_state_cache_args.rs @@ -1,4 +1,4 @@ -use clap::{builder::RangedU64ValueParser, Args}; +use clap::Args; use reth_rpc::eth::cache::{ DEFAULT_BLOCK_CACHE_MAX_LEN, DEFAULT_CONCURRENT_DB_REQUESTS, DEFAULT_ENV_CACHE_MAX_LEN, DEFAULT_RECEIPT_CACHE_MAX_LEN, @@ -13,7 +13,6 @@ pub struct RpcStateCacheArgs { #[arg( long = "rpc-cache.max-blocks", default_value_t = DEFAULT_BLOCK_CACHE_MAX_LEN, - value_parser = RangedU64ValueParser::::new().range(1..) )] pub max_blocks: u32, @@ -21,7 +20,6 @@ pub struct RpcStateCacheArgs { #[arg( long = "rpc-cache.max-receipts", default_value_t = DEFAULT_RECEIPT_CACHE_MAX_LEN, - value_parser = RangedU64ValueParser::::new().range(1..) )] pub max_receipts: u32, @@ -29,7 +27,6 @@ pub struct RpcStateCacheArgs { #[arg( long = "rpc-cache.max-envs", default_value_t = DEFAULT_ENV_CACHE_MAX_LEN, - value_parser = RangedU64ValueParser::::new().range(1..) )] pub max_envs: u32, @@ -37,7 +34,6 @@ pub struct RpcStateCacheArgs { #[arg( long = "rpc-cache.max-concurrent-db-requests", default_value_t = DEFAULT_CONCURRENT_DB_REQUESTS, - value_parser = RangedU64ValueParser::::new().range(1..) )] pub max_concurrent_db_requests: usize, } From 26a91c69f4441cc4992ba1af4fe4ace2bf5e4e20 Mon Sep 17 00:00:00 2001 From: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Date: Fri, 15 Dec 2023 13:27:49 +0200 Subject: [PATCH 143/277] feat: use nybbles crate (#5766) --- Cargo.lock | 15 + bin/reth/src/debug_cmd/in_memory_merkle.rs | 2 +- bin/reth/src/debug_cmd/merkle.rs | 2 +- crates/primitives/Cargo.toml | 2 + crates/primitives/src/trie/mod.rs | 2 +- crates/primitives/src/trie/nibbles.rs | 648 ++---------------- .../storage/db/src/tables/codecs/compact.rs | 10 +- crates/storage/db/src/tables/mod.rs | 4 +- crates/storage/db/src/tables/models/mod.rs | 8 +- crates/trie/src/trie.rs | 17 +- crates/trie/src/trie_cursor/account_cursor.rs | 18 +- crates/trie/src/updates.rs | 12 +- 12 files changed, 110 insertions(+), 630 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 881de09d6d0d..9d897c45332d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4577,6 +4577,20 @@ dependencies = [ "libc", ] +[[package]] +name = "nybbles" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47dddada2357f8e7786f4f4d837db7bdddec02c7c3e5da7840d92c70390f6dc0" +dependencies = [ + "alloy-rlp", + "arbitrary", + "const-hex", + "proptest", + "serde", + "smallvec", +] + [[package]] name = "object" version = "0.32.1" @@ -6209,6 +6223,7 @@ dependencies = [ "itertools 0.11.0", "modular-bitfield", "num_enum 0.7.1", + "nybbles", "once_cell", "plain_hasher", "pprof", diff --git a/bin/reth/src/debug_cmd/in_memory_merkle.rs b/bin/reth/src/debug_cmd/in_memory_merkle.rs index 357da8817129..4a942258c730 100644 --- a/bin/reth/src/debug_cmd/in_memory_merkle.rs +++ b/bin/reth/src/debug_cmd/in_memory_merkle.rs @@ -223,7 +223,7 @@ impl Command { (Some(in_mem), Some(incr)) => { pretty_assertions::assert_eq!(in_mem.0, incr.0, "Nibbles don't match"); if in_mem.1 != incr.1 && - matches!(in_mem.0, TrieKey::AccountNode(ref nibbles) if nibbles.len() > self.skip_node_depth.unwrap_or_default()) + matches!(in_mem.0, TrieKey::AccountNode(ref nibbles) if nibbles.0.len() > self.skip_node_depth.unwrap_or_default()) { in_mem_mismatched.push(in_mem); incremental_mismatched.push(incr); diff --git a/bin/reth/src/debug_cmd/merkle.rs b/bin/reth/src/debug_cmd/merkle.rs index 3745efc9f761..bcbfc4034955 100644 --- a/bin/reth/src/debug_cmd/merkle.rs +++ b/bin/reth/src/debug_cmd/merkle.rs @@ -311,7 +311,7 @@ impl Command { "Nibbles don't match" ); if incremental.1 != clean.1 && - clean.0.len() > self.skip_node_depth.unwrap_or_default() + clean.0 .0.len() > self.skip_node_depth.unwrap_or_default() { incremental_account_mismatched.push(incremental); clean_account_mismatched.push(clean); diff --git a/crates/primitives/Cargo.toml b/crates/primitives/Cargo.toml index eef82c7f025a..72e3f5b4b895 100644 --- a/crates/primitives/Cargo.toml +++ b/crates/primitives/Cargo.toml @@ -20,6 +20,7 @@ revm-primitives = { workspace = true, features = ["serde"] } alloy-primitives = { workspace = true, features = ["rand", "rlp"] } alloy-rlp = { workspace = true, features = ["arrayvec"] } ethers-core = { workspace = true, default-features = false, optional = true } +nybbles = { version = "0.1", features = ["serde", "rlp"] } # crypto secp256k1 = { workspace = true, features = ["global-context", "recovery"] } @@ -86,6 +87,7 @@ arbitrary = [ "revm-primitives/arbitrary", "reth-rpc-types/arbitrary", "reth-ethereum-forks/arbitrary", + "nybbles/arbitrary", "dep:arbitrary", "dep:proptest", "dep:proptest-derive", diff --git a/crates/primitives/src/trie/mod.rs b/crates/primitives/src/trie/mod.rs index 632ce3f4cdb7..0081678ac463 100644 --- a/crates/primitives/src/trie/mod.rs +++ b/crates/primitives/src/trie/mod.rs @@ -21,7 +21,7 @@ mod subnode; pub use self::{ account::TrieAccount, mask::TrieMask, - nibbles::{Nibbles, StoredNibblesSubKey}, + nibbles::{Nibbles, StoredNibbles, StoredNibblesSubKey}, storage::StorageTrieEntry, subnode::StoredSubNode, }; diff --git a/crates/primitives/src/trie/nibbles.rs b/crates/primitives/src/trie/nibbles.rs index fe60d18bd38f..5a46887f823b 100644 --- a/crates/primitives/src/trie/nibbles.rs +++ b/crates/primitives/src/trie/nibbles.rs @@ -1,656 +1,120 @@ -use crate::Bytes; use bytes::Buf; -use derive_more::{Deref, From, Index}; +use derive_more::Deref; use reth_codecs::Compact; use serde::{Deserialize, Serialize}; -use smallvec::SmallVec; -use std::{ - borrow::Borrow, - fmt, - mem::MaybeUninit, - ops::{Bound, RangeBounds}, -}; -/// The representation of nibbles of the merkle trie stored in the database. -#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, PartialOrd, Ord, Hash, Deref)] -pub struct StoredNibblesSubKey(pub Nibbles); - -impl From for StoredNibblesSubKey { - #[inline] - fn from(value: Nibbles) -> Self { - Self(value) - } -} +pub use nybbles::Nibbles; -impl From> for StoredNibblesSubKey { - #[inline] - fn from(value: Vec) -> Self { - Self(Nibbles::from_nibbles_unchecked(value)) - } -} - -impl From for Nibbles { - #[inline] - fn from(value: StoredNibblesSubKey) -> Self { - value.0 - } -} - -impl Compact for StoredNibblesSubKey { - fn to_compact(self, buf: &mut B) -> usize - where - B: bytes::BufMut + AsMut<[u8]>, - { - assert!(self.0.len() <= 64); - - // right-pad with zeros - buf.put_slice(&self.0[..]); - static ZERO: &[u8; 64] = &[0; 64]; - buf.put_slice(&ZERO[self.0.len()..]); - - buf.put_u8(self.0.len() as u8); - 64 + 1 - } - - fn from_compact(buf: &[u8], _len: usize) -> (Self, &[u8]) { - let len = buf[64] as usize; - (Self(Nibbles::from_nibbles_unchecked(&buf[..len])), &buf[65..]) - } -} - -/// Structure representing a sequence of nibbles. -/// -/// A nibble is a 4-bit value, and this structure is used to store the nibble sequence representing -/// the keys in a Merkle Patricia Trie (MPT). -/// Using nibbles simplifies trie operations and enables consistent key representation in the MPT. -/// -/// The internal representation is a [`SmallVec`] that stores one nibble per byte. This means that -/// each byte has its upper 4 bits set to zero and the lower 4 bits representing the nibble value. +/// The representation of nibbles of the merkle trie stored in the database. #[derive( + Clone, + Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash, - Index, - From, - Deref, - serde::Serialize, - serde::Deserialize, + Serialize, + Deserialize, + derive_more::Index, )] -#[cfg_attr(any(test, feature = "arbitrary"), derive(arbitrary::Arbitrary))] -pub struct Nibbles(SmallVec<[u8; 64]>); +pub struct StoredNibbles(pub Nibbles); -// Override `SmallVec::from` since it's not specialized for `Copy` types. -impl Clone for Nibbles { +impl From for StoredNibbles { #[inline] - fn clone(&self) -> Self { - Self(SmallVec::from_slice(&self.0)) + fn from(value: Nibbles) -> Self { + Self(value) } +} +impl From> for StoredNibbles { #[inline] - fn clone_from(&mut self, source: &Self) { - self.0.clone_from(&source.0); + fn from(value: Vec) -> Self { + Self(Nibbles::from_nibbles_unchecked(value)) } } -impl alloy_rlp::Encodable for Nibbles { +impl PartialEq<[u8]> for StoredNibbles { #[inline] - fn length(&self) -> usize { - alloy_rlp::Encodable::length(self.as_slice()) + fn eq(&self, other: &[u8]) -> bool { + self.0.as_slice() == other } +} +impl PartialOrd<[u8]> for StoredNibbles { #[inline] - fn encode(&self, out: &mut dyn alloy_rlp::BufMut) { - alloy_rlp::Encodable::encode(self.as_slice(), out) + fn partial_cmp(&self, other: &[u8]) -> Option { + self.0.as_slice().partial_cmp(other) } } -#[cfg(any(test, feature = "arbitrary"))] -impl proptest::arbitrary::Arbitrary for Nibbles { - type Parameters = (); - type Strategy = proptest::strategy::Map< - proptest::collection::VecStrategy>, - fn(Vec) -> Self, - >; - +impl core::borrow::Borrow<[u8]> for StoredNibbles { #[inline] - fn arbitrary_with((): ()) -> Self::Strategy { - use proptest::prelude::*; - proptest::collection::vec(0x0..=0xf, 0..64).prop_map(Self::from_nibbles_unchecked) + fn borrow(&self) -> &[u8] { + self.0.as_slice() } } -impl Compact for Nibbles { +impl Compact for StoredNibbles { fn to_compact(self, buf: &mut B) -> usize where B: bytes::BufMut + AsMut<[u8]>, { - buf.put_slice(self.as_slice()); - self.len() + buf.put_slice(self.0.as_slice()); + self.0.len() } fn from_compact(mut buf: &[u8], len: usize) -> (Self, &[u8]) { let nibbles = &buf[..len]; buf.advance(len); - (Nibbles::from_nibbles_unchecked(nibbles), buf) - } -} - -impl fmt::Debug for Nibbles { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_tuple("Nibbles").field(&crate::hex::encode(self.as_slice())).finish() + (Self(Nibbles::from_nibbles_unchecked(nibbles)), buf) } } -impl From> for Nibbles { - #[inline] - fn from(value: Vec) -> Self { - Self(SmallVec::from_vec(value)) - } -} - -impl From for Vec { - #[inline] - fn from(value: Nibbles) -> Self { - value.0.into_vec() - } -} +/// The representation of nibbles of the merkle trie stored in the database. +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, PartialOrd, Ord, Hash, Deref)] +pub struct StoredNibblesSubKey(pub Nibbles); -impl From for Bytes { +impl From for StoredNibblesSubKey { #[inline] fn from(value: Nibbles) -> Self { - value.0.into_vec().into() - } -} - -impl PartialEq<[u8]> for Nibbles { - #[inline] - fn eq(&self, other: &[u8]) -> bool { - self.as_slice() == other - } -} - -impl PartialEq for [u8] { - #[inline] - fn eq(&self, other: &Nibbles) -> bool { - self == other.as_slice() - } -} - -impl PartialOrd<[u8]> for Nibbles { - #[inline] - fn partial_cmp(&self, other: &[u8]) -> Option { - self.as_slice().partial_cmp(other) - } -} - -impl PartialOrd for [u8] { - #[inline] - fn partial_cmp(&self, other: &Nibbles) -> Option { - self.partial_cmp(other.as_slice()) - } -} - -impl Borrow<[u8]> for Nibbles { - #[inline] - fn borrow(&self) -> &[u8] { - self.as_slice() + Self(value) } } -impl Extend for Nibbles { +impl From> for StoredNibblesSubKey { #[inline] - fn extend>(&mut self, iter: T) { - self.0.extend(iter) + fn from(value: Vec) -> Self { + Self(Nibbles::from_nibbles_unchecked(value)) } } -impl Nibbles { - /// Creates a new empty [`Nibbles`] instance. - #[inline] - pub const fn new() -> Self { - Self(SmallVec::new_const()) - } - - /// Creates a new [`Nibbles`] instance with the given capacity. - #[inline] - pub fn with_capacity(capacity: usize) -> Self { - Self(SmallVec::with_capacity(capacity)) - } - - /// Creates a new [`Nibbles`] instance from nibble bytes, without checking their validity. - #[inline] - pub fn from_nibbles_unchecked>(nibbles: T) -> Self { - Self(SmallVec::from_slice(nibbles.as_ref())) - } - - /// Converts a byte slice into a [`Nibbles`] instance containing the nibbles (half-bytes or 4 - /// bits) that make up the input byte data. - #[inline] - pub fn unpack>(data: T) -> Self { - let data = data.as_ref(); - if data.len() <= 32 { - // SAFETY: checked length. - unsafe { Self::unpack_stack(data) } - } else { - Self::unpack_heap(data) - } - } - - /// Unpacks on the stack. - /// - /// # Safety - /// - /// `data.len()` must be less than or equal to 32. - unsafe fn unpack_stack(data: &[u8]) -> Self { - let mut nibbles = MaybeUninit::<[u8; 64]>::uninit(); - Self::unpack_to(data, nibbles.as_mut_ptr().cast()); - let unpacked_len = data.len() * 2; - Self(SmallVec::from_buf_and_len_unchecked(nibbles, unpacked_len)) - } - - /// Unpacks on the heap. - fn unpack_heap(data: &[u8]) -> Self { - // Collect into a vec directly to avoid the smallvec overhead since we know this is going on - // the heap. - let unpacked_len = data.len() * 2; - let mut nibbles = Vec::with_capacity(unpacked_len); - // SAFETY: enough capacity. - unsafe { Self::unpack_to(data, nibbles.as_mut_ptr()) }; - // SAFETY: within capacity and `unpack_to` initialized the memory. - unsafe { nibbles.set_len(unpacked_len) }; - Self(SmallVec::from_vec(nibbles)) - } - - /// Unpacks into the given pointer. - /// - /// # Safety - /// - /// `ptr` must be valid for at least `data.len() * 2` bytes. - #[inline] - unsafe fn unpack_to(data: &[u8], ptr: *mut u8) { - for (i, &byte) in data.iter().enumerate() { - ptr.add(i * 2).write(byte >> 4); - ptr.add(i * 2 + 1).write(byte & 0x0f); - } - } - - /// Packs the nibbles stored in the struct into a byte vector. - /// - /// This method combines each pair of consecutive nibbles into a single byte, - /// effectively reducing the size of the data by a factor of two. - /// If the number of nibbles is odd, the last nibble is shifted left by 4 bits and - /// added to the packed byte vector. - #[inline] - pub fn pack(&self) -> SmallVec<[u8; 32]> { - if self.len() <= 64 { - // SAFETY: checked length. - unsafe { self.pack_stack() } - } else { - self.pack_heap() - } - } - - /// Packs on the stack. - /// - /// # Safety - /// - /// `self.len()` must be less than or equal to 32. - unsafe fn pack_stack(&self) -> SmallVec<[u8; 32]> { - let mut nibbles = MaybeUninit::<[u8; 32]>::uninit(); - self.pack_to(nibbles.as_mut_ptr().cast()); - let packed_len = (self.len() + 1) / 2; - SmallVec::from_buf_and_len_unchecked(nibbles, packed_len) - } - - /// Packs on the heap. - fn pack_heap(&self) -> SmallVec<[u8; 32]> { - // Collect into a vec directly to avoid the smallvec overhead since we know this is going on - // the heap. - let packed_len = (self.len() + 1) / 2; - let mut vec = Vec::with_capacity(packed_len); - // SAFETY: enough capacity. - unsafe { self.pack_to(vec.as_mut_ptr()) }; - // SAFETY: within capacity and `pack_to` initialized the memory. - unsafe { vec.set_len(packed_len) }; - SmallVec::from_vec(vec) - } - - /// Packs into the given pointer. - /// - /// # Safety - /// - /// `ptr` must be valid for at least `self.len() / 2 + IS_ODD` bytes. - #[inline] - unsafe fn pack_to(&self, ptr: *mut u8) { - for i in 0..self.len() / 2 { - ptr.add(i).write(self.get_byte_unchecked(i * 2)); - } - if self.len() % 2 != 0 { - let i = self.len() / 2; - ptr.add(i).write(self.last().unwrap_unchecked() << 4); - } - } - - /// Gets the byte at the given index by combining two consecutive nibbles. - /// - /// # Safety - /// - /// `i..i + 1` must be in range. - #[inline] - unsafe fn get_byte_unchecked(&self, i: usize) -> u8 { - debug_assert!(i + 1 < self.len(), "index {i}..{} out of bounds of {}", i + 1, self.len()); - let hi = *self.get_unchecked(i); - let lo = *self.get_unchecked(i + 1); - (hi << 4) | lo - } - - /// Encodes a given path leaf as a compact array of bytes, where each byte represents two - /// "nibbles" (half-bytes or 4 bits) of the original hex data, along with additional information - /// about the leaf itself. - /// - /// The method takes the following input: - /// `is_leaf`: A boolean value indicating whether the current node is a leaf node or not. - /// - /// The first byte of the encoded vector is set based on the `is_leaf` flag and the parity of - /// the hex data length (even or odd number of nibbles). - /// - If the node is an extension with even length, the header byte is `0x00`. - /// - If the node is an extension with odd length, the header byte is `0x10 + `. - /// - If the node is a leaf with even length, the header byte is `0x20`. - /// - If the node is a leaf with odd length, the header byte is `0x30 + `. - /// - /// If there is an odd number of nibbles, store the first nibble in the lower 4 bits of the - /// first byte of encoded. - /// - /// # Returns - /// - /// A vector containing the compact byte representation of the nibble sequence, including the - /// header byte. - /// - /// This vector's length is `self.len() / 2 + 1`. For stack-allocated nibbles, this is at most - /// 33 bytes, so 36 was chosen as the stack capacity to round up to the next usize-aligned - /// size. - /// - /// # Examples - /// - /// ``` - /// # use reth_primitives::trie::Nibbles; - /// - /// // Extension node with an even path length: - /// let nibbles = Nibbles::from_nibbles_unchecked(&[0x0A, 0x0B, 0x0C, 0x0D]); - /// assert_eq!(nibbles.encode_path_leaf(false)[..], [0x00, 0xAB, 0xCD]); - /// - /// // Extension node with an odd path length: - /// let nibbles = Nibbles::from_nibbles_unchecked(&[0x0A, 0x0B, 0x0C]); - /// assert_eq!(nibbles.encode_path_leaf(false)[..], [0x1A, 0xBC]); - /// - /// // Leaf node with an even path length: - /// let nibbles = Nibbles::from_nibbles_unchecked(&[0x0A, 0x0B, 0x0C, 0x0D]); - /// assert_eq!(nibbles.encode_path_leaf(true)[..], [0x20, 0xAB, 0xCD]); - /// - /// // Leaf node with an odd path length: - /// let nibbles = Nibbles::from_nibbles_unchecked(&[0x0A, 0x0B, 0x0C]); - /// assert_eq!(nibbles.encode_path_leaf(true)[..], [0x3A, 0xBC]); - /// ``` - pub fn encode_path_leaf(&self, is_leaf: bool) -> SmallVec<[u8; 36]> { - let encoded_len = self.len() / 2 + 1; - let mut encoded = SmallVec::with_capacity(encoded_len); - // SAFETY: enough capacity. - unsafe { self.encode_path_leaf_to(is_leaf, encoded.as_mut_ptr()) }; - // SAFETY: within capacity and `encode_path_leaf_to` initialized the memory. - unsafe { encoded.set_len(encoded_len) }; - encoded - } - - /// # Safety - /// - /// `ptr` must be valid for at least `self.len() / 2 + 1` bytes. - #[inline] - unsafe fn encode_path_leaf_to(&self, is_leaf: bool, ptr: *mut u8) { - let odd_nibbles = self.len() % 2 != 0; - *ptr = self.encode_path_leaf_first_byte(is_leaf, odd_nibbles); - let mut nibble_idx = if odd_nibbles { 1 } else { 0 }; - for i in 0..self.len() / 2 { - ptr.add(i + 1).write(self.get_byte_unchecked(nibble_idx)); - nibble_idx += 2; - } - } - - #[inline] - fn encode_path_leaf_first_byte(&self, is_leaf: bool, odd_nibbles: bool) -> u8 { - match (is_leaf, odd_nibbles) { - (true, true) => 0x30 | self[0], - (true, false) => 0x20, - (false, true) => 0x10 | self[0], - (false, false) => 0x00, - } - } - - /// Increments the nibble sequence by one. - pub fn increment(&self) -> Option { - let mut incremented = self.clone(); - - for nibble in incremented.0.iter_mut().rev() { - debug_assert!(*nibble <= 0xf); - if *nibble < 0xf { - *nibble += 1; - return Some(incremented) - } else { - *nibble = 0; - } - } - - None - } - - /// The last element of the hex vector is used to determine whether the nibble sequence - /// represents a leaf or an extension node. If the last element is 0x10 (16), then it's a leaf. - #[inline] - pub fn is_leaf(&self) -> bool { - self.last() == Some(16) - } - - /// Returns `true` if the current nibble sequence starts with the given prefix. - #[inline] - pub fn has_prefix(&self, other: &[u8]) -> bool { - self.starts_with(other) - } - - /// Returns the nibble at the given index. - /// - /// # Panics - /// - /// Panics if the index is out of bounds. - #[inline] - #[track_caller] - pub fn at(&self, i: usize) -> usize { - self[i] as usize - } - - /// Returns the first nibble of the current nibble sequence. - #[inline] - pub fn first(&self) -> Option { - self.0.first().copied() - } - - /// Returns the last nibble of the current nibble sequence. - #[inline] - pub fn last(&self) -> Option { - self.0.last().copied() - } - - /// Returns the length of the common prefix between the current nibble sequence and the given. - #[inline] - pub fn common_prefix_length(&self, other: &[u8]) -> usize { - let len = std::cmp::min(self.len(), other.len()); - for i in 0..len { - if self[i] != other[i] { - return i - } - } - len - } - - /// Returns the nibbles as a byte slice. - #[inline] - pub fn as_slice(&self) -> &[u8] { - &self.0 - } - - /// Slice the current nibbles within the provided index range. - /// - /// # Panics - /// - /// Panics if the range is out of bounds. - #[inline] - #[track_caller] - pub fn slice(&self, range: impl RangeBounds) -> Self { - let start = match range.start_bound() { - Bound::Included(&n) => n, - Bound::Excluded(&n) => n.checked_add(1).expect("out of range"), - Bound::Unbounded => 0, - }; - let end = match range.end_bound() { - Bound::Included(&n) => n.checked_add(1).expect("out of range"), - Bound::Excluded(&n) => n, - Bound::Unbounded => self.len(), - }; - Self::from_nibbles_unchecked(&self[start..end]) - } - - /// Join two nibbles together. - #[inline] - pub fn join(&self, b: &Self) -> Self { - let mut nibbles = SmallVec::with_capacity(self.len() + b.len()); - nibbles.extend_from_slice(self); - nibbles.extend_from_slice(b); - Self(nibbles) - } - - /// Pushes a nibble to the end of the current nibbles. - #[inline] - pub fn push(&mut self, nibble: u8) { - self.0.push(nibble); - } - - /// Extend the current nibbles with another nibbles. - #[inline] - pub fn extend_from_slice(&mut self, b: impl AsRef<[u8]>) { - self.0.extend_from_slice(b.as_ref()); - } - - /// Truncates the current nibbles to the given length. - #[inline] - pub fn truncate(&mut self, new_len: usize) { - self.0.truncate(new_len); - } - - /// Clears the current nibbles. +impl From for Nibbles { #[inline] - pub fn clear(&mut self) { - self.0.clear(); + fn from(value: StoredNibblesSubKey) -> Self { + value.0 } } -#[cfg(test)] -mod tests { - use super::*; - use crate::hex; - use proptest::prelude::*; - - #[test] - fn hashed_regression() { - let nibbles = Nibbles::from_nibbles_unchecked(hex!("05010406040a040203030f010805020b050c04070003070e0909070f010b0a0805020301070c0a0902040b0f000f0006040a04050f020b090701000a0a040b")); - let path = nibbles.encode_path_leaf(true); - let expected = hex!("351464a4233f1852b5c47037e997f1ba852317ca924bf0f064a45f2b9710aa4b"); - assert_eq!(path[..], expected); - } - - #[test] - fn pack_nibbles() { - let tests = [ - (&[][..], &[][..]), - (&[0xa], &[0xa0]), - (&[0xa, 0x0], &[0xa0]), - (&[0xa, 0xb], &[0xab]), - (&[0xa, 0xb, 0x2], &[0xab, 0x20]), - (&[0xa, 0xb, 0x2, 0x0], &[0xab, 0x20]), - (&[0xa, 0xb, 0x2, 0x7], &[0xab, 0x27]), - ]; - for (input, expected) in tests { - assert!(input.iter().all(|&x| x <= 0xf)); - let nibbles = Nibbles::from_nibbles_unchecked(input); - let encoded = nibbles.pack(); - assert_eq!(&encoded[..], expected); - } - } - - #[test] - fn slice() { - const RAW: &[u8] = &hex!("05010406040a040203030f010805020b050c04070003070e0909070f010b0a0805020301070c0a0902040b0f000f0006040a04050f020b090701000a0a040b"); - - #[track_caller] - fn test_slice(range: impl RangeBounds, expected: &[u8]) { - let nibbles = Nibbles::from_nibbles_unchecked(RAW); - let sliced = nibbles.slice(range); - assert_eq!(sliced, Nibbles::from_nibbles_unchecked(expected)); - assert_eq!(sliced.as_slice(), expected); - } - - test_slice(0..0, &[]); - test_slice(0..1, &[0x05]); - test_slice(1..1, &[]); - test_slice(1..=1, &[0x01]); - test_slice(0..=1, &[0x05, 0x01]); - test_slice(0..2, &[0x05, 0x01]); +impl Compact for StoredNibblesSubKey { + fn to_compact(self, buf: &mut B) -> usize + where + B: bytes::BufMut + AsMut<[u8]>, + { + assert!(self.0.len() <= 64); - test_slice(..0, &[]); - test_slice(..1, &[0x05]); - test_slice(..=1, &[0x05, 0x01]); - test_slice(..2, &[0x05, 0x01]); + // right-pad with zeros + buf.put_slice(&self.0[..]); + static ZERO: &[u8; 64] = &[0; 64]; + buf.put_slice(&ZERO[self.0.len()..]); - test_slice(.., RAW); - test_slice(..RAW.len(), RAW); - test_slice(0.., RAW); - test_slice(0..RAW.len(), RAW); + buf.put_u8(self.0.len() as u8); + 64 + 1 } - proptest! { - #[test] - fn pack_unpack_roundtrip(input in any::>()) { - let nibbles = Nibbles::unpack(&input); - prop_assert!(nibbles.iter().all(|&nibble| nibble <= 0xf)); - let packed = nibbles.pack(); - prop_assert_eq!(&packed[..], input); - } - - #[test] - fn encode_path_first_byte(input in any::>()) { - prop_assume!(!input.is_empty()); - let input = Nibbles::unpack(input); - prop_assert!(input.iter().all(|&nibble| nibble <= 0xf)); - let input_is_odd = input.len() % 2 == 1; - - let compact_leaf = input.encode_path_leaf(true); - let leaf_flag = compact_leaf[0]; - // Check flag - assert_ne!(leaf_flag & 0x20, 0); - assert_eq!(input_is_odd, (leaf_flag & 0x10) != 0); - if input_is_odd { - assert_eq!(leaf_flag & 0x0f, input.first().unwrap()); - } - - - let compact_extension = input.encode_path_leaf(false); - let extension_flag = compact_extension[0]; - // Check first byte - assert_eq!(extension_flag & 0x20, 0); - assert_eq!(input_is_odd, (extension_flag & 0x10) != 0); - if input_is_odd { - assert_eq!(extension_flag & 0x0f, input.first().unwrap()); - } - } + fn from_compact(buf: &[u8], _len: usize) -> (Self, &[u8]) { + let len = buf[64] as usize; + (Self(Nibbles::from_nibbles_unchecked(&buf[..len])), &buf[65..]) } } diff --git a/crates/storage/db/src/tables/codecs/compact.rs b/crates/storage/db/src/tables/codecs/compact.rs index b005cc961ee6..bcbdb2b1eb30 100644 --- a/crates/storage/db/src/tables/codecs/compact.rs +++ b/crates/storage/db/src/tables/codecs/compact.rs @@ -9,17 +9,15 @@ use reth_primitives::{stage::StageCheckpoint, trie::*, *}; macro_rules! impl_compression_for_compact { ($($name:tt),+) => { $( - impl Compress for $name - { + impl Compress for $name { type Compressed = Vec; fn compress_to_buf>(self, buf: &mut B) { - let _ = Compact::to_compact(self, buf); + let _ = Compact::to_compact(self, buf); } } - impl Decompress for $name - { + impl Decompress for $name { fn decompress>(value: B) -> Result<$name, $crate::DatabaseError> { let value = value.as_ref(); let (obj, _) = Compact::from_compact(&value, value.len()); @@ -37,8 +35,8 @@ impl_compression_for_compact!( Receipt, TxType, StorageEntry, - Nibbles, BranchNodeCompact, + StoredNibbles, StoredNibblesSubKey, StorageTrieEntry, StoredBlockBodyIndices, diff --git a/crates/storage/db/src/tables/mod.rs b/crates/storage/db/src/tables/mod.rs index f8d4457ff658..275271f7bc39 100644 --- a/crates/storage/db/src/tables/mod.rs +++ b/crates/storage/db/src/tables/mod.rs @@ -36,7 +36,7 @@ use crate::{ }; use reth_primitives::{ stage::StageCheckpoint, - trie::{BranchNodeCompact, Nibbles, StorageTrieEntry, StoredNibblesSubKey}, + trie::{BranchNodeCompact, StorageTrieEntry, StoredNibbles, StoredNibblesSubKey}, Account, Address, BlockHash, BlockNumber, Bytecode, Header, IntegerList, PruneCheckpoint, PruneSegment, Receipt, StorageEntry, TransactionSignedNoHash, TxHash, TxNumber, B256, }; @@ -384,7 +384,7 @@ dupsort!( table!( /// Stores the current state's Merkle Patricia Tree. - ( AccountsTrie ) Nibbles | BranchNodeCompact + ( AccountsTrie ) StoredNibbles | BranchNodeCompact ); dupsort!( diff --git a/crates/storage/db/src/tables/models/mod.rs b/crates/storage/db/src/tables/models/mod.rs index 3fe332a60bbd..5b65c03f30fa 100644 --- a/crates/storage/db/src/tables/models/mod.rs +++ b/crates/storage/db/src/tables/models/mod.rs @@ -5,7 +5,7 @@ use crate::{ }; use reth_codecs::Compact; use reth_primitives::{ - trie::{Nibbles, StoredNibblesSubKey}, + trie::{StoredNibbles, StoredNibblesSubKey}, Address, PruneSegment, B256, }; @@ -102,18 +102,18 @@ impl Decode for String { } } -impl Encode for Nibbles { +impl Encode for StoredNibbles { type Encoded = Vec; // Delegate to the Compact implementation fn encode(self) -> Self::Encoded { - let mut buf = Vec::with_capacity(self.len()); + let mut buf = Vec::with_capacity(self.0.len()); self.to_compact(&mut buf); buf } } -impl Decode for Nibbles { +impl Decode for StoredNibbles { fn decode>(value: B) -> Result { let buf = value.as_ref(); Ok(Self::from_compact(buf, buf.len()).0) diff --git a/crates/trie/src/trie.rs b/crates/trie/src/trie.rs index ad385a65c7aa..b5053cbba643 100644 --- a/crates/trie/src/trie.rs +++ b/crates/trie/src/trie.rs @@ -1139,7 +1139,7 @@ mod tests { .iter() .filter_map(|entry| match entry { (TrieKey::AccountNode(nibbles), TrieOp::Update(node)) => { - Some((nibbles.clone(), node.clone())) + Some((nibbles.0.clone(), node.clone())) } _ => None, }) @@ -1163,12 +1163,13 @@ mod tests { // read the account updates from the db let mut accounts_trie = tx.tx_ref().cursor_read::().unwrap(); let walker = accounts_trie.walk(None).unwrap(); - let mut account_updates = HashMap::new(); - for item in walker { - let (key, node) = item.unwrap(); - account_updates.insert(key, node); - } - + let account_updates = walker + .into_iter() + .map(|item| { + let (key, node) = item.unwrap(); + (key.0, node) + }) + .collect(); assert_trie_updates(&account_updates); } @@ -1227,7 +1228,7 @@ mod tests { .iter() .filter_map(|entry| match entry { (TrieKey::StorageNode(_, nibbles), TrieOp::Update(node)) => { - Some((nibbles.clone().into(), node.clone())) + Some((nibbles.0.clone(), node.clone())) } _ => None, }) diff --git a/crates/trie/src/trie_cursor/account_cursor.rs b/crates/trie/src/trie_cursor/account_cursor.rs index 94e7be590146..95033fae6dda 100644 --- a/crates/trie/src/trie_cursor/account_cursor.rs +++ b/crates/trie/src/trie_cursor/account_cursor.rs @@ -1,7 +1,7 @@ use super::TrieCursor; use crate::updates::TrieKey; use reth_db::{cursor::DbCursorRO, tables, DatabaseError}; -use reth_primitives::trie::{BranchNodeCompact, Nibbles}; +use reth_primitives::trie::{BranchNodeCompact, StoredNibbles}; /// A cursor over the account trie. #[derive(Debug)] @@ -18,20 +18,20 @@ impl TrieCursor for AccountTrieCursor where C: DbCursorRO, { - type Key = Nibbles; + type Key = StoredNibbles; fn seek_exact( &mut self, key: Self::Key, ) -> Result, BranchNodeCompact)>, DatabaseError> { - Ok(self.0.seek_exact(key)?.map(|value| (value.0.to_vec(), value.1))) + Ok(self.0.seek_exact(key)?.map(|value| (value.0 .0.to_vec(), value.1))) } fn seek( &mut self, key: Self::Key, ) -> Result, BranchNodeCompact)>, DatabaseError> { - Ok(self.0.seek(key)?.map(|value| (value.0.to_vec(), value.1))) + Ok(self.0.seek(key)?.map(|value| (value.0 .0.to_vec(), value.1))) } fn current(&mut self) -> Result, DatabaseError> { @@ -79,13 +79,13 @@ mod tests { } let db_data = cursor.walk_range(..).unwrap().collect::, _>>().unwrap(); - assert_eq!(db_data[0].0.to_vec(), data[0]); - assert_eq!(db_data[1].0.to_vec(), data[1]); - assert_eq!(db_data[2].0.to_vec(), data[2]); - assert_eq!(db_data[3].0.to_vec(), data[3]); + assert_eq!(db_data[0].0 .0.to_vec(), data[0]); + assert_eq!(db_data[1].0 .0.to_vec(), data[1]); + assert_eq!(db_data[2].0 .0.to_vec(), data[2]); + assert_eq!(db_data[3].0 .0.to_vec(), data[3]); assert_eq!( - cursor.seek(hex!("0303040f").to_vec().into()).unwrap().map(|(k, _)| k.to_vec()), + cursor.seek(hex!("0303040f").to_vec().into()).unwrap().map(|(k, _)| k.0.to_vec()), Some(data[1].clone()) ); } diff --git a/crates/trie/src/updates.rs b/crates/trie/src/updates.rs index 9a3da6c54268..9d7bfcbb31a0 100644 --- a/crates/trie/src/updates.rs +++ b/crates/trie/src/updates.rs @@ -5,7 +5,7 @@ use reth_db::{ transaction::{DbTx, DbTxMut}, }; use reth_primitives::{ - trie::{BranchNodeCompact, Nibbles, StorageTrieEntry, StoredNibblesSubKey}, + trie::{BranchNodeCompact, Nibbles, StorageTrieEntry, StoredNibbles, StoredNibblesSubKey}, B256, }; use std::collections::{hash_map::IntoIter, HashMap}; @@ -14,7 +14,7 @@ use std::collections::{hash_map::IntoIter, HashMap}; #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] pub enum TrieKey { /// A node in the account trie. - AccountNode(Nibbles), + AccountNode(StoredNibbles), /// A node in the storage trie. StorageNode(B256, StoredNibblesSubKey), /// Storage trie of an account. @@ -79,9 +79,9 @@ impl TrieUpdates { /// Extend the updates with account trie updates. pub fn extend_with_account_updates(&mut self, updates: HashMap) { self.extend( - updates - .into_iter() - .map(|(nibbles, node)| (TrieKey::AccountNode(nibbles), TrieOp::Update(node))), + updates.into_iter().map(|(nibbles, node)| { + (TrieKey::AccountNode(nibbles.into()), TrieOp::Update(node)) + }), ); } @@ -121,7 +121,7 @@ impl TrieUpdates { } } TrieOp::Update(node) => { - if !nibbles.is_empty() { + if !nibbles.0.is_empty() { account_trie_cursor.upsert(nibbles, node)?; } } From 1eaa3ed5a5bcdd67852a05bd3b932316d3a8637d Mon Sep 17 00:00:00 2001 From: solidoracle <105349716+solidoracle@users.noreply.github.com> Date: Fri, 15 Dec 2023 13:00:28 +0100 Subject: [PATCH 144/277] chore: add reorg metrics to grafana (#5774) --- etc/grafana/dashboards/overview.json | 94 ++++++++++++++++++++++++++++ 1 file changed, 94 insertions(+) diff --git a/etc/grafana/dashboards/overview.json b/etc/grafana/dashboards/overview.json index 648a086b80b7..a9a21fb51d62 100644 --- a/etc/grafana/dashboards/overview.json +++ b/etc/grafana/dashboards/overview.json @@ -4902,6 +4902,100 @@ ], "title": "Canonicalization duration per action", "type": "timeseries" + },{ + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "none" + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 176 + }, + "id": 190, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": false + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "expr": "reth_blockchain_tree_latest_reorg_depth{instance=~\"$instance\"}", + "instant": false, + "legendFormat": "__auto", + "range": true, + "refId": "A" + } + ], + "title": "Latest Reorg Depth", + "type": "timeseries" }, { "collapsed": false, From faa9a22a7131e57feb277e6d5e9203b369c8099e Mon Sep 17 00:00:00 2001 From: Bjerg Date: Fri, 15 Dec 2023 14:57:41 +0200 Subject: [PATCH 145/277] refactor: use `BlockWithSenders` in executors (#5771) --- bin/reth/src/debug_cmd/build_block.rs | 7 +- bin/reth/src/debug_cmd/in_memory_merkle.rs | 10 +- crates/blockchain-tree/src/chain.rs | 8 +- crates/consensus/auto-seal/src/lib.rs | 19 +-- .../consensus/beacon/src/engine/test_utils.rs | 33 ++-- crates/primitives/src/block.rs | 14 ++ crates/revm/src/optimism/processor.rs | 29 ++-- crates/revm/src/processor.rs | 144 ++++++++++-------- crates/stages/src/stages/execution.rs | 19 +-- .../src/providers/database/provider.rs | 50 +++--- .../provider/src/test_utils/executor.rs | 15 +- .../storage/provider/src/traits/executor.rs | 21 +-- 12 files changed, 183 insertions(+), 186 deletions(-) diff --git a/bin/reth/src/debug_cmd/build_block.rs b/bin/reth/src/debug_cmd/build_block.rs index 4677bd551f15..984ea29b594b 100644 --- a/bin/reth/src/debug_cmd/build_block.rs +++ b/bin/reth/src/debug_cmd/build_block.rs @@ -269,11 +269,8 @@ impl Command { let executor_factory = EvmProcessorFactory::new(self.chain.clone()); let mut executor = executor_factory.with_state(blockchain_db.latest()?); - executor.execute_and_verify_receipt( - &block_with_senders.block.clone().unseal(), - U256::MAX, - None, - )?; + executor + .execute_and_verify_receipt(&block_with_senders.clone().unseal(), U256::MAX)?; let state = executor.take_output_state(); debug!(target: "reth::cli", ?state, "Executed block"); diff --git a/bin/reth/src/debug_cmd/in_memory_merkle.rs b/bin/reth/src/debug_cmd/in_memory_merkle.rs index 4a942258c730..41b42cf36908 100644 --- a/bin/reth/src/debug_cmd/in_memory_merkle.rs +++ b/bin/reth/src/debug_cmd/in_memory_merkle.rs @@ -13,6 +13,7 @@ use backon::{ConstantBuilder, Retryable}; use clap::Parser; use reth_config::Config; use reth_db::{init_db, DatabaseEnv}; +use reth_interfaces::executor::BlockValidationError; use reth_network::NetworkHandle; use reth_network_api::NetworkInfo; use reth_primitives::{fs, stage::StageId, BlockHashOrNumber, ChainSpec}; @@ -166,9 +167,12 @@ impl Command { let merkle_block_td = provider.header_td_by_number(merkle_block_number)?.unwrap_or_default(); executor.execute_and_verify_receipt( - &block.clone().unseal(), + &block + .clone() + .unseal() + .with_recovered_senders() + .ok_or(BlockValidationError::SenderRecoveryError)?, merkle_block_td + block.difficulty, - None, )?; let block_state = executor.take_output_state(); @@ -185,7 +189,7 @@ impl Command { if in_memory_state_root == block.state_root { info!(target: "reth::cli", state_root = ?in_memory_state_root, "Computed in-memory state root matches"); - return Ok(()) + return Ok(()); } let provider_rw = factory.provider_rw()?; diff --git a/crates/blockchain-tree/src/chain.rs b/crates/blockchain-tree/src/chain.rs index 1b01b2787931..d0204a8cd296 100644 --- a/crates/blockchain-tree/src/chain.rs +++ b/crates/blockchain-tree/src/chain.rs @@ -202,9 +202,6 @@ impl AppendableChain { // some checks are done before blocks comes here. externals.consensus.validate_header_against_parent(&block, parent_block)?; - let (block, senders) = block.into_components(); - let block = block.unseal(); - // get the state provider. let canonical_fork = bundle_state_data_provider.canonical_fork(); let state_provider = @@ -213,7 +210,8 @@ impl AppendableChain { let provider = BundleStateProvider::new(state_provider, bundle_state_data_provider); let mut executor = externals.executor_factory.with_state(&provider); - executor.execute_and_verify_receipt(&block, U256::MAX, Some(senders))?; + let block = block.unseal(); + executor.execute_and_verify_receipt(&block, U256::MAX)?; let bundle_state = executor.take_output_state(); // check state root if the block extends the canonical chain __and__ if state root @@ -225,7 +223,7 @@ impl AppendableChain { return Err(ConsensusError::BodyStateRootDiff( GotExpected { got: state_root, expected: block.state_root }.into(), ) - .into()) + .into()); } } diff --git a/crates/consensus/auto-seal/src/lib.rs b/crates/consensus/auto-seal/src/lib.rs index 823a28bd73d9..b4cd16ff06ed 100644 --- a/crates/consensus/auto-seal/src/lib.rs +++ b/crates/consensus/auto-seal/src/lib.rs @@ -23,8 +23,8 @@ use reth_interfaces::{ }; use reth_primitives::{ constants::{EMPTY_RECEIPTS, EMPTY_TRANSACTIONS, ETHEREUM_BLOCK_GAS_LIMIT}, - proofs, Address, Block, BlockBody, BlockHash, BlockHashOrNumber, BlockNumber, Bloom, ChainSpec, - Header, ReceiptWithBloom, SealedBlock, SealedHeader, TransactionSigned, B256, + proofs, Block, BlockBody, BlockHash, BlockHashOrNumber, BlockNumber, BlockWithSenders, Bloom, + ChainSpec, Header, ReceiptWithBloom, SealedBlock, SealedHeader, TransactionSigned, B256, EMPTY_OMMER_ROOT_HASH, U256, }; use reth_provider::{ @@ -299,9 +299,8 @@ impl StorageInner { /// This returns the poststate from execution and post-block changes, as well as the gas used. pub(crate) fn execute( &mut self, - block: &Block, + block: &BlockWithSenders, executor: &mut EVMProcessor<'_>, - senders: Vec

, ) -> Result<(BundleStateWithReceipts, u64), BlockExecutionError> { trace!(target: "consensus::auto", transactions=?&block.body, "executing transactions"); // TODO: there isn't really a parent beacon block root here, so not sure whether or not to @@ -310,8 +309,7 @@ impl StorageInner { // set the first block to find the correct index in bundle state executor.set_first_block(block.number); - let (receipts, gas_used) = - executor.execute_transactions(block, U256::ZERO, Some(senders))?; + let (receipts, gas_used) = executor.execute_transactions(block, U256::ZERO)?; // Save receipts. executor.save_receipts(receipts)?; @@ -379,9 +377,8 @@ impl StorageInner { ) -> Result<(SealedHeader, BundleStateWithReceipts), BlockExecutionError> { let header = self.build_header_template(&transactions, chain_spec.clone()); - let block = Block { header, body: transactions, ommers: vec![], withdrawals: None }; - - let senders = TransactionSigned::recover_signers(&block.body, block.body.len()) + let block = Block { header, body: transactions, ommers: vec![], withdrawals: None } + .with_recovered_senders() .ok_or(BlockExecutionError::Validation(BlockValidationError::SenderRecoveryError))?; trace!(target: "consensus::auto", transactions=?&block.body, "executing transactions"); @@ -393,9 +390,9 @@ impl StorageInner { .build(); let mut executor = EVMProcessor::new_with_state(chain_spec.clone(), db); - let (bundle_state, gas_used) = self.execute(&block, &mut executor, senders)?; + let (bundle_state, gas_used) = self.execute(&block, &mut executor)?; - let Block { header, body, .. } = block; + let Block { header, body, .. } = block.block; let body = BlockBody { transactions: body, ommers: vec![], withdrawals: None }; trace!(target: "consensus::auto", ?bundle_state, ?header, ?body, "executed block, calculating state root and completing header"); diff --git a/crates/consensus/beacon/src/engine/test_utils.rs b/crates/consensus/beacon/src/engine/test_utils.rs index 0622c7734223..ffe3874d3a4e 100644 --- a/crates/consensus/beacon/src/engine/test_utils.rs +++ b/crates/consensus/beacon/src/engine/test_utils.rs @@ -88,7 +88,7 @@ impl TestEnv { loop { let result = self.send_new_payload(payload.clone(), cancun_fields.clone()).await?; if !result.is_syncing() { - return Ok(result) + return Ok(result); } } } @@ -109,7 +109,7 @@ impl TestEnv { loop { let result = self.engine_handle.fork_choice_updated(state, None).await?; if !result.is_syncing() { - return Ok(result) + return Ok(result); } } } @@ -182,45 +182,34 @@ where { fn execute( &mut self, - block: &reth_primitives::Block, + block: &reth_primitives::BlockWithSenders, total_difficulty: U256, - senders: Option>, ) -> Result<(), BlockExecutionError> { match self { - EitherBlockExecutor::Left(a) => a.execute(block, total_difficulty, senders), - EitherBlockExecutor::Right(b) => b.execute(block, total_difficulty, senders), + EitherBlockExecutor::Left(a) => a.execute(block, total_difficulty), + EitherBlockExecutor::Right(b) => b.execute(block, total_difficulty), } } fn execute_and_verify_receipt( &mut self, - block: &reth_primitives::Block, + block: &reth_primitives::BlockWithSenders, total_difficulty: U256, - senders: Option>, ) -> Result<(), BlockExecutionError> { match self { - EitherBlockExecutor::Left(a) => { - a.execute_and_verify_receipt(block, total_difficulty, senders) - } - EitherBlockExecutor::Right(b) => { - b.execute_and_verify_receipt(block, total_difficulty, senders) - } + EitherBlockExecutor::Left(a) => a.execute_and_verify_receipt(block, total_difficulty), + EitherBlockExecutor::Right(b) => b.execute_and_verify_receipt(block, total_difficulty), } } fn execute_transactions( &mut self, - block: &reth_primitives::Block, + block: &reth_primitives::BlockWithSenders, total_difficulty: U256, - senders: Option>, ) -> Result<(Vec, u64), BlockExecutionError> { match self { - EitherBlockExecutor::Left(a) => { - a.execute_transactions(block, total_difficulty, senders) - } - EitherBlockExecutor::Right(b) => { - b.execute_transactions(block, total_difficulty, senders) - } + EitherBlockExecutor::Left(a) => a.execute_transactions(block, total_difficulty), + EitherBlockExecutor::Right(b) => b.execute_transactions(block, total_difficulty), } } diff --git a/crates/primitives/src/block.rs b/crates/primitives/src/block.rs index 61f2468107ff..417167fa67f5 100644 --- a/crates/primitives/src/block.rs +++ b/crates/primitives/src/block.rs @@ -53,6 +53,11 @@ impl Block { } } + /// Expensive operation that recovers transaction signer. See [SealedBlockWithSenders]. + pub fn senders(&self) -> Option> { + TransactionSigned::recover_signers(&self.body, self.body.len()) + } + /// Transform into a [`BlockWithSenders`]. /// /// # Panics @@ -71,6 +76,15 @@ impl Block { BlockWithSenders { block: self, senders } } + /// **Expensive**. Transform into a [`BlockWithSenders`] by recovering senders in the contained + /// transactions. + /// + /// Returns `None` if a transaction is invalid. + pub fn with_recovered_senders(self) -> Option { + let senders = self.senders()?; + Some(BlockWithSenders { block: self, senders }) + } + /// Returns whether or not the block contains any blob transactions. pub fn has_blob_transactions(&self) -> bool { self.body.iter().any(|tx| tx.is_eip4844()) diff --git a/crates/revm/src/optimism/processor.rs b/crates/revm/src/optimism/processor.rs index cef7cc204eac..5b33ad1626f0 100644 --- a/crates/revm/src/optimism/processor.rs +++ b/crates/revm/src/optimism/processor.rs @@ -3,7 +3,7 @@ use reth_interfaces::executor::{ BlockExecutionError, BlockValidationError, OptimismBlockExecutionError, }; use reth_primitives::{ - revm::compat::into_reth_log, revm_primitives::ResultAndState, Address, Block, Hardfork, + revm::compat::into_reth_log, revm_primitives::ResultAndState, BlockWithSenders, Hardfork, Receipt, U256, }; use reth_provider::{BlockExecutor, BlockExecutorStats, BundleStateWithReceipts}; @@ -14,22 +14,20 @@ use tracing::{debug, trace}; impl<'a> BlockExecutor for EVMProcessor<'a> { fn execute( &mut self, - block: &Block, + block: &BlockWithSenders, total_difficulty: U256, - senders: Option>, ) -> Result<(), BlockExecutionError> { - let receipts = self.execute_inner(block, total_difficulty, senders)?; + let receipts = self.execute_inner(block, total_difficulty)?; self.save_receipts(receipts) } fn execute_and_verify_receipt( &mut self, - block: &Block, + block: &BlockWithSenders, total_difficulty: U256, - senders: Option>, ) -> Result<(), BlockExecutionError> { // execute block - let receipts = self.execute_inner(block, total_difficulty, senders)?; + let receipts = self.execute_inner(block, total_difficulty)?; // TODO Before Byzantium, receipts contained state root that would mean that expensive // operation as hashing that is needed for state root got calculated in every @@ -45,7 +43,7 @@ impl<'a> BlockExecutor for EVMProcessor<'a> { block.timestamp, ) { debug!(target: "evm", ?error, ?receipts, "receipts verification failed"); - return Err(error) + return Err(error); }; self.stats.receipt_root_duration += time.elapsed(); } @@ -55,19 +53,16 @@ impl<'a> BlockExecutor for EVMProcessor<'a> { fn execute_transactions( &mut self, - block: &Block, + block: &BlockWithSenders, total_difficulty: U256, - senders: Option>, ) -> Result<(Vec, u64), BlockExecutionError> { self.init_env(&block.header, total_difficulty); // perf: do not execute empty blocks if block.body.is_empty() { - return Ok((Vec::new(), 0)) + return Ok((Vec::new(), 0)); } - let senders = self.recover_senders(&block.body, senders)?; - let is_regolith = self.chain_spec.fork(Hardfork::Regolith).active_at_timestamp(block.timestamp); @@ -84,7 +79,7 @@ impl<'a> BlockExecutor for EVMProcessor<'a> { let mut cumulative_gas_used = 0; let mut receipts = Vec::with_capacity(block.body.len()); - for (transaction, sender) in block.body.iter().zip(senders) { + for (sender, transaction) in block.transactions_with_sender() { let time = Instant::now(); // The sum of the transaction’s gas limit, Tg, and the gas utilized in this block prior, // must be no greater than the block’s gasLimit. @@ -96,7 +91,7 @@ impl<'a> BlockExecutor for EVMProcessor<'a> { transaction_gas_limit: transaction.gas_limit(), block_available_gas, } - .into()) + .into()); } // Cache the depositor account prior to the state transition for the deposit nonce. @@ -107,14 +102,14 @@ impl<'a> BlockExecutor for EVMProcessor<'a> { let depositor = (is_regolith && transaction.is_deposit()) .then(|| { self.db_mut() - .load_cache_account(sender) + .load_cache_account(*sender) .map(|acc| acc.account_info().unwrap_or_default()) }) .transpose() .map_err(|_| BlockExecutionError::ProviderError)?; // Execute transaction. - let ResultAndState { result, state } = self.transact(transaction, sender)?; + let ResultAndState { result, state } = self.transact(transaction, *sender)?; trace!( target: "evm", ?transaction, ?result, ?state, diff --git a/crates/revm/src/processor.rs b/crates/revm/src/processor.rs index 708943d88b77..a63e493daa09 100644 --- a/crates/revm/src/processor.rs +++ b/crates/revm/src/processor.rs @@ -7,9 +7,9 @@ use crate::{ use reth_interfaces::executor::{BlockExecutionError, BlockValidationError}; use reth_primitives::{ revm::env::{fill_cfg_and_block_env, fill_tx_env}, - Address, Block, BlockNumber, Bloom, ChainSpec, GotExpected, Hardfork, Header, PruneMode, - PruneModes, PruneSegmentError, Receipt, ReceiptWithBloom, Receipts, TransactionSigned, B256, - MINIMUM_PRUNING_DISTANCE, U256, + Address, Block, BlockNumber, BlockWithSenders, Bloom, ChainSpec, GotExpected, Hardfork, Header, + PruneMode, PruneModes, PruneSegmentError, Receipt, ReceiptWithBloom, Receipts, + TransactionSigned, B256, MINIMUM_PRUNING_DISTANCE, U256, }; use reth_provider::{ BlockExecutor, BlockExecutorStats, ProviderError, PrunableBlockExecutor, StateProvider, @@ -149,26 +149,6 @@ impl<'a> EVMProcessor<'a> { self.evm.db().expect("Database inside EVM is always set") } - pub(crate) fn recover_senders( - &mut self, - body: &[TransactionSigned], - senders: Option>, - ) -> Result, BlockExecutionError> { - if let Some(senders) = senders { - if body.len() == senders.len() { - Ok(senders) - } else { - Err(BlockValidationError::SenderRecoveryError.into()) - } - } else { - let time = Instant::now(); - let ret = TransactionSigned::recover_signers(body, body.len()) - .ok_or(BlockValidationError::SenderRecoveryError.into()); - self.stats.sender_recovery_duration += time.elapsed(); - ret - } - } - /// Initializes the config and block env. pub(crate) fn init_env(&mut self, header: &Header, total_difficulty: U256) { // Set state clear flag. @@ -283,14 +263,12 @@ impl<'a> EVMProcessor<'a> { /// Execute the block, verify gas usage and apply post-block state changes. pub(crate) fn execute_inner( &mut self, - block: &Block, + block: &BlockWithSenders, total_difficulty: U256, - senders: Option>, ) -> Result, BlockExecutionError> { self.init_env(&block.header, total_difficulty); self.apply_beacon_root_contract_call(block)?; - let (receipts, cumulative_gas_used) = - self.execute_transactions(block, total_difficulty, senders)?; + let (receipts, cumulative_gas_used) = self.execute_transactions(block, total_difficulty)?; // Check if gas used matches the value set in header. if block.gas_used != cumulative_gas_used { @@ -299,7 +277,7 @@ impl<'a> EVMProcessor<'a> { gas: GotExpected { got: cumulative_gas_used, expected: block.gas_used }, gas_spent_by_tx: receipts.gas_spent_by_tx()?, } - .into()) + .into()); } let time = Instant::now(); self.apply_post_execution_state_change(block, total_difficulty)?; @@ -358,7 +336,7 @@ impl<'a> EVMProcessor<'a> { self.prune_modes.receipts.map_or(false, |mode| mode.should_prune(block_number, tip)) { receipts.clear(); - return Ok(()) + return Ok(()); } // All receipts from the last 128 blocks are required for blockchain tree, even with @@ -366,7 +344,7 @@ impl<'a> EVMProcessor<'a> { let prunable_receipts = PruneMode::Distance(MINIMUM_PRUNING_DISTANCE).should_prune(block_number, tip); if !prunable_receipts { - return Ok(()) + return Ok(()); } let contract_log_pruner = self.prune_modes.receipts_log_filter.group_by_block(tip, None)?; @@ -399,22 +377,20 @@ impl<'a> EVMProcessor<'a> { impl<'a> BlockExecutor for EVMProcessor<'a> { fn execute( &mut self, - block: &Block, + block: &BlockWithSenders, total_difficulty: U256, - senders: Option>, ) -> Result<(), BlockExecutionError> { - let receipts = self.execute_inner(block, total_difficulty, senders)?; + let receipts = self.execute_inner(block, total_difficulty)?; self.save_receipts(receipts) } fn execute_and_verify_receipt( &mut self, - block: &Block, + block: &BlockWithSenders, total_difficulty: U256, - senders: Option>, ) -> Result<(), BlockExecutionError> { // execute block - let receipts = self.execute_inner(block, total_difficulty, senders)?; + let receipts = self.execute_inner(block, total_difficulty)?; // TODO Before Byzantium, receipts contained state root that would mean that expensive // operation as hashing that is needed for state root got calculated in every @@ -426,7 +402,7 @@ impl<'a> BlockExecutor for EVMProcessor<'a> { verify_receipt(block.header.receipts_root, block.header.logs_bloom, receipts.iter()) { debug!(target: "evm", ?error, ?receipts, "receipts verification failed"); - return Err(error) + return Err(error); }; self.stats.receipt_root_duration += time.elapsed(); } @@ -436,22 +412,19 @@ impl<'a> BlockExecutor for EVMProcessor<'a> { fn execute_transactions( &mut self, - block: &Block, + block: &BlockWithSenders, total_difficulty: U256, - senders: Option>, ) -> Result<(Vec, u64), BlockExecutionError> { self.init_env(&block.header, total_difficulty); // perf: do not execute empty blocks if block.body.is_empty() { - return Ok((Vec::new(), 0)) + return Ok((Vec::new(), 0)); } - let senders = self.recover_senders(&block.body, senders)?; - let mut cumulative_gas_used = 0; let mut receipts = Vec::with_capacity(block.body.len()); - for (transaction, sender) in block.body.iter().zip(senders) { + for (sender, transaction) in block.transactions_with_sender() { let time = Instant::now(); // The sum of the transaction’s gas limit, Tg, and the gas utilized in this block prior, // must be no greater than the block’s gasLimit. @@ -461,10 +434,10 @@ impl<'a> BlockExecutor for EVMProcessor<'a> { transaction_gas_limit: transaction.gas_limit(), block_available_gas, } - .into()) + .into()); } // Execute transaction. - let ResultAndState { result, state } = self.transact(transaction, sender)?; + let ResultAndState { result, state } = self.transact(transaction, *sender)?; trace!( target: "evm", ?transaction, ?result, ?state, @@ -546,7 +519,7 @@ pub fn verify_receipt<'a>( return Err(BlockValidationError::ReceiptRootDiff( GotExpected { got: receipts_root, expected: expected_receipts_root }.into(), ) - .into()) + .into()); } // Create header log bloom. @@ -555,7 +528,7 @@ pub fn verify_receipt<'a>( return Err(BlockValidationError::BloomLogDiff( GotExpected { got: logs_bloom, expected: expected_logs_bloom }.into(), ) - .into()) + .into()); } Ok(()) @@ -698,9 +671,16 @@ mod tests { // attempt to execute a block without parent beacon block root, expect err let err = executor .execute_and_verify_receipt( - &Block { header: header.clone(), body: vec![], ommers: vec![], withdrawals: None }, + &BlockWithSenders { + block: Block { + header: header.clone(), + body: vec![], + ommers: vec![], + withdrawals: None, + }, + senders: vec![], + }, U256::ZERO, - None, ) .expect_err( "Executing cancun block without parent beacon block root field should fail", @@ -716,9 +696,16 @@ mod tests { // Now execute a block with the fixed header, ensure that it does not fail executor .execute( - &Block { header: header.clone(), body: vec![], ommers: vec![], withdrawals: None }, + &BlockWithSenders { + block: Block { + header: header.clone(), + body: vec![], + ommers: vec![], + withdrawals: None, + }, + senders: vec![], + }, U256::ZERO, - None, ) .unwrap(); @@ -776,9 +763,16 @@ mod tests { // attempt to execute an empty block with parent beacon block root, this should not fail executor .execute_and_verify_receipt( - &Block { header: header.clone(), body: vec![], ommers: vec![], withdrawals: None }, + &BlockWithSenders { + block: Block { + header: header.clone(), + body: vec![], + ommers: vec![], + withdrawals: None, + }, + senders: vec![], + }, U256::ZERO, - None, ) .expect( "Executing a block with no transactions while cancun is active should not fail", @@ -833,9 +827,16 @@ mod tests { // attempt to execute an empty block with parent beacon block root, this should not fail executor .execute_and_verify_receipt( - &Block { header: header.clone(), body: vec![], ommers: vec![], withdrawals: None }, + &BlockWithSenders { + block: Block { + header: header.clone(), + body: vec![], + ommers: vec![], + withdrawals: None, + }, + senders: vec![], + }, U256::ZERO, - None, ) .expect( "Executing a block with no transactions while cancun is active should not fail", @@ -880,9 +881,16 @@ mod tests { header.parent_beacon_block_root = Some(B256::with_last_byte(0x69)); let _err = executor .execute_and_verify_receipt( - &Block { header: header.clone(), body: vec![], ommers: vec![], withdrawals: None }, + &BlockWithSenders { + block: Block { + header: header.clone(), + body: vec![], + ommers: vec![], + withdrawals: None, + }, + senders: vec![], + }, U256::ZERO, - None, ) .expect_err( "Executing genesis cancun block with non-zero parent beacon block root field should fail", @@ -895,9 +903,16 @@ mod tests { // call does not occur executor .execute( - &Block { header: header.clone(), body: vec![], ommers: vec![], withdrawals: None }, + &BlockWithSenders { + block: Block { + header: header.clone(), + body: vec![], + ommers: vec![], + withdrawals: None, + }, + senders: vec![], + }, U256::ZERO, - None, ) .unwrap(); @@ -958,9 +973,16 @@ mod tests { // Now execute a block with the fixed header, ensure that it does not fail executor .execute( - &Block { header: header.clone(), body: vec![], ommers: vec![], withdrawals: None }, + &BlockWithSenders { + block: Block { + header: header.clone(), + body: vec![], + ommers: vec![], + withdrawals: None, + }, + senders: vec![], + }, U256::ZERO, - None, ) .unwrap(); diff --git a/crates/stages/src/stages/execution.rs b/crates/stages/src/stages/execution.rs index 41a26165c9bb..8d56a5d00e43 100644 --- a/crates/stages/src/stages/execution.rs +++ b/crates/stages/src/stages/execution.rs @@ -114,7 +114,7 @@ impl ExecutionStage { input: ExecInput, ) -> Result { if input.target_reached() { - return Ok(ExecOutput::done(input.checkpoint())) + return Ok(ExecOutput::done(input.checkpoint())); } let start_block = input.next_block(); @@ -159,12 +159,9 @@ impl ExecutionStage { let time = Instant::now(); // Execute the block - let (block, senders) = block.into_components(); - executor.execute_and_verify_receipt(&block, td, Some(senders)).map_err(|error| { - StageError::Block { - block: Box::new(block.header.clone().seal_slow()), - error: BlockErrorKind::Execution(error), - } + executor.execute_and_verify_receipt(&block, td).map_err(|error| StageError::Block { + block: Box::new(block.header.clone().seal_slow()), + error: BlockErrorKind::Execution(error), })?; execution_duration += time.elapsed(); @@ -186,7 +183,7 @@ impl ExecutionStage { bundle_size_hint, cumulative_gas, ) { - break + break; } } let time = Instant::now(); @@ -363,7 +360,7 @@ impl Stage for ExecutionStage { if range.is_empty() { return Ok(UnwindOutput { checkpoint: input.checkpoint.with_block_number(input.unwind_to), - }) + }); } // get all batches for account change @@ -406,7 +403,7 @@ impl Stage for ExecutionStage { let mut rev_storage_changeset_walker = storage_changeset.walk_back(None)?; while let Some((key, _)) = rev_storage_changeset_walker.next().transpose()? { if key.block_number() < *range.start() { - break + break; } // delete all changesets rev_storage_changeset_walker.delete_current()?; @@ -426,7 +423,7 @@ impl Stage for ExecutionStage { while let Some(Ok((tx_number, receipt))) = reverse_walker.next() { if tx_number < first_tx_num { - break + break; } reverse_walker.delete_current()?; diff --git a/crates/storage/provider/src/providers/database/provider.rs b/crates/storage/provider/src/providers/database/provider.rs index 46884e9c5319..7b4cadaec076 100644 --- a/crates/storage/provider/src/providers/database/provider.rs +++ b/crates/storage/provider/src/providers/database/provider.rs @@ -139,7 +139,7 @@ where while let Some((sharded_key, list)) = item { // If the shard does not belong to the key, break. if !shard_belongs_to_key(&sharded_key) { - break + break; } cursor.delete_current()?; @@ -148,12 +148,12 @@ where let first = list.iter(0).next().expect("List can't be empty"); if first >= block_number as usize { item = cursor.prev()?; - continue + continue; } else if block_number <= sharded_key.as_ref().highest_block_number { // Filter out all elements greater than block number. - return Ok(list.iter(0).take_while(|i| *i < block_number as usize).collect::>()) + return Ok(list.iter(0).take_while(|i| *i < block_number as usize).collect::>()); } else { - return Ok(list.iter(0).collect::>()) + return Ok(list.iter(0).collect::>()); } } @@ -236,7 +236,7 @@ impl DatabaseProvider { range: RangeInclusive, ) -> ProviderResult { if range.is_empty() { - return Ok(BundleStateWithReceipts::default()) + return Ok(BundleStateWithReceipts::default()); } let start_block_number = *range.start(); @@ -414,7 +414,7 @@ impl DatabaseProvider { let block_bodies = self.get_or_take::(range)?; if block_bodies.is_empty() { - return Ok(Vec::new()) + return Ok(Vec::new()); } // Compute the first and last tx ID in the range @@ -423,7 +423,7 @@ impl DatabaseProvider { // If this is the case then all of the blocks in the range are empty if last_transaction < first_transaction { - return Ok(block_bodies.into_iter().map(|(n, _)| (n, Vec::new())).collect()) + return Ok(block_bodies.into_iter().map(|(n, _)| (n, Vec::new())).collect()); } // Get transactions and senders @@ -552,7 +552,7 @@ impl DatabaseProvider { let block_headers = self.get_or_take::(range.clone())?; if block_headers.is_empty() { - return Ok(Vec::new()) + return Ok(Vec::new()); } let block_header_hashes = @@ -658,7 +658,7 @@ impl DatabaseProvider { while let Some(Ok((entry_key, _))) = reverse_walker.next() { if selector(entry_key.clone()) <= key { - break + break; } reverse_walker.delete_current()?; deleted += 1; @@ -705,7 +705,7 @@ impl DatabaseProvider { } if deleted == limit { - break + break; } } } @@ -736,7 +736,7 @@ impl DatabaseProvider { } if deleted == limit { - break + break; } } } @@ -756,7 +756,7 @@ impl DatabaseProvider { // delete old shard so new one can be inserted. self.tx.delete::(shard_key, None)?; let list = list.iter(0).map(|i| i as u64).collect::>(); - return Ok(list) + return Ok(list); } Ok(Vec::new()) } @@ -948,7 +948,7 @@ impl HeaderProvider for DatabaseProvider { if let Some(td) = self.chain_spec.final_paris_total_difficulty(number) { // if this block is higher than the final paris(merge) block, return the final paris // difficulty - return Ok(Some(td)) + return Ok(Some(td)); } Ok(self.tx.get::(number)?.map(|td| td.0)) @@ -986,7 +986,7 @@ impl HeaderProvider for DatabaseProvider { .ok_or_else(|| ProviderError::HeaderNotFound(number.into()))?; let sealed = header.seal(hash); if !predicate(&sealed) { - break + break; } headers.push(sealed); } @@ -1064,7 +1064,7 @@ impl BlockReader for DatabaseProvider { None => return Ok(None), }; - return Ok(Some(Block { header, body: transactions, ommers, withdrawals })) + return Ok(Some(Block { header, body: transactions, ommers, withdrawals })); } } @@ -1088,11 +1088,11 @@ impl BlockReader for DatabaseProvider { // If the Paris (Merge) hardfork block is known and block is after it, return empty // ommers. if self.chain_spec.final_paris_total_difficulty(number).is_some() { - return Ok(Some(Vec::new())) + return Ok(Some(Vec::new())); } let ommers = self.tx.get::(number)?.map(|o| o.ommers); - return Ok(ommers) + return Ok(ommers); } Ok(None) @@ -1158,7 +1158,7 @@ impl BlockReader for DatabaseProvider { fn block_range(&self, range: RangeInclusive) -> ProviderResult> { if range.is_empty() { - return Ok(Vec::new()) + return Ok(Vec::new()); } let len = range.end().saturating_sub(*range.start()) as usize; @@ -1336,7 +1336,7 @@ impl TransactionsProvider for DatabaseProvider { excess_blob_gas: header.excess_blob_gas, }; - return Ok(Some((transaction, meta))) + return Ok(Some((transaction, meta))); } } } @@ -1367,7 +1367,7 @@ impl TransactionsProvider for DatabaseProvider { .map(|result| result.map(|(_, tx)| tx.into())) .collect::, _>>()?; Ok(Some(transactions)) - } + }; } } Ok(None) @@ -1452,7 +1452,7 @@ impl ReceiptProvider for DatabaseProvider { .map(|result| result.map(|(_, receipt)| receipt)) .collect::, _>>()?; Ok(Some(receipts)) - } + }; } } Ok(None) @@ -1474,7 +1474,7 @@ impl WithdrawalsProvider for DatabaseProvider { .get::(number) .map(|w| w.map(|w| w.withdrawals))? .unwrap_or_default(); - return Ok(Some(withdrawals)) + return Ok(Some(withdrawals)); } } Ok(None) @@ -1906,7 +1906,7 @@ impl HashingWriter for DatabaseProvider { root: GotExpected { got: state_root, expected: expected_state_root }, block_number: *range.end(), block_hash: end_block_hash, - }))) + }))); } trie_updates.flush(&self.tx)?; } @@ -2121,7 +2121,7 @@ impl BlockExecutionWriter for DatabaseProvider { root: GotExpected { got: new_state_root, expected: parent_state_root }, block_number: parent_number, block_hash: parent_hash, - }))) + }))); } trie_updates.flush(&self.tx)?; } @@ -2305,7 +2305,7 @@ impl BlockWriter for DatabaseProvider { ) -> ProviderResult<()> { if blocks.is_empty() { debug!(target: "providers::db", "Attempted to append empty block range"); - return Ok(()) + return Ok(()); } let first_number = blocks.first().unwrap().number; diff --git a/crates/storage/provider/src/test_utils/executor.rs b/crates/storage/provider/src/test_utils/executor.rs index 05d406cd6f97..b0fb2dd47961 100644 --- a/crates/storage/provider/src/test_utils/executor.rs +++ b/crates/storage/provider/src/test_utils/executor.rs @@ -4,7 +4,7 @@ use crate::{ }; use parking_lot::Mutex; use reth_interfaces::executor::BlockExecutionError; -use reth_primitives::{Address, Block, BlockNumber, ChainSpec, PruneModes, Receipt, U256}; +use reth_primitives::{BlockNumber, BlockWithSenders, ChainSpec, PruneModes, Receipt, U256}; use std::sync::Arc; /// Test executor with mocked result. #[derive(Debug)] @@ -13,33 +13,30 @@ pub struct TestExecutor(pub Option); impl BlockExecutor for TestExecutor { fn execute( &mut self, - _block: &Block, + _block: &BlockWithSenders, _total_difficulty: U256, - _senders: Option>, ) -> Result<(), BlockExecutionError> { if self.0.is_none() { - return Err(BlockExecutionError::UnavailableForTest) + return Err(BlockExecutionError::UnavailableForTest); } Ok(()) } fn execute_and_verify_receipt( &mut self, - _block: &Block, + _block: &BlockWithSenders, _total_difficulty: U256, - _senders: Option>, ) -> Result<(), BlockExecutionError> { if self.0.is_none() { - return Err(BlockExecutionError::UnavailableForTest) + return Err(BlockExecutionError::UnavailableForTest); } Ok(()) } fn execute_transactions( &mut self, - _block: &Block, + _block: &BlockWithSenders, _total_difficulty: U256, - _senders: Option>, ) -> Result<(Vec, u64), BlockExecutionError> { Err(BlockExecutionError::UnavailableForTest) } diff --git a/crates/storage/provider/src/traits/executor.rs b/crates/storage/provider/src/traits/executor.rs index 262c6618696b..6da01f2bce8a 100644 --- a/crates/storage/provider/src/traits/executor.rs +++ b/crates/storage/provider/src/traits/executor.rs @@ -2,7 +2,7 @@ use crate::{bundle_state::BundleStateWithReceipts, StateProvider}; use reth_interfaces::executor::BlockExecutionError; -use reth_primitives::{Address, Block, BlockNumber, ChainSpec, PruneModes, Receipt, U256}; +use reth_primitives::{BlockNumber, BlockWithSenders, ChainSpec, PruneModes, Receipt, U256}; use std::time::Duration; use tracing::debug; @@ -23,18 +23,10 @@ pub trait ExecutorFactory: Send + Sync + 'static { /// An executor capable of executing a block. pub trait BlockExecutor { /// Execute a block. - /// - /// The number of `senders` should be equal to the number of transactions in the block. - /// - /// If no senders are specified, the `execute` function MUST recover the senders for the - /// provided block's transactions internally. We use this to allow for calculating senders in - /// parallel in e.g. staged sync, so that execution can happen without paying for sender - /// recovery costs. fn execute( &mut self, - block: &Block, + block: &BlockWithSenders, total_difficulty: U256, - senders: Option>, ) -> Result<(), BlockExecutionError>; /// Executes the block and checks receipts. @@ -42,9 +34,8 @@ pub trait BlockExecutor { /// See [execute](BlockExecutor::execute) for more details. fn execute_and_verify_receipt( &mut self, - block: &Block, + block: &BlockWithSenders, total_difficulty: U256, - senders: Option>, ) -> Result<(), BlockExecutionError>; /// Runs the provided transactions and commits their state to the run-time database. @@ -61,9 +52,8 @@ pub trait BlockExecutor { /// See [execute](BlockExecutor::execute) for more details. fn execute_transactions( &mut self, - block: &Block, + block: &BlockWithSenders, total_difficulty: U256, - senders: Option>, ) -> Result<(Vec, u64), BlockExecutionError>; /// Return bundle state. This is output of executed blocks. @@ -99,8 +89,6 @@ pub struct BlockExecutorStats { pub merge_transitions_duration: Duration, /// Time needed to calculate receipt roots. pub receipt_root_duration: Duration, - /// Time needed to recover senders. - pub sender_recovery_duration: Duration, } impl BlockExecutorStats { @@ -113,7 +101,6 @@ impl BlockExecutorStats { apply_post_state = ?self.apply_post_execution_state_changes_duration, merge_transitions = ?self.merge_transitions_duration, receipt_root = ?self.receipt_root_duration, - sender_recovery = ?self.sender_recovery_duration, "Execution time" ); } From 3f7760d852ddfba81813db359539699bd2bf6868 Mon Sep 17 00:00:00 2001 From: Bjerg Date: Fri, 15 Dec 2023 15:05:52 +0200 Subject: [PATCH 146/277] refactor: make sender recovery explicit in provider (#5776) --- bin/reth/src/debug_cmd/in_memory_merkle.rs | 8 +- bin/reth/src/debug_cmd/merkle.rs | 8 +- crates/blockchain-tree/src/blockchain_tree.rs | 68 +++++++----- crates/consensus/beacon/src/engine/mod.rs | 105 ++++++++++-------- crates/stages/src/stages/execution.rs | 33 ++++-- crates/stages/src/stages/hashing_account.rs | 8 +- crates/stages/src/stages/mod.rs | 6 +- .../src/providers/database/metrics.rs | 2 - .../provider/src/providers/database/mod.rs | 15 ++- .../src/providers/database/provider.rs | 31 ++---- crates/storage/provider/src/traits/block.rs | 8 +- testing/ef-tests/src/cases/blockchain_test.rs | 7 +- 12 files changed, 157 insertions(+), 142 deletions(-) diff --git a/bin/reth/src/debug_cmd/in_memory_merkle.rs b/bin/reth/src/debug_cmd/in_memory_merkle.rs index 41b42cf36908..9e6c9bd9d9fc 100644 --- a/bin/reth/src/debug_cmd/in_memory_merkle.rs +++ b/bin/reth/src/debug_cmd/in_memory_merkle.rs @@ -195,7 +195,13 @@ impl Command { let provider_rw = factory.provider_rw()?; // Insert block, state and hashes - provider_rw.insert_block(block.clone(), None, None)?; + provider_rw.insert_block( + block + .clone() + .try_seal_with_senders() + .map_err(|_| BlockValidationError::SenderRecoveryError)?, + None, + )?; block_state.write_to_db(provider_rw.tx_ref(), OriginalValuesKnown::No)?; let storage_lists = provider_rw.changed_storages_with_range(block.number..=block.number)?; let storages = provider_rw.plain_state_storages(storage_lists)?; diff --git a/bin/reth/src/debug_cmd/merkle.rs b/bin/reth/src/debug_cmd/merkle.rs index bcbfc4034955..b590382af7b4 100644 --- a/bin/reth/src/debug_cmd/merkle.rs +++ b/bin/reth/src/debug_cmd/merkle.rs @@ -177,10 +177,10 @@ impl Command { Ok(senders) => senders, Err(err) => { warn!(target: "reth::cli", "Error sealing block with senders: {err:?}. Skipping..."); - continue + continue; } }; - provider_rw.insert_block(sealed_block.block, Some(sealed_block.senders), None)?; + provider_rw.insert_block(sealed_block, None)?; } // Check if any of hashing or merkle stages aren't on the same block number as @@ -277,7 +277,7 @@ impl Command { let clean_result = merkle_stage.execute(&provider_rw, clean_input); assert!(clean_result.is_ok(), "Clean state root calculation failed"); if clean_result.unwrap().done { - break + break; } } @@ -343,7 +343,7 @@ impl Command { clean.1.nibbles.len() > self.skip_node_depth.unwrap_or_default() { first_mismatched_storage = Some((incremental, clean)); - break + break; } } (Some(incremental), None) => { diff --git a/crates/blockchain-tree/src/blockchain_tree.rs b/crates/blockchain-tree/src/blockchain_tree.rs index 1e5e81c90e85..7797d26b251c 100644 --- a/crates/blockchain-tree/src/blockchain_tree.rs +++ b/crates/blockchain-tree/src/blockchain_tree.rs @@ -161,33 +161,35 @@ impl BlockchainTree { if block.number <= last_finalized_block { // check if block is canonical if self.is_block_hash_canonical(&block.hash)? { - return Ok(Some(BlockStatus::Valid)) + return Ok(Some(BlockStatus::Valid)); } // check if block is inside database if self.externals.provider_factory.provider()?.block_number(block.hash)?.is_some() { - return Ok(Some(BlockStatus::Valid)) + return Ok(Some(BlockStatus::Valid)); } return Err(BlockchainTreeError::PendingBlockIsFinalized { last_finalized: last_finalized_block, } - .into()) + .into()); } // check if block is part of canonical chain if self.is_block_hash_canonical(&block.hash)? { - return Ok(Some(BlockStatus::Valid)) + return Ok(Some(BlockStatus::Valid)); } // is block inside chain if let Some(status) = self.is_block_inside_chain(&block) { - return Ok(Some(status)) + return Ok(Some(status)); } // check if block is disconnected if let Some(block) = self.state.buffered_blocks.block(block) { - return Ok(Some(BlockStatus::Disconnected { missing_ancestor: block.parent_num_hash() })) + return Ok(Some(BlockStatus::Disconnected { + missing_ancestor: block.parent_num_hash(), + })); } Ok(None) @@ -278,7 +280,7 @@ impl BlockchainTree { // get canonical fork. let canonical_fork = self.canonical_fork(chain_id)?; - return Some(BundleStateData { state, parent_block_hashed, canonical_fork }) + return Some(BundleStateData { state, parent_block_hashed, canonical_fork }); } // check if there is canonical block @@ -288,7 +290,7 @@ impl BlockchainTree { canonical_fork: ForkBlock { number: canonical_number, hash: block_hash }, state: BundleStateWithReceipts::default(), parent_block_hashed: self.canonical_chain().inner().clone(), - }) + }); } None @@ -311,7 +313,7 @@ impl BlockchainTree { // check if block parent can be found in any side chain. if let Some(chain_id) = self.block_indices().get_blocks_chain_id(&parent.hash) { // found parent in side tree, try to insert there - return self.try_insert_block_into_side_chain(block, chain_id, block_validation_kind) + return self.try_insert_block_into_side_chain(block, chain_id, block_validation_kind); } // if not found, check if the parent can be found inside canonical chain. @@ -319,7 +321,7 @@ impl BlockchainTree { .is_block_hash_canonical(&parent.hash) .map_err(|err| InsertBlockError::new(block.block.clone(), err.into()))? { - return self.try_append_canonical_chain(block, block_validation_kind) + return self.try_append_canonical_chain(block, block_validation_kind); } // this is another check to ensure that if the block points to a canonical block its block @@ -335,7 +337,7 @@ impl BlockchainTree { block_number: block.number, }, block.block, - )) + )); } } @@ -412,7 +414,7 @@ impl BlockchainTree { return Err(InsertBlockError::execution_error( BlockValidationError::BlockPreMerge { hash: block.hash }.into(), block.block, - )) + )); } let parent_header = provider @@ -575,7 +577,7 @@ impl BlockchainTree { } else { // if there is no fork block that point to other chains, break the loop. // it means that this fork joins to canonical block. - break + break; } } hashes @@ -596,9 +598,9 @@ impl BlockchainTree { // get fork block chain if let Some(fork_chain_id) = self.block_indices().get_blocks_chain_id(&fork.hash) { chain_id = fork_chain_id; - continue + continue; } - break + break; } (self.block_indices().canonical_hash(&fork.number) == Some(fork.hash)).then_some(fork) } @@ -705,7 +707,7 @@ impl BlockchainTree { pub fn buffer_block(&mut self, block: SealedBlockWithSenders) -> Result<(), InsertBlockError> { // validate block consensus rules if let Err(err) = self.validate_block(&block) { - return Err(InsertBlockError::consensus_error(err, block.block)) + return Err(InsertBlockError::consensus_error(err, block.block)); } self.state.buffered_blocks.insert_block(block); @@ -722,17 +724,17 @@ impl BlockchainTree { ?block, "Failed to validate total difficulty for block {}: {e:?}", block.header.hash ); - return Err(e) + return Err(e); } if let Err(e) = self.externals.consensus.validate_header(block) { error!(?block, "Failed to validate header {}: {e:?}", block.header.hash); - return Err(e) + return Err(e); } if let Err(e) = self.externals.consensus.validate_block(block) { error!(?block, "Failed to validate block {}: {e:?}", block.header.hash); - return Err(e) + return Err(e); } Ok(()) @@ -753,7 +755,7 @@ impl BlockchainTree { Some(BlockStatus::Valid) } else { Some(BlockStatus::Accepted) - } + }; } None } @@ -794,7 +796,7 @@ impl BlockchainTree { // validate block consensus rules if let Err(err) = self.validate_block(&block) { - return Err(InsertBlockError::consensus_error(err, block.block)) + return Err(InsertBlockError::consensus_error(err, block.block)); } Ok(InsertPayloadOk::Inserted( @@ -963,7 +965,7 @@ impl BlockchainTree { } if header.is_none() && self.is_block_hash_inside_chain(*hash) { - return Ok(None) + return Ok(None); } if header.is_none() { @@ -1018,9 +1020,9 @@ impl BlockchainTree { return Err(CanonicalError::from(BlockValidationError::BlockPreMerge { hash: *block_hash, }) - .into()) + .into()); } - return Ok(CanonicalOutcome::AlreadyCanonical { header }) + return Ok(CanonicalOutcome::AlreadyCanonical { header }); } let Some(chain_id) = self.block_indices().get_blocks_chain_id(block_hash) else { @@ -1028,7 +1030,7 @@ impl BlockchainTree { return Err(CanonicalError::from(BlockchainTreeError::BlockHashNotFoundInChain { block_hash: *block_hash, }) - .into()) + .into()); }; let chain = self.state.chains.remove(&chain_id).expect("To be present"); @@ -1190,7 +1192,7 @@ impl BlockchainTree { block_number: tip.number, block_hash: tip.hash, }, - )))) + )))); } let (blocks, state) = chain.into_inner(); @@ -1214,7 +1216,7 @@ impl BlockchainTree { pub fn unwind(&mut self, unwind_to: BlockNumber) -> RethResult<()> { // nothing to be done if unwind_to is higher then the tip if self.block_indices().canonical_tip().number <= unwind_to { - return Ok(()) + return Ok(()); } // revert `N` blocks from current canonical chain and put them inside BlockchanTree let old_canon_chain = self.revert_canonical_from_database(unwind_to)?; @@ -1343,7 +1345,12 @@ mod tests { genesis.header.header.state_root = EMPTY_ROOT_HASH; let provider = factory.provider_rw().unwrap(); - provider.insert_block(genesis, None, None).unwrap(); + provider + .insert_block( + genesis.try_seal_with_senders().expect("invalid tx signature in genesis"), + None, + ) + .unwrap(); // insert first 10 blocks for i in 0..10 { @@ -1454,8 +1461,9 @@ mod tests { let provider_rw = provider_factory.provider_rw().unwrap(); provider_rw .insert_block( - SealedBlock::new(chain_spec.sealed_genesis_header(), Default::default()), - Some(Vec::new()), + SealedBlock::new(chain_spec.sealed_genesis_header(), Default::default()) + .try_seal_with_senders() + .unwrap(), None, ) .unwrap(); diff --git a/crates/consensus/beacon/src/engine/mod.rs b/crates/consensus/beacon/src/engine/mod.rs index b7242f3358a5..b9d9035cab63 100644 --- a/crates/consensus/beacon/src/engine/mod.rs +++ b/crates/consensus/beacon/src/engine/mod.rs @@ -355,7 +355,7 @@ where inconsistent_stage_checkpoint = stage_checkpoint, "Pipeline sync progress is inconsistent" ); - return Ok(self.blockchain.block_hash(first_stage_checkpoint)?) + return Ok(self.blockchain.block_hash(first_stage_checkpoint)?); } } @@ -431,7 +431,7 @@ where Ok(None) => { // we don't have the block yet and the distance exceeds the allowed // threshold - return Some(state.finalized_block_hash) + return Some(state.finalized_block_hash); } Ok(Some(_)) => { // we're fully synced to the finalized block @@ -472,7 +472,7 @@ where ) -> Option { // check pre merge block error if insert_err.map(|err| err.is_block_pre_merge()).unwrap_or_default() { - return Some(B256::ZERO) + return Some(B256::ZERO); } // If this is sent from new payload then the parent hash could be in a side chain, and is @@ -487,7 +487,7 @@ where // we need to check if the parent block is the last POW block, if so then the payload is // the first POS. The engine API spec mandates a zero hash to be returned: if parent_header.difficulty != U256::ZERO { - return Some(B256::ZERO) + return Some(B256::ZERO); } // parent is canonical POS block @@ -571,11 +571,11 @@ where // FCU resulted in a fatal error from which we can't recover let err = err.clone(); let _ = tx.send(Err(error)); - return OnForkchoiceUpdateOutcome::Fatal(err) + return OnForkchoiceUpdateOutcome::Fatal(err); } } let _ = tx.send(Err(error)); - return OnForkchoiceUpdateOutcome::Processed + return OnForkchoiceUpdateOutcome::Processed; } }; @@ -600,7 +600,7 @@ where if self.sync.has_reached_max_block(tip_number) { // Terminate the sync early if it's reached the maximum user // configured block. - return OnForkchoiceUpdateOutcome::ReachedMaxBlock + return OnForkchoiceUpdateOutcome::ReachedMaxBlock; } } ForkchoiceStatus::Syncing => { @@ -629,21 +629,21 @@ where ) -> RethResult { trace!(target: "consensus::engine", ?state, "Received new forkchoice state update"); if state.head_block_hash.is_zero() { - return Ok(OnForkChoiceUpdated::invalid_state()) + return Ok(OnForkChoiceUpdated::invalid_state()); } // check if the new head hash is connected to any ancestor that we previously marked as // invalid let lowest_buffered_ancestor_fcu = self.lowest_buffered_ancestor_or(state.head_block_hash); if let Some(status) = self.check_invalid_ancestor(lowest_buffered_ancestor_fcu) { - return Ok(OnForkChoiceUpdated::with_invalid(status)) + return Ok(OnForkChoiceUpdated::with_invalid(status)); } if self.sync.is_pipeline_active() { // We can only process new forkchoice updates if the pipeline is idle, since it requires // exclusive access to the database trace!(target: "consensus::engine", "Pipeline is syncing, skipping forkchoice update"); - return Ok(OnForkChoiceUpdated::syncing()) + return Ok(OnForkChoiceUpdated::syncing()); } if let Some(hook) = self.hooks.active_db_write_hook() { @@ -655,7 +655,7 @@ where "Hook is in progress, skipping forkchoice update. \ This may affect the performance of your node as a validator." ); - return Ok(OnForkChoiceUpdated::syncing()) + return Ok(OnForkChoiceUpdated::syncing()); } let start = Instant::now(); @@ -724,7 +724,7 @@ where // attributes if let Some(invalid_fcu_response) = self.ensure_consistent_state(state)? { trace!(target: "consensus::engine", ?state, head=?state.head_block_hash, "Forkchoice state is inconsistent, returning invalid response"); - return Ok(invalid_fcu_response) + return Ok(invalid_fcu_response); } // the CL requested to build a new payload on top of this new VALID head @@ -735,7 +735,7 @@ where ); trace!(target: "consensus::engine", status = ?payload_response, ?state, "Returning forkchoice status"); - return Ok(payload_response) + return Ok(payload_response); } PayloadStatus::new(PayloadStatusEnum::Valid, Some(state.head_block_hash)) @@ -744,7 +744,7 @@ where if let RethError::Canonical(ref err) = error { if err.is_fatal() { tracing::error!(target: "consensus::engine", ?err, "Encountered fatal error"); - return Err(error) + return Err(error); } } @@ -756,7 +756,7 @@ where self.ensure_consistent_state_with_status(state, &status)? { trace!(target: "consensus::engine", ?status, ?state, "Forkchoice state is inconsistent, returning invalid response"); - return Ok(invalid_fcu_response) + return Ok(invalid_fcu_response); } trace!(target: "consensus::engine", ?status, ?state, "Returning forkchoice status"); @@ -812,7 +812,7 @@ where // we likely do not have the finalized or safe blocks, and would return an incorrect // INVALID status instead. if status.is_valid() { - return self.ensure_consistent_state(state) + return self.ensure_consistent_state(state); } Ok(None) @@ -838,7 +838,7 @@ where if !state.finalized_block_hash.is_zero() && !self.blockchain.is_canonical(state.finalized_block_hash)? { - return Ok(Some(OnForkChoiceUpdated::invalid_state())) + return Ok(Some(OnForkChoiceUpdated::invalid_state())); } // Finalized block is consistent, so update it in the canon chain tracker. @@ -852,7 +852,7 @@ where if !state.safe_block_hash.is_zero() && !self.blockchain.is_canonical(state.safe_block_hash)? { - return Ok(Some(OnForkChoiceUpdated::invalid_state())) + return Ok(Some(OnForkChoiceUpdated::invalid_state())); } // Safe block is consistent, so update it in the canon chain tracker. @@ -913,7 +913,7 @@ where if !safe_block_hash.is_zero() { if self.blockchain.safe_block_hash()? == Some(safe_block_hash) { // nothing to update - return Ok(()) + return Ok(()); } let safe = @@ -933,7 +933,7 @@ where if !finalized_block_hash.is_zero() { if self.blockchain.finalized_block_hash()? == Some(finalized_block_hash) { // nothing to update - return Ok(()) + return Ok(()); } let finalized = self @@ -967,7 +967,7 @@ where if let Some(invalid_ancestor) = self.check_invalid_ancestor(state.head_block_hash) { warn!(target: "consensus::engine", ?error, ?state, ?invalid_ancestor, head=?state.head_block_hash, "Failed to canonicalize the head hash, head is also considered invalid"); debug!(target: "consensus::engine", head=?state.head_block_hash, current_error=?error, "Head was previously marked as invalid"); - return invalid_ancestor + return invalid_ancestor; } #[allow(clippy::single_match)] @@ -979,7 +979,7 @@ where return PayloadStatus::from_status(PayloadStatusEnum::Invalid { validation_error: error.to_string(), }) - .with_latest_valid_hash(B256::ZERO) + .with_latest_valid_hash(B256::ZERO); } RethError::Canonical(CanonicalError::BlockchainTree( BlockchainTreeError::BlockHashNotFoundInChain { .. }, @@ -1063,7 +1063,7 @@ where // begin a payload build process. In such an event, the forkchoiceState update MUST NOT // be rolled back. if attrs.timestamp <= head.timestamp { - return OnForkChoiceUpdated::invalid_payload_attributes() + return OnForkChoiceUpdated::invalid_payload_attributes(); } // 8. Client software MUST begin a payload build process building on top of @@ -1130,7 +1130,7 @@ where if let Some(status) = self.check_invalid_ancestor_with_head(lowest_buffered_ancestor, block.hash) { - return Ok(status) + return Ok(status); } let res = if self.sync.is_pipeline_idle() { @@ -1215,7 +1215,7 @@ where } let status = PayloadStatusEnum::from(error); - return Err(PayloadStatus::new(status, latest_valid_hash)) + return Err(PayloadStatus::new(status, latest_valid_hash)); } }; @@ -1270,7 +1270,7 @@ where let latest_valid_hash = self.latest_valid_hash_for_invalid_payload(parent_hash, None); let status = PayloadStatusEnum::from(PayloadError::InvalidVersionedHashes); - return Err(PayloadStatus::new(status, latest_valid_hash)) + return Err(PayloadStatus::new(status, latest_valid_hash)); } // we can use `zip` safely here because we already compared their length @@ -1282,7 +1282,7 @@ where let latest_valid_hash = self.latest_valid_hash_for_invalid_payload(parent_hash, None); let status = PayloadStatusEnum::from(PayloadError::InvalidVersionedHashes); - return Err(PayloadStatus::new(status, latest_valid_hash)) + return Err(PayloadStatus::new(status, latest_valid_hash)); } } } else if !block_versioned_hashes.is_empty() { @@ -1290,7 +1290,7 @@ where // provided in the new payload call, so the payload is invalid let latest_valid_hash = self.latest_valid_hash_for_invalid_payload(parent_hash, None); let status = PayloadStatusEnum::from(PayloadError::InvalidVersionedHashes); - return Err(PayloadStatus::new(status, latest_valid_hash)) + return Err(PayloadStatus::new(status, latest_valid_hash)); } Ok(()) @@ -1346,7 +1346,7 @@ where if let Some(status) = self.check_invalid_ancestor_with_head(block.parent_hash, block.hash) { - return Ok(status) + return Ok(status); } // not known to be invalid, but we don't know anything else @@ -1445,7 +1445,7 @@ where // check if the block's parent is already marked as invalid if self.check_invalid_ancestor_with_head(block.parent_hash, block.hash).is_some() { // can skip this invalid block - return + return; } match self @@ -1511,7 +1511,7 @@ where // threshold self.sync.set_pipeline_sync_target(target); // we can exit early here because the pipeline will take care of syncing - return + return; } // continue downloading the missing parent @@ -1614,7 +1614,7 @@ where } EngineSyncEvent::PipelineTaskDropped => { error!(target: "consensus::engine", "Failed to receive spawned pipeline"); - return Some(Err(BeaconConsensusEngineError::PipelineChannelClosed)) + return Some(Err(BeaconConsensusEngineError::PipelineChannelClosed)); } EngineSyncEvent::PipelineFinished { result, reached_max_block } => { return self.on_pipeline_finished(result, reached_max_block) @@ -1644,7 +1644,7 @@ where if reached_max_block { // Terminate the sync early if it's reached the maximum user // configured block. - return Some(Ok(())) + return Some(Ok(())); } if let ControlFlow::Unwind { bad_block, .. } = ctrl { @@ -1652,7 +1652,7 @@ where // update the `invalid_headers` cache with the new invalid headers self.invalid_headers.insert(*bad_block); - return None + return None; } // update the canon chain if continuous is enabled @@ -1670,7 +1670,7 @@ where }, Err(error) => { error!(target: "consensus::engine", ?error, "Error getting canonical header for continuous sync"); - return Some(Err(RethError::Provider(error).into())) + return Some(Err(RethError::Provider(error).into())); } }; self.blockchain.set_canonical_head(max_header); @@ -1682,7 +1682,7 @@ where // This is only possible if the node was run with `debug.tip` // argument and without CL. warn!(target: "consensus::engine", "No fork choice state available"); - return None + return None; } }; @@ -1752,7 +1752,7 @@ where } Err(error) => { error!(target: "consensus::engine", ?error, "Error restoring blockchain tree state"); - return Some(Err(error.into())) + return Some(Err(error.into())); } }; } @@ -1790,7 +1790,7 @@ where self.blockchain.connect_buffered_blocks_to_canonical_hashes() { error!(target: "consensus::engine", ?error, "Error connecting buffered blocks to canonical hashes on hook result"); - return Err(error.into()) + return Err(error.into()); } } } @@ -1843,7 +1843,7 @@ where }, )? { this.on_hook_result(result)?; - continue + continue; } // Process one incoming message from the CL. We don't drain the messages right away, @@ -1858,11 +1858,11 @@ where OnForkchoiceUpdateOutcome::Processed => {} OnForkchoiceUpdateOutcome::ReachedMaxBlock => { // reached the max block, we can terminate the future - return Poll::Ready(Ok(())) + return Poll::Ready(Ok(())); } OnForkchoiceUpdateOutcome::Fatal(err) => { // fatal error, we can terminate the future - return Poll::Ready(Err(RethError::Execution(err).into())) + return Poll::Ready(Err(RethError::Execution(err).into())); } } } @@ -1878,23 +1878,23 @@ where this.listeners.push_listener(tx); } } - continue + continue; } // Both running hook with db write access and engine messages are pending, // proceed to other polls - break + break; } // process sync events if any match this.sync.poll(cx) { Poll::Ready(sync_event) => { if let Some(res) = this.on_sync_event(sync_event) { - return Poll::Ready(res) + return Poll::Ready(res); } // this could have taken a while, so we start the next cycle to handle any new // engine messages - continue 'main + continue 'main; } Poll::Pending => { // no more sync events to process @@ -1922,13 +1922,13 @@ where // ensure we're polling until pending while also checking for new engine // messages before polling the next hook - continue 'main + continue 'main; } } // incoming engine messages and sync events are drained, so we can yield back // control - return Poll::Pending + return Poll::Pending; } } } @@ -2040,7 +2040,7 @@ mod tests { result, Err(BeaconConsensusEngineError::Pipeline(n)) if matches!(*n.as_ref(), PipelineError::Stage(StageError::ChannelClosed)) ); - break + break; } Err(TryRecvError::Empty) => { let _ = env @@ -2133,7 +2133,14 @@ mod tests { let factory = ProviderFactory::new(db, chain); let provider = factory.provider_rw().unwrap(); blocks - .try_for_each(|b| provider.insert_block(b.clone(), None, None).map(|_| ())) + .try_for_each(|b| { + provider + .insert_block( + b.clone().try_seal_with_senders().expect("invalid tx signature in block"), + None, + ) + .map(|_| ()) + }) .expect("failed to insert"); provider.commit().unwrap(); } diff --git a/crates/stages/src/stages/execution.rs b/crates/stages/src/stages/execution.rs index 8d56a5d00e43..b91330a7e050 100644 --- a/crates/stages/src/stages/execution.rs +++ b/crates/stages/src/stages/execution.rs @@ -492,6 +492,7 @@ mod tests { use alloy_rlp::Decodable; use assert_matches::assert_matches; use reth_db::{models::AccountBeforeTx, test_utils::create_test_rw_db}; + use reth_interfaces::executor::BlockValidationError; use reth_primitives::{ address, hex_literal::hex, keccak256, stage::StageUnitCheckpoint, Account, Bytecode, ChainSpecBuilder, PruneModes, SealedBlock, StorageEntry, B256, MAINNET, U256, @@ -551,8 +552,16 @@ mod tests { let genesis = SealedBlock::decode(&mut genesis_rlp).unwrap(); let mut block_rlp = hex!("f90262f901f9a075c371ba45999d87f4542326910a11af515897aebce5265d3f6acd1f1161f82fa01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347942adc25665018aa1fe0e6bc666dac8fc2697ff9baa098f2dcd87c8ae4083e7017a05456c14eea4b1db2032126e27b3b1563d57d7cc0a08151d548273f6683169524b66ca9fe338b9ce42bc3540046c828fd939ae23bcba03f4e5c2ec5b2170b711d97ee755c160457bb58d8daa338e835ec02ae6860bbabb901000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000083020000018502540be40082a8798203e800a00000000000000000000000000000000000000000000000000000000000000000880000000000000000f863f861800a8405f5e10094100000000000000000000000000000000000000080801ba07e09e26678ed4fac08a249ebe8ed680bf9051a5e14ad223e4b2b9d26e0208f37a05f6e3f188e3e6eab7d7d3b6568f5eac7d687b08d307d3154ccd8c87b4630509bc0").as_slice(); let block = SealedBlock::decode(&mut block_rlp).unwrap(); - provider.insert_block(genesis, None, None).unwrap(); - provider.insert_block(block.clone(), None, None).unwrap(); + provider + .insert_block( + genesis + .try_seal_with_senders() + .map_err(|_| BlockValidationError::SenderRecoveryError) + .unwrap(), + None, + ) + .unwrap(); + provider.insert_block(block.clone().try_seal_with_senders().unwrap(), None).unwrap(); provider.commit().unwrap(); let previous_stage_checkpoint = ExecutionCheckpoint { @@ -587,8 +596,8 @@ mod tests { let genesis = SealedBlock::decode(&mut genesis_rlp).unwrap(); let mut block_rlp = hex!("f90262f901f9a075c371ba45999d87f4542326910a11af515897aebce5265d3f6acd1f1161f82fa01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347942adc25665018aa1fe0e6bc666dac8fc2697ff9baa098f2dcd87c8ae4083e7017a05456c14eea4b1db2032126e27b3b1563d57d7cc0a08151d548273f6683169524b66ca9fe338b9ce42bc3540046c828fd939ae23bcba03f4e5c2ec5b2170b711d97ee755c160457bb58d8daa338e835ec02ae6860bbabb901000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000083020000018502540be40082a8798203e800a00000000000000000000000000000000000000000000000000000000000000000880000000000000000f863f861800a8405f5e10094100000000000000000000000000000000000000080801ba07e09e26678ed4fac08a249ebe8ed680bf9051a5e14ad223e4b2b9d26e0208f37a05f6e3f188e3e6eab7d7d3b6568f5eac7d687b08d307d3154ccd8c87b4630509bc0").as_slice(); let block = SealedBlock::decode(&mut block_rlp).unwrap(); - provider.insert_block(genesis, None, None).unwrap(); - provider.insert_block(block.clone(), None, None).unwrap(); + provider.insert_block(genesis.try_seal_with_senders().unwrap(), None).unwrap(); + provider.insert_block(block.clone().try_seal_with_senders().unwrap(), None).unwrap(); provider.commit().unwrap(); let previous_stage_checkpoint = ExecutionCheckpoint { @@ -623,8 +632,8 @@ mod tests { let genesis = SealedBlock::decode(&mut genesis_rlp).unwrap(); let mut block_rlp = hex!("f90262f901f9a075c371ba45999d87f4542326910a11af515897aebce5265d3f6acd1f1161f82fa01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347942adc25665018aa1fe0e6bc666dac8fc2697ff9baa098f2dcd87c8ae4083e7017a05456c14eea4b1db2032126e27b3b1563d57d7cc0a08151d548273f6683169524b66ca9fe338b9ce42bc3540046c828fd939ae23bcba03f4e5c2ec5b2170b711d97ee755c160457bb58d8daa338e835ec02ae6860bbabb901000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000083020000018502540be40082a8798203e800a00000000000000000000000000000000000000000000000000000000000000000880000000000000000f863f861800a8405f5e10094100000000000000000000000000000000000000080801ba07e09e26678ed4fac08a249ebe8ed680bf9051a5e14ad223e4b2b9d26e0208f37a05f6e3f188e3e6eab7d7d3b6568f5eac7d687b08d307d3154ccd8c87b4630509bc0").as_slice(); let block = SealedBlock::decode(&mut block_rlp).unwrap(); - provider.insert_block(genesis, None, None).unwrap(); - provider.insert_block(block.clone(), None, None).unwrap(); + provider.insert_block(genesis.try_seal_with_senders().unwrap(), None).unwrap(); + provider.insert_block(block.clone().try_seal_with_senders().unwrap(), None).unwrap(); provider.commit().unwrap(); let previous_checkpoint = StageCheckpoint { block_number: 1, stage_checkpoint: None }; @@ -653,8 +662,8 @@ mod tests { let genesis = SealedBlock::decode(&mut genesis_rlp).unwrap(); let mut block_rlp = hex!("f90262f901f9a075c371ba45999d87f4542326910a11af515897aebce5265d3f6acd1f1161f82fa01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347942adc25665018aa1fe0e6bc666dac8fc2697ff9baa098f2dcd87c8ae4083e7017a05456c14eea4b1db2032126e27b3b1563d57d7cc0a08151d548273f6683169524b66ca9fe338b9ce42bc3540046c828fd939ae23bcba03f4e5c2ec5b2170b711d97ee755c160457bb58d8daa338e835ec02ae6860bbabb901000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000083020000018502540be40082a8798203e800a00000000000000000000000000000000000000000000000000000000000000000880000000000000000f863f861800a8405f5e10094100000000000000000000000000000000000000080801ba07e09e26678ed4fac08a249ebe8ed680bf9051a5e14ad223e4b2b9d26e0208f37a05f6e3f188e3e6eab7d7d3b6568f5eac7d687b08d307d3154ccd8c87b4630509bc0").as_slice(); let block = SealedBlock::decode(&mut block_rlp).unwrap(); - provider.insert_block(genesis, None, None).unwrap(); - provider.insert_block(block.clone(), None, None).unwrap(); + provider.insert_block(genesis.try_seal_with_senders().unwrap(), None).unwrap(); + provider.insert_block(block.clone().try_seal_with_senders().unwrap(), None).unwrap(); provider.commit().unwrap(); // insert pre state @@ -759,8 +768,8 @@ mod tests { let genesis = SealedBlock::decode(&mut genesis_rlp).unwrap(); let mut block_rlp = hex!("f90262f901f9a075c371ba45999d87f4542326910a11af515897aebce5265d3f6acd1f1161f82fa01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347942adc25665018aa1fe0e6bc666dac8fc2697ff9baa098f2dcd87c8ae4083e7017a05456c14eea4b1db2032126e27b3b1563d57d7cc0a08151d548273f6683169524b66ca9fe338b9ce42bc3540046c828fd939ae23bcba03f4e5c2ec5b2170b711d97ee755c160457bb58d8daa338e835ec02ae6860bbabb901000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000083020000018502540be40082a8798203e800a00000000000000000000000000000000000000000000000000000000000000000880000000000000000f863f861800a8405f5e10094100000000000000000000000000000000000000080801ba07e09e26678ed4fac08a249ebe8ed680bf9051a5e14ad223e4b2b9d26e0208f37a05f6e3f188e3e6eab7d7d3b6568f5eac7d687b08d307d3154ccd8c87b4630509bc0").as_slice(); let block = SealedBlock::decode(&mut block_rlp).unwrap(); - provider.insert_block(genesis, None, None).unwrap(); - provider.insert_block(block.clone(), None, None).unwrap(); + provider.insert_block(genesis.try_seal_with_senders().unwrap(), None).unwrap(); + provider.insert_block(block.clone().try_seal_with_senders().unwrap(), None).unwrap(); provider.commit().unwrap(); // variables @@ -831,8 +840,8 @@ mod tests { let genesis = SealedBlock::decode(&mut genesis_rlp).unwrap(); let mut block_rlp = hex!("f9025ff901f7a0c86e8cc0310ae7c531c758678ddbfd16fc51c8cef8cec650b032de9869e8b94fa01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347942adc25665018aa1fe0e6bc666dac8fc2697ff9baa050554882fbbda2c2fd93fdc466db9946ea262a67f7a76cc169e714f105ab583da00967f09ef1dfed20c0eacfaa94d5cd4002eda3242ac47eae68972d07b106d192a0e3c8b47fbfc94667ef4cceb17e5cc21e3b1eebd442cebb27f07562b33836290db90100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008302000001830f42408238108203e800a00000000000000000000000000000000000000000000000000000000000000000880000000000000000f862f860800a83061a8094095e7baea6a6c7c4c2dfeb977efac326af552d8780801ba072ed817487b84ba367d15d2f039b5fc5f087d0a8882fbdf73e8cb49357e1ce30a0403d800545b8fc544f92ce8124e2255f8c3c6af93f28243a120585d4c4c6a2a3c0").as_slice(); let block = SealedBlock::decode(&mut block_rlp).unwrap(); - provider.insert_block(genesis, None, None).unwrap(); - provider.insert_block(block.clone(), None, None).unwrap(); + provider.insert_block(genesis.try_seal_with_senders().unwrap(), None).unwrap(); + provider.insert_block(block.clone().try_seal_with_senders().unwrap(), None).unwrap(); provider.commit().unwrap(); // variables diff --git a/crates/stages/src/stages/hashing_account.rs b/crates/stages/src/stages/hashing_account.rs index 308cfa71ea27..ffe5acdb9183 100644 --- a/crates/stages/src/stages/hashing_account.rs +++ b/crates/stages/src/stages/hashing_account.rs @@ -95,7 +95,7 @@ impl AccountHashingStage { let blocks = random_block_range(&mut rng, opts.blocks.clone(), B256::ZERO, opts.txs); for block in blocks { - provider.insert_block(block, None, None).unwrap(); + provider.insert_block(block.try_seal_with_senders().unwrap(), None).unwrap(); } let mut accounts = random_eoa_account_range(&mut rng, opts.accounts); { @@ -138,7 +138,7 @@ impl Stage for AccountHashingStage { input: ExecInput, ) -> Result { if input.target_reached() { - return Ok(ExecOutput::done(input.checkpoint())) + return Ok(ExecOutput::done(input.checkpoint())); } let (from_block, to_block) = input.next_block_range().into_inner(); @@ -238,7 +238,7 @@ impl Stage for AccountHashingStage { }, ); - return Ok(ExecOutput { checkpoint, done: false }) + return Ok(ExecOutput { checkpoint, done: false }); } } else { // Aggregate all transition changesets and make a list of accounts that have been @@ -549,7 +549,7 @@ mod tests { let start_block = input.next_block(); let end_block = output.checkpoint.block_number; if start_block > end_block { - return Ok(()) + return Ok(()); } } self.check_hashed_accounts() diff --git a/crates/stages/src/stages/mod.rs b/crates/stages/src/stages/mod.rs index ffe8ae1da1f6..67519e8e0af5 100644 --- a/crates/stages/src/stages/mod.rs +++ b/crates/stages/src/stages/mod.rs @@ -77,8 +77,8 @@ mod tests { let genesis = SealedBlock::decode(&mut genesis_rlp).unwrap(); let mut block_rlp = hex!("f90262f901f9a075c371ba45999d87f4542326910a11af515897aebce5265d3f6acd1f1161f82fa01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347942adc25665018aa1fe0e6bc666dac8fc2697ff9baa098f2dcd87c8ae4083e7017a05456c14eea4b1db2032126e27b3b1563d57d7cc0a08151d548273f6683169524b66ca9fe338b9ce42bc3540046c828fd939ae23bcba03f4e5c2ec5b2170b711d97ee755c160457bb58d8daa338e835ec02ae6860bbabb901000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000083020000018502540be40082a8798203e800a00000000000000000000000000000000000000000000000000000000000000000880000000000000000f863f861800a8405f5e10094100000000000000000000000000000000000000080801ba07e09e26678ed4fac08a249ebe8ed680bf9051a5e14ad223e4b2b9d26e0208f37a05f6e3f188e3e6eab7d7d3b6568f5eac7d687b08d307d3154ccd8c87b4630509bc0").as_slice(); let block = SealedBlock::decode(&mut block_rlp).unwrap(); - provider_rw.insert_block(genesis, None, None).unwrap(); - provider_rw.insert_block(block.clone(), None, None).unwrap(); + provider_rw.insert_block(genesis.try_seal_with_senders().unwrap(), None).unwrap(); + provider_rw.insert_block(block.clone().try_seal_with_senders().unwrap(), None).unwrap(); // Fill with bogus blocks to respect PruneMode distance. let mut head = block.hash; @@ -86,7 +86,7 @@ mod tests { for block_number in 2..=tip { let nblock = random_block(&mut rng, block_number, Some(head), Some(0), Some(0)); head = nblock.hash; - provider_rw.insert_block(nblock, None, None).unwrap(); + provider_rw.insert_block(nblock.try_seal_with_senders().unwrap(), None).unwrap(); } provider_rw.commit().unwrap(); diff --git a/crates/storage/provider/src/providers/database/metrics.rs b/crates/storage/provider/src/providers/database/metrics.rs index cdd4147292da..8fb3a1cd3b07 100644 --- a/crates/storage/provider/src/providers/database/metrics.rs +++ b/crates/storage/provider/src/providers/database/metrics.rs @@ -59,7 +59,6 @@ pub(crate) enum Action { InsertBlockBodyIndices, InsertTransactionBlock, - RecoverSigners, GetNextTxNum, GetParentTD, } @@ -86,7 +85,6 @@ impl Action { Action::InsertBlockWithdrawals => "insert block withdrawals", Action::InsertBlockBodyIndices => "insert block body indices", Action::InsertTransactionBlock => "insert transaction block", - Action::RecoverSigners => "recover signers", Action::GetNextTxNum => "get next tx num", Action::GetParentTD => "get parent TD", } diff --git a/crates/storage/provider/src/providers/database/mod.rs b/crates/storage/provider/src/providers/database/mod.rs index f2f6141b8971..b9c59e5c3c88 100644 --- a/crates/storage/provider/src/providers/database/mod.rs +++ b/crates/storage/provider/src/providers/database/mod.rs @@ -141,7 +141,7 @@ impl ProviderFactory { if block_number == provider.best_block_number().unwrap_or_default() && block_number == provider.last_block_number().unwrap_or_default() { - return Ok(Box::new(LatestStateProvider::new(provider.into_tx()))) + return Ok(Box::new(LatestStateProvider::new(provider.into_tx()))); } // +1 as the changeset that we want is the one that was applied after this block. @@ -566,7 +566,10 @@ mod tests { { let provider = factory.provider_rw().unwrap(); - assert_matches!(provider.insert_block(block.clone(), None, None), Ok(_)); + assert_matches!( + provider.insert_block(block.clone().try_seal_with_senders().unwrap(), None), + Ok(_) + ); assert_matches!( provider.transaction_sender(0), Ok(Some(sender)) if sender == block.body[0].recover_signer().unwrap() @@ -578,8 +581,7 @@ mod tests { let provider = factory.provider_rw().unwrap(); assert_matches!( provider.insert_block( - block.clone(), - None, + block.clone().try_seal_with_senders().unwrap(), Some(&PruneModes { sender_recovery: Some(PruneMode::Full), transaction_lookup: Some(PruneMode::Full), @@ -604,7 +606,10 @@ mod tests { for range in tx_ranges { let provider = factory.provider_rw().unwrap(); - assert_matches!(provider.insert_block(block.clone(), None, None), Ok(_)); + assert_matches!( + provider.insert_block(block.clone().try_seal_with_senders().unwrap(), None), + Ok(_) + ); let senders = provider.get_or_take::(range.clone()); assert_eq!( diff --git a/crates/storage/provider/src/providers/database/provider.rs b/crates/storage/provider/src/providers/database/provider.rs index 7b4cadaec076..6c85dc12c807 100644 --- a/crates/storage/provider/src/providers/database/provider.rs +++ b/crates/storage/provider/src/providers/database/provider.rs @@ -2150,8 +2150,7 @@ impl BlockExecutionWriter for DatabaseProvider { impl BlockWriter for DatabaseProvider { fn insert_block( &self, - block: SealedBlock, - senders: Option>, + block: SealedBlockWithSenders, prune_modes: Option<&PruneModes>, ) -> ProviderResult { let block_number = block.number; @@ -2185,7 +2184,7 @@ impl BlockWriter for DatabaseProvider { if !block.ommers.is_empty() { self.tx.put::( block_number, - StoredBlockOmmers { ommers: block.ommers }, + StoredBlockOmmers { ommers: block.block.ommers }, )?; durations_recorder.record_relative(metrics::Action::InsertBlockOmmers); } @@ -2199,29 +2198,14 @@ impl BlockWriter for DatabaseProvider { durations_recorder.record_relative(metrics::Action::GetNextTxNum); let first_tx_num = next_tx_num; - let tx_count = block.body.len() as u64; + let tx_count = block.block.body.len() as u64; // Ensures we have all the senders for the block's transactions. - let senders = match senders { - Some(senders) if block.body.len() == senders.len() => { - // senders have the correct length as transactions in the block - senders - } - _ => { - // recover senders from transactions - let senders = TransactionSigned::recover_signers(&block.body, block.body.len()) - .ok_or(ProviderError::SenderRecoveryError)?; - durations_recorder.record_relative(metrics::Action::RecoverSigners); - debug_assert_eq!(senders.len(), block.body.len(), "missing one or more senders"); - senders - } - }; - let mut tx_senders_elapsed = Duration::default(); let mut transactions_elapsed = Duration::default(); let mut tx_hash_numbers_elapsed = Duration::default(); - for (transaction, sender) in block.body.into_iter().zip(senders) { + for (transaction, sender) in block.block.body.into_iter().zip(block.senders.iter()) { let hash = transaction.hash(); if prune_modes @@ -2230,7 +2214,7 @@ impl BlockWriter for DatabaseProvider { .is_none() { let start = Instant::now(); - self.tx.put::(next_tx_num, sender)?; + self.tx.put::(next_tx_num, *sender)?; tx_senders_elapsed += start.elapsed(); } @@ -2266,7 +2250,7 @@ impl BlockWriter for DatabaseProvider { durations_recorder .record_duration(metrics::Action::InsertTxHashNumbers, tx_hash_numbers_elapsed); - if let Some(withdrawals) = block.withdrawals { + if let Some(withdrawals) = block.block.withdrawals { if !withdrawals.is_empty() { self.tx.put::( block_number, @@ -2317,8 +2301,7 @@ impl BlockWriter for DatabaseProvider { // Insert the blocks for block in blocks { - let (block, senders) = block.into_components(); - self.insert_block(block, Some(senders), prune_modes)?; + self.insert_block(block, prune_modes)?; durations_recorder.record_relative(metrics::Action::InsertBlock); } diff --git a/crates/storage/provider/src/traits/block.rs b/crates/storage/provider/src/traits/block.rs index c128393b9058..0d90c2046385 100644 --- a/crates/storage/provider/src/traits/block.rs +++ b/crates/storage/provider/src/traits/block.rs @@ -6,9 +6,8 @@ use auto_impl::auto_impl; use reth_db::models::StoredBlockBodyIndices; use reth_interfaces::provider::ProviderResult; use reth_primitives::{ - Address, Block, BlockHashOrNumber, BlockId, BlockNumber, BlockNumberOrTag, BlockWithSenders, - ChainSpec, Header, PruneModes, Receipt, SealedBlock, SealedBlockWithSenders, SealedHeader, - B256, + Block, BlockHashOrNumber, BlockId, BlockNumber, BlockNumberOrTag, BlockWithSenders, ChainSpec, + Header, PruneModes, Receipt, SealedBlock, SealedBlockWithSenders, SealedHeader, B256, }; use reth_trie::{hashed_cursor::HashedPostState, updates::TrieUpdates}; use std::ops::RangeInclusive; @@ -293,8 +292,7 @@ pub trait BlockWriter: Send + Sync { /// transition in the block. fn insert_block( &self, - block: SealedBlock, - senders: Option>, + block: SealedBlockWithSenders, prune_modes: Option<&PruneModes>, ) -> ProviderResult; diff --git a/testing/ef-tests/src/cases/blockchain_test.rs b/testing/ef-tests/src/cases/blockchain_test.rs index 7e7c1dc6cb65..2494afeff37f 100644 --- a/testing/ef-tests/src/cases/blockchain_test.rs +++ b/testing/ef-tests/src/cases/blockchain_test.rs @@ -87,8 +87,9 @@ impl Case for BlockchainTestCase { SealedBlock::new( case.genesis_block_header.clone().into(), BlockBody::default(), - ), - None, + ) + .try_seal_with_senders() + .unwrap(), None, ) .map_err(|err| Error::RethError(err.into()))?; @@ -98,7 +99,7 @@ impl Case for BlockchainTestCase { let last_block = case.blocks.iter().try_fold(None, |_, block| { let decoded = SealedBlock::decode(&mut block.rlp.as_ref())?; provider - .insert_block(decoded.clone(), None, None) + .insert_block(decoded.clone().try_seal_with_senders().unwrap(), None) .map_err(|err| Error::RethError(err.into()))?; Ok::, Error>(Some(decoded)) })?; From 84b1bb01db04a0fa40b78f21678ea5fc4f38016f Mon Sep 17 00:00:00 2001 From: Federico Gimenez Date: Fri, 15 Dec 2023 14:17:30 +0100 Subject: [PATCH 147/277] More descriptive reorg log (#5671) Co-authored-by: Matthias Seitz Co-authored-by: Dan Cline <6798349+Rjected@users.noreply.github.com> --- crates/blockchain-tree/src/blockchain_tree.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/blockchain-tree/src/blockchain_tree.rs b/crates/blockchain-tree/src/blockchain_tree.rs index 7797d26b251c..3b956fe2b1f6 100644 --- a/crates/blockchain-tree/src/blockchain_tree.rs +++ b/crates/blockchain-tree/src/blockchain_tree.rs @@ -1244,7 +1244,7 @@ impl BlockchainTree { let tip = provider_rw.last_block_number()?; let revert_range = (revert_until + 1)..=tip; - info!(target: "blockchain_tree", "Unwinding canonical chain blocks: {:?}", revert_range); + info!(target: "blockchain_tree", "REORG: revert canonical from database by unwinding chain blocks {:?}", revert_range); // read block and execution result from database. and remove traces of block from tables. let blocks_and_execution = provider_rw .take_block_and_execution_range( From a0129b6b88863c827beefeecd0f6874003a135eb Mon Sep 17 00:00:00 2001 From: Aditya Pandey Date: Fri, 15 Dec 2023 18:52:19 +0530 Subject: [PATCH 148/277] Archify EthStateCache (#5744) Co-authored-by: Matthias Seitz --- crates/rpc/rpc/src/eth/api/block.rs | 11 +++++--- crates/rpc/rpc/src/eth/api/fee_history.rs | 4 +-- crates/rpc/rpc/src/eth/cache/mod.rs | 33 +++++++++++++++-------- crates/rpc/rpc/src/eth/filter.rs | 6 ++--- crates/rpc/rpc/src/eth/logs_utils.rs | 17 ++++++------ crates/rpc/rpc/src/eth/pubsub.rs | 2 +- 6 files changed, 44 insertions(+), 29 deletions(-) diff --git a/crates/rpc/rpc/src/eth/api/block.rs b/crates/rpc/rpc/src/eth/api/block.rs index c1c835107cdb..e619d210a449 100644 --- a/crates/rpc/rpc/src/eth/api/block.rs +++ b/crates/rpc/rpc/src/eth/api/block.rs @@ -1,5 +1,7 @@ //! Contains RPC handler implementations specific to blocks. +use std::sync::Arc; + use crate::{ eth::{ api::transactions::build_transaction_receipt_with_block_receipts, @@ -65,7 +67,10 @@ where let mut block_and_receipts = None; if block_id.is_pending() { - block_and_receipts = self.provider().pending_block_and_receipts()?; + block_and_receipts = self + .provider() + .pending_block_and_receipts()? + .map(|(sb, receipts)| (sb, Arc::new(receipts))); } else if let Some(block_hash) = self.provider().block_hash_for_id(block_id)? { block_and_receipts = self.cache().get_block_and_receipts(block_hash).await?; } @@ -87,7 +92,7 @@ where let receipts = block .body .into_iter() - .zip(receipts.clone()) + .zip(receipts.iter()) .enumerate() .map(|(idx, (tx, receipt))| { let meta = TransactionMeta { @@ -106,7 +111,7 @@ where build_transaction_receipt_with_block_receipts( tx, meta, - receipt, + receipt.clone(), &receipts, #[cfg(feature = "optimism")] op_tx_meta, diff --git a/crates/rpc/rpc/src/eth/api/fee_history.rs b/crates/rpc/rpc/src/eth/api/fee_history.rs index 8c01d672c1b2..399f7765034b 100644 --- a/crates/rpc/rpc/src/eth/api/fee_history.rs +++ b/crates/rpc/rpc/src/eth/api/fee_history.rs @@ -70,7 +70,7 @@ impl FeeHistoryCache { /// Insert block data into the cache. async fn insert_blocks(&self, blocks: I) where - I: Iterator)>, + I: Iterator>)>, { let mut entries = self.inner.entries.write().await; @@ -244,7 +244,7 @@ pub async fn fee_history_cache_new_blocks_task( let (blocks, receipts): (Vec<_>, Vec<_>) = committed .blocks_and_receipts() .map(|(block, receipts)| { - (block.block.clone(), receipts.iter().flatten().cloned().collect::>()) + (block.block.clone(), Arc::new(receipts.iter().flatten().cloned().collect::>())) }) .unzip(); fee_history_cache.insert_blocks(blocks.into_iter().zip(receipts)).await; diff --git a/crates/rpc/rpc/src/eth/cache/mod.rs b/crates/rpc/rpc/src/eth/cache/mod.rs index 80cf92d1b76d..05c16868293c 100644 --- a/crates/rpc/rpc/src/eth/cache/mod.rs +++ b/crates/rpc/rpc/src/eth/cache/mod.rs @@ -40,7 +40,7 @@ type BlockTransactionsResponseSender = type BlockWithSendersResponseSender = oneshot::Sender>>; /// The type that can send the response to the requested receipts of a block. -type ReceiptsResponseSender = oneshot::Sender>>>; +type ReceiptsResponseSender = oneshot::Sender>>>>; /// The type that can send the response to a requested env type EnvResponseSender = oneshot::Sender>; @@ -52,7 +52,8 @@ type BlockLruCache = MultiConsumerLruCache< Either, >; -type ReceiptsLruCache = MultiConsumerLruCache, L, ReceiptsResponseSender>; +type ReceiptsLruCache = + MultiConsumerLruCache>, L, ReceiptsResponseSender>; type EnvLruCache = MultiConsumerLruCache; @@ -180,7 +181,7 @@ impl EthStateCache { pub async fn get_transactions_and_receipts( &self, block_hash: B256, - ) -> ProviderResult, Vec)>> { + ) -> ProviderResult, Arc>)>> { let transactions = self.get_block_transactions(block_hash); let receipts = self.get_receipts(block_hash); @@ -214,7 +215,10 @@ impl EthStateCache { /// Requests the [Receipt] for the block hash /// /// Returns `None` if the block was not found. - pub async fn get_receipts(&self, block_hash: B256) -> ProviderResult>> { + pub async fn get_receipts( + &self, + block_hash: B256, + ) -> ProviderResult>>> { let (response_tx, rx) = oneshot::channel(); let _ = self.to_service.send(CacheAction::GetReceipts { block_hash, response_tx }); rx.await.map_err(|_| ProviderError::CacheServiceUnavailable)? @@ -224,7 +228,7 @@ impl EthStateCache { pub async fn get_block_and_receipts( &self, block_hash: B256, - ) -> ProviderResult)>> { + ) -> ProviderResult>)>> { let block = self.get_sealed_block(block_hash); let receipts = self.get_receipts(block_hash); @@ -268,7 +272,7 @@ pub(crate) struct EthStateCacheService< LimitEnvs = ByLength, > where LimitBlocks: Limiter, - LimitReceipts: Limiter>, + LimitReceipts: Limiter>>, LimitEnvs: Limiter, { /// The type used to lookup data from disk @@ -318,7 +322,11 @@ where } } - fn on_new_receipts(&mut self, block_hash: B256, res: ProviderResult>>) { + fn on_new_receipts( + &mut self, + block_hash: B256, + res: ProviderResult>>>, + ) { if let Some(queued) = self.receipts_cache.remove(&block_hash) { // send the response to queued senders for tx in queued { @@ -426,7 +434,10 @@ where this.action_task_spawner.spawn_blocking(Box::pin(async move { // Acquire permit let _permit = rate_limiter.acquire().await; - let res = provider.receipts_by_block(block_hash.into()); + let res = provider + .receipts_by_block(block_hash.into()) + .map(|maybe_receipts| maybe_receipts.map(Arc::new)); + let _ = action_tx .send(CacheAction::ReceiptsResult { block_hash, res }); })); @@ -496,9 +507,9 @@ where for block_receipts in receipts { this.on_new_receipts( block_receipts.block_hash, - Ok(Some( + Ok(Some(Arc::new( block_receipts.receipts.into_iter().flatten().collect(), - )), + ))), ); } } @@ -517,7 +528,7 @@ enum CacheAction { GetEnv { block_hash: B256, response_tx: EnvResponseSender }, GetReceipts { block_hash: B256, response_tx: ReceiptsResponseSender }, BlockWithSendersResult { block_hash: B256, res: ProviderResult> }, - ReceiptsResult { block_hash: B256, res: ProviderResult>> }, + ReceiptsResult { block_hash: B256, res: ProviderResult>>> }, EnvResult { block_hash: B256, res: Box> }, CacheNewCanonicalChain { blocks: Vec, receipts: Vec }, } diff --git a/crates/rpc/rpc/src/eth/filter.rs b/crates/rpc/rpc/src/eth/filter.rs index 96cb687e6375..e6403d11a8e6 100644 --- a/crates/rpc/rpc/src/eth/filter.rs +++ b/crates/rpc/rpc/src/eth/filter.rs @@ -359,7 +359,7 @@ where &mut all_logs, &filter, (block_hash, block.number).into(), - block.body.into_iter().map(|tx| tx.hash()).zip(receipts), + block.body.into_iter().map(|tx| tx.hash()).zip(receipts.iter()), false, ); } @@ -406,7 +406,7 @@ where async fn block_and_receipts_by_number( &self, hash_or_number: BlockHashOrNumber, - ) -> EthResult)>> { + ) -> EthResult>)>> { let block_hash = match self.provider.convert_block_hash(hash_or_number)? { Some(hash) => hash, None => return Ok(None), @@ -467,7 +467,7 @@ where &mut all_logs, &filter_params, (block.number, block_hash).into(), - block.body.into_iter().map(|tx| tx.hash()).zip(receipts), + block.body.into_iter().map(|tx| tx.hash()).zip(receipts.iter()), false, ); diff --git a/crates/rpc/rpc/src/eth/logs_utils.rs b/crates/rpc/rpc/src/eth/logs_utils.rs index 2fdd711356d4..fc2edb045106 100644 --- a/crates/rpc/rpc/src/eth/logs_utils.rs +++ b/crates/rpc/rpc/src/eth/logs_utils.rs @@ -3,14 +3,14 @@ use reth_rpc_types::{FilteredParams, Log}; use reth_rpc_types_compat::log::from_primitive_log; /// Returns all matching logs of a block's receipts grouped with the hash of their transaction. -pub(crate) fn matching_block_logs( +pub(crate) fn matching_block_logs<'a, I>( filter: &FilteredParams, block: BlockNumHash, tx_and_receipts: I, removed: bool, ) -> Vec where - I: IntoIterator, + I: IntoIterator, { let mut all_logs = Vec::new(); append_matching_block_logs(&mut all_logs, filter, block, tx_and_receipts, removed); @@ -18,26 +18,25 @@ where } /// Appends all matching logs of a block's receipts grouped with the hash of their transaction -pub(crate) fn append_matching_block_logs( +pub(crate) fn append_matching_block_logs<'a, I>( all_logs: &mut Vec, filter: &FilteredParams, block: BlockNumHash, tx_and_receipts: I, removed: bool, ) where - I: IntoIterator, + I: IntoIterator, { let block_number_u256 = U256::from(block.number); // tracks the index of a log in the entire block let mut log_index: u32 = 0; for (transaction_idx, (transaction_hash, receipt)) in tx_and_receipts.into_iter().enumerate() { - let logs = receipt.logs; - for log in logs.into_iter() { - if log_matches_filter(block, &log, filter) { + for log in receipt.logs.iter() { + if log_matches_filter(block, log, filter) { let log = Log { address: log.address, - topics: log.topics, - data: log.data, + topics: log.topics.clone(), + data: log.data.clone(), block_hash: Some(block.hash), block_number: Some(block_number_u256), transaction_hash: Some(transaction_hash), diff --git a/crates/rpc/rpc/src/eth/pubsub.rs b/crates/rpc/rpc/src/eth/pubsub.rs index b720ebec1647..aa746464e43f 100644 --- a/crates/rpc/rpc/src/eth/pubsub.rs +++ b/crates/rpc/rpc/src/eth/pubsub.rs @@ -311,7 +311,7 @@ where let all_logs = logs_utils::matching_block_logs( &filter, block_receipts.block, - block_receipts.tx_receipts, + block_receipts.tx_receipts.iter().map(|(tx, receipt)| (*tx, receipt)), removed, ); futures::stream::iter(all_logs) From d73e410593de797fe8afc5dfe89b838db0cbd0a5 Mon Sep 17 00:00:00 2001 From: Thomas Coratger <60488569+tcoratger@users.noreply.github.com> Date: Fri, 15 Dec 2023 14:34:09 +0100 Subject: [PATCH 149/277] doc(blockchain-tree): add documentation for `MakeCanonicalAction` enum (#5739) --- crates/blockchain-tree/src/metrics.rs | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/crates/blockchain-tree/src/metrics.rs b/crates/blockchain-tree/src/metrics.rs index dcaaaafaaf33..7b5e22294ab6 100644 --- a/crates/blockchain-tree/src/metrics.rs +++ b/crates/blockchain-tree/src/metrics.rs @@ -58,16 +58,26 @@ impl MakeCanonicalDurationsRecorder { } } +/// Represents actions for making a canonical chain. #[derive(Debug, Copy, Clone)] pub(crate) enum MakeCanonicalAction { + /// Cloning old blocks for canonicalization. CloneOldBlocks, + /// Finding the canonical header. FindCanonicalHeader, + /// Splitting the chain for canonicalization. SplitChain, + /// Splitting chain forks for canonicalization. SplitChainForks, + /// Merging all chains for canonicalization. MergeAllChains, + /// Updating the canonical index during canonicalization. UpdateCanonicalIndex, + /// Committing the canonical chain to the database. CommitCanonicalChainToDatabase, + /// Reverting the canonical chain from the database. RevertCanonicalChainFromDatabase, + /// Inserting an old canonical chain. InsertOldCanonicalChain, } From 30efaf4a7244228d787465a8d5f906b71e154445 Mon Sep 17 00:00:00 2001 From: Matthias Seitz Date: Fri, 15 Dec 2023 14:49:29 +0100 Subject: [PATCH 150/277] chore: bump proptest + arbitrary (#5779) --- Cargo.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 00b8726f5639..c88763520ff7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -213,11 +213,11 @@ confy = "0.5" toml = "0.8" # misc-testing -arbitrary = "1.1" +arbitrary = "1.3" assert_matches = "1.5.0" tempfile = "3.8" criterion = "0.5" pprof = "0.13" -proptest = "1.0" +proptest = "1.4" proptest-derive = "0.4" serial_test = "2" From cc4bd7c30670cae776e46361a8e0544cb866dcab Mon Sep 17 00:00:00 2001 From: Dan Cline <6798349+Rjected@users.noreply.github.com> Date: Fri, 15 Dec 2023 16:01:12 +0200 Subject: [PATCH 151/277] fix: use the same ProviderFactory in reth node (#5778) --- bin/reth/src/node/mod.rs | 32 +++++++++---------- .../consensus/beacon/src/engine/test_utils.rs | 6 ++-- crates/prune/src/pruner.rs | 11 ++++--- 3 files changed, 24 insertions(+), 25 deletions(-) diff --git a/bin/reth/src/node/mod.rs b/bin/reth/src/node/mod.rs index b7b4c520c128..21f0de45b2fa 100644 --- a/bin/reth/src/node/mod.rs +++ b/bin/reth/src/node/mod.rs @@ -304,7 +304,8 @@ impl NodeCommand { debug!(target: "reth::cli", "configured blockchain tree"); // fetch the head block from the database - let head = self.lookup_head(Arc::clone(&db)).wrap_err("the head block is missing")?; + let head = + self.lookup_head(provider_factory.clone()).wrap_err("the head block is missing")?; // setup the blockchain provider let blockchain_db = @@ -346,7 +347,7 @@ impl NodeCommand { let default_peers_path = data_dir.known_peers_path(); let network_config = self.load_network_config( &config, - Arc::clone(&db), + provider_factory.clone(), ctx.task_executor.clone(), head, secret_key, @@ -389,7 +390,7 @@ impl NodeCommand { let max_block = if let Some(block) = self.debug.max_block { Some(block) } else if let Some(tip) = self.debug.tip { - Some(self.lookup_or_fetch_tip(&db, &network_client, tip).await?) + Some(self.lookup_or_fetch_tip(provider_factory.clone(), &network_client, tip).await?) } else { None }; @@ -425,7 +426,7 @@ impl NodeCommand { &config.stages, client.clone(), Arc::clone(&consensus), - provider_factory, + provider_factory.clone(), &ctx.task_executor, sync_metrics_tx, prune_config.clone(), @@ -445,7 +446,7 @@ impl NodeCommand { &config.stages, network_client.clone(), Arc::clone(&consensus), - provider_factory, + provider_factory.clone(), &ctx.task_executor, sync_metrics_tx, prune_config.clone(), @@ -476,7 +477,7 @@ impl NodeCommand { let pruner_events = if let Some(prune_config) = prune_config { let mut pruner = self.build_pruner( &prune_config, - db.clone(), + provider_factory, tree_config, snapshotter.highest_snapshot_receiver(), ); @@ -747,8 +748,7 @@ impl NodeCommand { /// Fetches the head block from the database. /// /// If the database is empty, returns the genesis block. - fn lookup_head(&self, db: DB) -> RethResult { - let factory = ProviderFactory::new(db, self.chain.clone()); + fn lookup_head(&self, factory: ProviderFactory) -> RethResult { let provider = factory.provider()?; let head = provider.get_stage_checkpoint(StageId::Finish)?.unwrap_or_default().block_number; @@ -780,7 +780,7 @@ impl NodeCommand { /// NOTE: The download is attempted with infinite retries. async fn lookup_or_fetch_tip( &self, - db: DB, + provider_factory: ProviderFactory, client: Client, tip: B256, ) -> RethResult @@ -788,7 +788,7 @@ impl NodeCommand { DB: Database, Client: HeadersClient, { - Ok(self.fetch_tip(db, client, BlockHashOrNumber::Hash(tip)).await?.number) + Ok(self.fetch_tip(provider_factory, client, BlockHashOrNumber::Hash(tip)).await?.number) } /// Attempt to look up the block with the given number and return the header. @@ -796,7 +796,7 @@ impl NodeCommand { /// NOTE: The download is attempted with infinite retries. async fn fetch_tip( &self, - db: DB, + factory: ProviderFactory, client: Client, tip: BlockHashOrNumber, ) -> RethResult @@ -804,7 +804,6 @@ impl NodeCommand { DB: Database, Client: HeadersClient, { - let factory = ProviderFactory::new(db, self.chain.clone()); let provider = factory.provider()?; let header = provider.header_by_hash_or_number(tip)?; @@ -832,7 +831,7 @@ impl NodeCommand { fn load_network_config( &self, config: &Config, - db: DB, + provider_factory: ProviderFactory, executor: TaskExecutor, head: Head, secret_key: SecretKey, @@ -862,7 +861,7 @@ impl NodeCommand { .sequencer_endpoint(self.rollup.sequencer_http.clone()) .disable_tx_gossip(self.rollup.disable_txpool_gossip); - cfg_builder.build(ProviderFactory::new(db, self.chain.clone())) + cfg_builder.build(provider_factory) } #[allow(clippy::too_many_arguments)] @@ -980,7 +979,7 @@ impl NodeCommand { fn build_pruner( &self, config: &PruneConfig, - db: DB, + provider_factory: ProviderFactory, tree_config: BlockchainTreeConfig, highest_snapshots_rx: HighestSnapshotsTracker, ) -> Pruner { @@ -1014,8 +1013,7 @@ impl NodeCommand { ); Pruner::new( - db, - self.chain.clone(), + provider_factory, segments.into_vec(), config.block_interval, self.chain.prune_delete_limit, diff --git a/crates/consensus/beacon/src/engine/test_utils.rs b/crates/consensus/beacon/src/engine/test_utils.rs index ffe3874d3a4e..8d2be2a1c093 100644 --- a/crates/consensus/beacon/src/engine/test_utils.rs +++ b/crates/consensus/beacon/src/engine/test_utils.rs @@ -514,11 +514,11 @@ where BlockchainTree::new(externals, config, None).expect("failed to create tree"), ); let latest = self.base_config.chain_spec.genesis_header().seal_slow(); - let blockchain_provider = BlockchainProvider::with_latest(provider_factory, tree, latest); + let blockchain_provider = + BlockchainProvider::with_latest(provider_factory.clone(), tree, latest); let pruner = Pruner::new( - db.clone(), - self.base_config.chain_spec.clone(), + provider_factory, vec![], 5, self.base_config.chain_spec.prune_delete_limit, diff --git a/crates/prune/src/pruner.rs b/crates/prune/src/pruner.rs index a74825fd4b90..69e11d7ab347 100644 --- a/crates/prune/src/pruner.rs +++ b/crates/prune/src/pruner.rs @@ -6,7 +6,7 @@ use crate::{ Metrics, PrunerError, PrunerEvent, }; use reth_db::database::Database; -use reth_primitives::{BlockNumber, ChainSpec, PruneMode, PruneProgress, PruneSegment}; +use reth_primitives::{BlockNumber, PruneMode, PruneProgress, PruneSegment}; use reth_provider::{ProviderFactory, PruneCheckpointReader}; use reth_snapshot::HighestSnapshotsTracker; use reth_tokio_util::EventListeners; @@ -46,8 +46,7 @@ pub struct Pruner { impl Pruner { /// Creates a new [Pruner]. pub fn new( - db: DB, - chain_spec: Arc, + provider_factory: ProviderFactory, segments: Vec>>, min_block_interval: usize, delete_limit: usize, @@ -55,7 +54,7 @@ impl Pruner { highest_snapshots_tracker: HighestSnapshotsTracker, ) -> Self { Self { - provider_factory: ProviderFactory::new(db, chain_spec), + provider_factory, segments, min_block_interval, previous_tip_block_number: None, @@ -267,12 +266,14 @@ mod tests { use crate::Pruner; use reth_db::test_utils::create_test_rw_db; use reth_primitives::MAINNET; + use reth_provider::ProviderFactory; use tokio::sync::watch; #[test] fn is_pruning_needed() { let db = create_test_rw_db(); - let mut pruner = Pruner::new(db, MAINNET.clone(), vec![], 5, 0, 5, watch::channel(None).1); + let provider_factory = ProviderFactory::new(db, MAINNET.clone()); + let mut pruner = Pruner::new(provider_factory, vec![], 5, 0, 5, watch::channel(None).1); // No last pruned block number was set before let first_block_number = 1; From bf37c8a076847d97a0ab76c9c347195661ee97dc Mon Sep 17 00:00:00 2001 From: Dan Cline <6798349+Rjected@users.noreply.github.com> Date: Fri, 15 Dec 2023 16:43:45 +0200 Subject: [PATCH 152/277] feat: add PageOps metrics to libmdbx-rs (#5786) --- .gitignore | 4 +- crates/storage/libmdbx-rs/src/environment.rs | 48 ++++++++++++++++++++ 2 files changed, 51 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 2a7f2c58955d..5b846518e387 100644 --- a/.gitignore +++ b/.gitignore @@ -9,7 +9,6 @@ target/ # MSVC Windows builds of rustc generate these, which store debugging information *.pdb - # Generated by Intellij-based IDEs. .idea @@ -43,5 +42,8 @@ lcov.info # Generated by ./etc/generate-jwt.sh jwttoken/ +# Cache directory for CCLS, if using it with MDBX sources +.ccls-cache/ + # Generated by CMake due to MDBX sources crates/storage/libmdbx-rs/mdbx-sys/libmdbx/cmake-build-debug \ No newline at end of file diff --git a/crates/storage/libmdbx-rs/src/environment.rs b/crates/storage/libmdbx-rs/src/environment.rs index c1cf3b68deee..911a6a3c2e46 100644 --- a/crates/storage/libmdbx-rs/src/environment.rs +++ b/crates/storage/libmdbx-rs/src/environment.rs @@ -411,6 +411,25 @@ impl Info { pub fn num_readers(&self) -> usize { self.0.mi_numreaders as usize } + + /// Return the internal page ops metrics + #[inline] + pub fn page_ops(&self) -> PageOps { + PageOps { + newly: self.0.mi_pgop_stat.newly, + cow: self.0.mi_pgop_stat.cow, + clone: self.0.mi_pgop_stat.clone, + split: self.0.mi_pgop_stat.split, + merge: self.0.mi_pgop_stat.merge, + spill: self.0.mi_pgop_stat.spill, + unspill: self.0.mi_pgop_stat.unspill, + wops: self.0.mi_pgop_stat.wops, + prefault: self.0.mi_pgop_stat.prefault, + mincore: self.0.mi_pgop_stat.mincore, + msync: self.0.mi_pgop_stat.msync, + fsync: self.0.mi_pgop_stat.fsync, + } + } } impl fmt::Debug for Environment { @@ -429,6 +448,35 @@ pub enum PageSize { Set(usize), } +/// Statistics of page operations overall of all (running, completed and aborted) transactions +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct PageOps { + /// Quantity of a new pages added + pub newly: u64, + /// Quantity of pages copied for update + pub cow: u64, + /// Quantity of parent's dirty pages clones for nested transactions + pub clone: u64, + /// Page splits + pub split: u64, + /// Page merges + pub merge: u64, + /// Quantity of spilled dirty pages + pub spill: u64, + /// Quantity of unspilled/reloaded pages + pub unspill: u64, + /// Number of explicit write operations (not a pages) to a disk + pub wops: u64, + /// Number of explicit msync/flush-to-disk operations + pub msync: u64, + /// Number of explicit fsync/flush-to-disk operations + pub fsync: u64, + /// Number of prefault write operations + pub prefault: u64, + /// Number of mincore() calls + pub mincore: u64, +} + #[derive(Clone, Debug, PartialEq, Eq)] pub struct Geometry { pub size: Option, From 6af8e0f7eaba048a1c187e2892994d8f5b07a916 Mon Sep 17 00:00:00 2001 From: Bjerg Date: Fri, 15 Dec 2023 16:49:01 +0200 Subject: [PATCH 153/277] chore: use new aquamarine macro (#5785) --- Cargo.lock | 6 +- Cargo.toml | 2 +- crates/blockchain-tree/src/blockchain_tree.rs | 51 ++++------- crates/net/network/src/fetch/client.rs | 37 +------- crates/net/network/src/manager.rs | 32 ++----- crates/net/network/src/swarm.rs | 43 +++------- crates/stages/src/pipeline/mod.rs | 48 ++--------- crates/transaction-pool/src/pool/txpool.rs | 85 ++++++------------- docs/mermaid/fetch-client.mmd | 31 +++++++ docs/mermaid/network-manager.mmd | 20 +++++ docs/mermaid/pipeline.mmd | 31 +++++++ docs/mermaid/swarm.mmd | 15 ++++ docs/mermaid/tree.mmd | 21 +++++ docs/mermaid/txpool.mmd | 32 +++++++ 14 files changed, 229 insertions(+), 225 deletions(-) create mode 100644 docs/mermaid/fetch-client.mmd create mode 100644 docs/mermaid/network-manager.mmd create mode 100644 docs/mermaid/pipeline.mmd create mode 100644 docs/mermaid/swarm.mmd create mode 100644 docs/mermaid/tree.mmd create mode 100644 docs/mermaid/txpool.mmd diff --git a/Cargo.lock b/Cargo.lock index 9d897c45332d..58b4c51730cd 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -332,16 +332,16 @@ checksum = "a4668cab20f66d8d020e1fbc0ebe47217433c1b6c8f2040faf858554e394ace6" [[package]] name = "aquamarine" -version = "0.3.2" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df752953c49ce90719c7bf1fc587bc8227aed04732ea0c0f85e5397d7fdbd1a1" +checksum = "074b80d14d0240b6ce94d68f059a2d26a5d77280ae142662365a21ef6e2594ef" dependencies = [ "include_dir", "itertools 0.10.5", "proc-macro-error", "proc-macro2", "quote", - "syn 1.0.109", + "syn 2.0.39", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index c88763520ff7..cc048a6df522 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -157,7 +157,7 @@ boa_engine = "0.17" boa_gc = "0.17" # misc -aquamarine = "0.3" +aquamarine = "0.4" bytes = "1.5" bitflags = "2.4" clap = "4" diff --git a/crates/blockchain-tree/src/blockchain_tree.rs b/crates/blockchain-tree/src/blockchain_tree.rs index 3b956fe2b1f6..20d878399f5b 100644 --- a/crates/blockchain-tree/src/blockchain_tree.rs +++ b/crates/blockchain-tree/src/blockchain_tree.rs @@ -37,40 +37,25 @@ use tracing::{debug, error, info, instrument, trace, warn}; #[cfg_attr(doc, aquamarine::aquamarine)] /// A Tree of chains. /// -/// Mermaid flowchart represents all the states a block can have inside the blockchaintree. -/// Green blocks belong to canonical chain and are saved inside then database, they are our main -/// chain. Pending blocks and sidechains are found in memory inside [`BlockchainTree`]. -/// Both pending and sidechains have same mechanisms only difference is when they get committed to -/// the database. For pending it is an append operation but for sidechains they need to move current -/// canonical blocks to BlockchainTree and commit the sidechain to the database to become canonical -/// chain (reorg). ```mermaid -/// flowchart BT -/// subgraph canonical chain -/// CanonState:::state -/// block0canon:::canon -->block1canon:::canon -->block2canon:::canon -->block3canon:::canon --> -/// block4canon:::canon --> block5canon:::canon end -/// block5canon --> block6pending1:::pending -/// block5canon --> block6pending2:::pending -/// subgraph sidechain2 -/// S2State:::state -/// block3canon --> block4s2:::sidechain --> block5s2:::sidechain -/// end -/// subgraph sidechain1 -/// S1State:::state -/// block2canon --> block3s1:::sidechain --> block4s1:::sidechain --> block5s1:::sidechain --> -/// block6s1:::sidechain end -/// classDef state fill:#1882C4 -/// classDef canon fill:#8AC926 -/// classDef pending fill:#FFCA3A -/// classDef sidechain fill:#FF595E -/// ``` -/// +/// The flowchart represents all the states a block can have inside the tree. /// -/// main functions: -/// * [BlockchainTree::insert_block]: Connect block to chain, execute it and if valid insert block -/// into the tree. -/// * [BlockchainTree::finalize_block]: Remove chains that are branch off the now finalized block. -/// * [BlockchainTree::make_canonical]: Check if we have the hash of block that is the current +/// - Green blocks belong to the canonical chain and are saved inside the database. +/// - Pending blocks and sidechains are found in-memory inside [`BlockchainTree`]. +/// +/// Both pending chains and sidechains have the same mechanisms, the only difference is when they +/// get committed to the database. +/// +/// For pending, it is an append operation, but for sidechains they need to move the current +/// canonical blocks to the tree (by removing them from the database), and commit the sidechain +/// blocks to the database to become the canonical chain (reorg). +/// +/// include_mmd!("docs/mermaid/tree.mmd") +/// +/// # Main functions +/// * [BlockchainTree::insert_block]: Connect a block to a chain, execute it, and if valid, insert +/// the block into the tree. +/// * [BlockchainTree::finalize_block]: Remove chains that branch off of the now finalized block. +/// * [BlockchainTree::make_canonical]: Check if we have the hash of a block that is the current /// canonical head and commit it to db. #[derive(Debug)] pub struct BlockchainTree { diff --git a/crates/net/network/src/fetch/client.rs b/crates/net/network/src/fetch/client.rs index a9febae7ec9d..eab4745065ea 100644 --- a/crates/net/network/src/fetch/client.rs +++ b/crates/net/network/src/fetch/client.rs @@ -18,44 +18,13 @@ use std::sync::{ }; use tokio::sync::{mpsc::UnboundedSender, oneshot}; +#[cfg_attr(doc, aquamarine::aquamarine)] /// Front-end API for fetching data from the network. /// /// Following diagram illustrates how a request, See [`HeadersClient::get_headers`] and /// [`BodiesClient::get_block_bodies`] is handled internally. -#[cfg_attr(doc, aquamarine::aquamarine)] -/// ```mermaid -/// sequenceDiagram -// participant Client as FetchClient -// participant Fetcher as StateFetcher -// participant State as NetworkState -// participant Session as Active Peer Session -// participant Peers as PeerManager -// loop Send Request, retry if retriable and remaining retries -// Client->>Fetcher: DownloadRequest{GetHeaders, GetBodies} -// Note over Client,Fetcher: Request and oneshot Sender sent via `request_tx` channel -// loop Process buffered requests -// State->>Fetcher: poll action -// Fetcher->>Fetcher: Select Available Peer -// Note over Fetcher: Peer is available if it's currently idle, no inflight requests -// Fetcher->>State: FetchAction::BlockDownloadRequest -// State->>Session: Delegate Request -// Note over State,Session: Request and oneshot Sender sent via `to_session_tx` channel -// end -// Session->>Session: Send Request to remote -// Session->>Session: Enforce Request timeout -// Session-->>State: Send Response Result via channel -// State->>Fetcher: Delegate Response -// Fetcher-->>Client: Send Response via channel -// opt Bad Response -// Client->>Peers: Penalize Peer -// end -// Peers->>Peers: Apply Reputation Change -// opt reputation dropped below threshold -// Peers->>State: Disconnect Session -// State->>Session: Delegate Disconnect -// end -// end -/// ``` +/// +/// include_mmd!("docs/mermaid/fetch-client.mmd") #[derive(Debug, Clone)] pub struct FetchClient { /// Sender half of the request channel. diff --git a/crates/net/network/src/manager.rs b/crates/net/network/src/manager.rs index 4441c2e51336..88cb8bdcbcdf 100644 --- a/crates/net/network/src/manager.rs +++ b/crates/net/network/src/manager.rs @@ -60,34 +60,14 @@ use tokio::sync::mpsc::{self, error::TrySendError}; use tokio_stream::wrappers::UnboundedReceiverStream; use tracing::{debug, error, trace, warn}; +#[cfg_attr(doc, aquamarine::aquamarine)] /// Manages the _entire_ state of the network. /// /// This is an endless [`Future`] that consistently drives the state of the entire network forward. /// /// The [`NetworkManager`] is the container type for all parts involved with advancing the network. -#[cfg_attr(doc, aquamarine::aquamarine)] -/// ```mermaid -/// graph TB -/// handle(NetworkHandle) -/// events(NetworkEvents) -/// transactions(Transactions Task) -/// ethrequest(ETH Request Task) -/// discovery(Discovery Task) -/// subgraph NetworkManager -/// direction LR -/// subgraph Swarm -/// direction TB -/// B1[(Session Manager)] -/// B2[(Connection Lister)] -/// B3[(Network State)] -/// end -/// end -/// handle <--> |request response channel| NetworkManager -/// NetworkManager --> |Network events| events -/// transactions <--> |transactions| NetworkManager -/// ethrequest <--> |ETH request handing| NetworkManager -/// discovery --> |Discovered peers| NetworkManager -/// ``` +/// +/// include_mmd!("docs/mermaid/network-manager.mmd") #[derive(Debug)] #[must_use = "The NetworkManager does nothing unless polled"] pub struct NetworkManager { @@ -543,7 +523,7 @@ where if self.handle.mode().is_stake() { // See [EIP-3675](https://eips.ethereum.org/EIPS/eip-3675#devp2p) warn!(target: "net", "Peer performed block propagation, but it is not supported in proof of stake (EIP-3675)"); - return + return; } let msg = NewBlockMessage { hash, block: Arc::new(block) }; self.swarm.state_mut().announce_new_block(msg); @@ -638,7 +618,7 @@ where // This is only possible if the channel was deliberately closed since we always // have an instance of `NetworkHandle` error!("Network message channel closed."); - return Poll::Ready(()) + return Poll::Ready(()); } Poll::Ready(Some(msg)) => this.on_handle_message(msg), }; @@ -909,7 +889,7 @@ where if budget == 0 { // make sure we're woken up again cx.waker().wake_by_ref(); - break + break; } } diff --git a/crates/net/network/src/swarm.rs b/crates/net/network/src/swarm.rs index ce647fe181e8..690a37b4b650 100644 --- a/crates/net/network/src/swarm.rs +++ b/crates/net/network/src/swarm.rs @@ -23,6 +23,7 @@ use std::{ }; use tracing::trace; +#[cfg_attr(doc, aquamarine::aquamarine)] /// Contains the connectivity related state of the network. /// /// A swarm emits [`SwarmEvent`]s when polled. @@ -44,24 +45,8 @@ use tracing::trace; /// request channel for the created session and sends requests it receives from the /// [`StateFetcher`], which receives request objects from the client interfaces responsible for /// downloading headers and bodies. -#[cfg_attr(doc, aquamarine::aquamarine)] -/// ```mermaid -/// graph TB -/// connections(TCP Listener) -/// Discovery[(Discovery)] -/// fetchRequest(Client Interfaces) -/// Sessions[(SessionManager)] -/// SessionTask[(Peer Session)] -/// State[(State)] -/// StateFetch[(State Fetcher)] -/// connections --> |incoming| Sessions -/// State --> |initiate outgoing| Sessions -/// Discovery --> |update peers| State -/// Sessions --> |spawns| SessionTask -/// SessionTask <--> |handle state requests| State -/// fetchRequest --> |request Headers, Bodies| StateFetch -/// State --> |poll pending requests| StateFetch -/// ``` +/// +/// include_mmd!("docs/mermaid/swarm.mmd") #[derive(Debug)] #[must_use = "Swarm does nothing unless polled"] pub(crate) struct Swarm { @@ -207,7 +192,7 @@ where ListenerEvent::Incoming { stream, remote_addr } => { // Reject incoming connection if node is shutting down. if self.is_shutting_down() { - return None + return None; } // ensure we can handle an incoming connection from this address if let Err(err) = @@ -225,13 +210,13 @@ where ); } } - return None + return None; } match self.sessions.on_incoming(stream, remote_addr) { Ok(session_id) => { trace!(target: "net", ?remote_addr, "Incoming connection"); - return Some(SwarmEvent::IncomingTcpConnection { session_id, remote_addr }) + return Some(SwarmEvent::IncomingTcpConnection { session_id, remote_addr }); } Err(err) => { trace!(target: "net", ?err, "Incoming connection rejected, capacity already reached."); @@ -250,7 +235,7 @@ where match event { StateAction::Connect { remote_addr, peer_id } => { self.dial_outbound(remote_addr, peer_id); - return Some(SwarmEvent::OutgoingTcpConnection { remote_addr, peer_id }) + return Some(SwarmEvent::OutgoingTcpConnection { remote_addr, peer_id }); } StateAction::Disconnect { peer_id, reason } => { self.sessions.disconnect(peer_id, reason); @@ -268,7 +253,7 @@ where StateAction::DiscoveredNode { peer_id, socket_addr, fork_id } => { // Don't try to connect to peer if node is shutting down if self.is_shutting_down() { - return None + return None; } // Insert peer only if no fork id or a valid fork id if fork_id.map_or_else(|| true, |f| self.sessions.is_valid_fork_id(f)) { @@ -317,7 +302,7 @@ where loop { while let Poll::Ready(action) = this.state.poll(cx) { if let Some(event) = this.on_state_action(action) { - return Poll::Ready(Some(event)) + return Poll::Ready(Some(event)); } } @@ -326,9 +311,9 @@ where Poll::Pending => {} Poll::Ready(event) => { if let Some(event) = this.on_session_event(event) { - return Poll::Ready(Some(event)) + return Poll::Ready(Some(event)); } - continue + continue; } } @@ -337,13 +322,13 @@ where Poll::Pending => {} Poll::Ready(event) => { if let Some(event) = this.on_connection(event) { - return Poll::Ready(Some(event)) + return Poll::Ready(Some(event)); } - continue + continue; } } - return Poll::Pending + return Poll::Pending; } } } diff --git a/crates/stages/src/pipeline/mod.rs b/crates/stages/src/pipeline/mod.rs index 344510b23332..2d416db5c9c7 100644 --- a/crates/stages/src/pipeline/mod.rs +++ b/crates/stages/src/pipeline/mod.rs @@ -49,39 +49,7 @@ pub type PipelineWithResult = (Pipeline, Result RunLoop --> NextStage -/// NextStage --> |No stages left| LoopDone -/// NextStage --> |Next stage| Execute -/// Execute --> |Not done| Execute -/// Execute --> |Unwind requested| StartUnwind -/// Execute --> |Done| NextStage -/// Execute --> |Error| Error -/// StartUnwind --> NextStageToUnwind -/// NextStageToUnwind --> |Next stage| UnwindStage -/// NextStageToUnwind --> |No stages left| RunLoop -/// UnwindStage --> |Error| Error -/// UnwindStage --> |Unwound| NextStageToUnwind -/// LoopDone --> |Target block reached| Done -/// LoopDone --> |Target block not reached| RunLoop -/// ``` +/// include_mmd!("docs/mermaid/pipeline.mmd") /// /// # Unwinding /// @@ -193,7 +161,7 @@ where max_block = ?self.max_block, "Terminating pipeline." ); - return Ok(()) + return Ok(()); } } } @@ -229,7 +197,7 @@ where ControlFlow::Continue { block_number } => self.progress.update(block_number), ControlFlow::Unwind { target, bad_block } => { self.unwind(target, Some(bad_block.number))?; - return Ok(ControlFlow::Unwind { target, bad_block }) + return Ok(ControlFlow::Unwind { target, bad_block }); } } @@ -272,7 +240,7 @@ where "Unwind point too far for stage" ); self.listeners.notify(PipelineEvent::Skipped { stage_id }); - continue + continue; } debug!( @@ -317,7 +285,7 @@ where } Err(err) => { self.listeners.notify(PipelineEvent::Error { stage_id }); - return Err(PipelineError::Stage(StageError::Fatal(Box::new(err)))) + return Err(PipelineError::Stage(StageError::Fatal(Box::new(err)))); } } } @@ -357,7 +325,7 @@ where // We reached the maximum block, so we skip the stage return Ok(ControlFlow::NoProgress { block_number: prev_checkpoint.map(|progress| progress.block_number), - }) + }); } let exec_input = ExecInput { target, checkpoint: prev_checkpoint }; @@ -412,7 +380,7 @@ where ControlFlow::Continue { block_number } } else { ControlFlow::NoProgress { block_number: Some(block_number) } - }) + }); } } Err(err) => { @@ -421,7 +389,7 @@ where if let Some(ctrl) = on_stage_error(&self.provider_factory, stage_id, prev_checkpoint, err)? { - return Ok(ctrl) + return Ok(ctrl); } } } diff --git a/crates/transaction-pool/src/pool/txpool.rs b/crates/transaction-pool/src/pool/txpool.rs index bcebb4d69507..617cdaa94c93 100644 --- a/crates/transaction-pool/src/pool/txpool.rs +++ b/crates/transaction-pool/src/pool/txpool.rs @@ -32,45 +32,12 @@ use std::{ sync::Arc, }; +#[cfg_attr(doc, aquamarine::aquamarine)] /// A pool that manages transactions. /// /// This pool maintains the state of all transactions and stores them accordingly. - -#[cfg_attr(doc, aquamarine::aquamarine)] -/// ```mermaid -/// graph TB -/// subgraph TxPool -/// direction TB -/// pool[(All Transactions)] -/// subgraph Subpools -/// direction TB -/// B3[(Queued)] -/// B1[(Pending)] -/// B2[(Basefee)] -/// B4[(Blob)] -/// end -/// end -/// discard([discard]) -/// production([Block Production]) -/// new([New Block]) -/// A[Incoming Tx] --> B[Validation] -->|insert| pool -/// pool --> |if ready + blobfee too low| B4 -/// pool --> |if ready| B1 -/// pool --> |if ready + basfee too low| B2 -/// pool --> |nonce gap or lack of funds| B3 -/// pool --> |update| pool -/// B1 --> |best| production -/// B2 --> |worst| discard -/// B3 --> |worst| discard -/// B4 --> |worst| discard -/// B1 --> |increased blob fee| B4 -/// B4 --> |decreased blob fee| B1 -/// B1 --> |increased base fee| B2 -/// B2 --> |decreased base fee| B1 -/// B3 --> |promote| B1 -/// B3 --> |promote| B2 -/// new --> |apply state changes| pool -/// ``` +/// +/// include_mmd!("docs/mermaid/txpool.mmd") pub struct TxPool { /// Contains the currently known information about the senders. sender_info: FnvHashMap, @@ -509,7 +476,7 @@ impl TxPool { on_chain_nonce: u64, ) -> PoolResult> { if self.contains(tx.hash()) { - return Err(PoolError::new(*tx.hash(), PoolErrorKind::AlreadyImported)) + return Err(PoolError::new(*tx.hash(), PoolErrorKind::AlreadyImported)); } // Update sender info with balance and nonce @@ -737,7 +704,7 @@ impl TxPool { } id = descendant; } else { - return + return; } } } @@ -964,7 +931,7 @@ impl AllTransactions { let count = entry.get_mut(); if *count == 1 { entry.remove(); - return + return; } *count -= 1; } @@ -1024,7 +991,7 @@ impl AllTransactions { ($iter:ident) => { 'this: while let Some((peek, _)) = iter.peek() { if peek.sender != id.sender { - break 'this + break 'this; } iter.next(); } @@ -1043,7 +1010,7 @@ impl AllTransactions { current: tx.subpool, destination: Destination::Discard, }); - continue 'transactions + continue 'transactions; } let ancestor = TransactionId::ancestor(id.nonce, info.state_nonce, id.sender); @@ -1066,7 +1033,7 @@ impl AllTransactions { // If there's a nonce gap, we can shortcircuit, because there's nothing to update yet. if tx.state.has_nonce_gap() { next_sender!(iter); - continue 'transactions + continue 'transactions; } // Since this is the first transaction of the sender, it has no parked ancestors @@ -1086,13 +1053,13 @@ impl AllTransactions { while let Some((peek, ref mut tx)) = iter.peek_mut() { if peek.sender != id.sender { // Found the next sender - continue 'transactions + continue 'transactions; } // can short circuit if tx.state.has_nonce_gap() { next_sender!(iter); - continue 'transactions + continue 'transactions; } // update cumulative cost @@ -1254,7 +1221,7 @@ impl AllTransactions { fn contains_conflicting_transaction(&self, tx: &ValidPoolTransaction) -> bool { let mut iter = self.txs_iter(tx.transaction_id.sender); if let Some((_, existing)) = iter.next() { - return tx.tx_type_conflicts_with(&existing.transaction) + return tx.tx_type_conflicts_with(&existing.transaction); } // no existing transaction for this sender false @@ -1278,7 +1245,7 @@ impl AllTransactions { if current_txs >= self.max_account_slots { return Err(InsertErr::ExceededSenderTransactionsCapacity { transaction: Arc::new(transaction), - }) + }); } } if transaction.gas_limit() > self.block_gas_limit { @@ -1286,12 +1253,12 @@ impl AllTransactions { block_gas_limit: self.block_gas_limit, tx_gas_limit: transaction.gas_limit(), transaction: Arc::new(transaction), - }) + }); } if self.contains_conflicting_transaction(&transaction) { // blob vs non blob transactions are mutually exclusive for the same sender - return Err(InsertErr::TxTypeConflict { transaction: Arc::new(transaction) }) + return Err(InsertErr::TxTypeConflict { transaction: Arc::new(transaction) }); } Ok(transaction) @@ -1311,12 +1278,12 @@ impl AllTransactions { if let Some(ancestor) = ancestor { let Some(ancestor_tx) = self.txs.get(&ancestor) else { // ancestor tx is missing, so we can't insert the new blob - return Err(InsertErr::BlobTxHasNonceGap { transaction: Arc::new(new_blob_tx) }) + return Err(InsertErr::BlobTxHasNonceGap { transaction: Arc::new(new_blob_tx) }); }; if ancestor_tx.state.has_nonce_gap() { // the ancestor transaction already has a nonce gap, so we can't insert the new // blob - return Err(InsertErr::BlobTxHasNonceGap { transaction: Arc::new(new_blob_tx) }) + return Err(InsertErr::BlobTxHasNonceGap { transaction: Arc::new(new_blob_tx) }); } // the max cost executing this transaction requires @@ -1325,7 +1292,7 @@ impl AllTransactions { // check if the new blob would go into overdraft if cumulative_cost > on_chain_balance { // the transaction would go into overdraft - return Err(InsertErr::Overdraft { transaction: Arc::new(new_blob_tx) }) + return Err(InsertErr::Overdraft { transaction: Arc::new(new_blob_tx) }); } // ensure that a replacement would not shift already propagated blob transactions into @@ -1342,14 +1309,14 @@ impl AllTransactions { cumulative_cost += tx.transaction.cost(); if tx.transaction.is_eip4844() && cumulative_cost > on_chain_balance { // the transaction would shift - return Err(InsertErr::Overdraft { transaction: Arc::new(new_blob_tx) }) + return Err(InsertErr::Overdraft { transaction: Arc::new(new_blob_tx) }); } } } } } else if new_blob_tx.cost() > on_chain_balance { // the transaction would go into overdraft - return Err(InsertErr::Overdraft { transaction: Arc::new(new_blob_tx) }) + return Err(InsertErr::Overdraft { transaction: Arc::new(new_blob_tx) }); } Ok(new_blob_tx) @@ -1369,7 +1336,7 @@ impl AllTransactions { if maybe_replacement.max_fee_per_gas() <= existing_transaction.max_fee_per_gas() * price_bump_multiplier { - return true + return true; } let existing_max_priority_fee_per_gas = @@ -1382,7 +1349,7 @@ impl AllTransactions { existing_max_priority_fee_per_gas != 0 && replacement_max_priority_fee_per_gas != 0 { - return true + return true; } // check max blob fee per gas @@ -1395,7 +1362,7 @@ impl AllTransactions { if replacement_max_blob_fee_per_gas <= existing_max_blob_fee_per_gas * price_bump_multiplier { - return true + return true; } } @@ -1484,7 +1451,7 @@ impl AllTransactions { let fee_cap = transaction.max_fee_per_gas(); if fee_cap < self.minimal_protocol_basefee as u128 { - return Err(InsertErr::FeeCapBelowMinimumProtocolFeeCap { transaction, fee_cap }) + return Err(InsertErr::FeeCapBelowMinimumProtocolFeeCap { transaction, fee_cap }); } if fee_cap >= self.pending_fees.base_fee as u128 { state.insert(TxState::ENOUGH_FEE_CAP_BLOCK); @@ -1523,7 +1490,7 @@ impl AllTransactions { return Err(InsertErr::Underpriced { transaction: pool_tx.transaction, existing: *entry.get().transaction.hash(), - }) + }); } let new_hash = *pool_tx.transaction.hash(); let new_transaction = pool_tx.transaction.clone(); @@ -1566,7 +1533,7 @@ impl AllTransactions { // If there's a nonce gap, we can shortcircuit if next_nonce != id.nonce { - break + break; } // close the nonce gap diff --git a/docs/mermaid/fetch-client.mmd b/docs/mermaid/fetch-client.mmd new file mode 100644 index 000000000000..28c89aaf9f7f --- /dev/null +++ b/docs/mermaid/fetch-client.mmd @@ -0,0 +1,31 @@ +sequenceDiagram + participant Client as FetchClient + participant Fetcher as StateFetcher + participant State as NetworkState + participant Session as Active Peer Session + participant Peers as PeerManager + loop Send Request, retry if retriable and remaining retries + Client->>Fetcher: DownloadRequest{GetHeaders, GetBodies} + Note over Client,Fetcher: Request and oneshot Sender sent via `request_tx` channel + loop Process buffered requests + State->>Fetcher: poll action + Fetcher->>Fetcher: Select Available Peer + Note over Fetcher: Peer is available if it's currently idle, no inflight requests + Fetcher->>State: FetchAction::BlockDownloadRequest + State->>Session: Delegate Request + Note over State,Session: Request and oneshot Sender sent via `to_session_tx` channel + end + Session->>Session: Send Request to remote + Session->>Session: Enforce Request timeout + Session-->>State: Send Response Result via channel + State->>Fetcher: Delegate Response + Fetcher-->>Client: Send Response via channel + opt Bad Response + Client->>Peers: Penalize Peer + end + Peers->>Peers: Apply Reputation Change + opt reputation dropped below threshold + Peers->>State: Disconnect Session + State->>Session: Delegate Disconnect + end + end diff --git a/docs/mermaid/network-manager.mmd b/docs/mermaid/network-manager.mmd new file mode 100644 index 000000000000..e34dbb177772 --- /dev/null +++ b/docs/mermaid/network-manager.mmd @@ -0,0 +1,20 @@ +graph TB + handle(NetworkHandle) + events(NetworkEvents) + transactions(Transactions Task) + ethrequest(ETH Request Task) + discovery(Discovery Task) + subgraph NetworkManager + direction LR + subgraph Swarm + direction TB + B1[(Session Manager)] + B2[(Connection Lister)] + B3[(Network State)] + end + end + handle <--> |request response channel| NetworkManager + NetworkManager --> |Network events| events + transactions <--> |transactions| NetworkManager + ethrequest <--> |ETH request handing| NetworkManager + discovery --> |Discovered peers| NetworkManager diff --git a/docs/mermaid/pipeline.mmd b/docs/mermaid/pipeline.mmd new file mode 100644 index 000000000000..23ff0fcafb97 --- /dev/null +++ b/docs/mermaid/pipeline.mmd @@ -0,0 +1,31 @@ +graph TB + Start[Start] + Done[Done] + Error[Error] + subgraph Unwind + StartUnwind(Unwind in reverse order of execution) + UnwindStage(Unwind stage) + NextStageToUnwind(Next stage) + end + subgraph Single loop + RunLoop(Run loop) + NextStage(Next stage) + LoopDone(Loop done) + subgraph Stage Execution + Execute(Execute stage) + end + end + Start --> RunLoop --> NextStage + NextStage --> |No stages left| LoopDone + NextStage --> |Next stage| Execute + Execute --> |Not done| Execute + Execute --> |Unwind requested| StartUnwind + Execute --> |Done| NextStage + Execute --> |Error| Error + StartUnwind --> NextStageToUnwind + NextStageToUnwind --> |Next stage| UnwindStage + NextStageToUnwind --> |No stages left| RunLoop + UnwindStage --> |Error| Error + UnwindStage --> |Unwound| NextStageToUnwind + LoopDone --> |Target block reached| Done + LoopDone --> |Target block not reached| RunLoop diff --git a/docs/mermaid/swarm.mmd b/docs/mermaid/swarm.mmd new file mode 100644 index 000000000000..afc17454f030 --- /dev/null +++ b/docs/mermaid/swarm.mmd @@ -0,0 +1,15 @@ +graph TB + connections(TCP Listener) + Discovery[(Discovery)] + fetchRequest(Client Interfaces) + Sessions[(SessionManager)] + SessionTask[(Peer Session)] + State[(State)] + StateFetch[(State Fetcher)] + connections --> |incoming| Sessions + State --> |initiate outgoing| Sessions + Discovery --> |update peers| State + Sessions --> |spawns| SessionTask + SessionTask <--> |handle state requests| State + fetchRequest --> |request Headers, Bodies| StateFetch + State --> |poll pending requests| StateFetch diff --git a/docs/mermaid/tree.mmd b/docs/mermaid/tree.mmd new file mode 100644 index 000000000000..c9b41b857b17 --- /dev/null +++ b/docs/mermaid/tree.mmd @@ -0,0 +1,21 @@ +flowchart BT + subgraph canonical chain + CanonState:::state + block0canon:::canon -->block1canon:::canon -->block2canon:::canon -->block3canon:::canon --> + block4canon:::canon --> block5canon:::canon + end + block5canon --> block6pending1:::pending + block5canon --> block6pending2:::pending + subgraph sidechain2 + S2State:::state + block3canon --> block4s2:::sidechain --> block5s2:::sidechain + end + subgraph sidechain1 + S1State:::state + block2canon --> block3s1:::sidechain --> block4s1:::sidechain --> block5s1:::sidechain --> + block6s1:::sidechain + end + classDef state fill:#1882C4 + classDef canon fill:#8AC926 + classDef pending fill:#FFCA3A + classDef sidechain fill:#FF595E diff --git a/docs/mermaid/txpool.mmd b/docs/mermaid/txpool.mmd new file mode 100644 index 000000000000..2f0e740bb766 --- /dev/null +++ b/docs/mermaid/txpool.mmd @@ -0,0 +1,32 @@ +graph TB + subgraph TxPool + direction TB + pool[(All Transactions)] + subgraph Subpools + direction TB + B3[(Queued)] + B1[(Pending)] + B2[(Basefee)] + B4[(Blob)] + end + end + discard([discard]) + production([Block Production]) + new([New Block]) + A[Incoming Tx] --> B[Validation] -->|insert| pool + pool --> |if ready + blobfee too low| B4 + pool --> |if ready| B1 + pool --> |if ready + basfee too low| B2 + pool --> |nonce gap or lack of funds| B3 + pool --> |update| pool + B1 --> |best| production + B2 --> |worst| discard + B3 --> |worst| discard + B4 --> |worst| discard + B1 --> |increased blob fee| B4 + B4 --> |decreased blob fee| B1 + B1 --> |increased base fee| B2 + B2 --> |decreased base fee| B1 + B3 --> |promote| B1 + B3 --> |promote| B2 + new --> |apply state changes| pool From ae31d4664740e1980967b8eaa2591d5188a6c037 Mon Sep 17 00:00:00 2001 From: Thomas Coratger <60488569+tcoratger@users.noreply.github.com> Date: Fri, 15 Dec 2023 20:44:18 +0100 Subject: [PATCH 154/277] refactor(primitives): add documentation and refactor `Genesis` implementation (#5788) --- crates/primitives/src/genesis.rs | 25 ++++++++++++++++++------- 1 file changed, 18 insertions(+), 7 deletions(-) diff --git a/crates/primitives/src/genesis.rs b/crates/primitives/src/genesis.rs index e1a1ecd53a4a..41fb00c02099 100644 --- a/crates/primitives/src/genesis.rs +++ b/crates/primitives/src/genesis.rs @@ -322,14 +322,11 @@ mod ethers_compat { EthashConfig as EthersEthashConfig, GenesisAccount as EthersGenesisAccount, }; + /// Converts an `ethers_core::utils::Genesis` into a `Genesis`. impl From for Genesis { + /// This conversion function extracts and maps specific fields from the input `genesis` to + /// create a new `Genesis` instance fn from(genesis: ethers_core::utils::Genesis) -> Genesis { - let alloc = genesis - .alloc - .iter() - .map(|(addr, account)| (addr.0.into(), account.clone().into())) - .collect::>(); - Genesis { config: genesis.config.into(), nonce: genesis.nonce.as_u64(), @@ -343,12 +340,23 @@ mod ethers_compat { // TODO: if/when ethers has cancun fields they should be added here excess_blob_gas: None, blob_gas_used: None, - alloc, + alloc: genesis + .alloc + .iter() + .map(|(addr, account)| (addr.0.into(), account.clone().into())) + .collect(), } } } + /// Converts an `EthersGenesisAccount` into a `GenesisAccount`. impl From for GenesisAccount { + /// Converts fields from `EthersGenesisAccount` to construct a `GenesisAccount`. + /// + /// - Maps the `balance` from limbs. + /// - Sets the `nonce`. + /// - Handles the `code` field, converting it if present. + /// - Processes the `storage` field into key-value pairs. fn from(genesis_account: EthersGenesisAccount) -> Self { Self { balance: U256::from_limbs(genesis_account.balance.0), @@ -361,6 +369,7 @@ mod ethers_compat { } } + /// Converts an `EthersChainConfig` into a `ChainConfig`. impl From for ChainConfig { fn from(chain_config: EthersChainConfig) -> Self { let EthersChainConfig { @@ -419,12 +428,14 @@ mod ethers_compat { } } + /// Converts an `EthersEthashConfig` into an `EthashConfig`. impl From for EthashConfig { fn from(_: EthersEthashConfig) -> Self { EthashConfig {} } } + /// Converts an `EthersCliqueConfig` into a `CliqueConfig`. impl From for CliqueConfig { fn from(clique_config: EthersCliqueConfig) -> Self { let EthersCliqueConfig { period, epoch } = clique_config; From 686129cb63f727821a6747eeb01ca61e1a9fda14 Mon Sep 17 00:00:00 2001 From: lmittmann <3458786+lmittmann@users.noreply.github.com> Date: Fri, 15 Dec 2023 21:20:45 +0100 Subject: [PATCH 155/277] feat(network): added `NetworkConfigBuilder.block_import(..)` (#5783) Co-authored-by: lmittmann --- crates/net/network/src/config.rs | 13 ++++++++++++- crates/net/network/src/import.rs | 2 ++ crates/net/network/src/lib.rs | 4 ++-- crates/net/network/src/message.rs | 6 ++++++ 4 files changed, 22 insertions(+), 3 deletions(-) diff --git a/crates/net/network/src/config.rs b/crates/net/network/src/config.rs index f97f4004f949..5e229dbdbbd0 100644 --- a/crates/net/network/src/config.rs +++ b/crates/net/network/src/config.rs @@ -169,6 +169,9 @@ pub struct NetworkConfigBuilder { head: Option, /// Whether tx gossip is disabled tx_gossip_disabled: bool, + /// The block importer type + #[serde(skip)] + block_import: Option>, /// Optimism Network Config Builder #[cfg(feature = "optimism")] optimism_network_config: OptimismNetworkConfigBuilder, @@ -204,6 +207,7 @@ impl NetworkConfigBuilder { extra_protocols: Default::default(), head: None, tx_gossip_disabled: false, + block_import: None, #[cfg(feature = "optimism")] optimism_network_config: OptimismNetworkConfigBuilder::default(), } @@ -396,6 +400,12 @@ impl NetworkConfigBuilder { self } + /// Sets the block import type. + pub fn block_import(mut self, block_import: Box) -> Self { + self.block_import = Some(block_import); + self + } + /// Sets the sequencer HTTP endpoint. #[cfg(feature = "optimism")] pub fn sequencer_endpoint(mut self, endpoint: Option) -> Self { @@ -427,6 +437,7 @@ impl NetworkConfigBuilder { extra_protocols, head, tx_gossip_disabled, + block_import, #[cfg(feature = "optimism")] optimism_network_config: OptimismNetworkConfigBuilder { sequencer_endpoint }, } = self; @@ -473,7 +484,7 @@ impl NetworkConfigBuilder { peers_config: peers_config.unwrap_or_default(), sessions_config: sessions_config.unwrap_or_default(), chain_spec, - block_import: Box::::default(), + block_import: block_import.unwrap_or(Box::::default()), network_mode, executor: executor.unwrap_or_else(|| Box::::default()), status, diff --git a/crates/net/network/src/import.rs b/crates/net/network/src/import.rs index f7564f977088..d127dab8fed7 100644 --- a/crates/net/network/src/import.rs +++ b/crates/net/network/src/import.rs @@ -1,3 +1,5 @@ +//! This module provides an abstraction over block import in the form of the `BlockImport` trait. + use crate::message::NewBlockMessage; use reth_primitives::PeerId; use std::task::{Context, Poll}; diff --git a/crates/net/network/src/lib.rs b/crates/net/network/src/lib.rs index eec19ee1c127..4c102b78f40e 100644 --- a/crates/net/network/src/lib.rs +++ b/crates/net/network/src/lib.rs @@ -122,10 +122,10 @@ pub mod error; pub mod eth_requests; mod fetch; mod flattened_response; -mod import; +pub mod import; mod listener; mod manager; -mod message; +pub mod message; mod metrics; mod network; pub mod peers; diff --git a/crates/net/network/src/message.rs b/crates/net/network/src/message.rs index 3e9ba3c4df75..17d202733d18 100644 --- a/crates/net/network/src/message.rs +++ b/crates/net/network/src/message.rs @@ -158,11 +158,17 @@ impl PeerRequest { /// Corresponding variant for [`PeerRequest`]. #[derive(Debug)] +#[allow(missing_docs)] pub enum PeerResponse { + /// Response to a [`GetBlockHeaders`] request. BlockHeaders { response: oneshot::Receiver> }, + /// Response to a [`GetBlockBodies`] request. BlockBodies { response: oneshot::Receiver> }, + /// Response to a [`GetPooledTransactions`] request. PooledTransactions { response: oneshot::Receiver> }, + /// Response to a [`GetNodeData`] request. NodeData { response: oneshot::Receiver> }, + /// Response to a [`GetReceipts`] request. Receipts { response: oneshot::Receiver> }, } From ed7e6afa472cd7d65f1b2852d82f2f3d57cc0b99 Mon Sep 17 00:00:00 2001 From: Thomas Coratger <60488569+tcoratger@users.noreply.github.com> Date: Fri, 15 Dec 2023 21:23:49 +0100 Subject: [PATCH 156/277] feat: add `sequential_transactions_by_sender` method for `MockTransactionSet` (#5741) --- crates/transaction-pool/src/pool/pending.rs | 85 +++++++++---------- .../transaction-pool/src/test_utils/mock.rs | 22 ++++- 2 files changed, 60 insertions(+), 47 deletions(-) diff --git a/crates/transaction-pool/src/pool/pending.rs b/crates/transaction-pool/src/pool/pending.rs index 3ae5569a14f2..c8ba97dc775f 100644 --- a/crates/transaction-pool/src/pool/pending.rs +++ b/crates/transaction-pool/src/pool/pending.rs @@ -564,15 +564,13 @@ impl Ord for PendingTransaction { #[cfg(test)] mod tests { - use std::collections::HashSet; - - use reth_primitives::address; - use super::*; use crate::{ test_utils::{MockOrdering, MockTransaction, MockTransactionFactory, MockTransactionSet}, PoolTransaction, }; + use reth_primitives::{address, TxType}; + use std::collections::HashSet; #[test] fn test_enforce_basefee() { @@ -709,86 +707,85 @@ mod tests { #[test] fn truncate_by_sender() { - // this test ensures that we evict from the pending pool by sender + // This test ensures that transactions are removed from the pending pool by sender. let mut f = MockTransactionFactory::default(); let mut pool = PendingPool::new(MockOrdering::default()); + // Addresses for simulated senders A, B, C, and D. let a = address!("000000000000000000000000000000000000000a"); let b = address!("000000000000000000000000000000000000000b"); let c = address!("000000000000000000000000000000000000000c"); let d = address!("000000000000000000000000000000000000000d"); - // TODO: make creating these mock tx chains easier - // create a chain of transactions by sender A, B, C - let a1 = MockTransaction::eip1559().with_sender(a); - let a2 = a1.next(); - let a3 = a2.next(); - let a4 = a3.next(); - - let b1 = MockTransaction::eip1559().with_sender(b); - let b2 = b1.next(); - let b3 = b2.next(); - - // C has the same number of txs as B - let c1 = MockTransaction::eip1559().with_sender(c); - let c2 = c1.next(); - let c3 = c2.next(); - - let d1 = MockTransaction::eip1559().with_sender(d); + // Create transaction chains for senders A, B, C, and D. + let a_txs = MockTransactionSet::sequential_transactions_by_sender(a, 4, TxType::EIP1559); + let b_txs = MockTransactionSet::sequential_transactions_by_sender(b, 3, TxType::EIP1559); + let c_txs = MockTransactionSet::sequential_transactions_by_sender(c, 3, TxType::EIP1559); + let d_txs = MockTransactionSet::sequential_transactions_by_sender(d, 1, TxType::EIP1559); + + // Set up expected pending transactions. + let expected_pending = vec![ + a_txs.transactions[0].clone(), + b_txs.transactions[0].clone(), + c_txs.transactions[0].clone(), + a_txs.transactions[1].clone(), + ] + .into_iter() + .map(|tx| (tx.sender(), tx.nonce())) + .collect::>(); - // just construct a list of all txs to add - let expected_pending = vec![a1.clone(), b1.clone(), c1.clone(), a2.clone()] - .into_iter() - .map(|tx| (tx.sender(), tx.nonce())) - .collect::>(); + // Set up expected removed transactions. let expected_removed = vec![ - d1.clone(), - c3.clone(), - b3.clone(), - a4.clone(), - c2.clone(), - b2.clone(), - a3.clone(), + d_txs.transactions[0].clone(), + c_txs.transactions[2].clone(), + b_txs.transactions[2].clone(), + a_txs.transactions[3].clone(), + c_txs.transactions[1].clone(), + b_txs.transactions[1].clone(), + a_txs.transactions[2].clone(), ] .into_iter() .map(|tx| (tx.sender(), tx.nonce())) .collect::>(); - let all_txs = vec![a1, a2, a3, a4.clone(), b1, b2, b3, c1, c2, c3, d1]; - // add all the transactions to the pool + // Consolidate all transactions into a single vector. + let all_txs = + [a_txs.into_vec(), b_txs.into_vec(), c_txs.into_vec(), d_txs.into_vec()].concat(); + + // Add all the transactions to the pool. for tx in all_txs { pool.add_transaction(f.validated_arc(tx), 0); } - // sanity check, make sure everything checks out + // Sanity check, ensuring everything is consistent. pool.assert_invariants(); - // let's set the max total txs to 4, since we remove txs for each sender first, we remove - // in this order: + // Define the maximum total transactions to be 4, removing transactions for each sender. + // Expected order of removal: // * d1, c3, b3, a4 // * c2, b2, a3 // - // and we are left with: + // Remaining transactions: // * a1, a2 // * b1 // * c1 let pool_limit = SubPoolLimit { max_txs: 4, max_size: usize::MAX }; - // truncate the pool + // Truncate the pool based on the defined limit. let removed = pool.truncate_pool(pool_limit); pool.assert_invariants(); assert_eq!(removed.len(), expected_removed.len()); - // get the inner txs from the removed txs + // Get the set of removed transactions and compare with the expected set. let removed = removed.into_iter().map(|tx| (tx.sender(), tx.nonce())).collect::>(); assert_eq!(removed, expected_removed); - // get the pending pool + // Retrieve the current pending transactions after truncation. let pending = pool.all().collect::>(); assert_eq!(pending.len(), expected_pending.len()); - // get the inner txs from the pending txs + // Get the set of pending transactions and compare with the expected set. let pending = pending.into_iter().map(|tx| (tx.sender(), tx.nonce())).collect::>(); assert_eq!(pending, expected_pending); diff --git a/crates/transaction-pool/src/test_utils/mock.rs b/crates/transaction-pool/src/test_utils/mock.rs index 200a7c092bec..5f0f80a06bc5 100644 --- a/crates/transaction-pool/src/test_utils/mock.rs +++ b/crates/transaction-pool/src/test_utils/mock.rs @@ -1145,8 +1145,12 @@ impl MockTransactionSet { Self { transactions } } - /// Create a list of dependent transactions with a common sender. The transactions start at the - /// given nonce, and the sender is incremented by the given tx_count. + /// Creates a series of dependent transactions for a given sender and nonce. + /// + /// This method generates a sequence of transactions starting from the provided nonce + /// for the given sender. + /// + /// The number of transactions created is determined by `tx_count`. pub fn dependent(sender: Address, from_nonce: u64, tx_count: usize, tx_type: TxType) -> Self { let mut txs = Vec::with_capacity(tx_count); let mut curr_tx = MockTransaction::new_from_type(tx_type).with_nonce(from_nonce); @@ -1156,7 +1160,19 @@ impl MockTransactionSet { txs.push(curr_tx.clone()); } - MockTransactionSet::new(txs) + Self::new(txs) + } + + /// Creates a chain of transactions for a given sender with a specified count. + /// + /// This method generates a sequence of transactions starting from the specified sender + /// and creates a chain of transactions based on the `tx_count`. + pub fn sequential_transactions_by_sender( + sender: Address, + tx_count: usize, + tx_type: TxType, + ) -> Self { + Self::dependent(sender, 0, tx_count, tx_type) } /// Add transactions to the [MockTransactionSet] From 570be24695e07d5312536af3defce148e11bd835 Mon Sep 17 00:00:00 2001 From: Siyuan Han <47173566+hsyodyssey@users.noreply.github.com> Date: Sat, 16 Dec 2023 12:30:49 +0800 Subject: [PATCH 157/277] fix(txpool): correct the EIP2930 Error logger in TxValidator (#5792) --- crates/transaction-pool/src/validate/eth.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/transaction-pool/src/validate/eth.rs b/crates/transaction-pool/src/validate/eth.rs index 340258c2f7c2..cd95619b5646 100644 --- a/crates/transaction-pool/src/validate/eth.rs +++ b/crates/transaction-pool/src/validate/eth.rs @@ -171,7 +171,7 @@ where if !self.eip2718 { return TransactionValidationOutcome::Invalid( transaction, - InvalidTransactionError::Eip1559Disabled.into(), + InvalidTransactionError::Eip2930Disabled.into(), ) } } From 9ae7ae2b3feb11c9e0b2ff38b652216e1f48d140 Mon Sep 17 00:00:00 2001 From: Thomas Coratger <60488569+tcoratger@users.noreply.github.com> Date: Sat, 16 Dec 2023 16:10:53 +0100 Subject: [PATCH 158/277] feat: better usage of `by_id` methods in transaction pools (#5791) --- crates/transaction-pool/src/pool/blob.rs | 13 ++++++----- crates/transaction-pool/src/pool/parked.rs | 22 +++++++++++-------- crates/transaction-pool/src/pool/pending.rs | 24 ++++++++++++--------- 3 files changed, 33 insertions(+), 26 deletions(-) diff --git a/crates/transaction-pool/src/pool/blob.rs b/crates/transaction-pool/src/pool/blob.rs index fc0bebbf8991..fb5ae3c8840b 100644 --- a/crates/transaction-pool/src/pool/blob.rs +++ b/crates/transaction-pool/src/pool/blob.rs @@ -47,11 +47,7 @@ impl BlobTransactions { pub(crate) fn add_transaction(&mut self, tx: Arc>) { assert!(tx.is_eip4844(), "transaction is not a blob tx"); let id = *tx.id(); - assert!( - !self.by_id.contains_key(&id), - "transaction already included {:?}", - self.by_id.contains_key(&id) - ); + assert!(!self.contains(&id), "transaction already included {:?}", self.contains(&id)); let submission_id = self.next_id(); // keep track of size @@ -204,12 +200,15 @@ impl BlobTransactions { } /// Returns `true` if the transaction with the given id is already included in this pool. - #[cfg(test)] - #[allow(unused)] pub(crate) fn contains(&self, id: &TransactionId) -> bool { self.by_id.contains_key(id) } + /// Retrieves a transaction with the given ID from the pool, if it exists. + fn get(&self, id: &TransactionId) -> Option<&BlobTransaction> { + self.by_id.get(id) + } + /// Asserts that the bijection between `by_id` and `all` is valid. #[cfg(any(test, feature = "test-utils"))] pub(crate) fn assert_invariants(&self) { diff --git a/crates/transaction-pool/src/pool/parked.rs b/crates/transaction-pool/src/pool/parked.rs index 13887ac3f3b3..6d8b153f406a 100644 --- a/crates/transaction-pool/src/pool/parked.rs +++ b/crates/transaction-pool/src/pool/parked.rs @@ -49,9 +49,9 @@ impl ParkedPool { pub fn add_transaction(&mut self, tx: Arc>) { let id = *tx.id(); assert!( - !self.by_id.contains_key(&id), + !self.contains(&id), "transaction already included {:?}", - self.by_id.contains_key(&id) + self.get(&id).unwrap().transaction.hash() ); let submission_id = self.next_id(); @@ -213,11 +213,15 @@ impl ParkedPool { } /// Returns `true` if the transaction with the given id is already included in this pool. - #[cfg(test)] pub(crate) fn contains(&self, id: &TransactionId) -> bool { self.by_id.contains_key(id) } + /// Retrieves a transaction with the given ID from the pool, if it exists. + fn get(&self, id: &TransactionId) -> Option<&ParkedPoolTransaction> { + self.by_id.get(id) + } + /// Asserts that the bijection between `by_id` and `best` is valid. #[cfg(any(test, feature = "test-utils"))] pub(crate) fn assert_invariants(&self) { @@ -236,7 +240,7 @@ impl ParkedPool> { let ids = self.satisfy_base_fee_ids(basefee); let mut txs = Vec::with_capacity(ids.len()); for id in ids { - txs.push(self.by_id.get(&id).expect("transaction exists").transaction.clone().into()); + txs.push(self.get(&id).expect("transaction exists").transaction.clone().into()); } txs } @@ -476,7 +480,7 @@ mod tests { let tx = f.validated_arc(MockTransaction::eip1559().inc_price()); pool.add_transaction(tx.clone()); - assert!(pool.by_id.contains_key(tx.id())); + assert!(pool.contains(tx.id())); assert_eq!(pool.len(), 1); let removed = pool.enforce_basefee(u64::MAX); @@ -498,8 +502,8 @@ mod tests { let descendant_tx = f.validated_arc(t.inc_nonce().decr_price()); pool.add_transaction(descendant_tx.clone()); - assert!(pool.by_id.contains_key(root_tx.id())); - assert!(pool.by_id.contains_key(descendant_tx.id())); + assert!(pool.contains(root_tx.id())); + assert!(pool.contains(descendant_tx.id())); assert_eq!(pool.len(), 2); let removed = pool.enforce_basefee(u64::MAX); @@ -514,8 +518,8 @@ mod tests { assert_eq!(removed.len(), 1); assert_eq!(pool2.len(), 1); // root got popped - descendant should be skipped - assert!(!pool2.by_id.contains_key(root_tx.id())); - assert!(pool2.by_id.contains_key(descendant_tx.id())); + assert!(!pool2.contains(root_tx.id())); + assert!(pool2.contains(descendant_tx.id())); } // remove root transaction via descendant tx fee diff --git a/crates/transaction-pool/src/pool/pending.rs b/crates/transaction-pool/src/pool/pending.rs index c8ba97dc775f..00cf64812717 100644 --- a/crates/transaction-pool/src/pool/pending.rs +++ b/crates/transaction-pool/src/pool/pending.rs @@ -273,7 +273,7 @@ impl PendingPool { /// Note: for a transaction with nonce higher than the current on chain nonce this will always /// return an ancestor since all transaction in this pool are gapless. fn ancestor(&self, id: &TransactionId) -> Option<&PendingTransaction> { - self.by_id.get(&id.unchecked_ancestor()?) + self.get(&id.unchecked_ancestor()?) } /// Adds a new transactions to the pending queue. @@ -287,9 +287,9 @@ impl PendingPool { base_fee: u64, ) { assert!( - !self.by_id.contains_key(tx.id()), + !self.contains(tx.id()), "transaction already included {:?}", - self.by_id.contains_key(tx.id()) + self.get(tx.id()).unwrap().transaction.hash() ); // keep track of size @@ -320,7 +320,7 @@ impl PendingPool { id: &TransactionId, ) -> Option>> { // mark the next as independent if it exists - if let Some(unlocked) = self.by_id.get(&id.descendant()) { + if let Some(unlocked) = self.get(&id.descendant()) { self.independent_transactions.insert(unlocked.clone()); }; self.remove_transaction(id) @@ -486,11 +486,15 @@ impl PendingPool { } /// Returns `true` if the transaction with the given id is already included in this pool. - #[cfg(test)] pub(crate) fn contains(&self, id: &TransactionId) -> bool { self.by_id.contains_key(id) } + /// Retrieves a transaction with the given ID from the pool, if it exists. + fn get(&self, id: &TransactionId) -> Option<&PendingTransaction> { + self.by_id.get(id) + } + /// Asserts that the bijection between `by_id` and `all` is valid. #[cfg(any(test, feature = "test-utils"))] pub(crate) fn assert_invariants(&self) { @@ -579,7 +583,7 @@ mod tests { let tx = f.validated_arc(MockTransaction::eip1559().inc_price()); pool.add_transaction(tx.clone(), 0); - assert!(pool.by_id.contains_key(tx.id())); + assert!(pool.contains(tx.id())); assert_eq!(pool.len(), 1); let removed = pool.update_base_fee(0); @@ -601,8 +605,8 @@ mod tests { let descendant_tx = f.validated_arc(t.inc_nonce().decr_price()); pool.add_transaction(descendant_tx.clone(), 0); - assert!(pool.by_id.contains_key(root_tx.id())); - assert!(pool.by_id.contains_key(descendant_tx.id())); + assert!(pool.contains(root_tx.id())); + assert!(pool.contains(descendant_tx.id())); assert_eq!(pool.len(), 2); assert_eq!(pool.independent_transactions.len(), 1); @@ -619,8 +623,8 @@ mod tests { assert_eq!(removed.len(), 1); assert_eq!(pool2.len(), 1); // descendant got popped - assert!(pool2.by_id.contains_key(root_tx.id())); - assert!(!pool2.by_id.contains_key(descendant_tx.id())); + assert!(pool2.contains(root_tx.id())); + assert!(!pool2.contains(descendant_tx.id())); } // remove root transaction via fee From d3fd2cd7225999f654efa90e0a1510857ed4b2ee Mon Sep 17 00:00:00 2001 From: Thomas Coratger <60488569+tcoratger@users.noreply.github.com> Date: Sat, 16 Dec 2023 16:11:00 +0100 Subject: [PATCH 159/277] refactor(primitives): small refactoring for pooled transactions (#5789) --- crates/primitives/src/transaction/pooled.rs | 76 ++++++++++++--------- 1 file changed, 43 insertions(+), 33 deletions(-) diff --git a/crates/primitives/src/transaction/pooled.rs b/crates/primitives/src/transaction/pooled.rs index 493c723a8342..19ec801057ae 100644 --- a/crates/primitives/src/transaction/pooled.rs +++ b/crates/primitives/src/transaction/pooled.rs @@ -75,22 +75,27 @@ impl PooledTransactionsElement { /// Converts from an EIP-4844 [TransactionSignedEcRecovered] to a /// [PooledTransactionsElementEcRecovered] with the given sidecar. /// - /// Returns the transaction is not an EIP-4844 transaction. + /// Returns an `Err` containing the original `TransactionSigned` if the transaction is not + /// EIP-4844. pub fn try_from_blob_transaction( tx: TransactionSigned, sidecar: BlobTransactionSidecar, ) -> Result { - let TransactionSigned { transaction, signature, hash } = tx; - if let Transaction::Eip4844(tx) = transaction { - Ok(PooledTransactionsElement::BlobTransaction(BlobTransaction { - transaction: tx, - signature, - hash, - sidecar, - })) - } else { - Err(TransactionSigned { transaction, signature, hash }) - } + Ok(match tx { + // If the transaction is an EIP-4844 transaction... + TransactionSigned { transaction: Transaction::Eip4844(tx), signature, hash } => { + // Construct a `PooledTransactionsElement::BlobTransaction` with provided sidecar. + PooledTransactionsElement::BlobTransaction(BlobTransaction { + transaction: tx, + signature, + hash, + sidecar, + }) + } + // If the transaction is not EIP-4844, return an error with the original + // transaction. + _ => return Err(tx), + }) } /// Heavy operation that return signature hash over rlp encoded transaction. @@ -109,8 +114,8 @@ impl PooledTransactionsElement { /// Reference to transaction hash. Used to identify transaction. pub fn hash(&self) -> &TxHash { match self { - PooledTransactionsElement::Legacy { hash, .. } => hash, - PooledTransactionsElement::Eip2930 { hash, .. } => hash, + PooledTransactionsElement::Legacy { hash, .. } | + PooledTransactionsElement::Eip2930 { hash, .. } | PooledTransactionsElement::Eip1559 { hash, .. } => hash, PooledTransactionsElement::BlobTransaction(tx) => &tx.hash, #[cfg(feature = "optimism")] @@ -121,8 +126,8 @@ impl PooledTransactionsElement { /// Returns the signature of the transaction. pub fn signature(&self) -> &Signature { match self { - Self::Legacy { signature, .. } => signature, - Self::Eip2930 { signature, .. } => signature, + Self::Legacy { signature, .. } | + Self::Eip2930 { signature, .. } | Self::Eip1559 { signature, .. } => signature, Self::BlobTransaction(blob_tx) => &blob_tx.signature, #[cfg(feature = "optimism")] @@ -148,8 +153,7 @@ impl PooledTransactionsElement { /// /// Returns `None` if the transaction's signature is invalid, see also [Self::recover_signer]. pub fn recover_signer(&self) -> Option
{ - let signature_hash = self.signature_hash(); - self.signature().recover_signer(signature_hash) + self.signature().recover_signer(self.signature_hash()) } /// Tries to recover signer and return [`PooledTransactionsElementEcRecovered`]. @@ -513,19 +517,26 @@ impl From for PooledTransactionsElement { #[cfg(any(test, feature = "arbitrary"))] impl<'a> arbitrary::Arbitrary<'a> for PooledTransactionsElement { + /// Generates an arbitrary `PooledTransactionsElement`. + /// + /// This function generates an arbitrary `PooledTransactionsElement` by creating a transaction + /// and, if applicable, generating a sidecar for blob transactions. + /// + /// It handles the generation of sidecars and constructs the resulting + /// `PooledTransactionsElement`. fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result { - let transaction = TransactionSigned::arbitrary(u)?; - - // this will have an empty sidecar - let pooled_txs_element = PooledTransactionsElement::from(transaction); - - // generate a sidecar for blob txs - if let PooledTransactionsElement::BlobTransaction(mut tx) = pooled_txs_element { - tx.sidecar = crate::BlobTransactionSidecar::arbitrary(u)?; - Ok(PooledTransactionsElement::BlobTransaction(tx)) - } else { - Ok(pooled_txs_element) - } + // Attempt to create a `TransactionSigned` with arbitrary data. + Ok(match PooledTransactionsElement::from(TransactionSigned::arbitrary(u)?) { + // If the generated `PooledTransactionsElement` is a blob transaction... + PooledTransactionsElement::BlobTransaction(mut tx) => { + // Generate a sidecar for the blob transaction using arbitrary data. + tx.sidecar = crate::BlobTransactionSidecar::arbitrary(u)?; + // Return the blob transaction with the generated sidecar. + PooledTransactionsElement::BlobTransaction(tx) + } + // If the generated `PooledTransactionsElement` is not a blob transaction... + tx => tx, + }) } } @@ -614,11 +625,10 @@ impl PooledTransactionsElementEcRecovered { } } +/// Converts a `TransactionSignedEcRecovered` into a `PooledTransactionsElementEcRecovered`. impl From for PooledTransactionsElementEcRecovered { fn from(tx: TransactionSignedEcRecovered) -> Self { - let signer = tx.signer; - let transaction = tx.signed_transaction.into(); - Self { transaction, signer } + Self { transaction: tx.signed_transaction.into(), signer: tx.signer } } } From 3e500ef7466bc59efa3af791513be203c481ad67 Mon Sep 17 00:00:00 2001 From: Alexey Shekhirin Date: Sat, 16 Dec 2023 19:31:43 +0200 Subject: [PATCH 160/277] feat(grafana): sort stages (#5796) --- etc/docker-compose.yml | 2 +- etc/grafana/dashboards/overview.json | 189 ++++++++++++--------------- 2 files changed, 88 insertions(+), 103 deletions(-) diff --git a/etc/docker-compose.yml b/etc/docker-compose.yml index c5f8e1334d86..7323163c5866 100644 --- a/etc/docker-compose.yml +++ b/etc/docker-compose.yml @@ -43,7 +43,7 @@ services: grafana: restart: unless-stopped - image: grafana/grafana:10.1.0 + image: grafana/grafana:10.1.1 depends_on: - reth - prometheus diff --git a/etc/grafana/dashboards/overview.json b/etc/grafana/dashboards/overview.json index a9a21fb51d62..333288987ed5 100644 --- a/etc/grafana/dashboards/overview.json +++ b/etc/grafana/dashboards/overview.json @@ -27,7 +27,7 @@ "type": "grafana", "id": "grafana", "name": "Grafana", - "version": "10.1.0" + "version": "10.1.1" }, { "type": "panel", @@ -197,8 +197,7 @@ "mode": "absolute", "steps": [ { - "color": "green", - "value": null + "color": "green" } ] } @@ -218,7 +217,9 @@ "minVizWidth": 0, "orientation": "horizontal", "reduceOptions": { - "calcs": ["last"], + "calcs": [ + "last" + ], "fields": "", "values": false }, @@ -242,6 +243,35 @@ } ], "title": "Stage checkpoints", + "transformations": [ + { + "id": "joinByField", + "options": { + "mode": "outer" + } + }, + { + "id": "organize", + "options": { + "excludeByName": {}, + "indexByName": { + "AccountHashing": 6, + "Bodies": 2, + "Execution": 4, + "Finish": 12, + "Headers": 0, + "IndexAccountHistory": 11, + "IndexStorageHistory": 10, + "MerkleExecute": 8, + "MerkleUnwind": 5, + "SenderRecovery": 3, + "StorageHashing": 7, + "TotalDifficulty": 1, + "TransactionLookup": 9 + } + } + } + ], "transparent": true, "type": "bargauge" }, @@ -678,8 +708,7 @@ "mode": "absolute", "steps": [ { - "color": "green", - "value": null + "color": "green" } ] }, @@ -773,8 +802,7 @@ "mode": "absolute", "steps": [ { - "color": "green", - "value": null + "color": "green" } ] }, @@ -868,8 +896,7 @@ "mode": "absolute", "steps": [ { - "color": "green", - "value": null + "color": "green" }, { "color": "red", @@ -967,8 +994,7 @@ "mode": "absolute", "steps": [ { - "color": "green", - "value": null + "color": "green" }, { "color": "red", @@ -1139,8 +1165,7 @@ "mode": "absolute", "steps": [ { - "color": "green", - "value": null + "color": "green" }, { "color": "red", @@ -1277,8 +1302,7 @@ "mode": "absolute", "steps": [ { - "color": "green", - "value": null + "color": "green" }, { "color": "red", @@ -1464,8 +1488,7 @@ "mode": "absolute", "steps": [ { - "color": "green", - "value": null + "color": "green" }, { "color": "red", @@ -1559,8 +1582,7 @@ "mode": "absolute", "steps": [ { - "color": "green", - "value": null + "color": "green" }, { "color": "red", @@ -1697,8 +1719,7 @@ "mode": "absolute", "steps": [ { - "color": "green", - "value": null + "color": "green" } ] }, @@ -1839,8 +1860,7 @@ "mode": "absolute", "steps": [ { - "color": "green", - "value": null + "color": "green" }, { "color": "red", @@ -1933,8 +1953,7 @@ "mode": "absolute", "steps": [ { - "color": "green", - "value": null + "color": "green" }, { "color": "red", @@ -2052,8 +2071,7 @@ "mode": "absolute", "steps": [ { - "color": "green", - "value": null + "color": "green" }, { "color": "red", @@ -2357,8 +2375,7 @@ "mode": "absolute", "steps": [ { - "color": "green", - "value": null + "color": "green" }, { "color": "red", @@ -2465,8 +2482,7 @@ "mode": "absolute", "steps": [ { - "color": "green", - "value": null + "color": "green" }, { "color": "red", @@ -2621,8 +2637,7 @@ "mode": "absolute", "steps": [ { - "color": "green", - "value": null + "color": "green" }, { "color": "red", @@ -2740,8 +2755,7 @@ "mode": "absolute", "steps": [ { - "color": "green", - "value": null + "color": "green" }, { "color": "red", @@ -2861,8 +2875,7 @@ "mode": "absolute", "steps": [ { - "color": "green", - "value": null + "color": "green" }, { "color": "red", @@ -3062,8 +3075,7 @@ "mode": "absolute", "steps": [ { - "color": "green", - "value": null + "color": "green" } ] }, @@ -3177,8 +3189,7 @@ "mode": "absolute", "steps": [ { - "color": "green", - "value": null + "color": "green" }, { "color": "red", @@ -3283,8 +3294,7 @@ "mode": "absolute", "steps": [ { - "color": "green", - "value": null + "color": "green" }, { "color": "red", @@ -3423,8 +3433,7 @@ "mode": "absolute", "steps": [ { - "color": "green", - "value": null + "color": "green" }, { "color": "red", @@ -3542,8 +3551,7 @@ "mode": "absolute", "steps": [ { - "color": "green", - "value": null + "color": "green" }, { "color": "red", @@ -3660,8 +3668,7 @@ "mode": "absolute", "steps": [ { - "color": "green", - "value": null + "color": "green" }, { "color": "red", @@ -3798,8 +3805,7 @@ "mode": "absolute", "steps": [ { - "color": "green", - "value": null + "color": "green" }, { "color": "red", @@ -3893,8 +3899,7 @@ "mode": "absolute", "steps": [ { - "color": "green", - "value": null + "color": "green" }, { "color": "red", @@ -3988,8 +3993,7 @@ "mode": "absolute", "steps": [ { - "color": "green", - "value": null + "color": "green" }, { "color": "red", @@ -4136,8 +4140,7 @@ "mode": "absolute", "steps": [ { - "color": "green", - "value": null + "color": "green" }, { "color": "red", @@ -4282,8 +4285,7 @@ "mode": "absolute", "steps": [ { - "color": "green", - "value": null + "color": "green" }, { "color": "red", @@ -4447,8 +4449,7 @@ "mode": "absolute", "steps": [ { - "color": "green", - "value": null + "color": "green" }, { "color": "red", @@ -4542,8 +4543,7 @@ "mode": "absolute", "steps": [ { - "color": "green", - "value": null + "color": "green" }, { "color": "red", @@ -4637,8 +4637,7 @@ "mode": "absolute", "steps": [ { - "color": "green", - "value": null + "color": "green" }, { "color": "red", @@ -4731,8 +4730,7 @@ "mode": "absolute", "steps": [ { - "color": "green", - "value": null + "color": "green" }, { "color": "red", @@ -4827,8 +4825,7 @@ "mode": "absolute", "steps": [ { - "color": "green", - "value": null + "color": "green" }, { "color": "red", @@ -4902,7 +4899,8 @@ ], "title": "Canonicalization duration per action", "type": "timeseries" - },{ + }, + { "datasource": { "type": "prometheus", "uid": "${DS_PROMETHEUS}" @@ -4948,8 +4946,7 @@ "mode": "absolute", "steps": [ { - "color": "green", - "value": null + "color": "green" }, { "color": "red", @@ -5059,8 +5056,7 @@ "mode": "absolute", "steps": [ { - "color": "green", - "value": null + "color": "green" }, { "color": "red", @@ -5153,8 +5149,7 @@ "mode": "absolute", "steps": [ { - "color": "green", - "value": null + "color": "green" }, { "color": "red", @@ -5259,8 +5254,7 @@ "mode": "absolute", "steps": [ { - "color": "green", - "value": null + "color": "green" }, { "color": "red", @@ -5368,8 +5362,7 @@ "mode": "absolute", "steps": [ { - "color": "green", - "value": null + "color": "green" }, { "color": "red", @@ -5462,8 +5455,7 @@ "mode": "absolute", "steps": [ { - "color": "green", - "value": null + "color": "green" }, { "color": "red", @@ -5556,8 +5548,7 @@ "mode": "absolute", "steps": [ { - "color": "green", - "value": null + "color": "green" }, { "color": "red", @@ -5662,8 +5653,7 @@ "mode": "absolute", "steps": [ { - "color": "green", - "value": null + "color": "green" }, { "color": "red", @@ -5823,8 +5813,7 @@ "mode": "absolute", "steps": [ { - "color": "green", - "value": null + "color": "green" }, { "color": "red", @@ -5919,8 +5908,7 @@ "mode": "absolute", "steps": [ { - "color": "green", - "value": null + "color": "green" }, { "color": "red", @@ -6015,8 +6003,7 @@ "mode": "absolute", "steps": [ { - "color": "green", - "value": null + "color": "green" }, { "color": "red", @@ -6124,8 +6111,7 @@ "mode": "absolute", "steps": [ { - "color": "green", - "value": null + "color": "green" }, { "color": "red", @@ -6220,8 +6206,7 @@ "mode": "absolute", "steps": [ { - "color": "green", - "value": null + "color": "green" }, { "color": "red", @@ -6329,8 +6314,7 @@ "mode": "absolute", "steps": [ { - "color": "green", - "value": null + "color": "green" }, { "color": "red", @@ -6543,8 +6527,7 @@ "mode": "absolute", "steps": [ { - "color": "green", - "value": null + "color": "green" }, { "color": "red", @@ -6713,6 +6696,7 @@ "type": "query" }, { + "current": {}, "datasource": { "type": "prometheus", "uid": "${DS_PROMETHEUS}" @@ -6735,6 +6719,7 @@ "type": "query" }, { + "current": {}, "datasource": { "type": "prometheus", "uid": "${DS_PROMETHEUS}" @@ -6766,6 +6751,6 @@ "timezone": "", "title": "reth", "uid": "2k8BXz24x", - "version": 12, + "version": 13, "weekStart": "" } From bfe12ce8853805b3074184d4beda62fa48db977e Mon Sep 17 00:00:00 2001 From: Matthias Seitz Date: Sat, 16 Dec 2023 18:32:59 +0100 Subject: [PATCH 161/277] chore: log full tx object in assert (#5797) --- crates/transaction-pool/src/pool/blob.rs | 2 +- crates/transaction-pool/src/pool/parked.rs | 2 +- crates/transaction-pool/src/pool/pending.rs | 7 ++++--- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/crates/transaction-pool/src/pool/blob.rs b/crates/transaction-pool/src/pool/blob.rs index fb5ae3c8840b..040462639110 100644 --- a/crates/transaction-pool/src/pool/blob.rs +++ b/crates/transaction-pool/src/pool/blob.rs @@ -47,7 +47,7 @@ impl BlobTransactions { pub(crate) fn add_transaction(&mut self, tx: Arc>) { assert!(tx.is_eip4844(), "transaction is not a blob tx"); let id = *tx.id(); - assert!(!self.contains(&id), "transaction already included {:?}", self.contains(&id)); + assert!(!self.contains(&id), "transaction already included {:?}", self.get(&id).unwrap()); let submission_id = self.next_id(); // keep track of size diff --git a/crates/transaction-pool/src/pool/parked.rs b/crates/transaction-pool/src/pool/parked.rs index 6d8b153f406a..e64770927bc4 100644 --- a/crates/transaction-pool/src/pool/parked.rs +++ b/crates/transaction-pool/src/pool/parked.rs @@ -51,7 +51,7 @@ impl ParkedPool { assert!( !self.contains(&id), "transaction already included {:?}", - self.get(&id).unwrap().transaction.hash() + self.get(&id).unwrap().transaction.transaction ); let submission_id = self.next_id(); diff --git a/crates/transaction-pool/src/pool/pending.rs b/crates/transaction-pool/src/pool/pending.rs index 00cf64812717..6a7b0409e727 100644 --- a/crates/transaction-pool/src/pool/pending.rs +++ b/crates/transaction-pool/src/pool/pending.rs @@ -289,7 +289,7 @@ impl PendingPool { assert!( !self.contains(tx.id()), "transaction already included {:?}", - self.get(tx.id()).unwrap().transaction.hash() + self.get(tx.id()).unwrap().transaction ); // keep track of size @@ -507,8 +507,9 @@ impl PendingPool { self.highest_nonces.len() <= self.all.len(), "independent_descendants.len() > all.len()" ); - assert!( - self.highest_nonces.len() == self.independent_transactions.len(), + assert_eq!( + self.highest_nonces.len(), + self.independent_transactions.len(), "independent.len() = independent_descendants.len()" ); } From 27caf7b4ce8723284405ac09cdf145a0bfed2699 Mon Sep 17 00:00:00 2001 From: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Date: Sat, 16 Dec 2023 20:11:26 +0200 Subject: [PATCH 162/277] perf: random changes (#5795) --- crates/blockchain-tree/src/blockchain_tree.rs | 2 +- crates/primitives/src/receipt.rs | 27 +- crates/storage/db/src/abstraction/cursor.rs | 20 +- .../db/src/implementation/mdbx/cursor.rs | 55 ++-- .../src/providers/database/provider.rs | 308 ++++++++---------- 5 files changed, 182 insertions(+), 230 deletions(-) diff --git a/crates/blockchain-tree/src/blockchain_tree.rs b/crates/blockchain-tree/src/blockchain_tree.rs index 20d878399f5b..1d874ee578ae 100644 --- a/crates/blockchain-tree/src/blockchain_tree.rs +++ b/crates/blockchain-tree/src/blockchain_tree.rs @@ -1809,7 +1809,7 @@ mod tests { .with_pending_blocks((block2.number + 1, HashSet::new())) .assert(&tree); - assert!(tree.make_canonical(&block1a_hash).is_ok()); + assert_matches!(tree.make_canonical(&block1a_hash), Ok(_)); // Trie state: // b2a b2 (side chain) // | / diff --git a/crates/primitives/src/receipt.rs b/crates/primitives/src/receipt.rs index 37c6fd37c13a..5bf07391e7ec 100644 --- a/crates/primitives/src/receipt.rs +++ b/crates/primitives/src/receipt.rs @@ -120,21 +120,18 @@ impl Receipts { /// Retrieves gas spent by transactions as a vector of tuples (transaction index, gas used). pub fn gas_spent_by_tx(&self) -> Result, PruneSegmentError> { - self.last() - .map(|block_r| { - block_r - .iter() - .enumerate() - .map(|(id, tx_r)| { - if let Some(receipt) = tx_r.as_ref() { - Ok((id as u64, receipt.cumulative_gas_used)) - } else { - Err(PruneSegmentError::ReceiptsPruned) - } - }) - .collect::, PruneSegmentError>>() - }) - .unwrap_or(Ok(vec![])) + let Some(block_r) = self.last() else { + return Ok(vec![]); + }; + let mut out = Vec::with_capacity(block_r.len()); + for (id, tx_r) in block_r.iter().enumerate() { + if let Some(receipt) = tx_r.as_ref() { + out.push((id as u64, receipt.cumulative_gas_used)); + } else { + return Err(PruneSegmentError::ReceiptsPruned); + } + } + Ok(out) } } diff --git a/crates/storage/db/src/abstraction/cursor.rs b/crates/storage/db/src/abstraction/cursor.rs index ef2707c301df..306b010f53bf 100644 --- a/crates/storage/db/src/abstraction/cursor.rs +++ b/crates/storage/db/src/abstraction/cursor.rs @@ -153,7 +153,7 @@ where } } -impl<'cursor, T: Table, CURSOR: DbCursorRO> std::iter::Iterator for Walker<'cursor, T, CURSOR> { +impl<'cursor, T: Table, CURSOR: DbCursorRO> Iterator for Walker<'cursor, T, CURSOR> { type Item = Result, DatabaseError>; fn next(&mut self) -> Option { let start = self.start.take(); @@ -227,9 +227,7 @@ impl<'cursor, T: Table, CURSOR: DbCursorRW + DbCursorRO> ReverseWalker<'cu } } -impl<'cursor, T: Table, CURSOR: DbCursorRO> std::iter::Iterator - for ReverseWalker<'cursor, T, CURSOR> -{ +impl<'cursor, T: Table, CURSOR: DbCursorRO> Iterator for ReverseWalker<'cursor, T, CURSOR> { type Item = Result, DatabaseError>; fn next(&mut self) -> Option { @@ -270,10 +268,9 @@ where } } -impl<'cursor, T: Table, CURSOR: DbCursorRO> std::iter::Iterator - for RangeWalker<'cursor, T, CURSOR> -{ +impl<'cursor, T: Table, CURSOR: DbCursorRO> Iterator for RangeWalker<'cursor, T, CURSOR> { type Item = Result, DatabaseError>; + fn next(&mut self) -> Option { if self.is_done { return None @@ -292,11 +289,10 @@ impl<'cursor, T: Table, CURSOR: DbCursorRO> std::iter::Iterator } }, Some(res @ Err(_)) => Some(res), - None if matches!(self.end_key, Bound::Unbounded) => { - self.is_done = true; + None => { + self.is_done = matches!(self.end_key, Bound::Unbounded); None } - _ => None, } } } @@ -361,9 +357,7 @@ impl<'cursor, T: DupSort, CURSOR: DbCursorRW + DbDupCursorRO> DupWalker<'c } } -impl<'cursor, T: DupSort, CURSOR: DbDupCursorRO> std::iter::Iterator - for DupWalker<'cursor, T, CURSOR> -{ +impl<'cursor, T: DupSort, CURSOR: DbDupCursorRO> Iterator for DupWalker<'cursor, T, CURSOR> { type Item = Result, DatabaseError>; fn next(&mut self) -> Option { let start = self.start.take(); diff --git a/crates/storage/db/src/implementation/mdbx/cursor.rs b/crates/storage/db/src/implementation/mdbx/cursor.rs index 63017be2d524..1c0cfa0e2bc2 100644 --- a/crates/storage/db/src/implementation/mdbx/cursor.rs +++ b/crates/storage/db/src/implementation/mdbx/cursor.rs @@ -1,8 +1,5 @@ //! Cursor wrapper for libmdbx-sys. -use reth_interfaces::db::{DatabaseWriteError, DatabaseWriteOperation}; -use std::{borrow::Cow, collections::Bound, marker::PhantomData, ops::RangeBounds}; - use crate::{ common::{PairResult, ValueOnlyResult}, cursor::{ @@ -10,11 +7,13 @@ use crate::{ ReverseWalker, Walker, }, metrics::{Operation, OperationMetrics}, - table::{Compress, DupSort, Encode, Table}, + table::{Compress, Decode, Decompress, DupSort, Encode, Table}, tables::utils::*, DatabaseError, }; +use reth_interfaces::db::{DatabaseWriteError, DatabaseWriteOperation}; use reth_libmdbx::{self, Error as MDBXError, TransactionKind, WriteFlags, RO, RW}; +use std::{borrow::Cow, collections::Bound, marker::PhantomData, ops::RangeBounds}; /// Read only Cursor. pub type CursorRO = Cursor; @@ -56,12 +55,17 @@ impl Cursor { } } -/// Takes `(key, value)` from the database and decodes it appropriately. -#[macro_export] -macro_rules! decode { - ($v:expr) => { - $v.map_err(|e| $crate::DatabaseError::Read(e.into()))?.map(decoder::).transpose() - }; +/// Decodes a `(key, value)` pair from the database. +#[allow(clippy::type_complexity)] +pub fn decode( + res: Result, Cow<'_, [u8]>)>, impl Into>, +) -> PairResult +where + T: Table, + T::Key: Decode, + T::Value: Decompress, +{ + res.map_err(|e| DatabaseError::Read(e.into()))?.map(decoder::).transpose() } /// Some types don't support compression (eg. B256), and we don't want to be copying them to the @@ -80,39 +84,36 @@ macro_rules! compress_to_buf_or_ref { impl DbCursorRO for Cursor { fn first(&mut self) -> PairResult { - decode!(self.inner.first()) + decode::(self.inner.first()) } fn seek_exact(&mut self, key: ::Key) -> PairResult { - decode!(self.inner.set_key(key.encode().as_ref())) + decode::(self.inner.set_key(key.encode().as_ref())) } fn seek(&mut self, key: ::Key) -> PairResult { - decode!(self.inner.set_range(key.encode().as_ref())) + decode::(self.inner.set_range(key.encode().as_ref())) } fn next(&mut self) -> PairResult { - decode!(self.inner.next()) + decode::(self.inner.next()) } fn prev(&mut self) -> PairResult { - decode!(self.inner.prev()) + decode::(self.inner.prev()) } fn last(&mut self) -> PairResult { - decode!(self.inner.last()) + decode::(self.inner.last()) } fn current(&mut self) -> PairResult { - decode!(self.inner.get_current()) + decode::(self.inner.get_current()) } fn walk(&mut self, start_key: Option) -> Result, DatabaseError> { let start = if let Some(start_key) = start_key { - self.inner - .set_range(start_key.encode().as_ref()) - .map_err(|e| DatabaseError::Read(e.into()))? - .map(decoder::) + decode::(self.inner.set_range(start_key.encode().as_ref())).transpose() } else { self.first().transpose() }; @@ -130,10 +131,8 @@ impl DbCursorRO for Cursor { unreachable!("Rust doesn't allow for Bound::Excluded in starting bounds"); } Bound::Unbounded => self.inner.first(), - } - .map_err(|e| DatabaseError::Read(e.into()))? - .map(decoder::); - + }; + let start = decode::(start).transpose(); Ok(RangeWalker::new(self, start, range.end_bound().cloned())) } @@ -142,7 +141,7 @@ impl DbCursorRO for Cursor { start_key: Option, ) -> Result, DatabaseError> { let start = if let Some(start_key) = start_key { - decode!(self.inner.set_range(start_key.encode().as_ref())) + decode::(self.inner.set_range(start_key.encode().as_ref())) } else { self.last() } @@ -155,12 +154,12 @@ impl DbCursorRO for Cursor { impl DbDupCursorRO for Cursor { /// Returns the next `(key, value)` pair of a DUPSORT table. fn next_dup(&mut self) -> PairResult { - decode!(self.inner.next_dup()) + decode::(self.inner.next_dup()) } /// Returns the next `(key, value)` pair skipping the duplicates. fn next_no_dup(&mut self) -> PairResult { - decode!(self.inner.next_nodup()) + decode::(self.inner.next_nodup()) } /// Returns the next `value` of a duplicate `key`. diff --git a/crates/storage/provider/src/providers/database/provider.rs b/crates/storage/provider/src/providers/database/provider.rs index 6c85dc12c807..56ac8f3f69d3 100644 --- a/crates/storage/provider/src/providers/database/provider.rs +++ b/crates/storage/provider/src/providers/database/provider.rs @@ -50,7 +50,7 @@ use revm::primitives::{BlockEnv, CfgEnv, SpecId}; use std::{ collections::{hash_map, BTreeMap, BTreeSet, HashMap, HashSet}, fmt::Debug, - ops::{Deref, DerefMut, Range, RangeBounds, RangeInclusive}, + ops::{Bound, Deref, DerefMut, Range, RangeBounds, RangeInclusive}, sync::{mpsc, Arc}, time::{Duration, Instant}, }; @@ -112,6 +112,54 @@ impl DatabaseProvider { } } +impl DatabaseProvider { + fn cursor_read_collect, R>( + &self, + range: impl RangeBounds, + mut f: impl FnMut(T::Value) -> Result, + ) -> Result, DatabaseError> { + self.cursor_read_collect_with_key::(range, |_, v| f(v)) + } + + fn cursor_read_collect_with_key, R>( + &self, + range: impl RangeBounds, + f: impl FnMut(T::Key, T::Value) -> Result, + ) -> Result, DatabaseError> { + let capacity = match range_size_hint(&range) { + Some(0) | None => return Ok(Vec::new()), + Some(capacity) => capacity, + }; + let mut cursor = self.tx.cursor_read::()?; + self.cursor_collect_with_capacity(&mut cursor, range, capacity, f) + } + + fn cursor_collect, R>( + &self, + cursor: &mut impl DbCursorRO, + range: impl RangeBounds, + mut f: impl FnMut(T::Value) -> Result, + ) -> Result, DatabaseError> { + let capacity = range_size_hint(&range).unwrap_or(0); + self.cursor_collect_with_capacity(cursor, range, capacity, |_, v| f(v)) + } + + fn cursor_collect_with_capacity, R>( + &self, + cursor: &mut impl DbCursorRO, + range: impl RangeBounds, + capacity: usize, + mut f: impl FnMut(T::Key, T::Value) -> Result, + ) -> Result, DatabaseError> { + let mut items = Vec::with_capacity(capacity); + for entry in cursor.walk_range(range)? { + let (key, value) = entry?; + items.push(f(key, value)?); + } + Ok(items) + } +} + /// For a given key, unwind all history shards that are below the given block number. /// /// S - Sharded key subtype. @@ -1179,14 +1227,7 @@ impl BlockReader for DatabaseProvider { // we skip the block. if let Some((_, block_body_indices)) = block_body_cursor.seek_exact(num)? { let tx_range = block_body_indices.tx_num_range(); - let body = if tx_range.is_empty() { - Vec::new() - } else { - tx_cursor - .walk_range(tx_range)? - .map(|result| result.map(|(_, tx)| tx.into())) - .collect::, _>>()? - }; + let body = self.cursor_collect(&mut tx_cursor, tx_range, |tx| Ok(tx.into()))?; // If we are past shanghai, then all blocks should have a withdrawal list, // even if empty @@ -1355,19 +1396,14 @@ impl TransactionsProvider for DatabaseProvider { &self, id: BlockHashOrNumber, ) -> ProviderResult>> { - let mut tx_cursor = self.tx.cursor_read::()?; if let Some(block_number) = self.convert_hash_or_number(id)? { if let Some(body) = self.block_body_indices(block_number)? { - let tx_range = body.tx_num_range(); - return if tx_range.is_empty() { - Ok(Some(Vec::new())) - } else { - let transactions = tx_cursor - .walk_range(tx_range)? - .map(|result| result.map(|(_, tx)| tx.into())) - .collect::, _>>()?; - Ok(Some(transactions)) - }; + return self + .cursor_read_collect::(body.tx_num_range(), |tx| { + Ok(tx.into()) + }) + .map(Some) + .map_err(Into::into); } } Ok(None) @@ -1377,48 +1413,25 @@ impl TransactionsProvider for DatabaseProvider { &self, range: impl RangeBounds, ) -> ProviderResult>> { - let mut results = Vec::new(); - let mut body_cursor = self.tx.cursor_read::()?; let mut tx_cursor = self.tx.cursor_read::()?; - for entry in body_cursor.walk_range(range)? { - let (_, body) = entry?; - let tx_num_range = body.tx_num_range(); - if tx_num_range.is_empty() { - results.push(Vec::new()); - } else { - results.push( - tx_cursor - .walk_range(tx_num_range)? - .map(|result| result.map(|(_, tx)| tx.into())) - .collect::, _>>()?, - ); - } - } - Ok(results) + self.cursor_read_collect::(range, |body| { + self.cursor_collect(&mut tx_cursor, body.tx_num_range(), |tx| Ok(tx.into())) + }) + .map_err(Into::into) } fn transactions_by_tx_range( &self, range: impl RangeBounds, ) -> ProviderResult> { - Ok(self - .tx - .cursor_read::()? - .walk_range(range)? - .map(|entry| entry.map(|tx| tx.1)) - .collect::, _>>()?) + self.cursor_read_collect::(range, Ok).map_err(Into::into) } fn senders_by_tx_range( &self, range: impl RangeBounds, ) -> ProviderResult> { - Ok(self - .tx - .cursor_read::()? - .walk_range(range)? - .map(|entry| entry.map(|sender| sender.1)) - .collect::, _>>()?) + self.cursor_read_collect::(range, Ok).map_err(Into::into) } fn transaction_sender(&self, id: TxNumber) -> ProviderResult> { @@ -1442,17 +1455,10 @@ impl ReceiptProvider for DatabaseProvider { fn receipts_by_block(&self, block: BlockHashOrNumber) -> ProviderResult>> { if let Some(number) = self.convert_hash_or_number(block)? { if let Some(body) = self.block_body_indices(number)? { - let tx_range = body.tx_num_range(); - return if tx_range.is_empty() { - Ok(Some(Vec::new())) - } else { - let mut receipts_cursor = self.tx.cursor_read::()?; - let receipts = receipts_cursor - .walk_range(tx_range)? - .map(|result| result.map(|(_, receipt)| receipt)) - .collect::, _>>()?; - Ok(Some(receipts)) - }; + return self + .cursor_read_collect::(body.tx_num_range(), Ok) + .map(Some) + .map_err(Into::into); } } Ok(None) @@ -1682,41 +1688,28 @@ impl HashingWriter for DatabaseProvider { &self, range: RangeInclusive, ) -> ProviderResult>> { - let mut hashed_accounts_cursor = self.tx.cursor_write::()?; - // Aggregate all block changesets and make a list of accounts that have been changed. + // Note that collecting and then reversing the order is necessary to ensure that the + // changes are applied in the correct order. let hashed_accounts = self .tx .cursor_read::()? .walk_range(range)? + .map(|entry| entry.map(|(_, e)| (keccak256(e.address), e.info))) .collect::, _>>()? .into_iter() .rev() - // fold all account to get the old balance/nonces and account that needs to be removed - .fold( - BTreeMap::new(), - |mut accounts: BTreeMap>, (_, account_before)| { - accounts.insert(account_before.address, account_before.info); - accounts - }, - ) - .into_iter() - // hash addresses and collect it inside sorted BTreeMap. - // We are doing keccak only once per address. - .map(|(address, account)| (keccak256(address), account)) .collect::>(); - hashed_accounts - .iter() - // Apply values to HashedState (if Account is None remove it); - .try_for_each(|(hashed_address, account)| -> ProviderResult<()> { - if let Some(account) = account { - hashed_accounts_cursor.upsert(*hashed_address, *account)?; - } else if hashed_accounts_cursor.seek_exact(*hashed_address)?.is_some() { - hashed_accounts_cursor.delete_current()?; - } - Ok(()) - })?; + // Apply values to HashedState, and remove the account if it's None. + let mut hashed_accounts_cursor = self.tx.cursor_write::()?; + for (hashed_address, account) in &hashed_accounts { + if let Some(account) = account { + hashed_accounts_cursor.upsert(*hashed_address, *account)?; + } else if hashed_accounts_cursor.seek_exact(*hashed_address)?.is_some() { + hashed_accounts_cursor.delete_current()?; + } + } Ok(hashed_accounts) } @@ -1726,24 +1719,15 @@ impl HashingWriter for DatabaseProvider { accounts: impl IntoIterator)>, ) -> ProviderResult>> { let mut hashed_accounts_cursor = self.tx.cursor_write::()?; - - let hashed_accounts = accounts.into_iter().fold( - BTreeMap::new(), - |mut map: BTreeMap>, (address, account)| { - map.insert(keccak256(address), account); - map - }, - ); - - hashed_accounts.iter().try_for_each(|(hashed_address, account)| -> ProviderResult<()> { + let hashed_accounts = + accounts.into_iter().map(|(ad, ac)| (keccak256(ad), ac)).collect::>(); + for (hashed_address, account) in &hashed_accounts { if let Some(account) = account { - hashed_accounts_cursor.upsert(*hashed_address, *account)? + hashed_accounts_cursor.upsert(*hashed_address, *account)?; } else if hashed_accounts_cursor.seek_exact(*hashed_address)?.is_some() { hashed_accounts_cursor.delete_current()?; } - Ok(()) - })?; - + } Ok(hashed_accounts) } @@ -1751,54 +1735,36 @@ impl HashingWriter for DatabaseProvider { &self, range: Range, ) -> ProviderResult>> { - let mut hashed_storage = self.tx.cursor_dup_write::()?; - // Aggregate all block changesets and make list of accounts that have been changed. - let hashed_storages = self - .tx - .cursor_read::()? + let mut changesets = self.tx.cursor_read::()?; + let mut hashed_storages = changesets .walk_range(range)? - .collect::, _>>()? - .into_iter() - .rev() - // fold all account to get the old balance/nonces and account that needs to be removed - .fold( - BTreeMap::new(), - |mut accounts: BTreeMap<(Address, B256), U256>, - (BlockNumberAddress((_, address)), storage_entry)| { - accounts.insert((address, storage_entry.key), storage_entry.value); - accounts - }, - ) - .into_iter() - // hash addresses and collect it inside sorted BTreeMap. - // We are doing keccak only once per address. - .map(|((address, key), value)| ((keccak256(address), keccak256(key)), value)) - .collect::>(); - - let mut hashed_storage_keys: HashMap> = HashMap::default(); - for (hashed_address, hashed_slot) in hashed_storages.keys() { - hashed_storage_keys.entry(*hashed_address).or_default().insert(*hashed_slot); - } + .map(|entry| { + entry.map(|(BlockNumberAddress((_, address)), storage_entry)| { + (keccak256(address), keccak256(storage_entry.key), storage_entry.value) + }) + }) + .collect::, _>>()?; + hashed_storages.sort_by_key(|(ha, hk, _)| (*ha, *hk)); - hashed_storages - .into_iter() - // Apply values to HashedStorage (if Value is zero just remove it); - .try_for_each(|((hashed_address, key), value)| -> ProviderResult<()> { - if hashed_storage - .seek_by_key_subkey(hashed_address, key)? - .filter(|entry| entry.key == key) - .is_some() - { - hashed_storage.delete_current()?; - } + // Apply values to HashedState, and remove the account if it's None. + let mut hashed_storage_keys: HashMap> = HashMap::new(); + let mut hashed_storage = self.tx.cursor_dup_write::()?; + for (hashed_address, key, value) in hashed_storages.into_iter().rev() { + hashed_storage_keys.entry(hashed_address).or_default().insert(key); - if value != U256::ZERO { - hashed_storage.upsert(hashed_address, StorageEntry { key, value })?; - } - Ok(()) - })?; + if hashed_storage + .seek_by_key_subkey(hashed_address, key)? + .filter(|entry| entry.key == key) + .is_some() + { + hashed_storage.delete_current()?; + } + if value != U256::ZERO { + hashed_storage.upsert(hashed_address, StorageEntry { key, value })?; + } + } Ok(hashed_storage_keys) } @@ -1958,29 +1924,18 @@ impl HistoryWriter for DatabaseProvider { &self, range: Range, ) -> ProviderResult { - let storage_changesets = self + let mut storage_changesets = self .tx .cursor_read::()? .walk_range(range)? + .map(|entry| { + entry.map(|(BlockNumberAddress((bn, address)), storage)| (address, storage.key, bn)) + }) .collect::, _>>()?; - let changesets = storage_changesets.len(); - - let last_indices = storage_changesets - .into_iter() - // reverse so we can get lowest block number where we need to unwind account. - .rev() - // fold all storages and get last block number - .fold( - BTreeMap::new(), - |mut accounts: BTreeMap<(Address, B256), u64>, (index, storage)| { - // we just need address and lowest block number. - accounts.insert((index.address(), storage.key), index.block_number()); - accounts - }, - ); + storage_changesets.sort_by_key(|(address, key, _)| (*address, *key)); let mut cursor = self.tx.cursor_write::()?; - for ((address, storage_key), rem_index) in last_indices { + for &(address, storage_key, rem_index) in &storage_changesets { let partial_shard = unwind_history_shards::<_, tables::StorageHistory, _>( &mut cursor, StorageShardedKey::last(address, storage_key), @@ -2001,6 +1956,7 @@ impl HistoryWriter for DatabaseProvider { } } + let changesets = storage_changesets.len(); Ok(changesets) } @@ -2008,27 +1964,17 @@ impl HistoryWriter for DatabaseProvider { &self, range: RangeInclusive, ) -> ProviderResult { - let account_changeset = self + let mut last_indices = self .tx .cursor_read::()? .walk_range(range)? + .map(|entry| entry.map(|(index, account)| (account.address, index))) .collect::, _>>()?; - let changesets = account_changeset.len(); - - let last_indices = account_changeset - .into_iter() - // reverse so we can get lowest block number where we need to unwind account. - .rev() - // fold all account and get last block number - .fold(BTreeMap::new(), |mut accounts: BTreeMap, (index, account)| { - // we just need address and lowest block number. - accounts.insert(account.address, index); - accounts - }); + last_indices.sort_by_key(|(a, _)| *a); // Unwind the account history index. let mut cursor = self.tx.cursor_write::()?; - for (address, rem_index) in last_indices { + for &(address, rem_index) in &last_indices { let partial_shard = unwind_history_shards::<_, tables::AccountHistory, _>( &mut cursor, ShardedKey::last(address), @@ -2046,6 +1992,7 @@ impl HistoryWriter for DatabaseProvider { } } + let changesets = last_indices.len(); Ok(changesets) } } @@ -2125,6 +2072,7 @@ impl BlockExecutionWriter for DatabaseProvider { } trie_updates.flush(&self.tx)?; } + // get blocks let blocks = self.get_take_block_range::(chain_spec, range.clone())?; let unwind_to = blocks.first().map(|b| b.number.saturating_sub(1)); @@ -2348,3 +2296,17 @@ impl PruneCheckpointWriter for DatabaseProvider { Ok(self.tx.put::(segment, checkpoint)?) } } + +fn range_size_hint(range: &impl RangeBounds) -> Option { + let start = match range.start_bound().cloned() { + Bound::Included(start) => start, + Bound::Excluded(start) => start.checked_add(1)?, + Bound::Unbounded => 0, + }; + let end = match range.end_bound().cloned() { + Bound::Included(end) => end.saturating_add(1), + Bound::Excluded(end) => end, + Bound::Unbounded => return None, + }; + end.checked_sub(start).map(|x| x as _) +} From df5042c46d6fd61e8581461925a4c284fe704065 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 18 Dec 2023 08:53:33 +0100 Subject: [PATCH 163/277] chore(deps): weekly `cargo update` (#5798) Co-authored-by: github-merge-queue --- Cargo.lock | 246 +++++++++++++++++++++++++---------------------------- 1 file changed, 115 insertions(+), 131 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 58b4c51730cd..63d66a49170a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -140,9 +140,9 @@ checksum = "0942ffc6dcaadf03badf6e6a2d0228460359d5e34b57ccdc720b7382dfbd5ec5" [[package]] name = "alloy-dyn-abi" -version = "0.5.2" +version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fafc3b20c6d069d9db47037f34acfb0e079c050fa5c1ff9e79855609b359b92b" +checksum = "74ab9cc043cd4b0a806f79e32624c148efecd9c9395e4a75000d51fdc9726be0" dependencies = [ "alloy-json-abi", "alloy-primitives", @@ -158,9 +158,9 @@ dependencies = [ [[package]] name = "alloy-json-abi" -version = "0.5.2" +version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d32061da2f184e5defab8e65a3057f88b7017cfe1ea9e2d6b413edb5ca76a54" +checksum = "cf8889c85658aae27e96515ff2c9200cb8d8c78baefad5aee088e49b47f5f6f3" dependencies = [ "alloy-primitives", "alloy-sol-type-parser", @@ -170,9 +170,9 @@ dependencies = [ [[package]] name = "alloy-primitives" -version = "0.5.2" +version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08ca2c09d5911548a5cb620382ea0e1af99d3c898ce0efecbbd274a4676cf53e" +checksum = "6c0e5e60ff0e0c34c553822dabcfe0f5fece5a8c52f08a915be8c737de4b03fa" dependencies = [ "alloy-rlp", "arbitrary", @@ -213,14 +213,14 @@ checksum = "c0391754c09fab4eae3404d19d0d297aa1c670c1775ab51d8a5312afeca23157" dependencies = [ "proc-macro2", "quote", - "syn 2.0.39", + "syn 2.0.41", ] [[package]] name = "alloy-sol-macro" -version = "0.5.2" +version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e40cea54ac58080a1b88ea6556866eac1902b321186c77d53ad2b5ebbbf0e038" +checksum = "a8c9d43ca0a56b356f35775deecc8f660ac99e34cbf4a33462d4bd8addd9ab6f" dependencies = [ "const-hex", "dunce", @@ -229,25 +229,25 @@ dependencies = [ "proc-macro-error", "proc-macro2", "quote", - "syn 2.0.39", + "syn 2.0.41", "syn-solidity", "tiny-keccak", ] [[package]] name = "alloy-sol-type-parser" -version = "0.5.2" +version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f23cb462613b2046da46dbf69ebaee458b7bfd3e9d7fe05adcce38a8d4b8a14f" +checksum = "41cdf1064e9b5160ae47b5190171a0655c8b4966b9657b04f48ff5d868684ade" dependencies = [ "winnow", ] [[package]] name = "alloy-sol-types" -version = "0.5.2" +version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f81aa34725607be118c395d62c1d8d97c8a343dd1ada5370ed508ed5232eab6a" +checksum = "c169266a4b5ecf6f471947be10690f0aa295063774853b50540708b267a96e51" dependencies = [ "alloy-primitives", "alloy-sol-macro", @@ -341,7 +341,7 @@ dependencies = [ "proc-macro-error", "proc-macro2", "quote", - "syn 2.0.39", + "syn 2.0.41", ] [[package]] @@ -558,7 +558,7 @@ checksum = "16e62a023e7c117e27523144c5d2459f4397fcc3cab0085af8e2224f643a0193" dependencies = [ "proc-macro2", "quote", - "syn 2.0.39", + "syn 2.0.41", ] [[package]] @@ -569,7 +569,7 @@ checksum = "a66537f1bb974b254c98ed142ff995236e81b9d0fe4db0575f46612cb15eb0f9" dependencies = [ "proc-macro2", "quote", - "syn 2.0.39", + "syn 2.0.41", ] [[package]] @@ -750,7 +750,7 @@ dependencies = [ "regex", "rustc-hash", "shlex", - "syn 2.0.39", + "syn 2.0.41", "which", ] @@ -771,7 +771,7 @@ dependencies = [ "regex", "rustc-hash", "shlex", - "syn 2.0.39", + "syn 2.0.41", ] [[package]] @@ -975,7 +975,7 @@ checksum = "005fa0c5bd20805466dda55eb34cd709bb31a2592bb26927b47714eeed6914d8" dependencies = [ "proc-macro2", "quote", - "syn 2.0.39", + "syn 2.0.41", "synstructure", ] @@ -1281,7 +1281,7 @@ dependencies = [ "heck", "proc-macro2", "quote", - "syn 2.0.39", + "syn 2.0.41", ] [[package]] @@ -1315,7 +1315,7 @@ dependencies = [ "proc-macro2", "quote", "serde", - "syn 2.0.39", + "syn 2.0.41", ] [[package]] @@ -1436,9 +1436,9 @@ dependencies = [ [[package]] name = "const-oid" -version = "0.9.5" +version = "0.9.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28c122c3980598d243d63d9a704629a2d748d101f278052ff068be5a4423ab6f" +checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8" [[package]] name = "const-str" @@ -1565,9 +1565,9 @@ checksum = "7059fff8937831a9ae6f0fe4d658ffabf58f2ca96aa9dec1c889f936f705f216" [[package]] name = "crossbeam-channel" -version = "0.5.8" +version = "0.5.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a33c2bf77f2df06183c3aa30d1e96c0695a313d4f9c453cc3762a6db39f99200" +checksum = "14c3242926edf34aec4ac3a77108ad4854bffaa2e4ddc1824124ce59231302d5" dependencies = [ "cfg-if", "crossbeam-utils", @@ -1575,9 +1575,9 @@ dependencies = [ [[package]] name = "crossbeam-deque" -version = "0.8.3" +version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce6fd6f855243022dcecf8702fef0c297d4338e226845fe067f6341ad9fa0cef" +checksum = "fca89a0e215bab21874660c67903c5f143333cab1da83d041c7ded6053774751" dependencies = [ "cfg-if", "crossbeam-epoch", @@ -1586,22 +1586,21 @@ dependencies = [ [[package]] name = "crossbeam-epoch" -version = "0.9.15" +version = "0.9.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae211234986c545741a7dc064309f67ee1e5ad243d0e48335adc0484d960bcc7" +checksum = "2d2fe95351b870527a5d09bf563ed3c97c0cffb87cf1c78a591bf48bb218d9aa" dependencies = [ "autocfg", "cfg-if", "crossbeam-utils", "memoffset", - "scopeguard", ] [[package]] name = "crossbeam-utils" -version = "0.8.16" +version = "0.8.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a22b2d63d4d1dc0b7f1b6b2747dd0088008a9be28b6ddf0b1e7d335e3037294" +checksum = "c06d96137f14f244c37f989d9fff8f95e6c18b918e71f36638f8c49112e4c78f" dependencies = [ "cfg-if", ] @@ -1741,7 +1740,7 @@ checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3" dependencies = [ "proc-macro2", "quote", - "syn 2.0.39", + "syn 2.0.41", ] [[package]] @@ -1813,7 +1812,7 @@ dependencies = [ "proc-macro2", "quote", "strsim 0.10.0", - "syn 2.0.39", + "syn 2.0.41", ] [[package]] @@ -1846,7 +1845,7 @@ checksum = "836a9bbc7ad63342d6d6e7b815ccab164bc77a2d95d84bc3117a8c0d5c98e2d5" dependencies = [ "darling_core 0.20.3", "quote", - "syn 2.0.39", + "syn 2.0.41", ] [[package]] @@ -1932,7 +1931,7 @@ checksum = "67e77553c4162a157adbf834ebae5b415acbecbeafc7a74b0e886657506a7611" dependencies = [ "proc-macro2", "quote", - "syn 2.0.39", + "syn 2.0.41", ] [[package]] @@ -2106,7 +2105,7 @@ checksum = "487585f4d0c6655fe74905e2504d8ad6908e4db67f744eb140876906c2f3175d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.39", + "syn 2.0.41", ] [[package]] @@ -2307,7 +2306,7 @@ dependencies = [ "heck", "proc-macro2", "quote", - "syn 2.0.39", + "syn 2.0.41", ] [[package]] @@ -2320,7 +2319,7 @@ dependencies = [ "num-traits", "proc-macro2", "quote", - "syn 2.0.39", + "syn 2.0.41", ] [[package]] @@ -2331,7 +2330,7 @@ checksum = "c2ad8cef1d801a4686bfd8919f0b30eac4c8e48968c437a6405ded4fb5272d2b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.39", + "syn 2.0.41", ] [[package]] @@ -2479,7 +2478,7 @@ dependencies = [ "regex", "serde", "serde_json", - "syn 2.0.39", + "syn 2.0.41", "toml 0.8.2", "walkdir", ] @@ -2497,7 +2496,7 @@ dependencies = [ "proc-macro2", "quote", "serde_json", - "syn 2.0.39", + "syn 2.0.41", ] [[package]] @@ -2523,7 +2522,7 @@ dependencies = [ "serde", "serde_json", "strum", - "syn 2.0.39", + "syn 2.0.41", "tempfile", "thiserror", "tiny-keccak", @@ -2644,9 +2643,9 @@ dependencies = [ [[package]] name = "eyre" -version = "0.6.10" +version = "0.6.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8bbb8258be8305fb0237d7b295f47bb24ff1b136a535f473baf40e70468515aa" +checksum = "b6267a1fa6f59179ea4afc8e50fd8612a3cc60bc858f786ff877a4a8cb042799" dependencies = [ "indenter", "once_cell", @@ -2861,7 +2860,7 @@ checksum = "53b153fd91e4b0147f4aced87be237c98248656bb01050b96bf3ee89220a8ddb" dependencies = [ "proc-macro2", "quote", - "syn 2.0.39", + "syn 2.0.41", ] [[package]] @@ -3162,9 +3161,9 @@ checksum = "6fe2267d4ed49bc07b63801559be28c718ea06c4738b7a03c94df7386d2cde46" [[package]] name = "hkdf" -version = "0.12.3" +version = "0.12.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "791a029f6b9fc27657f6f188ec6e5e43f6911f6f878e0dc5501396e09809d437" +checksum = "7b5f8eb2ad728638ea2c7d47a21db23b7b58a72ed6a38256b8a1849f15fbbdf7" dependencies = [ "hmac", ] @@ -3180,11 +3179,11 @@ dependencies = [ [[package]] name = "home" -version = "0.5.5" +version = "0.5.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5444c27eef6923071f7ebcc33e3444508466a76f7a2b93da00ed6e19f30c1ddb" +checksum = "e3d1354bf6b7235cb4a0576c2619fd4ed18183f689b12b006a0ee7329eeff9a5" dependencies = [ - "windows-sys 0.48.0", + "windows-sys 0.52.0", ] [[package]] @@ -3724,6 +3723,15 @@ dependencies = [ "either", ] +[[package]] +name = "itertools" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "25db6b064527c5d482d0423354fcd07a89a2dfe07b67892e62411946db7f07b0" +dependencies = [ + "either", +] + [[package]] name = "itoa" version = "1.0.10" @@ -3994,9 +4002,9 @@ checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" [[package]] name = "libc" -version = "0.2.150" +version = "0.2.151" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89d92a4743f9a61002fae18374ed11e7973f530cb3a3255fb354818118b2203c" +checksum = "302d7ab3130588088d277783b1e2d2e10c9e9e4a16dd9050e6ec93fb3e7048f4" [[package]] name = "libloading" @@ -4175,9 +4183,9 @@ dependencies = [ [[package]] name = "memmap2" -version = "0.9.0" +version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "deaba38d7abf1d4cca21cc89e932e542ba2b9258664d2a9ef0e61512039c9375" +checksum = "8f850157af41022bbb1b04ed15c011ce4d59520be82a4e3718b10c34b02cb85e" dependencies = [ "libc", ] @@ -4204,9 +4212,9 @@ dependencies = [ [[package]] name = "metrics-exporter-prometheus" -version = "0.12.1" +version = "0.12.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a4964177ddfdab1e3a2b37aec7cf320e14169abb0ed73999f558136409178d5" +checksum = "1d4fa7ce7c4862db464a37b0b31d89bca874562f034bd7993895572783d02950" dependencies = [ "base64 0.21.5", "hyper", @@ -4228,7 +4236,7 @@ checksum = "ddece26afd34c31585c74a4db0630c376df271c285d682d1e55012197830b6df" dependencies = [ "proc-macro2", "quote", - "syn 2.0.39", + "syn 2.0.41", ] [[package]] @@ -4553,7 +4561,7 @@ dependencies = [ "proc-macro-crate 1.3.1", "proc-macro2", "quote", - "syn 2.0.39", + "syn 2.0.41", ] [[package]] @@ -4565,7 +4573,7 @@ dependencies = [ "proc-macro-crate 2.0.1", "proc-macro2", "quote", - "syn 2.0.39", + "syn 2.0.41", ] [[package]] @@ -4889,7 +4897,7 @@ dependencies = [ "phf_shared", "proc-macro2", "quote", - "syn 2.0.39", + "syn 2.0.41", ] [[package]] @@ -4918,7 +4926,7 @@ checksum = "4359fd9c9171ec6e8c62926d6faaf553a8dc3f64e1507e76da7911b4f6a04405" dependencies = [ "proc-macro2", "quote", - "syn 2.0.39", + "syn 2.0.41", ] [[package]] @@ -5109,7 +5117,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ae005bd773ab59b4725093fd7df83fd7892f7d8eafb48dbd7de6e024e4215f9d" dependencies = [ "proc-macro2", - "syn 2.0.39", + "syn 2.0.41", ] [[package]] @@ -6069,7 +6077,7 @@ dependencies = [ "quote", "regex", "serial_test", - "syn 2.0.39", + "syn 2.0.41", "trybuild", ] @@ -7200,7 +7208,7 @@ checksum = "43576ca501357b9b071ac53cdc7da8ef0cbd9493d8df094cd821777ea6e894d3" dependencies = [ "proc-macro2", "quote", - "syn 2.0.39", + "syn 2.0.41", ] [[package]] @@ -7272,7 +7280,7 @@ dependencies = [ "darling 0.20.3", "proc-macro2", "quote", - "syn 2.0.39", + "syn 2.0.41", ] [[package]] @@ -7297,7 +7305,7 @@ checksum = "91d129178576168c589c9ec973feedf7d3126c01ac2bf08795109aa35b69fb8f" dependencies = [ "proc-macro2", "quote", - "syn 2.0.39", + "syn 2.0.41", ] [[package]] @@ -7614,17 +7622,7 @@ dependencies = [ "proc-macro2", "quote", "rustversion", - "syn 2.0.39", -] - -[[package]] -name = "subprocess" -version = "0.2.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c2e86926081dda636c546d8c5e641661049d7562a68f5488be4a1f7f66f6086" -dependencies = [ - "libc", - "winapi", + "syn 2.0.41", ] [[package]] @@ -7672,7 +7670,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1cccfffbc6bb3bb2d3a26cd2077f4d055f6808d266f9d4d158797a4c60510dfe" dependencies = [ "debugid", - "memmap2 0.9.0", + "memmap2 0.9.1", "stable_deref_trait", "uuid 1.6.1", ] @@ -7701,9 +7699,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.39" +version = "2.0.41" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23e78b90f2fcf45d3e842032ce32e3f2d1545ba6636271dcbf24fa306d87be7a" +checksum = "44c8b28c477cc3bf0e7966561e3460130e1255f7a1cf71931075f1c5e7a7e269" dependencies = [ "proc-macro2", "quote", @@ -7712,14 +7710,14 @@ dependencies = [ [[package]] name = "syn-solidity" -version = "0.5.2" +version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2c7ad08db24862d5b787a94714ff6b047935c3e3f60af944ac969404debd7ff" +checksum = "c4e95b65f5854377a31ebfa69d71b87c9d0d9922fddbfeb91a8eda4a0c5868eb" dependencies = [ "paste", "proc-macro2", "quote", - "syn 2.0.39", + "syn 2.0.41", ] [[package]] @@ -7730,7 +7728,7 @@ checksum = "285ba80e733fac80aa4270fbcdf83772a79b80aa35c97075320abfee4a915b06" dependencies = [ "proc-macro2", "quote", - "syn 2.0.39", + "syn 2.0.41", "unicode-xid", ] @@ -7791,9 +7789,9 @@ checksum = "3369f5ac52d5eb6ab48c6b4ffdc8efbcad6b89c765749064ba298f2c68a16a76" [[package]] name = "test-fuzz" -version = "4.0.4" +version = "4.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "de8cb3597f1463b9c98b21c08d11033166a57942e60e8044e7e3bb4a8ca5416b" +checksum = "470137c4c87413dd450bef3d516e6bf88a6dfc70e3f5e359dedbc7f9fc6992b3" dependencies = [ "serde", "test-fuzz-internal", @@ -7803,9 +7801,9 @@ dependencies = [ [[package]] name = "test-fuzz-internal" -version = "4.0.4" +version = "4.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3dd8da182ee4e8b195da3aa38f72b84d267bda3874cd6ef8dd29c03a71f866f2" +checksum = "9dc636f7d25fd41828408b08931216b93a4d18454a72e2d55d78bc3b78dc167e" dependencies = [ "bincode", "cargo_metadata", @@ -7814,26 +7812,25 @@ dependencies = [ [[package]] name = "test-fuzz-macro" -version = "4.0.4" +version = "4.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "86cb030b9e51def5bd7bf98b3ee6e81aae7f021ebf2e05e70029b768508c376f" +checksum = "4da89cfe93508da3676114d5a15aa26e29aa88d5230e22bc5ea5f3c1f08ef5f6" dependencies = [ "darling 0.20.3", "if_chain", - "itertools 0.11.0", + "itertools 0.12.0", "once_cell", + "prettyplease", "proc-macro2", "quote", - "subprocess", - "syn 2.0.39", - "toolchain_find", + "syn 2.0.41", ] [[package]] name = "test-fuzz-runtime" -version = "4.0.4" +version = "4.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd6e7a964e6c5b20df8b03572f7fa43aa28d80fa4871b3083e597ed32664f614" +checksum = "09f701acb832f8c7911fa32863683cd9e02fe83976ed6bc9a9f56c2e4fbea936" dependencies = [ "hex", "num-traits", @@ -7850,22 +7847,22 @@ checksum = "a38c90d48152c236a3ab59271da4f4ae63d678c5d7ad6b7714d7cb9760be5e4b" [[package]] name = "thiserror" -version = "1.0.50" +version = "1.0.51" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f9a7210f5c9a7156bb50aa36aed4c95afb51df0df00713949448cf9e97d382d2" +checksum = "f11c217e1416d6f036b870f14e0413d480dbf28edbee1f877abaf0206af43bb7" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.50" +version = "1.0.51" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "266b2e40bc00e5a6c09c3584011e08b06f123c00362c92b975ba9843aaaa14b8" +checksum = "01742297787513b79cf8e29d1056ede1313e2420b7b3b15d0a768b4921f549df" dependencies = [ "proc-macro2", "quote", - "syn 2.0.39", + "syn 2.0.41", ] [[package]] @@ -7990,7 +7987,7 @@ checksum = "5b8a1e28f2deaa14e508979454cb3a223b10b938b45af148bc0986de36f1923b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.39", + "syn 2.0.41", ] [[package]] @@ -8097,19 +8094,6 @@ dependencies = [ "winnow", ] -[[package]] -name = "toolchain_find" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ebc8c9a7f0a2966e1acdaf0461023d0b01471eeead645370cf4c3f5cff153f2a" -dependencies = [ - "home", - "once_cell", - "regex", - "semver 1.0.20", - "walkdir", -] - [[package]] name = "tower" version = "0.4.13" @@ -8217,7 +8201,7 @@ checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.39", + "syn 2.0.41", ] [[package]] @@ -8682,7 +8666,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn 2.0.39", + "syn 2.0.41", "wasm-bindgen-shared", ] @@ -8716,7 +8700,7 @@ checksum = "f0eb82fcb7930ae6219a7ecfd55b217f5f0893484b7a13022ebb2b2bf20b5283" dependencies = [ "proc-macro2", "quote", - "syn 2.0.39", + "syn 2.0.41", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -9030,9 +9014,9 @@ checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04" [[package]] name = "winnow" -version = "0.5.26" +version = "0.5.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b67b5f0a4e7a27a64c651977932b9dc5667ca7fc31ac44b03ed37a0cf42fdfff" +checksum = "6c830786f7720c2fd27a1a0e27a709dbd3c4d009b56d098fc742d4f4eab91fe2" dependencies = [ "memchr", ] @@ -9137,28 +9121,28 @@ checksum = "9e6936f0cce458098a201c245a11bef556c6a0181129c7034d10d76d1ec3a2b8" dependencies = [ "proc-macro2", "quote", - "syn 2.0.39", + "syn 2.0.41", "synstructure", ] [[package]] name = "zerocopy" -version = "0.7.30" +version = "0.7.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "306dca4455518f1f31635ec308b6b3e4eb1b11758cefafc782827d0aa7acb5c7" +checksum = "1c4061bedbb353041c12f413700357bec76df2c7e2ca8e4df8bac24c6bf68e3d" dependencies = [ "zerocopy-derive", ] [[package]] name = "zerocopy-derive" -version = "0.7.30" +version = "0.7.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be912bf68235a88fbefd1b73415cb218405958d1655b2ece9035a19920bdf6ba" +checksum = "b3c129550b3e6de3fd0ba67ba5c81818f9805e58b8d7fee80a3a59d2c9fc601a" dependencies = [ "proc-macro2", "quote", - "syn 2.0.39", + "syn 2.0.41", ] [[package]] @@ -9178,7 +9162,7 @@ checksum = "e6a647510471d372f2e6c2e6b7219e44d8c574d24fdc11c610a61455782f18c3" dependencies = [ "proc-macro2", "quote", - "syn 2.0.39", + "syn 2.0.41", "synstructure", ] @@ -9199,7 +9183,7 @@ checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" dependencies = [ "proc-macro2", "quote", - "syn 2.0.39", + "syn 2.0.41", ] [[package]] @@ -9222,7 +9206,7 @@ checksum = "7a4a1638a1934450809c2266a70362bfc96cd90550c073f5b8a55014d1010157" dependencies = [ "proc-macro2", "quote", - "syn 2.0.39", + "syn 2.0.41", ] [[package]] From b6831c66f05fe8d1de6cfc841020f0f113401837 Mon Sep 17 00:00:00 2001 From: leonarddt05 <139609434+leonarddt05@users.noreply.github.com> Date: Mon, 18 Dec 2023 10:54:31 +0300 Subject: [PATCH 164/277] docs: adjust arm device guide (#5799) --- book/installation/build-for-arm-devices.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/book/installation/build-for-arm-devices.md b/book/installation/build-for-arm-devices.md index d07d4cb49f13..10ecafd3ec51 100644 --- a/book/installation/build-for-arm-devices.md +++ b/book/installation/build-for-arm-devices.md @@ -1,6 +1,6 @@ # Building for ARM devices -Reth can be built for and run on ARM devices, but there are a few things to take into considerations before. +Reth can be built for and run on ARM devices, but there are a few things to take into consideration before. ## CPU Architecture @@ -8,7 +8,7 @@ First, you must have a 64-bit CPU and Operating System, otherwise some of the pr ## Memory Layout on AArch64 -Then, you must setup the virtual memory layout in such a way that the user space is sufficiently large. +Then, you must set up the virtual memory layout in such that the user space is sufficiently large. From [the Linux Kernel documentation](https://www.kernel.org/doc/html/v5.3/arm64/memory.html#:~:text=AArch64%20Linux%20uses%20either%203,for%20both%20user%20and%20kernel.), you can see that the memory layout with 4KB pages and a level-3 translation table limits the user space to 512GB, which is too low for Reth to sync on Ethereum mainnet. ## ARM Board Virtual Memory Limitation From 5e7bd64d93f4c7fc5c3a3bf0fa4fb5f28b9fb856 Mon Sep 17 00:00:00 2001 From: Matthias Seitz Date: Mon, 18 Dec 2023 08:59:59 +0100 Subject: [PATCH 165/277] chore: add note about grammar+typo prs (#5808) --- CONTRIBUTING.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 4e0cce56a5f1..1f9191be4398 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -39,6 +39,11 @@ There are fundamentally three ways an individual can contribute: **Anybody can participate in any stage of contribution**. We urge you to participate in the discussion around bugs and participate in reviewing PRs. +### Contributions Related to Spelling and Grammar + +At this time, we will not be accepting contributions that only fix spelling or grammatical errors in documentation, code or +elsewhere. + ### Asking for help From fcdd31a6f4ad88d9b2051da4644fcc00df2ce235 Mon Sep 17 00:00:00 2001 From: Matthias Seitz Date: Mon, 18 Dec 2023 09:03:37 +0100 Subject: [PATCH 166/277] chore: fix new clippy lint (#5806) --- crates/rpc/rpc/src/eth/api/transactions.rs | 61 +++++++++------------- 1 file changed, 24 insertions(+), 37 deletions(-) diff --git a/crates/rpc/rpc/src/eth/api/transactions.rs b/crates/rpc/rpc/src/eth/api/transactions.rs index cbff163e5247..e8f4c158eef1 100644 --- a/crates/rpc/rpc/src/eth/api/transactions.rs +++ b/crates/rpc/rpc/src/eth/api/transactions.rs @@ -950,45 +950,32 @@ where l1_block_info: Option, block_timestamp: u64, ) -> EthResult { - if let Some(l1_block_info) = l1_block_info { - let envelope_buf: Bytes = { - let mut envelope_buf = bytes::BytesMut::default(); - tx.encode_enveloped(&mut envelope_buf); - envelope_buf.freeze().into() - }; - - let (l1_fee, l1_data_gas) = match (!tx.is_deposit()) - .then(|| { - let inner_l1_fee = match l1_block_info.l1_tx_data_fee( - &self.inner.provider.chain_spec(), - block_timestamp, - &envelope_buf, - tx.is_deposit(), - ) { - Ok(inner_l1_fee) => inner_l1_fee, - Err(e) => return Err(e), - }; - let inner_l1_data_gas = match l1_block_info.l1_data_gas( - &self.inner.provider.chain_spec(), - block_timestamp, - &envelope_buf, - ) { - Ok(inner_l1_data_gas) => inner_l1_data_gas, - Err(e) => return Err(e), - }; - Ok((inner_l1_fee, inner_l1_data_gas)) - }) - .transpose() - .map_err(|_| EthApiError::InternalEthError)? - { - Some((l1_fee, l1_data_gas)) => (Some(l1_fee), Some(l1_data_gas)), - None => (None, None), - }; + let Some(l1_block_info) = l1_block_info else { return Ok(OptimismTxMeta::default()) }; + + let envelope_buf: Bytes = { + let mut envelope_buf = bytes::BytesMut::new(); + tx.encode_enveloped(&mut envelope_buf); + envelope_buf.freeze().into() + }; - Ok(OptimismTxMeta::new(Some(l1_block_info), l1_fee, l1_data_gas)) + let (l1_fee, l1_data_gas) = if tx.is_deposit() { + let inner_l1_fee = l1_block_info + .l1_tx_data_fee( + &self.inner.provider.chain_spec(), + block_timestamp, + &envelope_buf, + tx.is_deposit(), + ) + .map_err(|_| EthApiError::InternalEthError)?; + let inner_l1_data_gas = l1_block_info + .l1_data_gas(&self.inner.provider.chain_spec(), block_timestamp, &envelope_buf) + .map_err(|_| EthApiError::InternalEthError)?; + (Some(inner_l1_fee), Some(inner_l1_data_gas)) } else { - Ok(OptimismTxMeta::default()) - } + (None, None) + }; + + Ok(OptimismTxMeta::new(Some(l1_block_info), l1_fee, l1_data_gas)) } /// Helper function for `eth_sendRawTransaction` for Optimism. From bb6f6f43c2e9345b6f468beea9558486b3092a94 Mon Sep 17 00:00:00 2001 From: yjh Date: Mon, 18 Dec 2023 16:07:25 +0800 Subject: [PATCH 167/277] feat: support no-std for reth-codecs (#5756) --- crates/storage/codecs/Cargo.toml | 6 +++++- crates/storage/codecs/src/lib.rs | 12 ++++++++---- 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/crates/storage/codecs/Cargo.toml b/crates/storage/codecs/Cargo.toml index 18a028691b06..05d9e7403df6 100644 --- a/crates/storage/codecs/Cargo.toml +++ b/crates/storage/codecs/Cargo.toml @@ -24,7 +24,11 @@ proptest.workspace = true proptest-derive.workspace = true [features] -default = ["compact"] +default = ["compact", "std"] +std = [ + "alloy-primitives/std", + "bytes/std", +] compact = ["codecs-derive/compact"] scale = ["codecs-derive/scale"] postcard = ["codecs-derive/postcard"] diff --git a/crates/storage/codecs/src/lib.rs b/crates/storage/codecs/src/lib.rs index 93d5e4ec22b7..d3cdbaa01442 100644 --- a/crates/storage/codecs/src/lib.rs +++ b/crates/storage/codecs/src/lib.rs @@ -15,6 +15,10 @@ #![deny(unused_must_use, rust_2018_idioms)] #![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))] #![allow(clippy::non_canonical_clone_impl)] +#![cfg_attr(not(feature = "std"), no_std)] + +extern crate alloc; +use alloc::vec::Vec; pub use codecs_derive::*; @@ -78,7 +82,7 @@ macro_rules! impl_uint_compact { { let leading = self.leading_zeros() as usize / 8; buf.put_slice(&self.to_be_bytes()[leading..]); - std::mem::size_of::<$name>() - leading + core::mem::size_of::<$name>() - leading } #[inline] @@ -87,8 +91,8 @@ macro_rules! impl_uint_compact { return (0, buf); } - let mut arr = [0; std::mem::size_of::<$name>()]; - arr[std::mem::size_of::<$name>() - len..].copy_from_slice(&buf[..len]); + let mut arr = [0; core::mem::size_of::<$name>()]; + arr[core::mem::size_of::<$name>() - len..].copy_from_slice(&buf[..len]); buf.advance(len); ($name::from_be_bytes(arr), buf) } @@ -317,7 +321,7 @@ macro_rules! impl_compact_for_bytes { #[inline] fn from_compact(buf: &[u8], len: usize) -> (Self, &[u8]) { - let (v, buf) = <[u8; std::mem::size_of::<$name>()]>::from_compact(buf, len); + let (v, buf) = <[u8; core::mem::size_of::<$name>()]>::from_compact(buf, len); (Self::from(v), buf) } } From fb871bf3fa0b78e4df6a7a0f3e6f40e6526351ed Mon Sep 17 00:00:00 2001 From: Thomas Coratger <60488569+tcoratger@users.noreply.github.com> Date: Mon, 18 Dec 2023 09:15:33 +0100 Subject: [PATCH 168/277] refactor: small refactoring for `Header` structure (#5801) Co-authored-by: Matthias Seitz --- crates/primitives/src/header.rs | 89 ++++++++++++++++++--------------- 1 file changed, 49 insertions(+), 40 deletions(-) diff --git a/crates/primitives/src/header.rs b/crates/primitives/src/header.rs index f86c9f205986..707c5f27027a 100644 --- a/crates/primitives/src/header.rs +++ b/crates/primitives/src/header.rs @@ -132,12 +132,9 @@ impl Header { /// Checks if the header is empty - has no transactions and no ommers pub fn is_empty(&self) -> bool { - let txs_and_ommers_empty = self.transaction_root_is_empty() && self.ommers_hash_is_empty(); - if let Some(withdrawals_root) = self.withdrawals_root { - txs_and_ommers_empty && withdrawals_root == EMPTY_ROOT_HASH - } else { - txs_and_ommers_empty - } + self.transaction_root_is_empty() && + self.ommers_hash_is_empty() && + self.withdrawals_root.map_or(true, |root| root == EMPTY_ROOT_HASH) } /// Check if the ommers hash equals to empty hash list. @@ -236,51 +233,59 @@ impl Header { fn header_payload_length(&self) -> usize { let mut length = 0; - length += self.parent_hash.length(); - length += self.ommers_hash.length(); - length += self.beneficiary.length(); - length += self.state_root.length(); - length += self.transactions_root.length(); - length += self.receipts_root.length(); - length += self.logs_bloom.length(); - length += self.difficulty.length(); - length += U256::from(self.number).length(); - length += U256::from(self.gas_limit).length(); - length += U256::from(self.gas_used).length(); - length += self.timestamp.length(); - length += self.extra_data.length(); - length += self.mix_hash.length(); - length += B64::new(self.nonce.to_be_bytes()).length(); + length += self.parent_hash.length(); // Hash of the previous block. + length += self.ommers_hash.length(); // Hash of uncle blocks. + length += self.beneficiary.length(); // Address that receives rewards. + length += self.state_root.length(); // Root hash of the state object. + length += self.transactions_root.length(); // Root hash of transactions in the block. + length += self.receipts_root.length(); // Hash of transaction receipts. + length += self.logs_bloom.length(); // Data structure containing event logs. + length += self.difficulty.length(); // Difficulty value of the block. + length += U256::from(self.number).length(); // Block number. + length += U256::from(self.gas_limit).length(); // Maximum gas allowed. + length += U256::from(self.gas_used).length(); // Actual gas used. + length += self.timestamp.length(); // Block timestamp. + length += self.extra_data.length(); // Additional arbitrary data. + length += self.mix_hash.length(); // Hash used for mining. + length += B64::new(self.nonce.to_be_bytes()).length(); // Nonce for mining. if let Some(base_fee) = self.base_fee_per_gas { + // Adding base fee length if it exists. length += U256::from(base_fee).length(); } else if self.withdrawals_root.is_some() || self.blob_gas_used.is_some() || self.excess_blob_gas.is_some() || self.parent_beacon_block_root.is_some() { - length += 1; // EMPTY LIST CODE + // Placeholder code for empty lists. + length += 1; } if let Some(root) = self.withdrawals_root { + // Adding withdrawals_root length if it exists. length += root.length(); } else if self.blob_gas_used.is_some() || self.excess_blob_gas.is_some() || self.parent_beacon_block_root.is_some() { - length += 1; // EMPTY STRING CODE + // Placeholder code for a missing string value. + length += 1; } if let Some(blob_gas_used) = self.blob_gas_used { + // Adding blob_gas_used length if it exists. length += U256::from(blob_gas_used).length(); } else if self.excess_blob_gas.is_some() || self.parent_beacon_block_root.is_some() { - length += 1; // EMPTY LIST CODE + // Placeholder code for empty lists. + length += 1; } if let Some(excess_blob_gas) = self.excess_blob_gas { + // Adding excess_blob_gas length if it exists. length += U256::from(excess_blob_gas).length(); } else if self.parent_beacon_block_root.is_some() { - length += 1; // EMPTY LIST CODE + // Placeholder code for empty lists. + length += 1; } // Encode parent beacon block root length. If new fields are added, the above pattern will @@ -300,24 +305,28 @@ impl Header { impl Encodable for Header { fn encode(&self, out: &mut dyn BufMut) { + // Create a header indicating the encoded content is a list with the payload length computed + // from the header's payload calculation function. let list_header = alloy_rlp::Header { list: true, payload_length: self.header_payload_length() }; list_header.encode(out); - self.parent_hash.encode(out); - self.ommers_hash.encode(out); - self.beneficiary.encode(out); - self.state_root.encode(out); - self.transactions_root.encode(out); - self.receipts_root.encode(out); - self.logs_bloom.encode(out); - self.difficulty.encode(out); - U256::from(self.number).encode(out); - U256::from(self.gas_limit).encode(out); - U256::from(self.gas_used).encode(out); - self.timestamp.encode(out); - self.extra_data.encode(out); - self.mix_hash.encode(out); - B64::new(self.nonce.to_be_bytes()).encode(out); + + // Encode each header field sequentially + self.parent_hash.encode(out); // Encode parent hash. + self.ommers_hash.encode(out); // Encode ommer's hash. + self.beneficiary.encode(out); // Encode beneficiary. + self.state_root.encode(out); // Encode state root. + self.transactions_root.encode(out); // Encode transactions root. + self.receipts_root.encode(out); // Encode receipts root. + self.logs_bloom.encode(out); // Encode logs bloom. + self.difficulty.encode(out); // Encode difficulty. + U256::from(self.number).encode(out); // Encode block number. + U256::from(self.gas_limit).encode(out); // Encode gas limit. + U256::from(self.gas_used).encode(out); // Encode gas used. + self.timestamp.encode(out); // Encode timestamp. + self.extra_data.encode(out); // Encode extra data. + self.mix_hash.encode(out); // Encode mix hash. + B64::new(self.nonce.to_be_bytes()).encode(out); // Encode nonce. // Encode base fee. Put empty list if base fee is missing, // but withdrawals root is present. From 18c881532f30900c3ead6d02d22f119b67e2ac1a Mon Sep 17 00:00:00 2001 From: Niclas Blomberg Date: Mon, 18 Dec 2023 10:33:35 +0100 Subject: [PATCH 169/277] feat: add SSZ encoding for `SignedBidSubmission` (#5787) Co-authored-by: Matthias Seitz --- .../rpc/rpc-types/src/eth/engine/payload.rs | 191 ++++++++++++++++++ crates/rpc/rpc-types/src/eth/withdrawal.rs | 1 + crates/rpc/rpc-types/src/relay.rs | 73 +++++-- .../relay/signed_bid_submission_capella.ssz | Bin 0 -> 352239 bytes 4 files changed, 252 insertions(+), 13 deletions(-) create mode 100644 crates/rpc/rpc-types/test_data/relay/signed_bid_submission_capella.ssz diff --git a/crates/rpc/rpc-types/src/eth/engine/payload.rs b/crates/rpc/rpc-types/src/eth/engine/payload.rs index 9ae18a351b10..2790a78ebb30 100644 --- a/crates/rpc/rpc-types/src/eth/engine/payload.rs +++ b/crates/rpc/rpc-types/src/eth/engine/payload.rs @@ -121,6 +121,7 @@ pub struct ExecutionPayloadEnvelopeV3 { /// See also: #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] +#[cfg_attr(feature = "ssz", derive(ssz_derive::Encode, ssz_derive::Decode))] pub struct ExecutionPayloadV1 { pub parent_hash: B256, pub fee_recipient: Address, @@ -164,6 +165,97 @@ impl ExecutionPayloadV2 { } } +#[cfg(feature = "ssz")] +impl ssz::Decode for ExecutionPayloadV2 { + fn is_ssz_fixed_len() -> bool { + false + } + + fn from_ssz_bytes(bytes: &[u8]) -> Result { + let mut builder = ssz::SszDecoderBuilder::new(bytes); + + builder.register_type::()?; + builder.register_type::
()?; + builder.register_type::()?; + builder.register_type::()?; + builder.register_type::()?; + builder.register_type::()?; + builder.register_type::()?; + builder.register_type::()?; + builder.register_type::()?; + builder.register_type::()?; + builder.register_type::()?; + builder.register_type::()?; + builder.register_type::()?; + builder.register_type::>()?; + builder.register_type::>()?; + + let mut decoder = builder.build()?; + + Ok(Self { + payload_inner: ExecutionPayloadV1 { + parent_hash: decoder.decode_next()?, + fee_recipient: decoder.decode_next()?, + state_root: decoder.decode_next()?, + receipts_root: decoder.decode_next()?, + logs_bloom: decoder.decode_next()?, + prev_randao: decoder.decode_next()?, + block_number: decoder.decode_next()?, + gas_limit: decoder.decode_next()?, + gas_used: decoder.decode_next()?, + timestamp: decoder.decode_next()?, + extra_data: decoder.decode_next()?, + base_fee_per_gas: decoder.decode_next()?, + block_hash: decoder.decode_next()?, + transactions: decoder.decode_next()?, + }, + withdrawals: decoder.decode_next()?, + }) + } +} + +#[cfg(feature = "ssz")] +impl ssz::Encode for ExecutionPayloadV2 { + fn is_ssz_fixed_len() -> bool { + false + } + + fn ssz_bytes_len(&self) -> usize { + ::ssz_bytes_len(&self.payload_inner) + + ssz::BYTES_PER_LENGTH_OFFSET + + self.withdrawals.ssz_bytes_len() + } + + fn ssz_append(&self, buf: &mut Vec) { + let offset = ::ssz_fixed_len() * 5 + +
::ssz_fixed_len() + + ::ssz_fixed_len() + + ::ssz_fixed_len() * 4 + + ::ssz_fixed_len() + + ssz::BYTES_PER_LENGTH_OFFSET * 3; + + let mut encoder = ssz::SszEncoder::container(buf, offset); + + encoder.append(&self.payload_inner.parent_hash); + encoder.append(&self.payload_inner.fee_recipient); + encoder.append(&self.payload_inner.state_root); + encoder.append(&self.payload_inner.receipts_root); + encoder.append(&self.payload_inner.logs_bloom); + encoder.append(&self.payload_inner.prev_randao); + encoder.append(&self.payload_inner.block_number); + encoder.append(&self.payload_inner.gas_limit); + encoder.append(&self.payload_inner.gas_used); + encoder.append(&self.payload_inner.timestamp); + encoder.append(&self.payload_inner.extra_data); + encoder.append(&self.payload_inner.base_fee_per_gas); + encoder.append(&self.payload_inner.block_hash); + encoder.append(&self.payload_inner.transactions); + encoder.append(&self.withdrawals); + + encoder.finalize(); + } +} + /// This structure maps on the ExecutionPayloadV3 structure of the beacon chain spec. /// /// See also: @@ -196,8 +288,107 @@ impl ExecutionPayloadV3 { } } +#[cfg(feature = "ssz")] +impl ssz::Decode for ExecutionPayloadV3 { + fn is_ssz_fixed_len() -> bool { + false + } + + fn from_ssz_bytes(bytes: &[u8]) -> Result { + let mut builder = ssz::SszDecoderBuilder::new(bytes); + + builder.register_type::()?; + builder.register_type::
()?; + builder.register_type::()?; + builder.register_type::()?; + builder.register_type::()?; + builder.register_type::()?; + builder.register_type::()?; + builder.register_type::()?; + builder.register_type::()?; + builder.register_type::()?; + builder.register_type::()?; + builder.register_type::()?; + builder.register_type::()?; + builder.register_type::>()?; + builder.register_type::>()?; + builder.register_type::()?; + builder.register_type::()?; + + let mut decoder = builder.build()?; + + Ok(Self { + payload_inner: ExecutionPayloadV2 { + payload_inner: ExecutionPayloadV1 { + parent_hash: decoder.decode_next()?, + fee_recipient: decoder.decode_next()?, + state_root: decoder.decode_next()?, + receipts_root: decoder.decode_next()?, + logs_bloom: decoder.decode_next()?, + prev_randao: decoder.decode_next()?, + block_number: decoder.decode_next()?, + gas_limit: decoder.decode_next()?, + gas_used: decoder.decode_next()?, + timestamp: decoder.decode_next()?, + extra_data: decoder.decode_next()?, + base_fee_per_gas: decoder.decode_next()?, + block_hash: decoder.decode_next()?, + transactions: decoder.decode_next()?, + }, + withdrawals: decoder.decode_next()?, + }, + blob_gas_used: decoder.decode_next()?, + excess_blob_gas: decoder.decode_next()?, + }) + } +} + +#[cfg(feature = "ssz")] +impl ssz::Encode for ExecutionPayloadV3 { + fn is_ssz_fixed_len() -> bool { + false + } + + fn ssz_bytes_len(&self) -> usize { + ::ssz_bytes_len(&self.payload_inner) + + ::ssz_fixed_len() * 2 + } + + fn ssz_append(&self, buf: &mut Vec) { + let offset = ::ssz_fixed_len() * 5 + +
::ssz_fixed_len() + + ::ssz_fixed_len() + + ::ssz_fixed_len() * 6 + + ::ssz_fixed_len() + + ssz::BYTES_PER_LENGTH_OFFSET * 3; + + let mut encoder = ssz::SszEncoder::container(buf, offset); + + encoder.append(&self.payload_inner.payload_inner.parent_hash); + encoder.append(&self.payload_inner.payload_inner.fee_recipient); + encoder.append(&self.payload_inner.payload_inner.state_root); + encoder.append(&self.payload_inner.payload_inner.receipts_root); + encoder.append(&self.payload_inner.payload_inner.logs_bloom); + encoder.append(&self.payload_inner.payload_inner.prev_randao); + encoder.append(&self.payload_inner.payload_inner.block_number); + encoder.append(&self.payload_inner.payload_inner.gas_limit); + encoder.append(&self.payload_inner.payload_inner.gas_used); + encoder.append(&self.payload_inner.payload_inner.timestamp); + encoder.append(&self.payload_inner.payload_inner.extra_data); + encoder.append(&self.payload_inner.payload_inner.base_fee_per_gas); + encoder.append(&self.payload_inner.payload_inner.block_hash); + encoder.append(&self.payload_inner.payload_inner.transactions); + encoder.append(&self.payload_inner.withdrawals); + encoder.append(&self.blob_gas_used); + encoder.append(&self.excess_blob_gas); + + encoder.finalize(); + } +} + /// This includes all bundled blob related data of an executed payload. #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] +#[cfg_attr(feature = "ssz", derive(ssz_derive::Encode, ssz_derive::Decode))] pub struct BlobsBundleV1 { pub commitments: Vec, pub proofs: Vec, diff --git a/crates/rpc/rpc-types/src/eth/withdrawal.rs b/crates/rpc/rpc-types/src/eth/withdrawal.rs index 286803f455d0..c8f54d4ec6a3 100644 --- a/crates/rpc/rpc-types/src/eth/withdrawal.rs +++ b/crates/rpc/rpc-types/src/eth/withdrawal.rs @@ -13,6 +13,7 @@ pub const GWEI_TO_WEI: u64 = 1_000_000_000; #[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")] diff --git a/crates/rpc/rpc-types/src/relay.rs b/crates/rpc/rpc-types/src/relay.rs index 51ac83d21649..9b2b2910f557 100644 --- a/crates/rpc/rpc-types/src/relay.rs +++ b/crates/rpc/rpc-types/src/relay.rs @@ -3,8 +3,7 @@ use crate::{ beacon::{BlsPublicKey, BlsSignature}, - engine::BlobsBundleV1, - ExecutionPayload, + engine::{BlobsBundleV1, ExecutionPayloadV1, ExecutionPayloadV2, ExecutionPayloadV3}, }; use alloy_primitives::{Address, B256, U256}; use serde::{Deserialize, Serialize}; @@ -71,19 +70,39 @@ pub struct SignedBidTrace { pub signature: BlsSignature, } -/// Submission for the `/relay/v1/builder/blocks` endpoint. +/// Submission for the `/relay/v1/builder/blocks` endpoint (Bellatrix). #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] -pub struct SignedBidSubmission { +#[serde(deny_unknown_fields)] +#[cfg_attr(feature = "ssz", derive(ssz_derive::Decode, ssz_derive::Encode))] +pub struct SignedBidSubmissionV1 { pub message: BidTrace, + #[serde(with = "crate::beacon::payload::beacon_payload_v1")] + pub execution_payload: ExecutionPayloadV1, pub signature: BlsSignature, - #[serde(with = "crate::beacon::payload::beacon_payload")] - pub execution_payload: ExecutionPayload, +} + +/// Submission for the `/relay/v1/builder/blocks` endpoint (Capella). +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +#[serde(deny_unknown_fields)] +#[cfg_attr(feature = "ssz", derive(ssz_derive::Decode, ssz_derive::Encode))] +pub struct SignedBidSubmissionV2 { + pub message: BidTrace, + #[serde(with = "crate::beacon::payload::beacon_payload_v2")] + pub execution_payload: ExecutionPayloadV2, + pub signature: BlsSignature, +} + +/// Submission for the `/relay/v1/builder/blocks` endpoint (Deneb). +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +#[serde(deny_unknown_fields)] +#[cfg_attr(feature = "ssz", derive(ssz_derive::Decode, ssz_derive::Encode))] +pub struct SignedBidSubmissionV3 { + pub message: BidTrace, + #[serde(with = "crate::beacon::payload::beacon_payload_v3")] + pub execution_payload: ExecutionPayloadV3, /// The Deneb block bundle for this bid. - /// - /// Only valid for [ExecutionPayloadV3](crate::eth::engine::ExecutionPayloadV3). - // TODO should this be combined with `execution_payload`? - #[serde(default, skip_serializing_if = "Option::is_none")] - pub blobs_bundle: Option, + pub blobs_bundle: BlobsBundleV1, + pub signature: BlsSignature, } /// Query for the GET `/relay/v1/data/bidtraces/proposer_payload_delivered` @@ -234,12 +253,40 @@ mod tests { assert_eq!(json, serde_json::to_value(validators).unwrap()); } + #[test] + fn bellatrix_bid_submission() { + let s = r#"{"message":{"slot":"1","parent_hash":"0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2","block_hash":"0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2","builder_pubkey":"0x93247f2209abcacf57b75a51dafae777f9dd38bc7053d1af526f220a7489a6d3a2753e5f3e8b1cfe39b56f43611df74a", "proposer_pubkey": "0x93247f2209abcacf57b75a51dafae777f9dd38bc7053d1af526f220a7489a6d3a2753e5f3e8b1cfe39b56f43611df74a","proposer_fee_recipient":"0xabcf8e0d4e9587369b2301d0790347320302cc09","gas_limit":"1","gas_used":"1","value":"1"},"execution_payload":{"parent_hash":"0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2","fee_recipient":"0xabcf8e0d4e9587369b2301d0790347320302cc09","state_root":"0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2","receipts_root":"0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2","logs_bloom":"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000","prev_randao":"0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2","block_number":"1","gas_limit":"1","gas_used":"1","timestamp":"1","extra_data":"0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2","base_fee_per_gas":"1","block_hash":"0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2","transactions":["0x02f878831469668303f51d843b9ac9f9843b9aca0082520894c93269b73096998db66be0441e836d873535cb9c8894a19041886f000080c001a031cc29234036afbf9a1fb9476b463367cb1f957ac0b919b69bbc798436e604aaa018c4e9c3914eb27aadd0b91e10b18655739fcf8c1fc398763a9f1beecb8ddc86"]},"signature":"0x1b66ac1fb663c9bc59509846d6ec05345bd908eda73e670af888da41af171505cc411d61252fb6cb3fa0017b679f8bb2305b26a285fa2737f175668d0dff91cc1b66ac1fb663c9bc59509846d6ec05345bd908eda73e670af888da41af171505"}"#; + + let bid = serde_json::from_str::(s).unwrap(); + let json: serde_json::Value = serde_json::from_str(s).unwrap(); + assert_eq!(json, serde_json::to_value(bid).unwrap()); + } + + #[test] + fn capella_bid_submission() { + let s = r#"{"message":{"slot":"1","parent_hash":"0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2","block_hash":"0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2","builder_pubkey":"0x93247f2209abcacf57b75a51dafae777f9dd38bc7053d1af526f220a7489a6d3a2753e5f3e8b1cfe39b56f43611df74a", "proposer_pubkey": "0x93247f2209abcacf57b75a51dafae777f9dd38bc7053d1af526f220a7489a6d3a2753e5f3e8b1cfe39b56f43611df74a","proposer_fee_recipient":"0xabcf8e0d4e9587369b2301d0790347320302cc09","gas_limit":"1","gas_used":"1","value":"1"},"execution_payload":{"parent_hash":"0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2","fee_recipient":"0xabcf8e0d4e9587369b2301d0790347320302cc09","state_root":"0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2","receipts_root":"0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2","logs_bloom":"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000","prev_randao":"0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2","block_number":"1","gas_limit":"1","gas_used":"1","timestamp":"1","extra_data":"0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2","base_fee_per_gas":"1","block_hash":"0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2","transactions":["0x02f878831469668303f51d843b9ac9f9843b9aca0082520894c93269b73096998db66be0441e836d873535cb9c8894a19041886f000080c001a031cc29234036afbf9a1fb9476b463367cb1f957ac0b919b69bbc798436e604aaa018c4e9c3914eb27aadd0b91e10b18655739fcf8c1fc398763a9f1beecb8ddc86"],"withdrawals":[{"index":"1","validator_index":"1","address":"0xabcf8e0d4e9587369b2301d0790347320302cc09","amount":"32000000000"}]},"signature":"0x1b66ac1fb663c9bc59509846d6ec05345bd908eda73e670af888da41af171505cc411d61252fb6cb3fa0017b679f8bb2305b26a285fa2737f175668d0dff91cc1b66ac1fb663c9bc59509846d6ec05345bd908eda73e670af888da41af171505"}"#; + + let bid = serde_json::from_str::(s).unwrap(); + let json: serde_json::Value = serde_json::from_str(s).unwrap(); + assert_eq!(json, serde_json::to_value(bid).unwrap()); + } + #[test] fn deneb_bid_submission() { - let s = r#"{"message":{"slot":"1","parent_hash":"0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2","block_hash":"0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2","builder_pubkey":"0x93247f2209abcacf57b75a51dafae777f9dd38bc7053d1af526f220a7489a6d3a2753e5f3e8b1cfe39b56f43611df74a", "proposer_pubkey": "0x93247f2209abcacf57b75a51dafae777f9dd38bc7053d1af526f220a7489a6d3a2753e5f3e8b1cfe39b56f43611df74a","proposer_fee_recipient":"0xabcf8e0d4e9587369b2301d0790347320302cc09","gas_limit":"1","gas_used":"1","value":"1"},"execution_payload":{"parent_hash":"0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2","fee_recipient":"0xabcf8e0d4e9587369b2301d0790347320302cc09","state_root":"0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2","receipts_root":"0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2","logs_bloom":"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000","prev_randao":"0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2","block_number":"1","gas_limit":"1","gas_used":"1","timestamp":"1","extra_data":"0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2","base_fee_per_gas":"1","block_hash":"0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2","transactions":["0x02f878831469668303f51d843b9ac9f9843b9aca0082520894c93269b73096998db66be0441e836d873535cb9c8894a19041886f000080c001a031cc29234036afbf9a1fb9476b463367cb1f957ac0b919b69bbc798436e604aaa018c4e9c3914eb27aadd0b91e10b18655739fcf8c1fc398763a9f1beecb8ddc86"],"withdrawals":[{"index":"1","validator_index":"1","address":"0xabcf8e0d4e9587369b2301d0790347320302cc09","amount":"32000000000"}]},"blobs_bundle":{"commitments":[],"proofs":[],"blobs":[]},"signature":"0x1b66ac1fb663c9bc59509846d6ec05345bd908eda73e670af888da41af171505cc411d61252fb6cb3fa0017b679f8bb2305b26a285fa2737f175668d0dff91cc1b66ac1fb663c9bc59509846d6ec05345bd908eda73e670af888da41af171505"}"#; + let s = r#"{"message":{"slot":"1","parent_hash":"0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2","block_hash":"0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2","builder_pubkey":"0x93247f2209abcacf57b75a51dafae777f9dd38bc7053d1af526f220a7489a6d3a2753e5f3e8b1cfe39b56f43611df74a", "proposer_pubkey": "0x93247f2209abcacf57b75a51dafae777f9dd38bc7053d1af526f220a7489a6d3a2753e5f3e8b1cfe39b56f43611df74a","proposer_fee_recipient":"0xabcf8e0d4e9587369b2301d0790347320302cc09","gas_limit":"1","gas_used":"1","value":"1"},"execution_payload":{"parent_hash":"0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2","fee_recipient":"0xabcf8e0d4e9587369b2301d0790347320302cc09","state_root":"0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2","receipts_root":"0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2","logs_bloom":"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000","prev_randao":"0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2","block_number":"1","gas_limit":"1","gas_used":"1","timestamp":"1","extra_data":"0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2","base_fee_per_gas":"1","block_hash":"0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2","transactions":["0x02f878831469668303f51d843b9ac9f9843b9aca0082520894c93269b73096998db66be0441e836d873535cb9c8894a19041886f000080c001a031cc29234036afbf9a1fb9476b463367cb1f957ac0b919b69bbc798436e604aaa018c4e9c3914eb27aadd0b91e10b18655739fcf8c1fc398763a9f1beecb8ddc86"],"withdrawals":[{"index":"1","validator_index":"1","address":"0xabcf8e0d4e9587369b2301d0790347320302cc09","amount":"32000000000"}], "blob_gas_used":"1","excess_blob_gas":"1"},"blobs_bundle":{"commitments":[],"proofs":[],"blobs":[]},"signature":"0x1b66ac1fb663c9bc59509846d6ec05345bd908eda73e670af888da41af171505cc411d61252fb6cb3fa0017b679f8bb2305b26a285fa2737f175668d0dff91cc1b66ac1fb663c9bc59509846d6ec05345bd908eda73e670af888da41af171505"}"#; - let bid = serde_json::from_str::(s).unwrap(); + let bid = serde_json::from_str::(s).unwrap(); let json: serde_json::Value = serde_json::from_str(s).unwrap(); assert_eq!(json, serde_json::to_value(bid).unwrap()); } + + #[cfg(feature = "ssz")] + #[test] + fn capella_bid_submission_ssz() { + use ssz::{Decode, Encode}; + + let bytes = include_bytes!("../test_data/relay/signed_bid_submission_capella.ssz").to_vec(); + let bid = SignedBidSubmissionV2::from_ssz_bytes(&bytes).unwrap(); + assert_eq!(bytes, bid.as_ssz_bytes()); + } } diff --git a/crates/rpc/rpc-types/test_data/relay/signed_bid_submission_capella.ssz b/crates/rpc/rpc-types/test_data/relay/signed_bid_submission_capella.ssz new file mode 100644 index 0000000000000000000000000000000000000000..b0dd2999d1026d256ad787241acc47e352ffb5b1 GIT binary patch literal 352239 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ⅅ; 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 Date: Mon, 18 Dec 2023 02:03:39 -0800 Subject: [PATCH 170/277] perf: skip initial transaction lookup during log queries (#5805) Co-authored-by: Georgios Konstantopoulos --- crates/rpc/rpc/src/eth/filter.rs | 76 +++++++++------------- crates/rpc/rpc/src/eth/logs_utils.rs | 97 +++++++++++++++++++++------- crates/rpc/rpc/src/eth/pubsub.rs | 2 +- 3 files changed, 108 insertions(+), 67 deletions(-) diff --git a/crates/rpc/rpc/src/eth/filter.rs b/crates/rpc/rpc/src/eth/filter.rs index e6403d11a8e6..94aed3e80752 100644 --- a/crates/rpc/rpc/src/eth/filter.rs +++ b/crates/rpc/rpc/src/eth/filter.rs @@ -1,8 +1,8 @@ use super::cache::EthStateCache; use crate::{ eth::{ - error::{EthApiError, EthResult}, - logs_utils, + error::EthApiError, + logs_utils::{self, append_matching_block_logs}, }, result::{rpc_error_with_code, ToRpcResult}, EthSubscriptionIdProvider, @@ -11,11 +11,11 @@ use core::fmt; use async_trait::async_trait; use jsonrpsee::{core::RpcResult, server::IdProvider}; -use reth_primitives::{BlockHashOrNumber, IntoRecoveredTransaction, Receipt, SealedBlock, TxHash}; +use reth_primitives::{IntoRecoveredTransaction, TxHash}; use reth_provider::{BlockIdReader, BlockReader, EvmEnvProvider, ProviderError}; use reth_rpc_api::EthFilterApiServer; use reth_rpc_types::{ - Filter, FilterBlockOption, FilterChanges, FilterId, FilteredParams, Log, + BlockNumHash, Filter, FilterBlockOption, FilterChanges, FilterId, FilteredParams, Log, PendingTransactionFilterKind, }; use reth_tasks::TaskSpawner; @@ -351,17 +351,18 @@ where FilterBlockOption::AtBlockHash(block_hash) => { let mut all_logs = Vec::new(); // all matching logs in the block, if it exists - if let Some((block, receipts)) = - self.eth_cache.get_block_and_receipts(block_hash).await? - { - let filter = FilteredParams::new(Some(filter)); - logs_utils::append_matching_block_logs( - &mut all_logs, - &filter, - (block_hash, block.number).into(), - block.body.into_iter().map(|tx| tx.hash()).zip(receipts.iter()), - false, - ); + if let Some(block_number) = self.provider.block_number_for_id(block_hash.into())? { + if let Some(receipts) = self.eth_cache.get_receipts(block_hash).await? { + let filter = FilteredParams::new(Some(filter)); + logs_utils::append_matching_block_logs( + &mut all_logs, + &self.provider, + &filter, + (block_hash, block_number).into(), + &receipts, + false, + )?; + } } Ok(all_logs) } @@ -402,19 +403,6 @@ where Ok(id) } - /// Fetches both receipts and block for the given block number. - async fn block_and_receipts_by_number( - &self, - hash_or_number: BlockHashOrNumber, - ) -> EthResult>)>> { - let block_hash = match self.provider.convert_block_hash(hash_or_number)? { - Some(hash) => hash, - None => return Ok(None), - }; - - Ok(self.eth_cache.get_block_and_receipts(block_hash).await?) - } - /// Returns all logs in the given _inclusive_ range that match the filter /// /// Returns an error if: @@ -447,29 +435,29 @@ where let headers = self.provider.headers_range(from..=to)?; for (idx, header) in headers.iter().enumerate() { - // these are consecutive headers, so we can use the parent hash of the next block to - // get the current header's hash - let num_hash: BlockHashOrNumber = headers - .get(idx + 1) - .map(|h| h.parent_hash.into()) - .unwrap_or_else(|| header.number.into()); - // only if filter matches if FilteredParams::matches_address(header.logs_bloom, &address_filter) && FilteredParams::matches_topics(header.logs_bloom, &topics_filter) { - if let Some((block, receipts)) = - self.block_and_receipts_by_number(num_hash).await? - { - let block_hash = block.hash; - - logs_utils::append_matching_block_logs( + // these are consecutive headers, so we can use the parent hash of the next + // block to get the current header's hash + let block_hash = match headers.get(idx + 1) { + Some(parent) => parent.parent_hash, + None => self + .provider + .block_hash(header.number)? + .ok_or(ProviderError::BlockNotFound(header.number.into()))?, + }; + + if let Some(receipts) = self.eth_cache.get_receipts(block_hash).await? { + append_matching_block_logs( &mut all_logs, + &self.provider, &filter_params, - (block.number, block_hash).into(), - block.body.into_iter().map(|tx| tx.hash()).zip(receipts.iter()), + BlockNumHash::new(header.number, block_hash), + &receipts, false, - ); + )?; // size check but only if range is multiple blocks, so we always return all // logs of a single block diff --git a/crates/rpc/rpc/src/eth/logs_utils.rs b/crates/rpc/rpc/src/eth/logs_utils.rs index fc2edb045106..0bc37956433c 100644 --- a/crates/rpc/rpc/src/eth/logs_utils.rs +++ b/crates/rpc/rpc/src/eth/logs_utils.rs @@ -1,46 +1,98 @@ -use reth_primitives::{BlockNumHash, ChainInfo, Receipt, TxHash, U256}; +use super::filter::FilterError; +use alloy_primitives::TxHash; +use reth_primitives::{BlockNumHash, ChainInfo, Receipt, U256}; +use reth_provider::{BlockReader, ProviderError}; use reth_rpc_types::{FilteredParams, Log}; use reth_rpc_types_compat::log::from_primitive_log; -/// Returns all matching logs of a block's receipts grouped with the hash of their transaction. -pub(crate) fn matching_block_logs<'a, I>( +/// Returns all matching of a block's receipts when the transaction hashes are known. +pub(crate) fn matching_block_logs_with_tx_hashes<'a, I>( filter: &FilteredParams, - block: BlockNumHash, - tx_and_receipts: I, + block_num_hash: BlockNumHash, + tx_hashes_and_receipts: I, removed: bool, ) -> Vec where I: IntoIterator, { let mut all_logs = Vec::new(); - append_matching_block_logs(&mut all_logs, filter, block, tx_and_receipts, removed); + // Tracks the index of a log in the entire block. + let mut log_index: u32 = 0; + // Iterate over transaction hashes and receipts and append matching logs. + for (receipt_idx, (tx_hash, receipt)) in tx_hashes_and_receipts.into_iter().enumerate() { + let logs = &receipt.logs; + for log in logs { + if log_matches_filter(block_num_hash, log, filter) { + let log = Log { + address: log.address, + topics: log.topics.clone(), + data: log.data.clone(), + block_hash: Some(block_num_hash.hash), + block_number: Some(U256::from(block_num_hash.number)), + transaction_hash: Some(tx_hash), + // The transaction and receipt index is always the same. + transaction_index: Some(U256::from(receipt_idx)), + log_index: Some(U256::from(log_index)), + removed, + }; + all_logs.push(log); + } + log_index += 1; + } + } all_logs } -/// Appends all matching logs of a block's receipts grouped with the hash of their transaction -pub(crate) fn append_matching_block_logs<'a, I>( +/// Appends all matching logs of a block's receipts. +/// If the log matches, look up the corresponding transaction hash. +pub(crate) fn append_matching_block_logs( all_logs: &mut Vec, + provider: impl BlockReader, filter: &FilteredParams, - block: BlockNumHash, - tx_and_receipts: I, + block_num_hash: BlockNumHash, + receipts: &[Receipt], removed: bool, -) where - I: IntoIterator, -{ - let block_number_u256 = U256::from(block.number); - // tracks the index of a log in the entire block +) -> Result<(), FilterError> { + // Tracks the index of a log in the entire block. let mut log_index: u32 = 0; - for (transaction_idx, (transaction_hash, receipt)) in tx_and_receipts.into_iter().enumerate() { - for log in receipt.logs.iter() { - if log_matches_filter(block, log, filter) { + + // Lazy loaded number of the first transaction in the block. + // This is useful for blocks with multiple matching logs because it prevents + // re-querying the block body indices. + let mut loaded_first_tx_num = None; + + // Iterate over receipts and append matching logs. + for (receipt_idx, receipt) in receipts.iter().enumerate() { + let logs = &receipt.logs; + for log in logs { + if log_matches_filter(block_num_hash, log, filter) { + let first_tx_num = match loaded_first_tx_num { + Some(num) => num, + None => { + let block_body_indices = + provider.block_body_indices(block_num_hash.number)?.ok_or( + ProviderError::BlockBodyIndicesNotFound(block_num_hash.number), + )?; + loaded_first_tx_num = Some(block_body_indices.first_tx_num); + block_body_indices.first_tx_num + } + }; + + // This is safe because Transactions and Receipts have the same keys. + let transaction_id = first_tx_num + receipt_idx as u64; + let transaction = provider + .transaction_by_id(transaction_id)? + .ok_or(ProviderError::TransactionNotFound(transaction_id.into()))?; + let log = Log { address: log.address, topics: log.topics.clone(), data: log.data.clone(), - block_hash: Some(block.hash), - block_number: Some(block_number_u256), - transaction_hash: Some(transaction_hash), - transaction_index: Some(U256::from(transaction_idx)), + block_hash: Some(block_num_hash.hash), + block_number: Some(U256::from(block_num_hash.number)), + transaction_hash: Some(transaction.hash()), + // The transaction and receipt index is always the same. + transaction_index: Some(U256::from(receipt_idx)), log_index: Some(U256::from(log_index)), removed, }; @@ -49,6 +101,7 @@ pub(crate) fn append_matching_block_logs<'a, I>( log_index += 1; } } + Ok(()) } /// Returns true if the log matches the filter and should be included diff --git a/crates/rpc/rpc/src/eth/pubsub.rs b/crates/rpc/rpc/src/eth/pubsub.rs index aa746464e43f..ded49d2a1bde 100644 --- a/crates/rpc/rpc/src/eth/pubsub.rs +++ b/crates/rpc/rpc/src/eth/pubsub.rs @@ -308,7 +308,7 @@ where }) .flat_map(futures::stream::iter) .flat_map(move |(block_receipts, removed)| { - let all_logs = logs_utils::matching_block_logs( + let all_logs = logs_utils::matching_block_logs_with_tx_hashes( &filter, block_receipts.block, block_receipts.tx_receipts.iter().map(|(tx, receipt)| (*tx, receipt)), From 1d25829f2ebaadd8ce5b821f33c421b75f27bc6d Mon Sep 17 00:00:00 2001 From: joshieDo <93316087+joshieDo@users.noreply.github.com> Date: Mon, 18 Dec 2023 10:42:48 +0000 Subject: [PATCH 171/277] feat: share decompressor buffer (#5777) --- crates/primitives/src/compression/mod.rs | 45 +++++++++++++++---- crates/primitives/src/transaction/mod.rs | 18 ++------ .../codecs/derive/src/compact/generator.rs | 15 ++----- 3 files changed, 43 insertions(+), 35 deletions(-) diff --git a/crates/primitives/src/compression/mod.rs b/crates/primitives/src/compression/mod.rs index f708e311a118..dee4a8891424 100644 --- a/crates/primitives/src/compression/mod.rs +++ b/crates/primitives/src/compression/mod.rs @@ -16,10 +16,9 @@ thread_local! { ); /// Thread Transaction decompressor. - pub static TRANSACTION_DECOMPRESSOR: RefCell> = RefCell::new( - Decompressor::with_dictionary(TRANSACTION_DICTIONARY) - .expect("Failed to initialize decompressor."), - ); + pub static TRANSACTION_DECOMPRESSOR: RefCell = RefCell::new( + ReusableDecompressor::new(Decompressor::with_dictionary(TRANSACTION_DICTIONARY).expect("Failed to initialize decompressor.")) + ); /// Thread receipt compressor. pub static RECEIPT_COMPRESSOR: RefCell> = RefCell::new( @@ -28,8 +27,38 @@ thread_local! { ); /// Thread receipt decompressor. - pub static RECEIPT_DECOMPRESSOR: RefCell> = RefCell::new( - Decompressor::with_dictionary(RECEIPT_DICTIONARY) - .expect("Failed to initialize decompressor."), - ); + pub static RECEIPT_DECOMPRESSOR: RefCell = RefCell::new( + ReusableDecompressor::new(Decompressor::with_dictionary(RECEIPT_DICTIONARY).expect("Failed to initialize decompressor.")) +); +} + +/// Reusable decompressor that uses its own internal buffer. +#[allow(missing_debug_implementations)] +pub struct ReusableDecompressor { + /// zstd decompressor + decompressor: Decompressor<'static>, + /// buffer to decompress to. + buf: Vec, +} + +impl ReusableDecompressor { + fn new(decompressor: Decompressor<'static>) -> Self { + Self { decompressor, buf: Vec::with_capacity(4096) } + } + + /// Decompresses `src` reusing the decompressor and its internal buffer. + pub fn decompress(&mut self, src: &[u8]) -> &[u8] { + // `decompress_to_buffer` will return an error if the output buffer doesn't have + // enough capacity. However we don't actually have information on the required + // length. So we hope for the best, and keep trying again with a fairly bigger size + // if it fails. + while let Err(err) = self.decompressor.decompress_to_buffer(src, &mut self.buf) { + let err = err.to_string(); + if !err.contains("Destination buffer is too small") { + panic!("Failed to decompress: {}", err); + } + self.buf.reserve(self.buf.capacity() + 24_000); + } + &self.buf + } } diff --git a/crates/primitives/src/transaction/mod.rs b/crates/primitives/src/transaction/mod.rs index fce4629aaf1b..448852582baf 100644 --- a/crates/primitives/src/transaction/mod.rs +++ b/crates/primitives/src/transaction/mod.rs @@ -897,25 +897,13 @@ impl Compact for TransactionSignedNoHash { let zstd_bit = bitflags >> 3; let (transaction, buf) = if zstd_bit != 0 { TRANSACTION_DECOMPRESSOR.with(|decompressor| { - let mut decompressor = decompressor.borrow_mut(); - let mut tmp: Vec = Vec::with_capacity(200); - - // `decompress_to_buffer` will return an error if the output buffer doesn't have - // enough capacity. However we don't actually have information on the required - // length. So we hope for the best, and keep trying again with a fairly bigger size - // if it fails. - while let Err(err) = decompressor.decompress_to_buffer(buf, &mut tmp) { - let err = err.to_string(); - if !err.contains("Destination buffer is too small") { - panic!("Failed to decompress: {}", err); - } - tmp.reserve(tmp.capacity() + 24_000); - } + let decompressor = &mut decompressor.borrow_mut(); // TODO: enforce that zstd is only present at a "top" level type let transaction_type = (bitflags & 0b110) >> 1; - let (transaction, _) = Transaction::from_compact(tmp.as_slice(), transaction_type); + let (transaction, _) = + Transaction::from_compact(decompressor.decompress(buf), transaction_type); (transaction, buf) }) diff --git a/crates/storage/codecs/derive/src/compact/generator.rs b/crates/storage/codecs/derive/src/compact/generator.rs index daa4cae87596..370d74eec2a1 100644 --- a/crates/storage/codecs/derive/src/compact/generator.rs +++ b/crates/storage/codecs/derive/src/compact/generator.rs @@ -115,20 +115,11 @@ fn generate_from_compact(fields: &FieldList, ident: &Ident, is_zstd: bool) -> To quote! { if flags.__zstd() != 0 { #decompressor.with(|decompressor| { - let mut decompressor = decompressor.borrow_mut(); - - let mut tmp: Vec = Vec::with_capacity(300); - - while let Err(err) = decompressor.decompress_to_buffer(&buf[..], &mut tmp) { - let err = err.to_string(); - if !err.contains("Destination buffer is too small") { - panic!("Failed to decompress: {}", err); - } - tmp.reserve(tmp.capacity() + 10_000); - } + let decompressor = &mut decompressor.borrow_mut(); + let decompressed = decompressor.decompress(buf); let mut original_buf = buf; - let mut buf: &[u8] = tmp.as_slice(); + let mut buf: &[u8] = decompressed; #(#lines)* (obj, original_buf) }) From 61c9587a24f8b253196be21ef75ebfcb9a364945 Mon Sep 17 00:00:00 2001 From: Matthias Seitz Date: Mon, 18 Dec 2023 12:45:24 +0100 Subject: [PATCH 172/277] feat: add execution payload validator (#5811) --- Cargo.lock | 11 ++ Cargo.toml | 2 + bin/reth/Cargo.toml | 1 + bin/reth/src/lib.rs | 6 + crates/consensus/beacon/Cargo.toml | 2 + crates/consensus/beacon/src/engine/mod.rs | 148 +++++------------- crates/payload/validator/Cargo.toml | 15 ++ crates/payload/validator/src/lib.rs | 130 +++++++++++++++ crates/primitives/src/block.rs | 24 ++- .../rpc-types-compat/src/engine/payload.rs | 1 + crates/rpc/rpc-types/src/eth/engine/cancun.rs | 42 +++++ .../rpc/rpc-types/src/eth/engine/payload.rs | 9 +- 12 files changed, 279 insertions(+), 112 deletions(-) create mode 100644 crates/payload/validator/Cargo.toml create mode 100644 crates/payload/validator/src/lib.rs diff --git a/Cargo.lock b/Cargo.lock index 63d66a49170a..8e0af142e409 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5610,6 +5610,7 @@ dependencies = [ "reth-network-api", "reth-nippy-jar", "reth-payload-builder", + "reth-payload-validator", "reth-primitives", "reth-provider", "reth-prune", @@ -5699,6 +5700,7 @@ dependencies = [ "reth-interfaces", "reth-metrics", "reth-payload-builder", + "reth-payload-validator", "reth-primitives", "reth-provider", "reth-prune", @@ -6212,6 +6214,15 @@ dependencies = [ "tracing", ] +[[package]] +name = "reth-payload-validator" +version = "0.1.0-alpha.13" +dependencies = [ + "reth-primitives", + "reth-rpc-types", + "reth-rpc-types-compat", +] + [[package]] name = "reth-primitives" version = "0.1.0-alpha.13" diff --git a/Cargo.toml b/Cargo.toml index cc048a6df522..2d18c023d662 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -21,6 +21,7 @@ members = [ "crates/net/network-api/", "crates/payload/basic/", "crates/payload/builder/", + "crates/payload/validator/", "crates/primitives/", "crates/prune/", "crates/revm/", @@ -116,6 +117,7 @@ reth-network = { path = "crates/net/network" } reth-network-api = { path = "crates/net/network-api" } reth-nippy-jar = { path = "crates/storage/nippy-jar" } reth-payload-builder = { path = "crates/payload/builder" } +reth-payload-validator = { path = "crates/payload/validator" } reth-primitives = { path = "crates/primitives" } reth-provider = { path = "crates/storage/provider" } reth-prune = { path = "crates/prune" } diff --git a/bin/reth/Cargo.toml b/bin/reth/Cargo.toml index 1619c0cf8a3e..4f2a2f1b3570 100644 --- a/bin/reth/Cargo.toml +++ b/bin/reth/Cargo.toml @@ -43,6 +43,7 @@ reth-tracing.workspace = true reth-tasks.workspace = true reth-net-nat.workspace = true reth-payload-builder.workspace = true +reth-payload-validator.workspace = true reth-basic-payload-builder.workspace = true reth-discv4.workspace = true reth-prune.workspace = true diff --git a/bin/reth/src/lib.rs b/bin/reth/src/lib.rs index efb70dc36779..973f988c0f9e 100644 --- a/bin/reth/src/lib.rs +++ b/bin/reth/src/lib.rs @@ -46,6 +46,12 @@ pub mod test_vectors; pub mod utils; pub mod version; +/// Re-exported payload related types +pub mod payload { + pub use reth_payload_builder::*; + pub use reth_payload_validator::ExecutionPayloadValidator; +} + /// Re-exported from `reth_provider`. pub mod providers { pub use reth_provider::*; diff --git a/crates/consensus/beacon/Cargo.toml b/crates/consensus/beacon/Cargo.toml index 3f892778ba0f..6986fd63afd2 100644 --- a/crates/consensus/beacon/Cargo.toml +++ b/crates/consensus/beacon/Cargo.toml @@ -18,10 +18,12 @@ reth-provider.workspace = true reth-rpc-types.workspace = true reth-tasks.workspace = true reth-payload-builder.workspace = true +reth-payload-validator.workspace = true reth-prune.workspace = true reth-snapshot.workspace = true reth-rpc-types-compat.workspace = true reth-tokio-util.workspace = true + # async tokio = { workspace = true, features = ["sync"] } tokio-stream.workspace = true diff --git a/crates/consensus/beacon/src/engine/mod.rs b/crates/consensus/beacon/src/engine/mod.rs index b9d9035cab63..ccbdd113167f 100644 --- a/crates/consensus/beacon/src/engine/mod.rs +++ b/crates/consensus/beacon/src/engine/mod.rs @@ -22,18 +22,18 @@ use reth_interfaces::{ }; use reth_payload_builder::{PayloadBuilderAttributes, PayloadBuilderHandle}; use reth_primitives::{ - constants::EPOCH_SLOTS, stage::StageId, BlockNumHash, BlockNumber, ChainSpec, Head, Header, - SealedBlock, SealedHeader, B256, U256, + constants::EPOCH_SLOTS, stage::StageId, BlockNumHash, BlockNumber, Head, Header, SealedBlock, + SealedHeader, B256, U256, }; use reth_provider::{ BlockIdReader, BlockReader, BlockSource, CanonChainTracker, ChainSpecProvider, ProviderError, StageCheckpointReader, }; use reth_rpc_types::engine::{ - CancunPayloadFields, ExecutionPayload, PayloadAttributes, PayloadError, PayloadStatus, - PayloadStatusEnum, PayloadValidationError, + CancunPayloadFields, ExecutionPayload, PayloadAttributes, PayloadStatus, PayloadStatusEnum, + PayloadValidationError, }; -use reth_rpc_types_compat::engine::payload::{try_into_block, validate_block_hash}; + use reth_stages::{ControlFlow, Pipeline, PipelineError}; use reth_tasks::TaskSpawner; use reth_tokio_util::EventListeners; @@ -73,6 +73,7 @@ mod forkchoice; use crate::hooks::{EngineHookEvent, EngineHooks, PolledHook}; pub use forkchoice::ForkchoiceStatus; use reth_interfaces::blockchain_tree::BlockValidationKind; +use reth_payload_validator::ExecutionPayloadValidator; mod metrics; @@ -187,6 +188,8 @@ where forkchoice_state_tracker: ForkchoiceStateTracker, /// The payload store. payload_builder: PayloadBuilderHandle, + /// Validator for execution payloads + payload_validator: ExecutionPayloadValidator, /// Listeners for engine events. listeners: EventListeners, /// Tracks the header of invalid payloads that were rejected by the engine because they're @@ -293,6 +296,7 @@ where ); let mut this = Self { sync, + payload_validator: ExecutionPayloadValidator::new(blockchain.chain_spec()), blockchain, sync_state_updater, engine_message_rx: UnboundedReceiverStream::new(rx), @@ -669,7 +673,7 @@ where // On Optimism, the proposers are allowed to reorg their own chain at will. cfg_if::cfg_if! { if #[cfg(feature = "optimism")] { - if self.chain_spec().is_optimism() { + if self.blockchain.chain_spec().is_optimism() { debug!( target: "consensus::engine", fcu_head_num=?header.number, @@ -1176,6 +1180,21 @@ where /// - the versioned hashes passed with the payload do not exactly match transaction /// versioned hashes /// - the block does not contain blob transactions if it is pre-cancun + // This validates the following engine API rule: + // + // 3. Given the expected array of blob versioned hashes client software **MUST** run its + // validation by taking the following steps: + // + // 1. Obtain the actual array by concatenating blob versioned hashes lists + // (`tx.blob_versioned_hashes`) of each [blob + // transaction](https://eips.ethereum.org/EIPS/eip-4844#new-transaction-type) included + // in the payload, respecting the order of inclusion. If the payload has no blob + // transactions the expected array **MUST** be `[]`. + // + // 2. Return `{status: INVALID, latestValidHash: null, validationError: errorMessage | null}` + // if the expected and the actual arrays don't match. + // + // This validation **MUST** be instantly run in all cases even during active sync process. fn ensure_well_formed_payload( &self, payload: ExecutionPayload, @@ -1183,117 +1202,26 @@ where ) -> Result { let parent_hash = payload.parent_hash(); - let block_hash = payload.block_hash(); - let block_res = match try_into_block( - payload, - cancun_fields.as_ref().map(|fields| fields.parent_beacon_block_root), - ) { - Ok(block) => { - // make sure there are no blob transactions in the payload if it is pre-cancun - if !self.chain_spec().is_cancun_active_at_timestamp(block.timestamp) && - block.has_blob_transactions() - { - Err(PayloadError::PreCancunBlockWithBlobTransactions) - } else { - validate_block_hash(block_hash, block) - } - } - Err(error) => Err(error), - }; - - let block = match block_res { - Ok(block) => block, + match self.payload_validator.ensure_well_formed_payload(payload, cancun_fields.into()) { + Ok(block) => Ok(block), Err(error) => { error!(target: "consensus::engine", ?error, "Invalid payload"); + // we need to convert the error to a payload status (response to the CL) - let mut latest_valid_hash = None; - if !error.is_block_hash_mismatch() { - // Engine-API rule: - // > `latestValidHash: null` if the blockHash validation has failed - latest_valid_hash = - self.latest_valid_hash_for_invalid_payload(parent_hash, None); - } - let status = PayloadStatusEnum::from(error); - - return Err(PayloadStatus::new(status, latest_valid_hash)); - } - }; - - let block_versioned_hashes = block - .blob_transactions() - .iter() - .filter_map(|tx| tx.as_eip4844().map(|blob_tx| &blob_tx.blob_versioned_hashes)) - .flatten() - .collect::>(); - - self.validate_versioned_hashes(parent_hash, block_versioned_hashes, cancun_fields)?; - - Ok(block) - } - - /// Returns the currently configured [ChainSpec]. - fn chain_spec(&self) -> Arc { - self.blockchain.chain_spec() - } - - /// Validates that the versioned hashes in the block match the versioned hashes passed in the - /// [CancunPayloadFields], if the cancun payload fields are provided. If the payload fields are - /// not provided, but versioned hashes exist in the block, this returns a [PayloadStatus] with - /// the [PayloadError::InvalidVersionedHashes] error. - /// - /// This validates versioned hashes according to the Engine API Cancun spec: - /// - fn validate_versioned_hashes( - &self, - parent_hash: B256, - block_versioned_hashes: Vec<&B256>, - cancun_fields: Option, - ) -> Result<(), PayloadStatus> { - // This validates the following engine API rule: - // - // 3. Given the expected array of blob versioned hashes client software **MUST** run its - // validation by taking the following steps: - // - // 1. Obtain the actual array by concatenating blob versioned hashes lists - // (`tx.blob_versioned_hashes`) of each [blob - // transaction](https://eips.ethereum.org/EIPS/eip-4844#new-transaction-type) included - // in the payload, respecting the order of inclusion. If the payload has no blob - // transactions the expected array **MUST** be `[]`. - // - // 2. Return `{status: INVALID, latestValidHash: null, validationError: errorMessage | - // null}` if the expected and the actual arrays don't match. - // - // This validation **MUST** be instantly run in all cases even during active sync process. - if let Some(fields) = cancun_fields { - if block_versioned_hashes.len() != fields.versioned_hashes.len() { - // if the lengths don't match then we know that the payload is invalid let latest_valid_hash = - self.latest_valid_hash_for_invalid_payload(parent_hash, None); - let status = PayloadStatusEnum::from(PayloadError::InvalidVersionedHashes); - return Err(PayloadStatus::new(status, latest_valid_hash)); - } + if error.is_block_hash_mismatch() || error.is_invalid_versioned_hashes() { + // Engine-API rules: + // > `latestValidHash: null` if the blockHash validation has failed () + // > `latestValidHash: null` if the expected and the actual arrays don't match () + None + } else { + self.latest_valid_hash_for_invalid_payload(parent_hash, None) + }; - // we can use `zip` safely here because we already compared their length - let zipped_versioned_hashes = - fields.versioned_hashes.iter().zip(block_versioned_hashes); - for (payload_versioned_hash, block_versioned_hash) in zipped_versioned_hashes { - if payload_versioned_hash != block_versioned_hash { - // One of the hashes does not match - return invalid - let latest_valid_hash = - self.latest_valid_hash_for_invalid_payload(parent_hash, None); - let status = PayloadStatusEnum::from(PayloadError::InvalidVersionedHashes); - return Err(PayloadStatus::new(status, latest_valid_hash)); - } + let status = PayloadStatusEnum::from(error); + Err(PayloadStatus::new(status, latest_valid_hash)) } - } else if !block_versioned_hashes.is_empty() { - // there are versioned hashes in the block but no expected versioned hashes were - // provided in the new payload call, so the payload is invalid - let latest_valid_hash = self.latest_valid_hash_for_invalid_payload(parent_hash, None); - let status = PayloadStatusEnum::from(PayloadError::InvalidVersionedHashes); - return Err(PayloadStatus::new(status, latest_valid_hash)); } - - Ok(()) } /// When the pipeline is active, the tree is unable to commit any additional blocks since the diff --git a/crates/payload/validator/Cargo.toml b/crates/payload/validator/Cargo.toml new file mode 100644 index 000000000000..f3003d62e309 --- /dev/null +++ b/crates/payload/validator/Cargo.toml @@ -0,0 +1,15 @@ +[package] +name = "reth-payload-validator" +version.workspace = true +edition.workspace = true +rust-version.workspace = true +license.workspace = true +homepage.workspace = true +repository.workspace = true +description = "Payload validation support" + +[dependencies] +# reth +reth-primitives.workspace = true +reth-rpc-types.workspace = true +reth-rpc-types-compat.workspace = true \ No newline at end of file diff --git a/crates/payload/validator/src/lib.rs b/crates/payload/validator/src/lib.rs new file mode 100644 index 000000000000..c4c7d1379498 --- /dev/null +++ b/crates/payload/validator/src/lib.rs @@ -0,0 +1,130 @@ +//! Payload Validation support. + +#![doc( + html_logo_url = "https://raw.githubusercontent.com/paradigmxyz/reth/main/assets/reth-docs.png", + html_favicon_url = "https://avatars0.githubusercontent.com/u/97369466?s=256", + issue_tracker_base_url = "https://github.com/paradigmxyz/reth/issues/" +)] +#![warn( + missing_debug_implementations, + missing_docs, + unreachable_pub, + unused_crate_dependencies, + rustdoc::all +)] +#![deny(unused_must_use, rust_2018_idioms)] +#![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))] + +use reth_primitives::{ChainSpec, SealedBlock}; +use reth_rpc_types::{engine::MaybeCancunPayloadFields, ExecutionPayload, PayloadError}; +use reth_rpc_types_compat::engine::payload::{try_into_block, validate_block_hash}; +use std::sync::Arc; + +/// Execution payload validator. +#[derive(Clone, Debug)] +pub struct ExecutionPayloadValidator { + /// Chain spec to validate against. + chain_spec: Arc, +} + +impl ExecutionPayloadValidator { + /// Create a new validator. + pub fn new(chain_spec: Arc) -> Self { + Self { chain_spec } + } + + /// Returns the chain spec used by the validator. + #[inline] + pub fn chain_spec(&self) -> &ChainSpec { + &self.chain_spec + } + + /// Returns true if the Cancun hardfork is active at the given timestamp. + #[inline] + fn is_cancun_active_at_timestamp(&self, timestamp: u64) -> bool { + self.chain_spec().is_cancun_active_at_timestamp(timestamp) + } + + /// Cancun specific checks for EIP-4844 blob transactions. + /// + /// Ensures that the number of blob versioned hashes matches the number hashes included in the + /// _separate_ block_versioned_hashes of the cancun payload fields. + fn ensure_matching_blob_versioned_hashes( + &self, + sealed_block: &SealedBlock, + cancun_fields: &MaybeCancunPayloadFields, + ) -> Result<(), PayloadError> { + let num_blob_versioned_hashes = sealed_block.blob_versioned_hashes_iter().count(); + // Additional Cancun checks for blob transactions + if let Some(versioned_hashes) = cancun_fields.versioned_hashes() { + if num_blob_versioned_hashes != versioned_hashes.len() { + // Number of blob versioned hashes does not match + return Err(PayloadError::InvalidVersionedHashes) + } + // we can use `zip` safely here because we already compared their length + for (payload_versioned_hash, block_versioned_hash) in + versioned_hashes.iter().zip(sealed_block.blob_versioned_hashes_iter()) + { + if payload_versioned_hash != block_versioned_hash { + return Err(PayloadError::InvalidVersionedHashes) + } + } + } else { + // No Cancun fields, if block includes any blobs, this is an error + if num_blob_versioned_hashes > 0 { + return Err(PayloadError::InvalidVersionedHashes) + } + } + + Ok(()) + } + + /// Ensures that the given payload does not violate any consensus rules that concern the block's + /// layout, like: + /// - missing or invalid base fee + /// - invalid extra data + /// - invalid transactions + /// - incorrect hash + /// - the versioned hashes passed with the payload do not exactly match transaction + /// versioned hashes + /// - the block does not contain blob transactions if it is pre-cancun + /// + /// The checks are done in the order that conforms with the engine-API specification. + /// + /// This is intended to be invoked after receiving the payload from the CLI. + /// The additional [MaybeCancunPayloadFields] are not part of the payload, but are additional fields in the `engine_newPayloadV3` RPC call, See also + /// + /// If the cancun fields are provided this also validates that the versioned hashes in the block + /// match the versioned hashes passed in the + /// [CancunPayloadFields](reth_rpc_types::engine::CancunPayloadFields), if the cancun payload + /// fields are provided. If the payload fields are not provided, but versioned hashes exist + /// in the block, this is considered an error: [PayloadError::InvalidVersionedHashes]. + /// + /// This validates versioned hashes according to the Engine API Cancun spec: + /// + pub fn ensure_well_formed_payload( + &self, + payload: ExecutionPayload, + cancun_fields: MaybeCancunPayloadFields, + ) -> Result { + let block_hash = payload.block_hash(); + + // First parse the block + let block = try_into_block(payload, cancun_fields.parent_beacon_block_root())?; + + let cancun_active = self.is_cancun_active_at_timestamp(block.timestamp); + + if !cancun_active && block.has_blob_transactions() { + // cancun not active but blob transactions present + return Err(PayloadError::PreCancunBlockWithBlobTransactions); + } + + // Ensure the hash included in the payload matches the block hash + let sealed_block = validate_block_hash(block_hash, block)?; + + // EIP-4844 checks + self.ensure_matching_blob_versioned_hashes(&sealed_block, &cancun_fields)?; + + Ok(sealed_block) + } +} diff --git a/crates/primitives/src/block.rs b/crates/primitives/src/block.rs index 417167fa67f5..a1acbbfd7f1f 100644 --- a/crates/primitives/src/block.rs +++ b/crates/primitives/src/block.rs @@ -86,6 +86,7 @@ impl Block { } /// Returns whether or not the block contains any blob transactions. + #[inline] pub fn has_blob_transactions(&self) -> bool { self.body.iter().any(|tx| tx.is_eip4844()) } @@ -233,9 +234,30 @@ impl SealedBlock { ) } + /// Returns an iterator over all blob transactions of the block + #[inline] + pub fn blob_transactions_iter(&self) -> impl Iterator + '_ { + self.body.iter().filter(|tx| tx.is_eip4844()) + } + /// Returns only the blob transactions, if any, from the block body. + #[inline] pub fn blob_transactions(&self) -> Vec<&TransactionSigned> { - self.body.iter().filter(|tx| tx.is_eip4844()).collect() + self.blob_transactions_iter().collect() + } + + /// Returns an iterator over all blob versioned hashes from the block body. + #[inline] + pub fn blob_versioned_hashes_iter(&self) -> impl Iterator + '_ { + self.blob_transactions_iter() + .filter_map(|tx| tx.as_eip4844().map(|blob_tx| &blob_tx.blob_versioned_hashes)) + .flatten() + } + + /// Returns all blob versioned hashes from the block body. + #[inline] + pub fn blob_versioned_hashes(&self) -> Vec<&B256> { + self.blob_versioned_hashes_iter().collect() } /// Expensive operation that recovers transaction signer. See [SealedBlockWithSenders]. diff --git a/crates/rpc/rpc-types-compat/src/engine/payload.rs b/crates/rpc/rpc-types-compat/src/engine/payload.rs index b007accc5b3f..d5c21d2a3fe2 100644 --- a/crates/rpc/rpc-types-compat/src/engine/payload.rs +++ b/crates/rpc/rpc-types-compat/src/engine/payload.rs @@ -304,6 +304,7 @@ pub fn try_into_sealed_block( /// /// If the provided block hash does not match the block hash computed from the provided block, this /// returns [PayloadError::BlockHash]. +#[inline] pub fn validate_block_hash( expected_block_hash: B256, block: Block, diff --git a/crates/rpc/rpc-types/src/eth/engine/cancun.rs b/crates/rpc/rpc-types/src/eth/engine/cancun.rs index 70ac709fe971..3ddfb52f1c1a 100644 --- a/crates/rpc/rpc-types/src/eth/engine/cancun.rs +++ b/crates/rpc/rpc-types/src/eth/engine/cancun.rs @@ -15,3 +15,45 @@ pub struct CancunPayloadFields { /// The expected blob versioned hashes. pub versioned_hashes: Vec, } + +/// A container type for [CancunPayloadFields] that may or may not be present. +#[derive(Debug, Clone, PartialEq, Eq, Hash, Default)] +pub struct MaybeCancunPayloadFields { + fields: Option, +} + +impl MaybeCancunPayloadFields { + /// Returns a new `MaybeCancunPayloadFields` with no cancun fields. + pub const fn none() -> Self { + Self { fields: None } + } + + /// Returns a new `MaybeCancunPayloadFields` with the given cancun fields. + pub fn into_inner(self) -> Option { + self.fields + } + + /// Returns the parent beacon block root, if any. + pub fn parent_beacon_block_root(&self) -> Option { + self.fields.as_ref().map(|fields| fields.parent_beacon_block_root) + } + + /// Returns the blob versioned hashes, if any. + pub fn versioned_hashes(&self) -> Option<&Vec> { + self.fields.as_ref().map(|fields| &fields.versioned_hashes) + } +} + +impl From for MaybeCancunPayloadFields { + #[inline] + fn from(fields: CancunPayloadFields) -> Self { + Self { fields: Some(fields) } + } +} + +impl From> for MaybeCancunPayloadFields { + #[inline] + fn from(fields: Option) -> Self { + Self { fields } + } +} diff --git a/crates/rpc/rpc-types/src/eth/engine/payload.rs b/crates/rpc/rpc-types/src/eth/engine/payload.rs index 2790a78ebb30..3f0ff5c870a2 100644 --- a/crates/rpc/rpc-types/src/eth/engine/payload.rs +++ b/crates/rpc/rpc-types/src/eth/engine/payload.rs @@ -576,10 +576,17 @@ pub enum PayloadError { } impl PayloadError { - /// Returns `true` if the error is caused by invalid extra data. + /// Returns `true` if the error is caused by a block hash mismatch. + #[inline] pub fn is_block_hash_mismatch(&self) -> bool { matches!(self, PayloadError::BlockHash { .. }) } + + /// Returns `true` if the error is caused by invalid block hashes (Cancun). + #[inline] + pub fn is_invalid_versioned_hashes(&self) -> bool { + matches!(self, PayloadError::InvalidVersionedHashes) + } } /// This structure contains a body of an execution payload. From 900fe7ea4e77ff7fa102468261b55d80c03a713d Mon Sep 17 00:00:00 2001 From: Dan Cline <6798349+Rjected@users.noreply.github.com> Date: Mon, 18 Dec 2023 13:55:21 +0200 Subject: [PATCH 173/277] feat: make NodeState generic over DB with DatabaseMetadata (#5691) --- bin/reth/src/node/events.rs | 35 ++++--- .../db/src/abstraction/database_metrics.rs | 62 +++++++++++- .../storage/db/src/implementation/mdbx/mod.rs | 97 +++++++++++++------ crates/storage/db/src/lib.rs | 11 ++- 4 files changed, 157 insertions(+), 48 deletions(-) diff --git a/bin/reth/src/node/events.rs b/bin/reth/src/node/events.rs index 0fb17f43b33c..c6d206de6dcd 100644 --- a/bin/reth/src/node/events.rs +++ b/bin/reth/src/node/events.rs @@ -3,7 +3,7 @@ use crate::node::cl_events::ConsensusLayerHealthEvent; use futures::Stream; use reth_beacon_consensus::BeaconConsensusEngineEvent; -use reth_db::DatabaseEnv; +use reth_db::{database::Database, database_metrics::DatabaseMetadata}; use reth_interfaces::consensus::ForkchoiceState; use reth_network::{NetworkEvent, NetworkHandle}; use reth_network_api::PeersInfo; @@ -17,7 +17,6 @@ use std::{ fmt::{Display, Formatter}, future::Future, pin::Pin, - sync::Arc, task::{Context, Poll}, time::{Duration, Instant}, }; @@ -28,11 +27,11 @@ use tracing::{info, warn}; const INFO_MESSAGE_INTERVAL: Duration = Duration::from_secs(25); /// The current high-level state of the node. -struct NodeState { +struct NodeState { /// Database environment. /// Used for freelist calculation reported in the "Status" log message. /// See [EventHandler::poll]. - db: Arc, + db: DB, /// Connection to the network. network: Option, /// The stage currently being executed. @@ -41,12 +40,8 @@ struct NodeState { latest_block: Option, } -impl NodeState { - fn new( - db: Arc, - network: Option, - latest_block: Option, - ) -> Self { +impl NodeState { + fn new(db: DB, network: Option, latest_block: Option) -> Self { Self { db, network, current_stage: None, latest_block } } @@ -200,6 +195,12 @@ impl NodeState { } } +impl NodeState { + fn freelist(&self) -> Option { + self.db.metadata().freelist_size() + } +} + /// Helper type for formatting of optional fields: /// - If [Some(x)], then `x` is written /// - If [None], then `None` is written @@ -270,13 +271,14 @@ impl From for NodeEvent { /// Displays relevant information to the user from components of the node, and periodically /// displays the high-level status of the node. -pub async fn handle_events( +pub async fn handle_events( network: Option, latest_block_number: Option, events: E, - db: Arc, + db: DB, ) where E: Stream + Unpin, + DB: DatabaseMetadata + Database + 'static, { let state = NodeState::new(db, network, latest_block_number); @@ -290,17 +292,18 @@ pub async fn handle_events( /// Handles events emitted by the node and logs them accordingly. #[pin_project::pin_project] -struct EventHandler { - state: NodeState, +struct EventHandler { + state: NodeState, #[pin] events: E, #[pin] info_interval: Interval, } -impl Future for EventHandler +impl Future for EventHandler where E: Stream + Unpin, + DB: DatabaseMetadata + Database + 'static, { type Output = (); @@ -308,7 +311,7 @@ where let mut this = self.project(); while this.info_interval.poll_tick(cx).is_ready() { - let freelist = OptionalField(this.state.db.freelist().ok()); + let freelist = OptionalField(this.state.freelist()); if let Some(CurrentStage { stage_id, eta, checkpoint, target }) = &this.state.current_stage diff --git a/crates/storage/db/src/abstraction/database_metrics.rs b/crates/storage/db/src/abstraction/database_metrics.rs index 717e1b342072..10f0e476ea6a 100644 --- a/crates/storage/db/src/abstraction/database_metrics.rs +++ b/crates/storage/db/src/abstraction/database_metrics.rs @@ -1,10 +1,38 @@ +use metrics::{counter, gauge, histogram, Label}; use std::sync::Arc; /// Represents a type that can report metrics, used mainly with the database. The `report_metrics` /// method can be used as a prometheus hook. pub trait DatabaseMetrics { /// Reports metrics for the database. - fn report_metrics(&self); + fn report_metrics(&self) { + for (name, value, labels) in self.gauge_metrics() { + gauge!(name, value, labels); + } + + for (name, value, labels) in self.counter_metrics() { + counter!(name, value, labels); + } + + for (name, value, labels) in self.histogram_metrics() { + histogram!(name, value, labels); + } + } + + /// Returns a list of [Gauge](metrics::Gauge) metrics for the database. + fn gauge_metrics(&self) -> Vec<(&'static str, f64, Vec/` + pub fn data_dir_path(&self) -> PathBuf { + self.0.as_ref().into() + } + /// Returns the path to the db directory for this chain. /// /// `//db` diff --git a/bin/reth/src/node/mod.rs b/bin/reth/src/node/mod.rs index e8a8eecea164..23cfc896a631 100644 --- a/bin/reth/src/node/mod.rs +++ b/bin/reth/src/node/mod.rs @@ -4,91 +4,20 @@ use crate::{ args::{ - get_secret_key, utils::{chain_help, genesis_value_parser, parse_socket_address, SUPPORTED_CHAINS}, DatabaseArgs, DebugArgs, DevArgs, NetworkArgs, PayloadBuilderArgs, PruningArgs, RpcServerArgs, TxPoolArgs, }, - cli::{ - components::RethNodeComponentsImpl, - config::{RethRpcConfig, RethTransactionPoolConfig}, - ext::{RethCliExt, RethNodeCommandConfig}, - }, - dirs::{ChainPath, DataDirPath, MaybePlatformPath}, - init::init_genesis, - node::cl_events::ConsensusLayerHealthEvents, - prometheus_exporter, + cli::{db_type::DatabaseBuilder, ext::RethCliExt, node_builder::NodeConfig}, + dirs::{DataDirPath, MaybePlatformPath}, runner::CliContext, - utils::get_single_header, - version::SHORT_VERSION, }; use clap::{value_parser, Parser}; -use eyre::Context; -use futures::{future::Either, pin_mut, stream, stream_select, StreamExt}; -use metrics_exporter_prometheus::PrometheusHandle; -use reth_auto_seal_consensus::{AutoSealBuilder, AutoSealConsensus, MiningMode}; -use reth_beacon_consensus::{ - hooks::{EngineHooks, PruneHook}, - BeaconConsensus, BeaconConsensusEngine, MIN_BLOCKS_FOR_PIPELINE_RUN, -}; -use reth_blockchain_tree::{ - config::BlockchainTreeConfig, externals::TreeExternals, BlockchainTree, ShareableBlockchainTree, -}; -use reth_config::{ - config::{PruneConfig, StageConfig}, - Config, -}; -use reth_db::{database::Database, database_metrics::DatabaseMetrics, init_db}; -use reth_downloaders::{ - bodies::bodies::BodiesDownloaderBuilder, - headers::reverse_headers::ReverseHeadersDownloaderBuilder, -}; -use reth_interfaces::{ - consensus::Consensus, - p2p::{ - bodies::{client::BodiesClient, downloader::BodyDownloader}, - either::EitherDownloader, - headers::{client::HeadersClient, downloader::HeaderDownloader}, - }, - RethResult, -}; -use reth_network::{NetworkBuilder, NetworkConfig, NetworkEvents, NetworkHandle, NetworkManager}; -use reth_network_api::{NetworkInfo, PeersInfo}; -use reth_primitives::{ - constants::eip4844::{LoadKzgSettingsError, MAINNET_KZG_TRUSTED_SETUP}, - fs, - kzg::KzgSettings, - stage::StageId, - BlockHashOrNumber, BlockNumber, ChainSpec, DisplayHardforks, Head, SealedHeader, B256, -}; -use reth_provider::{ - providers::BlockchainProvider, BlockHashReader, BlockReader, CanonStateSubscriptions, - HeaderProvider, HeaderSyncMode, ProviderFactory, StageCheckpointReader, -}; -use reth_prune::PrunerBuilder; -use reth_revm::EvmProcessorFactory; -use reth_revm_inspectors::stack::Hook; -use reth_rpc_engine_api::EngineApi; -use reth_stages::{ - prelude::*, - stages::{ - AccountHashingStage, ExecutionStage, ExecutionStageThresholds, IndexAccountHistoryStage, - IndexStorageHistoryStage, MerkleStage, SenderRecoveryStage, StorageHashingStage, - TotalDifficultyStage, TransactionLookupStage, - }, -}; -use reth_tasks::TaskExecutor; -use reth_transaction_pool::{ - blobstore::InMemoryBlobStore, TransactionPool, TransactionValidationTaskExecutor, -}; -use secp256k1::SecretKey; -use std::{ - net::{SocketAddr, SocketAddrV4}, - path::PathBuf, - sync::Arc, -}; -use tokio::sync::{mpsc::unbounded_channel, oneshot, watch}; -use tracing::*; +use reth_auto_seal_consensus::AutoSealConsensus; +use reth_beacon_consensus::BeaconConsensus; +use reth_interfaces::consensus::Consensus; +use reth_primitives::ChainSpec; +use std::{net::SocketAddr, path::PathBuf, sync::Arc}; pub mod cl_events; pub mod events; @@ -237,357 +166,57 @@ impl NodeCommand { } /// Execute `node` command - pub async fn execute(mut self, ctx: CliContext) -> eyre::Result<()> { - info!(target: "reth::cli", "reth {} starting", SHORT_VERSION); - - // Raise the fd limit of the process. - // Does not do anything on windows. - let _ = fdlimit::raise_fd_limit(); - - // get config - let config = self.load_config()?; - - let prometheus_handle = self.install_prometheus_recorder()?; - - let data_dir = self.data_dir(); - let db_path = data_dir.db_path(); - - info!(target: "reth::cli", path = ?db_path, "Opening database"); - let db = Arc::new(init_db(&db_path, self.db.log_level)?.with_metrics()); - info!(target: "reth::cli", "Database opened"); - - let mut provider_factory = ProviderFactory::new(Arc::clone(&db), Arc::clone(&self.chain)); - - // configure snapshotter - let snapshotter = reth_snapshot::Snapshotter::new( - provider_factory.clone(), - data_dir.snapshots_path(), - self.chain.snapshot_block_interval, - )?; - - provider_factory = provider_factory - .with_snapshots(data_dir.snapshots_path(), snapshotter.highest_snapshot_receiver())?; - - self.start_metrics_endpoint(prometheus_handle, Arc::clone(&db)).await?; - - debug!(target: "reth::cli", chain=%self.chain.chain, genesis=?self.chain.genesis_hash(), "Initializing genesis"); - - let genesis_hash = init_genesis(Arc::clone(&db), self.chain.clone())?; - - info!(target: "reth::cli", "{}", DisplayHardforks::new(self.chain.hardforks())); - - let consensus = self.consensus(); - - debug!(target: "reth::cli", "Spawning stages metrics listener task"); - let (sync_metrics_tx, sync_metrics_rx) = unbounded_channel(); - let sync_metrics_listener = reth_stages::MetricsListener::new(sync_metrics_rx); - ctx.task_executor.spawn_critical("stages metrics listener task", sync_metrics_listener); - - let prune_config = - self.pruning.prune_config(Arc::clone(&self.chain))?.or(config.prune.clone()); - - // configure blockchain tree - let tree_externals = TreeExternals::new( - provider_factory.clone(), - Arc::clone(&consensus), - EvmProcessorFactory::new(self.chain.clone()), - ); - let tree_config = BlockchainTreeConfig::default(); - let tree = BlockchainTree::new( - tree_externals, - tree_config, - prune_config.clone().map(|config| config.segments), - )? - .with_sync_metrics_tx(sync_metrics_tx.clone()); - let canon_state_notification_sender = tree.canon_state_notification_sender(); - let blockchain_tree = ShareableBlockchainTree::new(tree); - debug!(target: "reth::cli", "configured blockchain tree"); - - // fetch the head block from the database - let head = - self.lookup_head(provider_factory.clone()).wrap_err("the head block is missing")?; - - // setup the blockchain provider - let blockchain_db = - BlockchainProvider::new(provider_factory.clone(), blockchain_tree.clone())?; - let blob_store = InMemoryBlobStore::default(); - let validator = TransactionValidationTaskExecutor::eth_builder(Arc::clone(&self.chain)) - .with_head_timestamp(head.timestamp) - .kzg_settings(self.kzg_settings()?) - .with_additional_tasks(1) - .build_with_tasks(blockchain_db.clone(), ctx.task_executor.clone(), blob_store.clone()); - - let transaction_pool = - reth_transaction_pool::Pool::eth_pool(validator, blob_store, self.txpool.pool_config()); - info!(target: "reth::cli", "Transaction pool initialized"); - - // spawn txpool maintenance task - { - let pool = transaction_pool.clone(); - let chain_events = blockchain_db.canonical_state_stream(); - let client = blockchain_db.clone(); - ctx.task_executor.spawn_critical( - "txpool maintenance task", - reth_transaction_pool::maintain::maintain_transaction_pool_future( - client, - pool, - chain_events, - ctx.task_executor.clone(), - Default::default(), - ), - ); - debug!(target: "reth::cli", "Spawned txpool maintenance task"); - } - - info!(target: "reth::cli", "Connecting to P2P network"); - let network_secret_path = - self.network.p2p_secret_key.clone().unwrap_or_else(|| data_dir.p2p_secret_path()); - debug!(target: "reth::cli", ?network_secret_path, "Loading p2p key file"); - let secret_key = get_secret_key(&network_secret_path)?; - let default_peers_path = data_dir.known_peers_path(); - let network_config = self.load_network_config( - &config, - provider_factory.clone(), - ctx.task_executor.clone(), - head, - secret_key, - default_peers_path.clone(), - ); - - let network_client = network_config.client.clone(); - let mut network_builder = NetworkManager::builder(network_config).await?; - - let components = RethNodeComponentsImpl { - provider: blockchain_db.clone(), - pool: transaction_pool.clone(), - network: network_builder.handle(), - task_executor: ctx.task_executor.clone(), - events: blockchain_db.clone(), - }; - - // allow network modifications - self.ext.configure_network(network_builder.network_mut(), &components)?; - - // launch network - let network = self.start_network( - network_builder, - &ctx.task_executor, - transaction_pool.clone(), - network_client, - default_peers_path, - ); - - info!(target: "reth::cli", peer_id = %network.peer_id(), local_addr = %network.local_addr(), enode = %network.local_node_record(), "Connected to P2P network"); - debug!(target: "reth::cli", peer_id = ?network.peer_id(), "Full peer ID"); - let network_client = network.fetch_client().await?; - - self.ext.on_components_initialized(&components)?; - - debug!(target: "reth::cli", "Spawning payload builder service"); - let payload_builder = self.ext.spawn_payload_builder_service(&self.builder, &components)?; - - let (consensus_engine_tx, consensus_engine_rx) = unbounded_channel(); - let max_block = if let Some(block) = self.debug.max_block { - Some(block) - } else if let Some(tip) = self.debug.tip { - Some(self.lookup_or_fetch_tip(provider_factory.clone(), &network_client, tip).await?) - } else { - None - }; - - // Configure the pipeline - let (mut pipeline, client) = if self.dev.dev { - info!(target: "reth::cli", "Starting Reth in dev mode"); - - let mining_mode = if let Some(interval) = self.dev.block_time { - MiningMode::interval(interval) - } else if let Some(max_transactions) = self.dev.block_max_transactions { - MiningMode::instant( - max_transactions, - transaction_pool.pending_transactions_listener(), - ) - } else { - info!(target: "reth::cli", "No mining mode specified, defaulting to ReadyTransaction"); - MiningMode::instant(1, transaction_pool.pending_transactions_listener()) - }; - - let (_, client, mut task) = AutoSealBuilder::new( - Arc::clone(&self.chain), - blockchain_db.clone(), - transaction_pool.clone(), - consensus_engine_tx.clone(), - canon_state_notification_sender, - mining_mode, - ) - .build(); - - let mut pipeline = self - .build_networked_pipeline( - &config.stages, - client.clone(), - Arc::clone(&consensus), - provider_factory.clone(), - &ctx.task_executor, - sync_metrics_tx, - prune_config.clone(), - max_block, - ) - .await?; - - let pipeline_events = pipeline.events(); - task.set_pipeline_events(pipeline_events); - debug!(target: "reth::cli", "Spawning auto mine task"); - ctx.task_executor.spawn(Box::pin(task)); - - (pipeline, EitherDownloader::Left(client)) - } else { - let pipeline = self - .build_networked_pipeline( - &config.stages, - network_client.clone(), - Arc::clone(&consensus), - provider_factory.clone(), - &ctx.task_executor, - sync_metrics_tx, - prune_config.clone(), - max_block, - ) - .await?; - - (pipeline, EitherDownloader::Right(network_client)) - }; - - let pipeline_events = pipeline.events(); - - let initial_target = if let Some(tip) = self.debug.tip { - // Set the provided tip as the initial pipeline target. - debug!(target: "reth::cli", %tip, "Tip manually set"); - Some(tip) - } else if self.debug.continuous { - // Set genesis as the initial pipeline target. - // This will allow the downloader to start - debug!(target: "reth::cli", "Continuous sync mode enabled"); - Some(genesis_hash) - } else { - None - }; - - let mut hooks = EngineHooks::new(); - - let pruner_events = if let Some(prune_config) = prune_config { - let mut pruner = PrunerBuilder::new(prune_config.clone()) - .max_reorg_depth(tree_config.max_reorg_depth() as usize) - .prune_delete_limit(self.chain.prune_delete_limit) - .build(provider_factory, snapshotter.highest_snapshot_receiver()); + pub async fn execute(self, ctx: CliContext) -> eyre::Result<()> { + let Self { + datadir, + config, + chain, + metrics, + trusted_setup_file, + instance, + network, + rpc, + txpool, + builder, + debug, + db, + dev, + pruning, + #[cfg(feature = "optimism")] + rollup, + ext, + } = self; - let events = pruner.events(); - hooks.add(PruneHook::new(pruner, Box::new(ctx.task_executor.clone()))); + // set up real database + let database = DatabaseBuilder::Real(datadir); - info!(target: "reth::cli", ?prune_config, "Pruner initialized"); - Either::Left(events) - } else { - Either::Right(stream::empty()) + // set up node config + let node_config = NodeConfig { + database, + config, + chain, + metrics, + instance, + trusted_setup_file, + network, + rpc, + txpool, + builder, + debug, + db, + dev, + pruning, + #[cfg(feature = "optimism")] + rollup, }; - // Configure the consensus engine - let (beacon_consensus_engine, beacon_engine_handle) = BeaconConsensusEngine::with_channel( - client, - pipeline, - blockchain_db.clone(), - Box::new(ctx.task_executor.clone()), - Box::new(network.clone()), - max_block, - self.debug.continuous, - payload_builder.clone(), - initial_target, - MIN_BLOCKS_FOR_PIPELINE_RUN, - consensus_engine_tx, - consensus_engine_rx, - hooks, - )?; - info!(target: "reth::cli", "Consensus engine initialized"); - - let events = stream_select!( - network.event_listener().map(Into::into), - beacon_engine_handle.event_listener().map(Into::into), - pipeline_events.map(Into::into), - if self.debug.tip.is_none() { - Either::Left( - ConsensusLayerHealthEvents::new(Box::new(blockchain_db.clone())) - .map(Into::into), - ) - } else { - Either::Right(stream::empty()) - }, - pruner_events.map(Into::into) - ); - ctx.task_executor.spawn_critical( - "events task", - events::handle_events(Some(network.clone()), Some(head.number), events, db.clone()), - ); - - let engine_api = EngineApi::new( - blockchain_db.clone(), - self.chain.clone(), - beacon_engine_handle, - payload_builder.into(), - Box::new(ctx.task_executor.clone()), - ); - info!(target: "reth::cli", "Engine API handler initialized"); - - // extract the jwt secret from the args if possible - let default_jwt_path = data_dir.jwt_path(); - let jwt_secret = self.rpc.auth_jwt_secret(default_jwt_path)?; - - // adjust rpc port numbers based on instance number - self.adjust_instance_ports(); + let executor = ctx.task_executor; - // Start RPC servers - let _rpc_server_handles = - self.rpc.start_servers(&components, engine_api, jwt_secret, &mut self.ext).await?; + // launch the node + let handle = node_config.launch::(ext, executor).await?; - // Run consensus engine to completion - let (tx, rx) = oneshot::channel(); - info!(target: "reth::cli", "Starting consensus engine"); - ctx.task_executor.spawn_critical_blocking("consensus engine", async move { - let res = beacon_consensus_engine.await; - let _ = tx.send(res); - }); - - self.ext.on_node_started(&components)?; - - // If `enable_genesis_walkback` is set to true, the rollup client will need to - // perform the derivation pipeline from genesis, validating the data dir. - // When set to false, set the finalized, safe, and unsafe head block hashes - // on the rollup client using a fork choice update. This prevents the rollup - // client from performing the derivation pipeline from genesis, and instead - // starts syncing from the current tip in the DB. - #[cfg(feature = "optimism")] - if self.chain.is_optimism() && !self.rollup.enable_genesis_walkback { - let client = _rpc_server_handles.auth.http_client(); - reth_rpc_api::EngineApiClient::fork_choice_updated_v2( - &client, - reth_rpc_types::engine::ForkchoiceState { - head_block_hash: head.hash, - safe_block_hash: head.hash, - finalized_block_hash: head.hash, - }, - None, - ) - .await?; - } - - rx.await??; - - info!(target: "reth::cli", "Consensus engine has exited."); - - if self.debug.terminate { - Ok(()) - } else { - // The pipeline has finished downloading blocks up to `--debug.tip` or - // `--debug.max-block`. Keep other node components alive for further usage. - futures::future::pending().await - } + // wait for node exit + handle.wait_for_node_exit().await } /// Returns the [Consensus] instance to use. @@ -601,425 +230,6 @@ impl NodeCommand { Arc::new(BeaconConsensus::new(Arc::clone(&self.chain))) } } - - /// Constructs a [Pipeline] that's wired to the network - #[allow(clippy::too_many_arguments)] - async fn build_networked_pipeline( - &self, - config: &StageConfig, - client: Client, - consensus: Arc, - provider_factory: ProviderFactory, - task_executor: &TaskExecutor, - metrics_tx: reth_stages::MetricEventsSender, - prune_config: Option, - max_block: Option, - ) -> eyre::Result> - where - DB: Database + Unpin + Clone + 'static, - Client: HeadersClient + BodiesClient + Clone + 'static, - { - // building network downloaders using the fetch client - let header_downloader = ReverseHeadersDownloaderBuilder::new(config.headers) - .build(client.clone(), Arc::clone(&consensus)) - .into_task_with(task_executor); - - let body_downloader = BodiesDownloaderBuilder::new(config.bodies) - .build(client, Arc::clone(&consensus), provider_factory.clone()) - .into_task_with(task_executor); - - let pipeline = self - .build_pipeline( - provider_factory, - config, - header_downloader, - body_downloader, - consensus, - max_block, - self.debug.continuous, - metrics_tx, - prune_config, - ) - .await?; - - Ok(pipeline) - } - - /// Returns the chain specific path to the data dir. - fn data_dir(&self) -> ChainPath { - self.datadir.unwrap_or_chain_default(self.chain.chain) - } - - /// Returns the path to the config file. - fn config_path(&self) -> PathBuf { - self.config.clone().unwrap_or_else(|| self.data_dir().config_path()) - } - - /// Loads the reth config with the given datadir root - fn load_config(&self) -> eyre::Result { - let config_path = self.config_path(); - let mut config = confy::load_path::(&config_path) - .wrap_err_with(|| format!("Could not load config file {:?}", config_path))?; - - info!(target: "reth::cli", path = ?config_path, "Configuration loaded"); - - // Update the config with the command line arguments - config.peers.connect_trusted_nodes_only = self.network.trusted_only; - - if !self.network.trusted_peers.is_empty() { - info!(target: "reth::cli", "Adding trusted nodes"); - self.network.trusted_peers.iter().for_each(|peer| { - config.peers.trusted_nodes.insert(*peer); - }); - } - - Ok(config) - } - - /// Loads the trusted setup params from a given file path or falls back to - /// `MAINNET_KZG_TRUSTED_SETUP`. - fn kzg_settings(&self) -> eyre::Result> { - if let Some(ref trusted_setup_file) = self.trusted_setup_file { - let trusted_setup = KzgSettings::load_trusted_setup_file(trusted_setup_file) - .map_err(LoadKzgSettingsError::KzgError)?; - Ok(Arc::new(trusted_setup)) - } else { - Ok(Arc::clone(&MAINNET_KZG_TRUSTED_SETUP)) - } - } - - fn install_prometheus_recorder(&self) -> eyre::Result { - prometheus_exporter::install_recorder() - } - - async fn start_metrics_endpoint( - &self, - prometheus_handle: PrometheusHandle, - db: Metrics, - ) -> eyre::Result<()> - where - Metrics: DatabaseMetrics + 'static + Send + Sync, - { - if let Some(listen_addr) = self.metrics { - info!(target: "reth::cli", addr = %listen_addr, "Starting metrics endpoint"); - prometheus_exporter::serve( - listen_addr, - prometheus_handle, - db, - metrics_process::Collector::default(), - ) - .await?; - } - - Ok(()) - } - - /// Spawns the configured network and associated tasks and returns the [NetworkHandle] connected - /// to that network. - fn start_network( - &self, - builder: NetworkBuilder, - task_executor: &TaskExecutor, - pool: Pool, - client: C, - default_peers_path: PathBuf, - ) -> NetworkHandle - where - C: BlockReader + HeaderProvider + Clone + Unpin + 'static, - Pool: TransactionPool + Unpin + 'static, - { - let (handle, network, txpool, eth) = - builder.transactions(pool).request_handler(client).split_with_handle(); - - task_executor.spawn_critical("p2p txpool", txpool); - task_executor.spawn_critical("p2p eth request handler", eth); - - let known_peers_file = self.network.persistent_peers_file(default_peers_path); - task_executor - .spawn_critical_with_graceful_shutdown_signal("p2p network task", |shutdown| { - run_network_until_shutdown(shutdown, network, known_peers_file) - }); - - handle - } - - /// Fetches the head block from the database. - /// - /// If the database is empty, returns the genesis block. - fn lookup_head(&self, factory: ProviderFactory) -> RethResult { - let provider = factory.provider()?; - - let head = provider.get_stage_checkpoint(StageId::Finish)?.unwrap_or_default().block_number; - - let header = provider - .header_by_number(head)? - .expect("the header for the latest block is missing, database is corrupt"); - - let total_difficulty = provider - .header_td_by_number(head)? - .expect("the total difficulty for the latest block is missing, database is corrupt"); - - let hash = provider - .block_hash(head)? - .expect("the hash for the latest block is missing, database is corrupt"); - - Ok(Head { - number: head, - hash, - difficulty: header.difficulty, - total_difficulty, - timestamp: header.timestamp, - }) - } - - /// Attempt to look up the block number for the tip hash in the database. - /// If it doesn't exist, download the header and return the block number. - /// - /// NOTE: The download is attempted with infinite retries. - async fn lookup_or_fetch_tip( - &self, - provider_factory: ProviderFactory, - client: Client, - tip: B256, - ) -> RethResult - where - DB: Database, - Client: HeadersClient, - { - Ok(self.fetch_tip(provider_factory, client, BlockHashOrNumber::Hash(tip)).await?.number) - } - - /// Attempt to look up the block with the given number and return the header. - /// - /// NOTE: The download is attempted with infinite retries. - async fn fetch_tip( - &self, - factory: ProviderFactory, - client: Client, - tip: BlockHashOrNumber, - ) -> RethResult - where - DB: Database, - Client: HeadersClient, - { - let provider = factory.provider()?; - - let header = provider.header_by_hash_or_number(tip)?; - - // try to look up the header in the database - if let Some(header) = header { - info!(target: "reth::cli", ?tip, "Successfully looked up tip block in the database"); - return Ok(header.seal_slow()) - } - - info!(target: "reth::cli", ?tip, "Fetching tip block from the network."); - loop { - match get_single_header(&client, tip).await { - Ok(tip_header) => { - info!(target: "reth::cli", ?tip, "Successfully fetched tip"); - return Ok(tip_header) - } - Err(error) => { - error!(target: "reth::cli", %error, "Failed to fetch the tip. Retrying..."); - } - } - } - } - - fn load_network_config( - &self, - config: &Config, - provider_factory: ProviderFactory, - executor: TaskExecutor, - head: Head, - secret_key: SecretKey, - default_peers_path: PathBuf, - ) -> NetworkConfig> { - let cfg_builder = self - .network - .network_config(config, self.chain.clone(), secret_key, default_peers_path) - .with_task_executor(Box::new(executor)) - .set_head(head) - .listener_addr(SocketAddr::V4(SocketAddrV4::new( - self.network.addr, - // set discovery port based on instance number - self.network.port + self.instance - 1, - ))) - .discovery_addr(SocketAddr::V4(SocketAddrV4::new( - self.network.addr, - // set discovery port based on instance number - self.network.port + self.instance - 1, - ))); - - // When `sequencer_endpoint` is configured, the node will forward all transactions to a - // Sequencer node for execution and inclusion on L1, and disable its own txpool - // gossip to prevent other parties in the network from learning about them. - #[cfg(feature = "optimism")] - let cfg_builder = cfg_builder - .sequencer_endpoint(self.rollup.sequencer_http.clone()) - .disable_tx_gossip(self.rollup.disable_txpool_gossip); - - cfg_builder.build(provider_factory) - } - - #[allow(clippy::too_many_arguments)] - async fn build_pipeline( - &self, - provider_factory: ProviderFactory, - config: &StageConfig, - header_downloader: H, - body_downloader: B, - consensus: Arc, - max_block: Option, - continuous: bool, - metrics_tx: reth_stages::MetricEventsSender, - prune_config: Option, - ) -> eyre::Result> - where - DB: Database + Clone + 'static, - H: HeaderDownloader + 'static, - B: BodyDownloader + 'static, - { - let mut builder = Pipeline::builder(); - - if let Some(max_block) = max_block { - debug!(target: "reth::cli", max_block, "Configuring builder to use max block"); - builder = builder.with_max_block(max_block) - } - - let (tip_tx, tip_rx) = watch::channel(B256::ZERO); - use reth_revm_inspectors::stack::InspectorStackConfig; - let factory = reth_revm::EvmProcessorFactory::new(self.chain.clone()); - - let stack_config = InspectorStackConfig { - use_printer_tracer: self.debug.print_inspector, - hook: if let Some(hook_block) = self.debug.hook_block { - Hook::Block(hook_block) - } else if let Some(tx) = self.debug.hook_transaction { - Hook::Transaction(tx) - } else if self.debug.hook_all { - Hook::All - } else { - Hook::None - }, - }; - - let factory = factory.with_stack_config(stack_config); - - let prune_modes = prune_config.map(|prune| prune.segments).unwrap_or_default(); - - let header_mode = - if continuous { HeaderSyncMode::Continuous } else { HeaderSyncMode::Tip(tip_rx) }; - let pipeline = builder - .with_tip_sender(tip_tx) - .with_metrics_tx(metrics_tx.clone()) - .add_stages( - DefaultStages::new( - provider_factory.clone(), - header_mode, - Arc::clone(&consensus), - header_downloader, - body_downloader, - factory.clone(), - ) - .set( - TotalDifficultyStage::new(consensus) - .with_commit_threshold(config.total_difficulty.commit_threshold), - ) - .set(SenderRecoveryStage { - commit_threshold: config.sender_recovery.commit_threshold, - }) - .set( - ExecutionStage::new( - factory, - ExecutionStageThresholds { - max_blocks: config.execution.max_blocks, - max_changes: config.execution.max_changes, - max_cumulative_gas: config.execution.max_cumulative_gas, - }, - config - .merkle - .clean_threshold - .max(config.account_hashing.clean_threshold) - .max(config.storage_hashing.clean_threshold), - prune_modes.clone(), - ) - .with_metrics_tx(metrics_tx), - ) - .set(AccountHashingStage::new( - config.account_hashing.clean_threshold, - config.account_hashing.commit_threshold, - )) - .set(StorageHashingStage::new( - config.storage_hashing.clean_threshold, - config.storage_hashing.commit_threshold, - )) - .set(MerkleStage::new_execution(config.merkle.clean_threshold)) - .set(TransactionLookupStage::new( - config.transaction_lookup.commit_threshold, - prune_modes.transaction_lookup, - )) - .set(IndexAccountHistoryStage::new( - config.index_account_history.commit_threshold, - prune_modes.account_history, - )) - .set(IndexStorageHistoryStage::new( - config.index_storage_history.commit_threshold, - prune_modes.storage_history, - )), - ) - .build(provider_factory); - - Ok(pipeline) - } - - /// Change rpc port numbers based on the instance number. - fn adjust_instance_ports(&mut self) { - // auth port is scaled by a factor of instance * 100 - self.rpc.auth_port += self.instance * 100 - 100; - // http port is scaled by a factor of -instance - self.rpc.http_port -= self.instance - 1; - // ws port is scaled by a factor of instance * 2 - self.rpc.ws_port += self.instance * 2 - 2; - } -} - -/// Drives the [NetworkManager] future until a [Shutdown](reth_tasks::shutdown::Shutdown) signal is -/// received. If configured, this writes known peers to `persistent_peers_file` afterwards. -async fn run_network_until_shutdown( - shutdown: reth_tasks::shutdown::GracefulShutdown, - network: NetworkManager, - persistent_peers_file: Option, -) where - C: BlockReader + HeaderProvider + Clone + Unpin + 'static, -{ - pin_mut!(network, shutdown); - - let mut graceful_guard = None; - tokio::select! { - _ = &mut network => {}, - guard = shutdown => { - graceful_guard = Some(guard); - }, - } - - if let Some(file_path) = persistent_peers_file { - let known_peers = network.all_peers().collect::>(); - if let Ok(known_peers) = serde_json::to_string_pretty(&known_peers) { - trace!(target: "reth::cli", peers_file =?file_path, num_peers=%known_peers.len(), "Saving current peers"); - let parent_dir = file_path.parent().map(fs::create_dir_all).transpose(); - match parent_dir.and_then(|_| fs::write(&file_path, known_peers)) { - Ok(_) => { - info!(target: "reth::cli", peers_file=?file_path, "Wrote network peers to file"); - } - Err(err) => { - warn!(target: "reth::cli", ?err, peers_file=?file_path, "Failed to write network peers to file"); - } - } - } - } - - drop(graceful_guard) } #[cfg(test)] @@ -1150,7 +360,7 @@ mod tests { #[test] fn parse_instance() { let mut cmd = NodeCommand::<()>::parse_from(["reth"]); - cmd.adjust_instance_ports(); + cmd.rpc.adjust_instance_ports(cmd.instance); cmd.network.port = DEFAULT_DISCOVERY_PORT + cmd.instance - 1; // check rpc port numbers assert_eq!(cmd.rpc.auth_port, 8551); @@ -1160,7 +370,7 @@ mod tests { assert_eq!(cmd.network.port, 30303); let mut cmd = NodeCommand::<()>::parse_from(["reth", "--instance", "2"]); - cmd.adjust_instance_ports(); + cmd.rpc.adjust_instance_ports(cmd.instance); cmd.network.port = DEFAULT_DISCOVERY_PORT + cmd.instance - 1; // check rpc port numbers assert_eq!(cmd.rpc.auth_port, 8651); @@ -1170,7 +380,7 @@ mod tests { assert_eq!(cmd.network.port, 30304); let mut cmd = NodeCommand::<()>::parse_from(["reth", "--instance", "3"]); - cmd.adjust_instance_ports(); + cmd.rpc.adjust_instance_ports(cmd.instance); cmd.network.port = DEFAULT_DISCOVERY_PORT + cmd.instance - 1; // check rpc port numbers assert_eq!(cmd.rpc.auth_port, 8751); diff --git a/bin/reth/src/utils.rs b/bin/reth/src/utils.rs index 74af5288cebb..38a824fb9a99 100644 --- a/bin/reth/src/utils.rs +++ b/bin/reth/src/utils.rs @@ -15,9 +15,11 @@ use reth_interfaces::p2p::{ headers::client::{HeadersClient, HeadersRequest}, priority::Priority, }; +use reth_network::NetworkManager; use reth_primitives::{ fs, BlockHashOrNumber, ChainSpec, HeadersDirection, SealedBlock, SealedHeader, }; +use reth_provider::BlockReader; use reth_rpc::{JwtError, JwtSecret}; use std::{ env::VarError, @@ -25,7 +27,7 @@ use std::{ rc::Rc, sync::Arc, }; -use tracing::{debug, info}; +use tracing::{debug, info, trace, warn}; /// Exposing `open_db_read_only` function pub mod db { @@ -255,8 +257,8 @@ impl ListFilter { self.len = len; } } -/// Attempts to retrieve or create a JWT secret from the specified path. +/// Attempts to retrieve or create a JWT secret from the specified path. pub fn get_or_create_jwt_secret_from_path(path: &Path) -> Result { if path.exists() { debug!(target: "reth::cli", ?path, "Reading JWT auth secret file"); @@ -266,3 +268,26 @@ pub fn get_or_create_jwt_secret_from_path(path: &Path) -> Result(network: &NetworkManager, persistent_peers_file: Option) +where + C: BlockReader + Unpin, +{ + if let Some(file_path) = persistent_peers_file { + let known_peers = network.all_peers().collect::>(); + if let Ok(known_peers) = serde_json::to_string_pretty(&known_peers) { + trace!(target: "reth::cli", peers_file =?file_path, num_peers=%known_peers.len(), "Saving current peers"); + let parent_dir = file_path.parent().map(fs::create_dir_all).transpose(); + match parent_dir.and_then(|_| fs::write(&file_path, known_peers)) { + Ok(_) => { + info!(target: "reth::cli", peers_file=?file_path, "Wrote network peers to file"); + } + Err(err) => { + warn!(target: "reth::cli", ?err, peers_file=?file_path, "Failed to write network peers to file"); + } + } + } + } +} diff --git a/crates/net/network/src/manager.rs b/crates/net/network/src/manager.rs index 88cb8bdcbcdf..456b542e33a5 100644 --- a/crates/net/network/src/manager.rs +++ b/crates/net/network/src/manager.rs @@ -33,7 +33,7 @@ use crate::{ transactions::NetworkTransactionEvent, FetchClient, NetworkBuilder, }; -use futures::{Future, StreamExt}; +use futures::{pin_mut, Future, StreamExt}; use parking_lot::Mutex; use reth_eth_wire::{ capability::{Capabilities, CapabilityMessage}, @@ -45,6 +45,7 @@ use reth_network_api::ReputationChangeKind; use reth_primitives::{ForkId, NodeRecord, PeerId, B256}; use reth_provider::{BlockNumReader, BlockReader}; use reth_rpc_types::{EthProtocolInfo, NetworkStatus}; +use reth_tasks::shutdown::GracefulShutdown; use reth_tokio_util::EventListeners; use secp256k1::SecretKey; use std::{ @@ -596,6 +597,34 @@ where } } +impl NetworkManager +where + C: BlockReader + Unpin, +{ + /// Drives the [NetworkManager] future until a [GracefulShutdown] signal is received. + /// + /// This also run the given function `shutdown_hook` afterwards. + pub async fn run_until_graceful_shutdown( + self, + shutdown: GracefulShutdown, + shutdown_hook: impl FnOnce(&mut Self), + ) { + let network = self; + pin_mut!(network, shutdown); + + let mut graceful_guard = None; + tokio::select! { + _ = &mut network => {}, + guard = shutdown => { + graceful_guard = Some(guard); + }, + } + + shutdown_hook(&mut network); + drop(graceful_guard); + } +} + impl Future for NetworkManager where C: BlockReader + Unpin, diff --git a/crates/storage/db/src/lib.rs b/crates/storage/db/src/lib.rs index 16ef51b4b24e..a57779d4f043 100644 --- a/crates/storage/db/src/lib.rs +++ b/crates/storage/db/src/lib.rs @@ -226,9 +226,15 @@ pub mod test_utils { } } + /// Get a temporary directory path to use for the database + pub fn tempdir_path() -> PathBuf { + let builder = tempfile::Builder::new().prefix("reth-test-").rand_bytes(8).tempdir(); + builder.expect(ERROR_TEMPDIR).into_path() + } + /// Create read/write database for testing pub fn create_test_rw_db() -> Arc> { - let path = tempfile::TempDir::new().expect(ERROR_TEMPDIR).into_path(); + let path = tempdir_path(); let emsg = format!("{}: {:?}", ERROR_DB_CREATION, path); let db = init_db(&path, None).expect(&emsg); @@ -245,7 +251,7 @@ pub mod test_utils { /// Create read only database for testing pub fn create_test_ro_db() -> Arc> { - let path = tempfile::TempDir::new().expect(ERROR_TEMPDIR).into_path(); + let path = tempdir_path(); { init_db(path.as_path(), None).expect(ERROR_DB_CREATION); } diff --git a/crates/tracing/src/lib.rs b/crates/tracing/src/lib.rs index 61481013ff44..2fc551a5f65e 100644 --- a/crates/tracing/src/lib.rs +++ b/crates/tracing/src/lib.rs @@ -33,7 +33,8 @@ pub type BoxedLayer = Box + Send + Sync>; /// Initializes a new [Subscriber] based on the given layers. pub fn init(layers: Vec>) { - tracing_subscriber::registry().with(layers).init(); + // To avoid panicking in tests, we silently fail if we cannot initialize the subscriber. + let _ = tracing_subscriber::registry().with(layers).try_init(); } /// Builds a new tracing layer that writes to stdout. From 31161c9544bbf234715220a3b3d5071208e7d0e8 Mon Sep 17 00:00:00 2001 From: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Date: Thu, 28 Dec 2023 17:08:58 +0100 Subject: [PATCH 214/277] test: enable arbitrary feature for dependencies (#5882) --- crates/primitives/Cargo.toml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/crates/primitives/Cargo.toml b/crates/primitives/Cargo.toml index 1b296bf2bffb..87347f99506e 100644 --- a/crates/primitives/Cargo.toml +++ b/crates/primitives/Cargo.toml @@ -69,6 +69,8 @@ proptest.workspace = true proptest-derive.workspace = true rand.workspace = true revm-primitives = { workspace = true, features = ["arbitrary"] } +nybbles = { version = "0.1", features = ["arbitrary"] } +alloy-trie = { version = "0.1", features = ["arbitrary"] } serde_json.workspace = true test-fuzz = "4" toml.workspace = true From b1b059fe9bf3e6ad6f67eb259398fdb0903c9d02 Mon Sep 17 00:00:00 2001 From: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Date: Thu, 28 Dec 2023 17:23:50 +0100 Subject: [PATCH 215/277] chore: clean up lints (#5881) --- bin/reth/src/args/network_args.rs | 3 +- bin/reth/src/args/rpc_server_args.rs | 1 - bin/reth/src/cli/ext.rs | 1 - bin/reth/src/init.rs | 2 - crates/consensus/auto-seal/src/task.rs | 2 +- .../consensus/beacon/src/engine/forkchoice.rs | 12 +-- .../beacon/src/engine/hooks/controller.rs | 2 +- crates/consensus/beacon/src/engine/sync.rs | 2 +- crates/ethereum-forks/src/lib.rs | 1 - .../interfaces/src/blockchain_tree/error.rs | 18 ++--- crates/interfaces/src/test_utils/bodies.rs | 13 ++-- .../interfaces/src/test_utils/generators.rs | 14 ++-- crates/interfaces/src/test_utils/headers.rs | 32 +++----- crates/interfaces/src/test_utils/mod.rs | 2 - crates/metrics/metrics-derive/src/lib.rs | 3 - crates/net/discv4/src/test_utils.rs | 18 ++--- .../net/downloaders/src/bodies/test_utils.rs | 4 +- .../src/headers/reverse_headers.rs | 13 ++-- .../net/downloaders/src/headers/test_utils.rs | 4 +- crates/net/downloaders/src/lib.rs | 1 - .../downloaders/src/test_utils/file_client.rs | 48 +++--------- .../downloaders/src/test_utils/file_codec.rs | 2 +- crates/net/downloaders/src/test_utils/mod.rs | 27 ++++--- crates/net/ecies/src/algorithm.rs | 1 + crates/net/ecies/src/mac.rs | 1 + crates/net/eth-wire/src/p2pstream.rs | 44 ++++++----- crates/net/eth-wire/src/types/message.rs | 1 + crates/net/eth-wire/tests/fuzz_roundtrip.rs | 1 - crates/net/network/src/discovery.rs | 1 - crates/net/network/src/eth_requests.rs | 3 +- crates/net/network/src/fetch/mod.rs | 4 +- crates/net/network/src/message.rs | 13 +--- crates/net/network/src/session/active.rs | 4 +- crates/net/network/src/session/config.rs | 1 - crates/net/network/src/session/handle.rs | 1 - crates/net/network/src/state.rs | 2 +- crates/net/network/src/transactions.rs | 1 - crates/net/network/tests/it/multiplex.rs | 2 +- crates/payload/basic/src/lib.rs | 2 +- crates/primitives/src/constants/mod.rs | 3 +- crates/primitives/src/lib.rs | 1 - .../revm-inspectors/src/tracing/config.rs | 2 +- crates/rpc/ipc/src/server/future.rs | 4 +- crates/rpc/ipc/src/server/ipc.rs | 2 +- crates/rpc/ipc/src/server/mod.rs | 2 +- crates/rpc/rpc-types/src/beacon/mod.rs | 3 +- crates/rpc/rpc-types/src/beacon/payload.rs | 3 +- crates/rpc/rpc-types/src/eth/account.rs | 1 + crates/rpc/rpc-types/src/eth/engine/mod.rs | 6 +- .../rpc/rpc-types/src/eth/trace/geth/mod.rs | 3 +- .../rpc-types/src/eth/transaction/typed.rs | 3 +- crates/rpc/rpc-types/src/mev.rs | 1 + crates/rpc/rpc-types/src/net.rs | 2 +- crates/rpc/rpc-types/src/relay/mod.rs | 6 +- crates/rpc/rpc/src/eth/bundle.rs | 2 +- crates/rpc/rpc/src/otterscan.rs | 33 ++++---- crates/stages/src/error.rs | 2 +- crates/stages/src/lib.rs | 1 - crates/stages/src/test_utils/mod.rs | 5 +- crates/stages/src/test_utils/runner.rs | 5 +- crates/stages/src/test_utils/test_db.rs | 24 +++--- crates/storage/codecs/derive/src/lib.rs | 2 - crates/storage/codecs/src/lib.rs | 1 - crates/storage/db/benches/criterion.rs | 5 +- crates/storage/db/benches/hash_keys.rs | 11 +-- crates/storage/db/benches/iai.rs | 76 +++++++++++-------- crates/storage/db/benches/utils.rs | 9 +-- crates/storage/db/src/abstraction/mock.rs | 2 +- .../storage/db/src/tables/codecs/fuzz/mod.rs | 4 +- crates/storage/db/src/tables/codecs/mod.rs | 3 +- .../storage/db/src/tables/codecs/postcard.rs | 43 ----------- crates/storage/libmdbx-rs/src/environment.rs | 7 +- crates/storage/libmdbx-rs/src/error.rs | 2 +- crates/storage/libmdbx-rs/src/lib.rs | 4 +- crates/storage/libmdbx-rs/src/transaction.rs | 1 + .../storage/nippy-jar/src/compression/zstd.rs | 20 ++--- crates/storage/nippy-jar/src/lib.rs | 9 +-- crates/storage/nippy-jar/src/writer.rs | 7 ++ .../provider/src/providers/chain_info.rs | 6 +- .../src/providers/database/provider.rs | 2 +- .../src/providers/state/historical.rs | 2 +- .../provider/src/providers/state/latest.rs | 2 +- crates/transaction-pool/src/blobstore/mod.rs | 2 +- crates/transaction-pool/src/identifier.rs | 2 +- crates/transaction-pool/src/pool/blob.rs | 5 +- crates/transaction-pool/src/pool/parked.rs | 2 +- crates/transaction-pool/src/pool/txpool.rs | 10 +-- crates/transaction-pool/src/test_utils/gen.rs | 11 +-- .../transaction-pool/src/test_utils/mock.rs | 65 ++++++++-------- crates/transaction-pool/src/test_utils/mod.rs | 18 ++--- .../transaction-pool/src/test_utils/pool.rs | 43 +++++------ docs/crates/network.md | 2 +- 92 files changed, 353 insertions(+), 446 deletions(-) delete mode 100644 crates/storage/db/src/tables/codecs/postcard.rs diff --git a/bin/reth/src/args/network_args.rs b/bin/reth/src/args/network_args.rs index fd9c88d6c14f..48863dafbf86 100644 --- a/bin/reth/src/args/network_args.rs +++ b/bin/reth/src/args/network_args.rs @@ -54,8 +54,7 @@ pub struct NetworkArgs { #[arg(long, verbatim_doc_comment)] pub no_persist_peers: bool, - #[allow(rustdoc::invalid_html_tags)] - /// NAT resolution method (any|none|upnp|publicip|extip:) + /// NAT resolution method (any|none|upnp|publicip|extip:\) #[arg(long, default_value = "any")] pub nat: NatResolver, diff --git a/bin/reth/src/args/rpc_server_args.rs b/bin/reth/src/args/rpc_server_args.rs index f89a74550eb9..64db2104e63f 100644 --- a/bin/reth/src/args/rpc_server_args.rs +++ b/bin/reth/src/args/rpc_server_args.rs @@ -219,7 +219,6 @@ impl RpcServerArgs { /// Returns the handles for the launched regular RPC server(s) (if any) and the server handle /// for the auth server that handles the `engine_` API that's accessed by the consensus /// layer. - #[allow(clippy::too_many_arguments)] pub async fn start_servers( &self, components: &Reth, diff --git a/bin/reth/src/cli/ext.rs b/bin/reth/src/cli/ext.rs index 4bfbeb64d105..939c11ae549e 100644 --- a/bin/reth/src/cli/ext.rs +++ b/bin/reth/src/cli/ext.rs @@ -146,7 +146,6 @@ pub trait RethNodeCommandConfig: fmt::Debug { // The default payload builder is implemented on the unit type. #[cfg(not(feature = "optimism"))] - #[allow(clippy::let_unit_value)] let payload_builder = reth_basic_payload_builder::EthereumPayloadBuilder::default(); // Optimism's payload builder is implemented on the OptimismPayloadBuilder type. diff --git a/bin/reth/src/init.rs b/bin/reth/src/init.rs index a4e83403655f..7c2111f0758d 100644 --- a/bin/reth/src/init.rs +++ b/bin/reth/src/init.rs @@ -46,7 +46,6 @@ impl From for InitDatabaseError { } /// Write the genesis block if it has not already been written -#[allow(clippy::field_reassign_with_default)] pub fn init_genesis( db: Arc, chain: Arc, @@ -235,7 +234,6 @@ mod tests { }; use std::collections::HashMap; - #[allow(clippy::type_complexity)] fn collect_table_entries( tx: &::TX, ) -> Result>, InitDatabaseError> diff --git a/crates/consensus/auto-seal/src/task.rs b/crates/consensus/auto-seal/src/task.rs index 2ace064890e2..047e82be9e22 100644 --- a/crates/consensus/auto-seal/src/task.rs +++ b/crates/consensus/auto-seal/src/task.rs @@ -33,7 +33,7 @@ pub struct MiningTask { pool: Pool, /// backlog of sets of transactions ready to be mined queued: VecDeque::Transaction>>>>, - /// TODO: ideally this would just be a sender of hashes + // TODO: ideally this would just be a sender of hashes to_engine: UnboundedSender, /// Used to notify consumers of new blocks canon_state_notification: CanonStateNotificationSender, diff --git a/crates/consensus/beacon/src/engine/forkchoice.rs b/crates/consensus/beacon/src/engine/forkchoice.rs index bab7593357ed..54f5df568e66 100644 --- a/crates/consensus/beacon/src/engine/forkchoice.rs +++ b/crates/consensus/beacon/src/engine/forkchoice.rs @@ -46,31 +46,31 @@ impl ForkchoiceStateTracker { } /// Returns whether the latest received FCU is valid: [ForkchoiceStatus::Valid] - #[allow(unused)] + #[allow(dead_code)] pub(crate) fn is_latest_valid(&self) -> bool { self.latest_status().map(|s| s.is_valid()).unwrap_or(false) } /// Returns whether the latest received FCU is syncing: [ForkchoiceStatus::Syncing] - #[allow(unused)] + #[allow(dead_code)] pub(crate) fn is_latest_syncing(&self) -> bool { self.latest_status().map(|s| s.is_syncing()).unwrap_or(false) } /// Returns whether the latest received FCU is syncing: [ForkchoiceStatus::Invalid] - #[allow(unused)] + #[allow(dead_code)] pub(crate) fn is_latest_invalid(&self) -> bool { self.latest_status().map(|s| s.is_invalid()).unwrap_or(false) } /// Returns the last valid head hash. - #[allow(unused)] + #[allow(dead_code)] pub(crate) fn last_valid_head(&self) -> Option { self.last_valid.as_ref().map(|s| s.head_block_hash) } /// Returns the head hash of the latest received FCU to which we need to sync. - #[allow(unused)] + #[allow(dead_code)] pub(crate) fn sync_target(&self) -> Option { self.last_syncing.as_ref().map(|s| s.head_block_hash) } @@ -88,7 +88,7 @@ impl ForkchoiceStateTracker { /// Represents a forkchoice update and tracks the status we assigned to it. #[derive(Debug, Clone)] -#[allow(unused)] +#[allow(dead_code)] pub(crate) struct ReceivedForkchoiceState { state: ForkchoiceState, status: ForkchoiceStatus, diff --git a/crates/consensus/beacon/src/engine/hooks/controller.rs b/crates/consensus/beacon/src/engine/hooks/controller.rs index a477a4c05932..fa5ca447a3c6 100644 --- a/crates/consensus/beacon/src/engine/hooks/controller.rs +++ b/crates/consensus/beacon/src/engine/hooks/controller.rs @@ -10,7 +10,7 @@ use tracing::debug; #[derive(Debug)] pub(crate) struct PolledHook { - #[allow(unused)] + #[allow(dead_code)] pub(crate) name: &'static str, pub(crate) event: EngineHookEvent, pub(crate) action: Option, diff --git a/crates/consensus/beacon/src/engine/sync.rs b/crates/consensus/beacon/src/engine/sync.rs index 10c18e742089..f7d2f8c588f9 100644 --- a/crates/consensus/beacon/src/engine/sync.rs +++ b/crates/consensus/beacon/src/engine/sync.rs @@ -120,7 +120,7 @@ where } /// Returns `true` if a pipeline target is queued and will be triggered on the next `poll`. - #[allow(unused)] + #[allow(dead_code)] pub(crate) fn is_pipeline_sync_pending(&self) -> bool { self.pending_pipeline_target.is_some() && self.pipeline_state.is_idle() } diff --git a/crates/ethereum-forks/src/lib.rs b/crates/ethereum-forks/src/lib.rs index 49aed4605856..c03b0f84051a 100644 --- a/crates/ethereum-forks/src/lib.rs +++ b/crates/ethereum-forks/src/lib.rs @@ -14,7 +14,6 @@ #![warn(missing_debug_implementations, missing_docs, unreachable_pub, rustdoc::all)] #![deny(unused_must_use, rust_2018_idioms, unused_crate_dependencies)] #![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))] -#![allow(clippy::non_canonical_clone_impl)] mod forkid; mod hardfork; diff --git a/crates/interfaces/src/blockchain_tree/error.rs b/crates/interfaces/src/blockchain_tree/error.rs index ac7d4552770d..7d20b1831138 100644 --- a/crates/interfaces/src/blockchain_tree/error.rs +++ b/crates/interfaces/src/blockchain_tree/error.rs @@ -9,7 +9,6 @@ use reth_primitives::{BlockHash, BlockNumber, SealedBlock}; /// Various error cases that can occur when a block violates tree assumptions. #[derive(Debug, Clone, Copy, thiserror::Error, Eq, PartialEq)] -#[allow(missing_docs)] pub enum BlockchainTreeError { /// Thrown if the block number is lower than the last finalized block number. #[error("block number is lower than the last finalized block number #{last_finalized}")] @@ -41,7 +40,7 @@ pub enum BlockchainTreeError { /// The block hash that could not be found. block_hash: BlockHash, }, - // Thrown if the block failed to buffer + /// Thrown if the block failed to buffer #[error("block with hash {block_hash} failed to buffer")] BlockBufferingFailed { /// The block hash of the block that failed to buffer. @@ -53,7 +52,6 @@ pub enum BlockchainTreeError { pub type CanonicalResult = Result; /// Canonical Errors -#[allow(missing_docs)] #[derive(thiserror::Error, Debug, Clone, PartialEq, Eq)] pub enum CanonicalError { /// Error originating from validation operations. @@ -63,17 +61,17 @@ pub enum CanonicalError { #[error(transparent)] BlockchainTree(#[from] BlockchainTreeError), /// Error indicating a transaction reverted during execution. - #[error("transaction error on revert: {inner}")] - CanonicalRevert { inner: String }, + #[error("transaction error on revert: {0}")] + CanonicalRevert(String), /// Error indicating a transaction failed to commit during execution. - #[error("transaction error on commit: {inner}")] - CanonicalCommit { inner: String }, + #[error("transaction error on commit: {0}")] + CanonicalCommit(String), } impl CanonicalError { /// Returns `true` if the error is fatal. pub fn is_fatal(&self) -> bool { - matches!(self, Self::CanonicalCommit { .. } | Self::CanonicalRevert { .. }) + matches!(self, Self::CanonicalCommit(_) | Self::CanonicalRevert(_)) } } @@ -270,8 +268,8 @@ impl InsertBlockErrorKind { } InsertBlockErrorKind::Canonical(err) => match err { CanonicalError::BlockchainTree(_) | - CanonicalError::CanonicalCommit { .. } | - CanonicalError::CanonicalRevert { .. } => false, + CanonicalError::CanonicalCommit(_) | + CanonicalError::CanonicalRevert(_) => false, CanonicalError::Validation(_) => true, }, InsertBlockErrorKind::BlockchainTree(_) => false, diff --git a/crates/interfaces/src/test_utils/bodies.rs b/crates/interfaces/src/test_utils/bodies.rs index 2c79476adc0d..e1d42a2a57f4 100644 --- a/crates/interfaces/src/test_utils/bodies.rs +++ b/crates/interfaces/src/test_utils/bodies.rs @@ -4,13 +4,10 @@ use crate::p2p::{ error::PeerRequestResult, priority::Priority, }; -use futures::{future, Future, FutureExt}; -use reth_primitives::{BlockBody, WithPeerId, B256}; -use std::{ - fmt::{Debug, Formatter}, - pin::Pin, -}; -use tokio::sync::oneshot::{self, Receiver}; +use futures::FutureExt; +use reth_primitives::{BlockBody, B256}; +use std::fmt::{Debug, Formatter}; +use tokio::sync::oneshot; /// A test client for fetching bodies pub struct TestBodiesClient { @@ -46,7 +43,7 @@ where _priority: Priority, ) -> Self::Output { let (tx, rx) = oneshot::channel(); - tx.send((self.responder)(hashes)); + let _ = tx.send((self.responder)(hashes)); Box::pin(rx.map(|x| match x { Ok(value) => value, Err(err) => Err(err.into()), diff --git a/crates/interfaces/src/test_utils/generators.rs b/crates/interfaces/src/test_utils/generators.rs index eed6e5c4a4a5..ff4392fa2312 100644 --- a/crates/interfaces/src/test_utils/generators.rs +++ b/crates/interfaces/src/test_utils/generators.rs @@ -4,15 +4,15 @@ use rand::{ }; use reth_primitives::{ proofs, sign_message, Account, Address, BlockNumber, Bytes, Header, Log, Receipt, SealedBlock, - SealedHeader, Signature, StorageEntry, Transaction, TransactionKind, TransactionSigned, - TxLegacy, B256, U256, + SealedHeader, StorageEntry, Transaction, TransactionKind, TransactionSigned, TxLegacy, B256, + U256, }; -use secp256k1::{KeyPair, Message as SecpMessage, Secp256k1, SecretKey, SECP256K1}; +use secp256k1::{KeyPair, Secp256k1}; use std::{ cmp::{max, min}, collections::{hash_map::DefaultHasher, BTreeMap}, hash::Hasher, - ops::{Range, RangeInclusive, Sub}, + ops::{Range, RangeInclusive}, }; // TODO(onbjerg): Maybe we should split this off to its own crate, or move the helpers to the @@ -220,7 +220,7 @@ where let mut changesets = Vec::new(); - blocks.into_iter().for_each(|block| { + for _block in blocks { let mut changeset = Vec::new(); let (from, to, mut transfer, new_entries) = random_account_change( rng, @@ -263,7 +263,7 @@ where prev_to.balance = prev_to.balance.wrapping_add(transfer); changesets.push(changeset); - }); + } let final_state = state .into_iter() @@ -388,7 +388,7 @@ pub fn random_log(rng: &mut R, address: Option
, topics_count: O mod test { use super::*; use reth_primitives::{ - hex, keccak256, public_key_to_address, AccessList, Address, TransactionKind, TxEip1559, + hex, public_key_to_address, AccessList, Signature, TransactionKind, TxEip1559, }; use secp256k1::KeyPair; use std::str::FromStr; diff --git a/crates/interfaces/src/test_utils/headers.rs b/crates/interfaces/src/test_utils/headers.rs index bf761846b27a..24b187c63d2e 100644 --- a/crates/interfaces/src/test_utils/headers.rs +++ b/crates/interfaces/src/test_utils/headers.rs @@ -1,4 +1,5 @@ //! Testing support for headers related interfaces. + use crate::{ consensus::{self, Consensus, ConsensusError}, p2p::{ @@ -6,19 +7,16 @@ use crate::{ error::{DownloadError, DownloadResult, PeerRequestResult, RequestError}, headers::{ client::{HeadersClient, HeadersRequest}, - downloader::{validate_header_download, HeaderDownloader, SyncTarget}, + downloader::{HeaderDownloader, SyncTarget}, error::HeadersDownloaderResult, }, priority::Priority, }, }; -use futures::{future, Future, FutureExt, Stream, StreamExt}; -use reth_eth_wire::BlockHeaders; +use futures::{Future, FutureExt, Stream, StreamExt}; use reth_primitives::{ - BlockHash, BlockNumber, Head, Header, HeadersDirection, PeerId, SealedBlock, SealedHeader, - WithPeerId, B256, U256, + Header, HeadersDirection, PeerId, SealedBlock, SealedHeader, WithPeerId, U256, }; -use reth_rpc_types::engine::ForkchoiceState; use std::{ fmt, pin::Pin, @@ -28,12 +26,7 @@ use std::{ }, task::{ready, Context, Poll}, }; -use tokio::sync::{ - oneshot::{error::RecvError, Receiver}, - watch, - watch::error::SendError, - Mutex, -}; +use tokio::sync::Mutex; /// A test downloader which just returns the values that have been pushed to it. #[derive(Debug)] @@ -67,11 +60,6 @@ impl TestHeaderDownloader { done: false, } } - - /// Validate whether the header is valid in relation to it's parent - fn validate(&self, header: &SealedHeader, parent: &SealedHeader) -> DownloadResult<()> { - validate_header_download(&self.consensus, header, parent) - } } impl HeaderDownloader for TestHeaderDownloader { @@ -94,7 +82,7 @@ impl Stream for TestHeaderDownloader { return Poll::Ready(Some(Ok(std::mem::take(&mut this.queued_headers)))) } if this.download.is_none() { - this.download.insert(this.create_download()); + this.download = Some(this.create_download()); } match ready!(this.download.as_mut().unwrap().poll_next_unpin(cx)) { @@ -293,8 +281,8 @@ impl Consensus for TestConsensus { fn validate_header_against_parent( &self, - header: &SealedHeader, - parent: &SealedHeader, + _header: &SealedHeader, + _parent: &SealedHeader, ) -> Result<(), ConsensusError> { if self.fail_validation() { Err(consensus::ConsensusError::BaseFeeMissing) @@ -305,8 +293,8 @@ impl Consensus for TestConsensus { fn validate_header_with_total_difficulty( &self, - header: &Header, - total_difficulty: U256, + _header: &Header, + _total_difficulty: U256, ) -> Result<(), ConsensusError> { if self.fail_validation() { Err(consensus::ConsensusError::BaseFeeMissing) diff --git a/crates/interfaces/src/test_utils/mod.rs b/crates/interfaces/src/test_utils/mod.rs index 60c73bdaa212..e56cd8f00f6b 100644 --- a/crates/interfaces/src/test_utils/mod.rs +++ b/crates/interfaces/src/test_utils/mod.rs @@ -1,5 +1,3 @@ -#![allow(unused)] - mod bodies; mod full_block; mod headers; diff --git a/crates/metrics/metrics-derive/src/lib.rs b/crates/metrics/metrics-derive/src/lib.rs index 2c4e0f6a7a53..24e7bafa0e6c 100644 --- a/crates/metrics/metrics-derive/src/lib.rs +++ b/crates/metrics/metrics-derive/src/lib.rs @@ -12,9 +12,6 @@ use proc_macro::TokenStream; use syn::{parse_macro_input, DeriveInput}; -#[allow(unused_extern_crates)] -extern crate proc_macro; - mod expand; mod metric; mod with_attrs; diff --git a/crates/net/discv4/src/test_utils.rs b/crates/net/discv4/src/test_utils.rs index 893b8abfec4c..a1b05d9d283e 100644 --- a/crates/net/discv4/src/test_utils.rs +++ b/crates/net/discv4/src/test_utils.rs @@ -1,6 +1,6 @@ //! Mock discovery support -#![allow(missing_docs, unused)] +#![allow(missing_docs)] use crate::{ proto::{FindNode, Message, Neighbours, NodeEndpoint, Packet, Ping, Pong}, @@ -26,7 +26,7 @@ use tokio::{ task::{JoinHandle, JoinSet}, }; use tokio_stream::{Stream, StreamExt}; -use tracing::{debug, error}; +use tracing::debug; /// Mock discovery node #[derive(Debug)] @@ -34,7 +34,7 @@ pub struct MockDiscovery { local_addr: SocketAddr, local_enr: NodeRecord, secret_key: SecretKey, - udp: Arc, + _udp: Arc, _tasks: JoinSet<()>, /// Receiver for incoming messages ingress: IngressReceiver, @@ -79,7 +79,7 @@ impl MockDiscovery { local_addr, local_enr, secret_key, - udp: socket, + _udp: socket, pending_pongs: Default::default(), pending_neighbours: Default::default(), command_rx, @@ -88,7 +88,7 @@ impl MockDiscovery { } /// Spawn and consume the stream. - pub fn spawn(mut self) -> JoinHandle<()> { + pub fn spawn(self) -> JoinHandle<()> { tokio::task::spawn(async move { let _: Vec<_> = self.collect().await; }) @@ -282,8 +282,7 @@ pub fn rng_message(rng: &mut impl RngCore) -> Message { #[cfg(test)] mod tests { use super::*; - use crate::{Discv4Event, PingReason}; - use reth_primitives::{hex_literal::hex, ForkHash, ForkId}; + use crate::Discv4Event; use std::net::{IpAddr, Ipv4Addr}; /// This test creates two local UDP sockets. The mocked discovery service responds to specific @@ -294,10 +293,9 @@ mod tests { let mut rng = thread_rng(); let (_, mut service) = create_discv4().await; - let (mut mockv4, mut cmd) = MockDiscovery::new().await.unwrap(); + let (mut mockv4, _cmd) = MockDiscovery::new().await.unwrap(); let mock_enr = mockv4.local_enr(); - let mock_addr = mockv4.local_addr(); // we only want to test internally service.local_enr_mut().address = IpAddr::V4(Ipv4Addr::UNSPECIFIED); @@ -314,7 +312,7 @@ mod tests { // process the mock pong let event = mockv4.next().await.unwrap(); match event { - MockEvent::Pong { ping, pong, to } => { + MockEvent::Pong { ping: _, pong: _, to } => { assert_eq!(to, SocketAddr::new(IpAddr::V4(Ipv4Addr::LOCALHOST), discv_addr.port())); } MockEvent::Neighbours { .. } => { diff --git a/crates/net/downloaders/src/bodies/test_utils.rs b/crates/net/downloaders/src/bodies/test_utils.rs index 42261a02bf4a..40e5301293d8 100644 --- a/crates/net/downloaders/src/bodies/test_utils.rs +++ b/crates/net/downloaders/src/bodies/test_utils.rs @@ -1,5 +1,7 @@ -#![allow(unused)] //! Test helper impls for generating bodies + +#![allow(dead_code)] + use reth_db::{database::Database, tables, transaction::DbTxMut, DatabaseEnv}; use reth_interfaces::{db, p2p::bodies::response::BlockResponse}; use reth_primitives::{Block, BlockBody, SealedBlock, SealedHeader, B256}; diff --git a/crates/net/downloaders/src/headers/reverse_headers.rs b/crates/net/downloaders/src/headers/reverse_headers.rs index debee8629775..21f2511a7c8d 100644 --- a/crates/net/downloaders/src/headers/reverse_headers.rs +++ b/crates/net/downloaders/src/headers/reverse_headers.rs @@ -209,28 +209,28 @@ where header: &SealedHeader, request: HeadersRequest, peer_id: PeerId, - ) -> Result<(), HeadersResponseError> { + ) -> Result<(), Box> { match self.existing_sync_target() { SyncTargetBlock::Hash(hash) | SyncTargetBlock::HashAndNumber { hash, .. } if header.hash() != hash => { - Err(HeadersResponseError { + Err(Box::new(HeadersResponseError { request, peer_id: Some(peer_id), error: DownloadError::InvalidTip( GotExpected { got: header.hash(), expected: hash }.into(), ), - }) + })) } SyncTargetBlock::Number(number) if header.number != number => { - Err(HeadersResponseError { + Err(Box::new(HeadersResponseError { request, peer_id: Some(peer_id), error: DownloadError::InvalidTipNumber(GotExpected { got: header.number, expected: number, }), - }) + })) } _ => Ok(()), } @@ -243,7 +243,6 @@ where /// Returns an error if the given headers are invalid. /// /// Caution: this expects the `headers` to be sorted with _falling_ block numbers - #[allow(clippy::result_large_err)] fn process_next_headers( &mut self, request: HeadersRequest, @@ -352,7 +351,6 @@ where } /// Handles the response for the request for the sync target - #[allow(clippy::result_large_err)] fn on_sync_target_outcome( &mut self, response: HeadersRequestOutcome, @@ -442,7 +440,6 @@ where } /// Invoked when we received a response - #[allow(clippy::result_large_err)] fn on_headers_outcome( &mut self, response: HeadersRequestOutcome, diff --git a/crates/net/downloaders/src/headers/test_utils.rs b/crates/net/downloaders/src/headers/test_utils.rs index 2e07e6bfd328..c04f08b4cae6 100644 --- a/crates/net/downloaders/src/headers/test_utils.rs +++ b/crates/net/downloaders/src/headers/test_utils.rs @@ -1,5 +1,7 @@ -#![allow(unused)] //! Test helper impls for generating bodies + +#![allow(dead_code)] + use reth_primitives::SealedHeader; /// Returns a new [SealedHeader] that's the child header of the given `parent`. diff --git a/crates/net/downloaders/src/lib.rs b/crates/net/downloaders/src/lib.rs index f96208657a7c..042c749eaefa 100644 --- a/crates/net/downloaders/src/lib.rs +++ b/crates/net/downloaders/src/lib.rs @@ -9,7 +9,6 @@ html_favicon_url = "https://avatars0.githubusercontent.com/u/97369466?s=256", issue_tracker_base_url = "https://github.com/paradigmxyz/reth/issues/" )] -#![allow(clippy::result_large_err)] // TODO(danipopes): fix this #![warn(missing_debug_implementations, missing_docs, unreachable_pub, rustdoc::all)] #![deny(unused_must_use, rust_2018_idioms)] #![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))] diff --git a/crates/net/downloaders/src/test_utils/file_client.rs b/crates/net/downloaders/src/test_utils/file_client.rs index 69320fe4b171..01721ef6806e 100644 --- a/crates/net/downloaders/src/test_utils/file_client.rs +++ b/crates/net/downloaders/src/test_utils/file_client.rs @@ -1,34 +1,18 @@ use super::file_codec::BlockFileCodec; -use alloy_rlp::{Decodable, Header as RlpHeader}; use itertools::Either; -use reth_interfaces::{ - p2p::{ - bodies::client::{BodiesClient, BodiesFut}, - download::DownloadClient, - error::RequestError, - headers::client::{HeadersClient, HeadersFut, HeadersRequest}, - priority::Priority, - }, - sync::{NetworkSyncUpdater, SyncState, SyncStateProvider}, +use reth_interfaces::p2p::{ + bodies::client::{BodiesClient, BodiesFut}, + download::DownloadClient, + error::RequestError, + headers::client::{HeadersClient, HeadersFut, HeadersRequest}, + priority::Priority, }; use reth_primitives::{ - Block, BlockBody, BlockHash, BlockHashOrNumber, BlockNumber, Header, HeadersDirection, PeerId, - B256, -}; -use std::{ - collections::HashMap, - iter::zip, - path::Path, - sync::{ - atomic::{AtomicBool, Ordering}, - Arc, - }, + BlockBody, BlockHash, BlockHashOrNumber, BlockNumber, Header, HeadersDirection, PeerId, B256, }; +use std::{self, collections::HashMap, path::Path}; use thiserror::Error; -use tokio::{ - fs::File, - io::{AsyncReadExt, BufReader}, -}; +use tokio::{fs::File, io::AsyncReadExt}; use tokio_stream::StreamExt; use tokio_util::codec::FramedRead; use tracing::{trace, warn}; @@ -250,14 +234,12 @@ mod tests { use crate::{ bodies::{ bodies::BodiesDownloaderBuilder, - test_utils::{create_raw_bodies, insert_headers, zip_blocks}, + test_utils::{insert_headers, zip_blocks}, }, headers::{reverse_headers::ReverseHeadersDownloaderBuilder, test_utils::child_header}, test_utils::{generate_bodies, generate_bodies_file}, }; - use alloy_rlp::Encodable; use assert_matches::assert_matches; - use futures::SinkExt; use futures_util::stream::StreamExt; use reth_db::test_utils::create_test_rw_db; use reth_interfaces::{ @@ -269,12 +251,7 @@ mod tests { }; use reth_primitives::{SealedHeader, MAINNET}; use reth_provider::ProviderFactory; - use std::{ - io::{Read, Seek, SeekFrom, Write}, - sync::Arc, - }; - use tokio::io::{AsyncSeekExt, AsyncWriteExt, BufWriter}; - use tokio_util::codec::FramedWrite; + use std::sync::Arc; #[tokio::test] async fn streams_bodies_from_buffer() { @@ -337,8 +314,7 @@ mod tests { #[tokio::test] async fn test_download_headers_from_file() { // Generate some random blocks - let db = create_test_rw_db(); - let (file, headers, mut bodies) = generate_bodies_file(0..=19).await; + let (file, headers, _) = generate_bodies_file(0..=19).await; // now try to read them back let client = Arc::new(FileClient::from_file(file).await.unwrap()); diff --git a/crates/net/downloaders/src/test_utils/file_codec.rs b/crates/net/downloaders/src/test_utils/file_codec.rs index b0cc90448d4e..25a42dba4a90 100644 --- a/crates/net/downloaders/src/test_utils/file_codec.rs +++ b/crates/net/downloaders/src/test_utils/file_codec.rs @@ -31,7 +31,7 @@ impl Decoder for BlockFileCodec { if src.is_empty() { return Ok(None) } - let mut buf_slice = &mut src.as_ref(); + let buf_slice = &mut src.as_ref(); let body = Block::decode(buf_slice)?; src.advance(src.len() - buf_slice.len()); Ok(Some(body)) diff --git a/crates/net/downloaders/src/test_utils/mod.rs b/crates/net/downloaders/src/test_utils/mod.rs index 4009e7431d7e..85dd0dfdd2ec 100644 --- a/crates/net/downloaders/src/test_utils/mod.rs +++ b/crates/net/downloaders/src/test_utils/mod.rs @@ -1,24 +1,23 @@ -#![allow(unused)] -//! Test helper impls +//! Test helper impls. + +#![allow(dead_code)] + use crate::bodies::test_utils::create_raw_bodies; use futures::SinkExt; -use reth_interfaces::test_utils::generators::random_block_range; +use reth_interfaces::test_utils::{generators, generators::random_block_range}; use reth_primitives::{BlockBody, SealedHeader, B256}; use std::{collections::HashMap, io::SeekFrom, ops::RangeInclusive}; -use tokio::{ - fs::File, - io::{AsyncSeekExt, AsyncWriteExt, BufWriter}, -}; +use tokio::{fs::File, io::AsyncSeekExt}; use tokio_util::codec::FramedWrite; mod bodies_client; -mod file_client; -mod file_codec; - pub use bodies_client::TestBodiesClient; + +mod file_client; pub use file_client::{FileClient, FileClientError}; + +mod file_codec; pub(crate) use file_codec::BlockFileCodec; -use reth_interfaces::test_utils::generators; /// Metrics scope used for testing. pub(crate) const TEST_SCOPE: &str = "downloaders.test"; @@ -51,12 +50,12 @@ pub(crate) fn generate_bodies( /// Generate a set of bodies, write them to a temporary file, and return the file along with the /// bodies and corresponding block hashes pub(crate) async fn generate_bodies_file( - rng: RangeInclusive, + range: RangeInclusive, ) -> (tokio::fs::File, Vec, HashMap) { - let (headers, mut bodies) = generate_bodies(0..=19); + let (headers, bodies) = generate_bodies(range); let raw_block_bodies = create_raw_bodies(headers.clone().iter(), &mut bodies.clone()); - let mut file: File = tempfile::tempfile().unwrap().into(); + let file: File = tempfile::tempfile().unwrap().into(); let mut writer = FramedWrite::new(file, BlockFileCodec); // rlp encode one after the other diff --git a/crates/net/ecies/src/algorithm.rs b/crates/net/ecies/src/algorithm.rs index 66ddd00174b5..54d9dd194bc7 100644 --- a/crates/net/ecies/src/algorithm.rs +++ b/crates/net/ecies/src/algorithm.rs @@ -1,4 +1,5 @@ #![allow(missing_docs)] + use crate::{ error::ECIESErrorImpl, mac::{HeaderBytes, MAC}, diff --git a/crates/net/ecies/src/mac.rs b/crates/net/ecies/src/mac.rs index 6768fd7b71fd..6c2fab327902 100644 --- a/crates/net/ecies/src/mac.rs +++ b/crates/net/ecies/src/mac.rs @@ -1,4 +1,5 @@ #![allow(missing_docs)] + use aes::Aes256Enc; use block_padding::NoPadding; use cipher::BlockEncrypt; diff --git a/crates/net/eth-wire/src/p2pstream.rs b/crates/net/eth-wire/src/p2pstream.rs index 3ac340add7d6..5809de260284 100644 --- a/crates/net/eth-wire/src/p2pstream.rs +++ b/crates/net/eth-wire/src/p2pstream.rs @@ -1,35 +1,31 @@ -#![allow(dead_code, unreachable_pub, missing_docs, unused_variables)] - -use std::{ - collections::VecDeque, - fmt, io, - pin::Pin, - task::{ready, Context, Poll}, - time::Duration, +use crate::{ + capability::SharedCapabilities, + disconnect::CanDisconnect, + errors::{P2PHandshakeError, P2PStreamError}, + pinger::{Pinger, PingerEvent}, + DisconnectReason, HelloMessage, HelloMessageWithProtocols, }; - use alloy_rlp::{Decodable, Encodable, Error as RlpError, EMPTY_LIST_CODE}; use futures::{Sink, SinkExt, StreamExt}; use pin_project::pin_project; -#[cfg(feature = "serde")] -use serde::{Deserialize, Serialize}; -use tokio_stream::Stream; -use tracing::{debug, trace}; - use reth_codecs::derive_arbitrary; use reth_metrics::metrics::counter; use reth_primitives::{ bytes::{Buf, BufMut, Bytes, BytesMut}, hex, GotExpected, }; - -use crate::{ - capability::SharedCapabilities, - disconnect::CanDisconnect, - errors::{P2PHandshakeError, P2PStreamError}, - pinger::{Pinger, PingerEvent}, - DisconnectReason, HelloMessage, HelloMessageWithProtocols, +use std::{ + collections::VecDeque, + fmt, io, + pin::Pin, + task::{ready, Context, Poll}, + time::Duration, }; +use tokio_stream::Stream; +use tracing::{debug, trace}; + +#[cfg(feature = "serde")] +use serde::{Deserialize, Serialize}; /// [`MAX_PAYLOAD_SIZE`] is the maximum size of an uncompressed message payload. /// This is defined in [EIP-706](https://eips.ethereum.org/EIPS/eip-706). @@ -56,6 +52,7 @@ const PING_INTERVAL: Duration = Duration::from_secs(60); /// [`GRACE_PERIOD`] determines the amount of time to wait for a peer to disconnect after sending a /// [`P2PMessage::Disconnect`] message. +#[allow(dead_code)] const GRACE_PERIOD: Duration = Duration::from_secs(2); /// [`MAX_P2P_CAPACITY`] is the maximum number of messages that can be buffered to be sent in the @@ -323,9 +320,12 @@ impl P2PStream { } } +/// Gracefully disconnects the connection by sending a disconnect message and stop reading new +/// messages. pub trait DisconnectP2P { /// Starts to gracefully disconnect. fn start_disconnect(&mut self, reason: DisconnectReason) -> Result<(), P2PStreamError>; + /// Returns `true` if the connection is about to disconnect. fn is_disconnecting(&self) -> bool; } @@ -842,6 +842,8 @@ mod tests { P2PStreamError::Disconnected(reason) => assert_eq!(reason, expected_disconnect), e => panic!("unexpected err: {e}"), } + + handle.await.unwrap(); } #[tokio::test] diff --git a/crates/net/eth-wire/src/types/message.rs b/crates/net/eth-wire/src/types/message.rs index e33f17b5207b..fe247c0a892b 100644 --- a/crates/net/eth-wire/src/types/message.rs +++ b/crates/net/eth-wire/src/types/message.rs @@ -1,4 +1,5 @@ #![allow(missing_docs)] + use super::{ broadcast::NewBlockHashes, BlockBodies, BlockHeaders, GetBlockBodies, GetBlockHeaders, GetNodeData, GetPooledTransactions, GetReceipts, NewBlock, NewPooledTransactionHashes66, diff --git a/crates/net/eth-wire/tests/fuzz_roundtrip.rs b/crates/net/eth-wire/tests/fuzz_roundtrip.rs index 05369d50e9d1..a4da724163df 100644 --- a/crates/net/eth-wire/tests/fuzz_roundtrip.rs +++ b/crates/net/eth-wire/tests/fuzz_roundtrip.rs @@ -38,7 +38,6 @@ macro_rules! fuzz_type_and_name { ( $x:ty, $fuzzname:ident ) => { /// Fuzzes the round-trip encoding of the type. #[test_fuzz] - #[allow(non_snake_case)] fn $fuzzname(thing: $x) { crate::roundtrip_fuzz::<$x>(thing) } diff --git a/crates/net/network/src/discovery.rs b/crates/net/network/src/discovery.rs index 72583f01bb2d..2f6177666944 100644 --- a/crates/net/network/src/discovery.rs +++ b/crates/net/network/src/discovery.rs @@ -117,7 +117,6 @@ impl Discovery { } /// Updates the `eth:ForkId` field in discv4. - #[allow(unused)] pub(crate) fn update_fork_id(&self, fork_id: ForkId) { if let Some(discv4) = &self.discv4 { // use forward-compatible forkid entry diff --git a/crates/net/network/src/eth_requests.rs b/crates/net/network/src/eth_requests.rs index 8491d7463027..c10d94682b25 100644 --- a/crates/net/network/src/eth_requests.rs +++ b/crates/net/network/src/eth_requests.rs @@ -59,8 +59,8 @@ pub struct EthRequestHandler { /// The client type that can interact with the chain. client: C, /// Used for reporting peers. - #[allow(unused)] // TODO use to report spammers + #[allow(dead_code)] peers: PeersHandle, /// Incoming request from the [NetworkManager](crate::NetworkManager). incoming_requests: ReceiverStream, @@ -272,7 +272,6 @@ where /// This is the key type for spam detection cache. The counter is ignored during `PartialEq` and /// `Hash`. #[derive(Debug, PartialEq, Hash)] -#[allow(unused)] struct RespondedGetBlockHeaders { req: (PeerId, GetBlockHeaders), } diff --git a/crates/net/network/src/fetch/mod.rs b/crates/net/network/src/fetch/mod.rs index cda61fc230b0..988428261d1b 100644 --- a/crates/net/network/src/fetch/mod.rs +++ b/crates/net/network/src/fetch/mod.rs @@ -355,8 +355,8 @@ impl PeerState { #[derive(Debug)] struct Request { /// The issued request object - /// TODO: this can be attached to the response in error case - #[allow(unused)] + // TODO: this can be attached to the response in error case + #[allow(dead_code)] request: Req, response: oneshot::Sender, } diff --git a/crates/net/network/src/message.rs b/crates/net/network/src/message.rs index 17d202733d18..8f8a5e13e337 100644 --- a/crates/net/network/src/message.rs +++ b/crates/net/network/src/message.rs @@ -56,14 +56,12 @@ pub enum PeerMessage { /// All `eth` request variants. EthRequest(PeerRequest), /// Other than eth namespace message - #[allow(unused)] Other(RawCapabilityMessage), } /// Request Variants that only target block related data. #[derive(Debug, Clone, PartialEq, Eq)] #[allow(missing_docs)] -#[allow(clippy::enum_variant_names)] pub enum BlockRequest { GetBlockHeaders(GetBlockHeaders), GetBlockBodies(GetBlockBodies), @@ -71,7 +69,7 @@ pub enum BlockRequest { /// Protocol related request messages that expect a response #[derive(Debug)] -#[allow(clippy::enum_variant_names, missing_docs)] +#[allow(missing_docs)] pub enum PeerRequest { /// Request Block headers from the peer. /// @@ -265,15 +263,8 @@ impl PeerResponseResult { } /// Returns whether this result is an error. - #[allow(unused)] pub fn is_err(&self) -> bool { - match self { - PeerResponseResult::BlockHeaders(res) => res.is_err(), - PeerResponseResult::BlockBodies(res) => res.is_err(), - PeerResponseResult::PooledTransactions(res) => res.is_err(), - PeerResponseResult::NodeData(res) => res.is_err(), - PeerResponseResult::Receipts(res) => res.is_err(), - } + self.err().is_some() } } diff --git a/crates/net/network/src/session/active.rs b/crates/net/network/src/session/active.rs index a8424713cd34..984deb629e65 100644 --- a/crates/net/network/src/session/active.rs +++ b/crates/net/network/src/session/active.rs @@ -60,7 +60,7 @@ const TIMEOUT_SCALING: u32 = 3; /// - incoming _internal_ requests/broadcasts via the request/command channel /// - incoming requests/broadcasts _from remote_ via the connection /// - responses for handled ETH requests received from the remote peer. -#[allow(unused)] +#[allow(dead_code)] pub(crate) struct ActiveSession { /// Keeps track of request ids. pub(crate) next_id: u64, @@ -662,7 +662,7 @@ pub(crate) struct ReceivedRequest { /// Receiver half of the channel that's supposed to receive the proper response. rx: PeerResponse, /// Timestamp when we read this msg from the wire. - #[allow(unused)] + #[allow(dead_code)] received: Instant, } diff --git a/crates/net/network/src/session/config.rs b/crates/net/network/src/session/config.rs index 8116c49fb635..038297b2bd8d 100644 --- a/crates/net/network/src/session/config.rs +++ b/crates/net/network/src/session/config.rs @@ -179,7 +179,6 @@ impl SessionCounter { } } - #[allow(unused)] pub(crate) fn ensure_pending_outbound(&self) -> Result<(), ExceedsSessionLimit> { Self::ensure(self.pending_outbound, self.limits.max_pending_outbound) } diff --git a/crates/net/network/src/session/handle.rs b/crates/net/network/src/session/handle.rs index 3b73c9e1fc1f..8a7b71e6363e 100644 --- a/crates/net/network/src/session/handle.rs +++ b/crates/net/network/src/session/handle.rs @@ -254,7 +254,6 @@ pub enum ActiveSessionMessage { message: PeerMessage, }, /// Received a message that does not match the announced capabilities of the peer. - #[allow(unused)] InvalidMessage { /// Identifier of the remote peer. peer_id: PeerId, diff --git a/crates/net/network/src/state.rs b/crates/net/network/src/state.rs index 63351f5f9967..7b32efb88d8c 100644 --- a/crates/net/network/src/state.rs +++ b/crates/net/network/src/state.rs @@ -471,7 +471,7 @@ pub(crate) struct ActivePeer { /// Best block of the peer. pub(crate) best_hash: B256, /// The capabilities of the remote peer. - #[allow(unused)] + #[allow(dead_code)] pub(crate) capabilities: Arc, /// A communication channel directly to the session task. pub(crate) request_tx: PeerRequestSender, diff --git a/crates/net/network/src/transactions.rs b/crates/net/network/src/transactions.rs index 398e0ceeed00..bc7b0c958bbc 100644 --- a/crates/net/network/src/transactions.rs +++ b/crates/net/network/src/transactions.rs @@ -1067,7 +1067,6 @@ struct Peer { /// negotiated version of the session. version: EthVersion, /// The peer's client version. - #[allow(unused)] client_version: Arc, } diff --git a/crates/net/network/tests/it/multiplex.rs b/crates/net/network/tests/it/multiplex.rs index 3026a8efec87..6ed8125331ff 100644 --- a/crates/net/network/tests/it/multiplex.rs +++ b/crates/net/network/tests/it/multiplex.rs @@ -166,9 +166,9 @@ struct ProtocolState { } #[derive(Debug)] -#[allow(dead_code)] enum ProtocolEvent { Established { + #[allow(dead_code)] direction: Direction, peer_id: PeerId, to_connection: mpsc::UnboundedSender, diff --git a/crates/payload/basic/src/lib.rs b/crates/payload/basic/src/lib.rs index a275ad5d78a1..2b5c397f2be5 100644 --- a/crates/payload/basic/src/lib.rs +++ b/crates/payload/basic/src/lib.rs @@ -648,7 +648,7 @@ pub struct PayloadConfig { /// The chain spec. chain_spec: Arc, /// The rollup's compute pending block configuration option. - /// TODO(clabby): Implement this feature. + // TODO(clabby): Implement this feature. #[cfg(feature = "optimism")] #[allow(dead_code)] compute_pending_block: bool, diff --git a/crates/primitives/src/constants/mod.rs b/crates/primitives/src/constants/mod.rs index 2905e5c9dc76..59e14602bcc4 100644 --- a/crates/primitives/src/constants/mod.rs +++ b/crates/primitives/src/constants/mod.rs @@ -34,8 +34,7 @@ pub const EPOCH_DURATION: Duration = Duration::from_secs(12 * EPOCH_SLOTS); pub const BEACON_NONCE: u64 = 0u64; /// The default Ethereum block gas limit. -/// -/// TODO: This should be a chain spec parameter. +// TODO: This should be a chain spec parameter. /// See . pub const ETHEREUM_BLOCK_GAS_LIMIT: u64 = 30_000_000; diff --git a/crates/primitives/src/lib.rs b/crates/primitives/src/lib.rs index 6f0858af4e06..37445e1dfb1b 100644 --- a/crates/primitives/src/lib.rs +++ b/crates/primitives/src/lib.rs @@ -15,7 +15,6 @@ #![warn(missing_debug_implementations, missing_docs, unreachable_pub, rustdoc::all)] #![deny(unused_must_use, rust_2018_idioms)] #![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))] -#![allow(clippy::non_canonical_clone_impl)] mod account; pub mod basefee; diff --git a/crates/revm/revm-inspectors/src/tracing/config.rs b/crates/revm/revm-inspectors/src/tracing/config.rs index 6bc85f76c9c2..386a9ae233ae 100644 --- a/crates/revm/revm-inspectors/src/tracing/config.rs +++ b/crates/revm/revm-inspectors/src/tracing/config.rs @@ -184,7 +184,7 @@ pub(crate) enum TraceStyle { /// Parity style tracer Parity, /// Geth style tracer - #[allow(unused)] + #[allow(dead_code)] Geth, } diff --git a/crates/rpc/ipc/src/server/future.rs b/crates/rpc/ipc/src/server/future.rs index 942c25f3fc2c..eabf1da48375 100644 --- a/crates/rpc/ipc/src/server/future.rs +++ b/crates/rpc/ipc/src/server/future.rs @@ -177,7 +177,7 @@ pub(crate) struct ServerHandle(Arc>); impl ServerHandle { /// Wait for the server to stop. - #[allow(unused)] + #[allow(dead_code)] pub(crate) async fn stopped(self) { self.0.closed().await } @@ -201,7 +201,7 @@ impl ConnectionGuard { } } - #[allow(unused)] + #[allow(dead_code)] pub(crate) fn available_connections(&self) -> usize { self.0.available_permits() } diff --git a/crates/rpc/ipc/src/server/ipc.rs b/crates/rpc/ipc/src/server/ipc.rs index 2de6534144a8..3bdeded986d9 100644 --- a/crates/rpc/ipc/src/server/ipc.rs +++ b/crates/rpc/ipc/src/server/ipc.rs @@ -238,7 +238,7 @@ fn execute_notification(notif: Notif<'_>, max_log_length: u32) -> MethodResponse response } -#[allow(unused)] +#[allow(dead_code)] pub(crate) struct HandleRequest { pub(crate) methods: Methods, pub(crate) max_request_body_size: u32, diff --git a/crates/rpc/ipc/src/server/mod.rs b/crates/rpc/ipc/src/server/mod.rs index 96ca731b921d..8d3c8f38b177 100644 --- a/crates/rpc/ipc/src/server/mod.rs +++ b/crates/rpc/ipc/src/server/mod.rs @@ -208,7 +208,7 @@ impl std::fmt::Debug for IpcServer { /// Data required by the server to handle requests received via an IPC connection #[derive(Debug, Clone)] -#[allow(unused)] +#[allow(dead_code)] pub(crate) struct ServiceData { /// Registered server methods. pub(crate) methods: Methods, diff --git a/crates/rpc/rpc-types/src/beacon/mod.rs b/crates/rpc/rpc-types/src/beacon/mod.rs index 7264a9036dbe..946ba38e421b 100644 --- a/crates/rpc/rpc-types/src/beacon/mod.rs +++ b/crates/rpc/rpc-types/src/beacon/mod.rs @@ -1,5 +1,6 @@ +//! Types for the Ethereum 2.0 RPC protocol (beacon chain). + #![allow(missing_docs)] -//! Types for the Ethereum 2.0 RPC protocol (beacon chain) use alloy_primitives::FixedBytes; use constants::{BLS_PUBLIC_KEY_BYTES_LEN, BLS_SIGNATURE_BYTES_LEN}; diff --git a/crates/rpc/rpc-types/src/beacon/payload.rs b/crates/rpc/rpc-types/src/beacon/payload.rs index 132c63409018..225d590ccf6f 100644 --- a/crates/rpc/rpc-types/src/beacon/payload.rs +++ b/crates/rpc/rpc-types/src/beacon/payload.rs @@ -1,4 +1,3 @@ -#![allow(missing_docs)] //! Payload support for the beacon API. //! //! Internal helper module to deserialize/serialize the payload attributes for the beacon API, which @@ -9,6 +8,8 @@ //! //! See also +#![allow(missing_docs)] + use crate::{ beacon::{withdrawals::BeaconWithdrawal, BlsPublicKey}, engine::ExecutionPayloadV3, diff --git a/crates/rpc/rpc-types/src/eth/account.rs b/crates/rpc/rpc-types/src/eth/account.rs index 7188dc98ab2a..755eff7b16cb 100644 --- a/crates/rpc/rpc-types/src/eth/account.rs +++ b/crates/rpc/rpc-types/src/eth/account.rs @@ -1,4 +1,5 @@ #![allow(missing_docs)] + use crate::serde_helpers::storage::JsonStorageKey; use alloy_primitives::{Address, Bytes, B256, B512, U256, U64}; use serde::{Deserialize, Serialize}; diff --git a/crates/rpc/rpc-types/src/eth/engine/mod.rs b/crates/rpc/rpc-types/src/eth/engine/mod.rs index 334adf0565b3..e8fcb660262c 100644 --- a/crates/rpc/rpc-types/src/eth/engine/mod.rs +++ b/crates/rpc/rpc-types/src/eth/engine/mod.rs @@ -1,5 +1,9 @@ +//! Engine API types: +//! +//! and , +//! following the execution specs . + #![allow(missing_docs)] -//! Engine API types: and following the execution specs mod cancun; mod forkchoice; diff --git a/crates/rpc/rpc-types/src/eth/trace/geth/mod.rs b/crates/rpc/rpc-types/src/eth/trace/geth/mod.rs index d0f92411e663..f6b4979da07a 100644 --- a/crates/rpc/rpc-types/src/eth/trace/geth/mod.rs +++ b/crates/rpc/rpc-types/src/eth/trace/geth/mod.rs @@ -1,5 +1,6 @@ +//! Geth tracing types. + #![allow(missing_docs)] -//! Geth tracing types use crate::{state::StateOverride, BlockOverrides}; use alloy_primitives::{Bytes, B256, U256}; diff --git a/crates/rpc/rpc-types/src/eth/transaction/typed.rs b/crates/rpc/rpc-types/src/eth/transaction/typed.rs index ccb779e0fbd2..6dd0314cbe62 100644 --- a/crates/rpc/rpc-types/src/eth/transaction/typed.rs +++ b/crates/rpc/rpc-types/src/eth/transaction/typed.rs @@ -1,8 +1,9 @@ -#![allow(missing_docs)] //! The [`TransactionRequest`][crate::TransactionRequest] is a universal representation for a //! transaction deserialized from the json input of an RPC call. Depending on what fields are set, //! it can be converted into the container type [`TypedTransactionRequest`]. +#![allow(missing_docs)] + use crate::{ eth::transaction::AccessList, kzg::{Blob, Bytes48}, diff --git a/crates/rpc/rpc-types/src/mev.rs b/crates/rpc/rpc-types/src/mev.rs index a37dc8afef8c..89671fe3e3ce 100644 --- a/crates/rpc/rpc-types/src/mev.rs +++ b/crates/rpc/rpc-types/src/mev.rs @@ -1,6 +1,7 @@ //! MEV bundle type bindings #![allow(missing_docs)] + use crate::{BlockId, BlockNumberOrTag, Log}; use alloy_primitives::{Address, Bytes, TxHash, B256, U256, U64}; use serde::{ diff --git a/crates/rpc/rpc-types/src/net.rs b/crates/rpc/rpc-types/src/net.rs index 291241ea3c33..cf8474dcb039 100644 --- a/crates/rpc/rpc-types/src/net.rs +++ b/crates/rpc/rpc-types/src/net.rs @@ -69,7 +69,7 @@ impl NodeRecord { } /// Creates a new record from a socket addr and peer id. - #[allow(unused)] + #[allow(dead_code)] pub fn new(addr: SocketAddr, id: PeerId) -> Self { Self { address: addr.ip(), tcp_port: addr.port(), udp_port: addr.port(), id } } diff --git a/crates/rpc/rpc-types/src/relay/mod.rs b/crates/rpc/rpc-types/src/relay/mod.rs index e4882891e8e6..c6dcfa20dac7 100644 --- a/crates/rpc/rpc-types/src/relay/mod.rs +++ b/crates/rpc/rpc-types/src/relay/mod.rs @@ -1,5 +1,6 @@ +//! Relay API bindings: + #![allow(missing_docs)] -//! Relay API bindings use crate::{ beacon::{BlsPublicKey, BlsSignature}, @@ -318,7 +319,8 @@ mod tests { fn capella_bid_submission_ssz() { use ssz::{Decode, Encode}; - let bytes = include_bytes!("../test_data/relay/signed_bid_submission_capella.ssz").to_vec(); + let bytes = + include_bytes!("../../test_data/relay/signed_bid_submission_capella.ssz").to_vec(); let bid = SignedBidSubmissionV2::from_ssz_bytes(&bytes).unwrap(); assert_eq!(bytes, bid.as_ssz_bytes()); } diff --git a/crates/rpc/rpc/src/eth/bundle.rs b/crates/rpc/rpc/src/eth/bundle.rs index c60f77d3f9b3..9f3a8311058a 100644 --- a/crates/rpc/rpc/src/eth/bundle.rs +++ b/crates/rpc/rpc/src/eth/bundle.rs @@ -193,7 +193,7 @@ struct EthBundleInner { /// Access to commonly used code of the `eth` namespace eth_api: Eth, // restrict the number of concurrent tracing calls. - #[allow(unused)] + #[allow(dead_code)] blocking_task_guard: BlockingTaskGuard, } diff --git a/crates/rpc/rpc/src/otterscan.rs b/crates/rpc/rpc/src/otterscan.rs index 91650985afdd..fa6f50bdafa4 100644 --- a/crates/rpc/rpc/src/otterscan.rs +++ b/crates/rpc/rpc/src/otterscan.rs @@ -1,4 +1,3 @@ -#![allow(dead_code, unused_variables)] use crate::result::internal_rpc_err; use async_trait::async_trait; use jsonrpsee::core::RpcResult; @@ -11,7 +10,7 @@ use reth_rpc_types::{ const API_LEVEL: u64 = 8; -/// Otterscan Api +/// Otterscan API. #[derive(Debug)] pub struct OtterscanApi { eth: Eth, @@ -40,17 +39,17 @@ where } /// Handler for `ots_getInternalOperations` - async fn get_internal_operations(&self, tx_hash: TxHash) -> RpcResult> { + async fn get_internal_operations(&self, _tx_hash: TxHash) -> RpcResult> { Err(internal_rpc_err("unimplemented")) } /// Handler for `ots_getTransactionError` - async fn get_transaction_error(&self, tx_hash: TxHash) -> RpcResult { + async fn get_transaction_error(&self, _tx_hash: TxHash) -> RpcResult { Err(internal_rpc_err("unimplemented")) } /// Handler for `ots_traceTransaction` - async fn trace_transaction(&self, tx_hash: TxHash) -> RpcResult { + async fn trace_transaction(&self, _tx_hash: TxHash) -> RpcResult { Err(internal_rpc_err("unimplemented")) } @@ -72,9 +71,9 @@ where /// Handler for `getBlockTransactions` async fn get_block_transactions( &self, - block_number: BlockNumberOrTag, - page_number: usize, - page_size: usize, + _block_number: BlockNumberOrTag, + _page_number: usize, + _page_size: usize, ) -> RpcResult { Err(internal_rpc_err("unimplemented")) } @@ -82,9 +81,9 @@ where /// Handler for `searchTransactionsBefore` async fn search_transactions_before( &self, - address: Address, - block_number: BlockNumberOrTag, - page_size: usize, + _address: Address, + _block_number: BlockNumberOrTag, + _page_size: usize, ) -> RpcResult { Err(internal_rpc_err("unimplemented")) } @@ -92,9 +91,9 @@ where /// Handler for `searchTransactionsAfter` async fn search_transactions_after( &self, - address: Address, - block_number: BlockNumberOrTag, - page_size: usize, + _address: Address, + _block_number: BlockNumberOrTag, + _page_size: usize, ) -> RpcResult { Err(internal_rpc_err("unimplemented")) } @@ -102,14 +101,14 @@ where /// Handler for `getTransactionBySenderAndNonce` async fn get_transaction_by_sender_and_nonce( &self, - sender: Address, - nonce: u64, + _sender: Address, + _nonce: u64, ) -> RpcResult> { Err(internal_rpc_err("unimplemented")) } /// Handler for `getContractCreator` - async fn get_contract_creator(&self, address: Address) -> RpcResult> { + async fn get_contract_creator(&self, _address: Address) -> RpcResult> { Err(internal_rpc_err("unimplemented")) } } diff --git a/crates/stages/src/error.rs b/crates/stages/src/error.rs index 4a4df0d26987..55dfbe3b5cf9 100644 --- a/crates/stages/src/error.rs +++ b/crates/stages/src/error.rs @@ -123,7 +123,7 @@ pub enum PipelineError { Provider(#[from] ProviderError), /// The pipeline encountered an error while trying to send an event. #[error("pipeline encountered an error while trying to send an event")] - Channel(#[from] SendError), + Channel(#[from] Box>), /// The stage encountered an internal error. #[error(transparent)] Internal(Box), diff --git a/crates/stages/src/lib.rs b/crates/stages/src/lib.rs index e59597ebabc1..f6ae6b8a5fc8 100644 --- a/crates/stages/src/lib.rs +++ b/crates/stages/src/lib.rs @@ -64,7 +64,6 @@ html_favicon_url = "https://avatars0.githubusercontent.com/u/97369466?s=256", issue_tracker_base_url = "https://github.com/paradigmxyz/reth/issues/" )] -#![allow(clippy::result_large_err)] // TODO(danipopes): fix this #![warn(missing_debug_implementations, missing_docs, unreachable_pub, rustdoc::all)] #![deny(unused_must_use, rust_2018_idioms)] #![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))] diff --git a/crates/stages/src/test_utils/mod.rs b/crates/stages/src/test_utils/mod.rs index b74b3e9455c1..f48aa46d5003 100644 --- a/crates/stages/src/test_utils/mod.rs +++ b/crates/stages/src/test_utils/mod.rs @@ -1,10 +1,13 @@ -#![allow(unused)] use reth_primitives::stage::StageId; +#[cfg(test)] mod macros; +#[cfg(test)] pub(crate) use macros::*; +#[cfg(test)] mod runner; +#[cfg(test)] pub(crate) use runner::{ ExecuteStageTestRunner, StageTestRunner, TestRunnerError, UnwindStageTestRunner, }; diff --git a/crates/stages/src/test_utils/runner.rs b/crates/stages/src/test_utils/runner.rs index 17289b9cf20b..70a4df029997 100644 --- a/crates/stages/src/test_utils/runner.rs +++ b/crates/stages/src/test_utils/runner.rs @@ -2,9 +2,8 @@ use super::TestStageDB; use crate::{ExecInput, ExecOutput, Stage, StageError, StageExt, UnwindInput, UnwindOutput}; use reth_db::{test_utils::TempDatabase, DatabaseEnv}; use reth_interfaces::db::DatabaseError; -use reth_primitives::MAINNET; -use reth_provider::{ProviderError, ProviderFactory}; -use std::{borrow::Borrow, sync::Arc}; +use reth_provider::ProviderError; +use std::sync::Arc; use tokio::sync::oneshot; #[derive(thiserror::Error, Debug)] diff --git a/crates/stages/src/test_utils/test_db.rs b/crates/stages/src/test_utils/test_db.rs index 4582bb86acf4..54af31db0cc5 100644 --- a/crates/stages/src/test_utils/test_db.rs +++ b/crates/stages/src/test_utils/test_db.rs @@ -3,25 +3,19 @@ use reth_db::{ cursor::{DbCursorRO, DbCursorRW, DbDupCursorRO}, database::Database, models::{AccountBeforeTx, StoredBlockBodyIndices}, - table::{Table, TableRow}, + table::Table, tables, test_utils::{create_test_rw_db, create_test_rw_db_with_path, TempDatabase}, transaction::{DbTx, DbTxMut}, DatabaseEnv, DatabaseError as DbError, }; -use reth_interfaces::{provider::ProviderResult, test_utils::generators::ChangeSet, RethResult}; +use reth_interfaces::{provider::ProviderResult, test_utils::generators::ChangeSet}; use reth_primitives::{ keccak256, Account, Address, BlockNumber, Receipt, SealedBlock, SealedHeader, StorageEntry, TxHash, TxNumber, B256, MAINNET, U256, }; -use reth_provider::{DatabaseProviderRO, DatabaseProviderRW, HistoryWriter, ProviderFactory}; -use std::{ - borrow::Borrow, - collections::BTreeMap, - ops::{Deref, RangeInclusive}, - path::{Path, PathBuf}, - sync::Arc, -}; +use reth_provider::{HistoryWriter, ProviderFactory}; +use std::{collections::BTreeMap, path::Path, sync::Arc}; /// Test database that is used for testing stage implementations. #[derive(Debug)] @@ -46,7 +40,7 @@ impl TestStageDB { where F: FnOnce(&::TXMut) -> ProviderResult<()>, { - let mut tx = self.factory.provider_rw()?; + let tx = self.factory.provider_rw()?; f(tx.tx_ref())?; tx.commit().expect("failed to commit"); Ok(()) @@ -245,18 +239,20 @@ impl TestStageDB { let hashed_entry = StorageEntry { key: keccak256(entry.key), ..entry }; let mut cursor = tx.cursor_dup_write::()?; - if let Some(e) = cursor + if cursor .seek_by_key_subkey(address, entry.key)? .filter(|e| e.key == entry.key) + .is_some() { cursor.delete_current()?; } cursor.upsert(address, entry)?; let mut cursor = tx.cursor_dup_write::()?; - if let Some(e) = cursor + if cursor .seek_by_key_subkey(hashed_address, hashed_entry.key)? .filter(|e| e.key == hashed_entry.key) + .is_some() { cursor.delete_current()?; } @@ -299,7 +295,7 @@ impl TestStageDB { }) } - pub fn insert_history(&self, changesets: I, block_offset: Option) -> ProviderResult<()> + pub fn insert_history(&self, changesets: I, _block_offset: Option) -> ProviderResult<()> where I: IntoIterator, { diff --git a/crates/storage/codecs/derive/src/lib.rs b/crates/storage/codecs/derive/src/lib.rs index f0133b95b1f4..0b412788b094 100644 --- a/crates/storage/codecs/derive/src/lib.rs +++ b/crates/storage/codecs/derive/src/lib.rs @@ -5,8 +5,6 @@ html_favicon_url = "https://avatars0.githubusercontent.com/u/97369466?s=256", issue_tracker_base_url = "https://github.com/paradigmxyz/reth/issues/" )] -// TODO(danipopes): add these warnings -// #![warn(missing_debug_implementations, missing_docs, unreachable_pub, rustdoc::all)] #![deny(unused_must_use, rust_2018_idioms)] #![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))] diff --git a/crates/storage/codecs/src/lib.rs b/crates/storage/codecs/src/lib.rs index d3cdbaa01442..993c323cb1fe 100644 --- a/crates/storage/codecs/src/lib.rs +++ b/crates/storage/codecs/src/lib.rs @@ -14,7 +14,6 @@ )] #![deny(unused_must_use, rust_2018_idioms)] #![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))] -#![allow(clippy::non_canonical_clone_impl)] #![cfg_attr(not(feature = "std"), no_std)] extern crate alloc; diff --git a/crates/storage/db/benches/criterion.rs b/crates/storage/db/benches/criterion.rs index 0fb51a583762..223c3d1d19ab 100644 --- a/crates/storage/db/benches/criterion.rs +++ b/crates/storage/db/benches/criterion.rs @@ -1,14 +1,13 @@ -#![allow(dead_code, unused_imports, non_snake_case)] - use criterion::{ black_box, criterion_group, criterion_main, measurement::WallTime, BenchmarkGroup, Criterion, }; use pprof::criterion::{Output, PProfProfiler}; use reth_db::{ cursor::{DbCursorRO, DbCursorRW, DbDupCursorRO, DbDupCursorRW}, + table::{Decode, Decompress, DupSort}, tables::*, + transaction::DbTx, }; -use std::time::Instant; criterion_group! { name = benches; diff --git a/crates/storage/db/benches/hash_keys.rs b/crates/storage/db/benches/hash_keys.rs index 58d005efe4d2..e2534d0b2cc4 100644 --- a/crates/storage/db/benches/hash_keys.rs +++ b/crates/storage/db/benches/hash_keys.rs @@ -1,5 +1,3 @@ -#![allow(dead_code, unused_imports, non_snake_case)] - use criterion::{ black_box, criterion_group, criterion_main, measurement::WallTime, BenchmarkGroup, Criterion, }; @@ -10,12 +8,8 @@ use proptest::{ strategy::{Strategy, ValueTree}, test_runner::TestRunner, }; -use reth_db::{ - cursor::{DbCursorRW, DbDupCursorRO, DbDupCursorRW}, - TxHashNumber, -}; -use std::{collections::HashSet, time::Instant}; -use test_fuzz::runtime::num_traits::Zero; +use reth_db::{cursor::DbCursorRW, TxHashNumber}; +use std::collections::HashSet; criterion_group! { name = benches; @@ -216,6 +210,7 @@ where } #[derive(Debug)] +#[allow(dead_code)] struct TableStats { page_size: usize, leaf_pages: usize, diff --git a/crates/storage/db/benches/iai.rs b/crates/storage/db/benches/iai.rs index a5023b166c60..e4c6b7533941 100644 --- a/crates/storage/db/benches/iai.rs +++ b/crates/storage/db/benches/iai.rs @@ -1,46 +1,60 @@ -#![allow(dead_code, unused_imports, non_snake_case)] +#![allow(non_snake_case)] -use iai::main; use paste::paste; +use reth_db::table::{Compress, Decode, Decompress, Encode, Table}; + +mod utils; +use utils::*; macro_rules! impl_iai_inner { ( - $(($name:tt, $mod:tt, $compress:tt, $decompress:tt, $encode:tt, $decode:tt, $seqread:tt, $randread:tt, $seqwrite:tt, $randwrite:tt))+ + $(($name:ident, $mod:ident, $compress:ident, $decompress:ident, $encode:ident, $decode:ident, $seqread:ident, $randread:ident, $seqwrite:ident, $randwrite:ident))+ ) => { - $( - mod $mod { - use iai::{black_box}; - include!("./utils.rs"); + $( + mod $mod { + use super::*; + use std::hint::black_box; - pub fn $compress() { - for (_, _, v, _) in black_box(load_vectors::()) { - black_box(v.compress()); - } + pub fn $compress() { + for (_, _, v, _) in black_box(load_vectors::()) { + black_box(v.compress()); } - pub fn $decompress() { - for (_, _, _, comp) in black_box(load_vectors::()) { - let _ = black_box(::Value::decompress(comp)); - } + } + + pub fn $decompress() { + for (_, _, _, comp) in black_box(load_vectors::()) { + let _ = black_box(::Value::decompress(comp)); } - pub fn $encode() { - for (k, _, _, _) in black_box(load_vectors::()) { - black_box(k.encode()); - } + } + + pub fn $encode() { + for (k, _, _, _) in black_box(load_vectors::()) { + black_box(k.encode()); } - pub fn $decode() { - for (_, enc, _, _) in black_box(load_vectors::()) { - let _ = black_box(::Key::decode(enc)); - } + } + + pub fn $decode() { + for (_, enc, _, _) in black_box(load_vectors::()) { + let _ = black_box(::Key::decode(enc)); } - pub fn $seqread() {} - pub fn $randread() {} - pub fn $seqwrite() {} - pub fn $randwrite() {} } - use $mod::*; - )+ - main!( + #[allow(dead_code)] + pub fn $seqread() {} + + #[allow(dead_code)] + pub fn $randread() {} + + #[allow(dead_code)] + pub fn $seqwrite() {} + + #[allow(dead_code)] + pub fn $randwrite() {} + } + use $mod::*; + )+ + + iai::main!( $( $compress, $decompress, @@ -52,7 +66,7 @@ macro_rules! impl_iai_inner { } macro_rules! impl_iai { - ($($name:tt),+) => { + ($($name:ident),+) => { paste! { impl_iai_inner!( $( diff --git a/crates/storage/db/benches/utils.rs b/crates/storage/db/benches/utils.rs index 67ce2307ff03..ad290d16b175 100644 --- a/crates/storage/db/benches/utils.rs +++ b/crates/storage/db/benches/utils.rs @@ -1,9 +1,8 @@ -#[allow(unused_imports)] use reth_db::{ database::Database, - table::*, + table::{Compress, Encode, Table, TableRow}, test_utils::create_test_rw_db_with_path, - transaction::{DbTx, DbTxMut}, + transaction::DbTxMut, DatabaseEnv, }; use reth_primitives::{fs, Bytes}; @@ -19,7 +18,7 @@ const RANDOM_INDEXES: [usize; 10] = [23, 2, 42, 5, 3, 99, 54, 0, 33, 64]; /// Returns bench vectors in the format: `Vec<(Key, EncodedKey, Value, CompressedValue)>`. #[allow(dead_code)] -fn load_vectors() -> Vec<(T::Key, Bytes, T::Value, Bytes)> +pub(crate) fn load_vectors() -> Vec<(T::Key, Bytes, T::Value, Bytes)> where T: Default, T::Key: Default + Clone + for<'de> serde::Deserialize<'de>, @@ -50,7 +49,7 @@ where /// Sets up a clear database at `bench_db_path`. #[allow(clippy::ptr_arg)] #[allow(dead_code)] -fn set_up_db( +pub(crate) fn set_up_db( bench_db_path: &Path, pair: &Vec<(::Key, Bytes, ::Value, Bytes)>, ) -> DatabaseEnv diff --git a/crates/storage/db/src/abstraction/mock.rs b/crates/storage/db/src/abstraction/mock.rs index c2801e345640..cb20105addd2 100644 --- a/crates/storage/db/src/abstraction/mock.rs +++ b/crates/storage/db/src/abstraction/mock.rs @@ -15,7 +15,7 @@ use core::ops::Bound; use std::{collections::BTreeMap, ops::RangeBounds}; /// Mock database used for testing with inner BTreeMap structure -/// TODO +// TODO #[derive(Clone, Debug, Default)] pub struct DatabaseMock { /// Main data. TODO (Make it table aware) diff --git a/crates/storage/db/src/tables/codecs/fuzz/mod.rs b/crates/storage/db/src/tables/codecs/fuzz/mod.rs index 3fabd992ba1f..d47800b0a397 100644 --- a/crates/storage/db/src/tables/codecs/fuzz/mod.rs +++ b/crates/storage/db/src/tables/codecs/fuzz/mod.rs @@ -66,11 +66,11 @@ macro_rules! impl_fuzzer_key { /// Fuzzer generates a random instance of the object and proceeds to compress and decompress it. It /// then makes sure that it matches the original object. -#[allow(unused)] +#[allow(unused_macros)] macro_rules! impl_fuzzer_value { ($($name:tt),+) => { $( - impl_fuzzer_with_input!(($name, $name, Compress, compress, Decompress, decompress)); + impl_fuzzer_value_with_input!($name, $name); )+ }; } diff --git a/crates/storage/db/src/tables/codecs/mod.rs b/crates/storage/db/src/tables/codecs/mod.rs index 39716669d250..1cb009df588c 100644 --- a/crates/storage/db/src/tables/codecs/mod.rs +++ b/crates/storage/db/src/tables/codecs/mod.rs @@ -1,9 +1,8 @@ -//! Integrates different codecs into table::Encode and table::Decode +//! Integrates different codecs into `table::Encode` and `table::Decode`. mod compact; pub use compact::CompactU256; pub mod fuzz; -mod postcard; mod scale; diff --git a/crates/storage/db/src/tables/codecs/postcard.rs b/crates/storage/db/src/tables/codecs/postcard.rs deleted file mode 100644 index fe7d5b01ef24..000000000000 --- a/crates/storage/db/src/tables/codecs/postcard.rs +++ /dev/null @@ -1,43 +0,0 @@ -#![allow(unused)] - -use crate::{ - table::{Decode, Encode}, - DatabaseError, -}; -use postcard::{from_bytes, to_allocvec, to_vec}; -use reth_primitives::*; - -// Just add `Serialize` and `Deserialize`, and set impl_heapless_postcard!(T, MaxSize(T)) -// -// -// use serde::{Deserialize, Serialize}; -// -// #[derive(Serialize, Deserialize )] -// pub struct T { -// } -// -// impl_heapless_postcard!(T, MaxSize(T)) -macro_rules! impl_postcard { - ($($name:tt),+) => { - $( - impl Encode for $name { - type Encoded = Vec; - - fn encode(self) -> Self::Encoded { - to_allocvec(&self).expect("Failed to encode") - } - } - - impl Decode for $name { - fn decode>(value: B) -> Result { - from_bytes(&value.into()).map_err(|e| Error::Decode(e.into())) - } - } - )+ - }; -} - -type VecU8 = Vec; - -//#[cfg(feature = "bench-postcard")] -//impl_postcard!(VecU8, Receipt, B256, U256, Address, u8, u16, u64, Header, Account, Log, TxType); diff --git a/crates/storage/libmdbx-rs/src/environment.rs b/crates/storage/libmdbx-rs/src/environment.rs index 911a6a3c2e46..4c63e3a4aad2 100644 --- a/crates/storage/libmdbx-rs/src/environment.rs +++ b/crates/storage/libmdbx-rs/src/environment.rs @@ -291,12 +291,12 @@ impl EnvironmentKind { } #[derive(Copy, Clone, Debug)] -pub(crate) struct TxnPtr(pub *mut ffi::MDBX_txn); +pub(crate) struct TxnPtr(pub(crate) *mut ffi::MDBX_txn); unsafe impl Send for TxnPtr {} unsafe impl Sync for TxnPtr {} #[derive(Copy, Clone, Debug)] -pub(crate) struct EnvPtr(pub *mut ffi::MDBX_env); +pub(crate) struct EnvPtr(pub(crate) *mut ffi::MDBX_env); unsafe impl Send for EnvPtr {} unsafe impl Sync for EnvPtr {} @@ -309,6 +309,7 @@ pub(crate) enum TxnManagerMessage { /// Environment statistics. /// /// Contains information about the size and layout of an MDBX environment or database. +#[derive(Debug)] #[repr(transparent)] pub struct Stat(ffi::MDBX_stat); @@ -362,6 +363,7 @@ impl Stat { } } +#[derive(Debug)] #[repr(transparent)] pub struct GeometryInfo(ffi::MDBX_envinfo__bindgen_ty_1); @@ -374,6 +376,7 @@ impl GeometryInfo { /// Environment information. /// /// Contains environment information about the map size, readers, last txn id etc. +#[derive(Debug)] #[repr(transparent)] pub struct Info(ffi::MDBX_envinfo); diff --git a/crates/storage/libmdbx-rs/src/error.rs b/crates/storage/libmdbx-rs/src/error.rs index 3b33caa865d0..98c91222d8f9 100644 --- a/crates/storage/libmdbx-rs/src/error.rs +++ b/crates/storage/libmdbx-rs/src/error.rs @@ -174,7 +174,7 @@ impl fmt::Display for Error { } #[inline] -pub fn mdbx_result(err_code: c_int) -> Result { +pub(crate) fn mdbx_result(err_code: c_int) -> Result { match err_code { ffi::MDBX_SUCCESS => Ok(false), ffi::MDBX_RESULT_TRUE => Ok(true), diff --git a/crates/storage/libmdbx-rs/src/lib.rs b/crates/storage/libmdbx-rs/src/lib.rs index 89232e8aeb80..ee9e204f9510 100644 --- a/crates/storage/libmdbx-rs/src/lib.rs +++ b/crates/storage/libmdbx-rs/src/lib.rs @@ -4,9 +4,7 @@ html_favicon_url = "https://avatars0.githubusercontent.com/u/97369466?s=256", issue_tracker_base_url = "https://github.com/paradigmxyz/reth/issues/" )] -#![allow(clippy::type_complexity)] -// TODO(danipopes): add these warnings -// #![warn(missing_debug_implementations, missing_docs, unreachable_pub, rustdoc::all)] +#![warn(missing_debug_implementations, unreachable_pub, rustdoc::all)] // TODO(danipopes): missing_docs #![deny(unused_must_use, rust_2018_idioms)] #![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))] diff --git a/crates/storage/libmdbx-rs/src/transaction.rs b/crates/storage/libmdbx-rs/src/transaction.rs index 5428eae3a46e..a79b1f213761 100644 --- a/crates/storage/libmdbx-rs/src/transaction.rs +++ b/crates/storage/libmdbx-rs/src/transaction.rs @@ -545,6 +545,7 @@ impl TransactionPtr { /// /// Contains information about latency of commit stages. /// Inner struct stores this info in 1/65536 of seconds units. +#[derive(Debug)] #[repr(transparent)] pub struct CommitLatency(ffi::MDBX_commit_latency); diff --git a/crates/storage/nippy-jar/src/compression/zstd.rs b/crates/storage/nippy-jar/src/compression/zstd.rs index a70d566ca435..83cb7f71b44e 100644 --- a/crates/storage/nippy-jar/src/compression/zstd.rs +++ b/crates/storage/nippy-jar/src/compression/zstd.rs @@ -237,7 +237,7 @@ impl Compression for Zstd { mod dictionaries_serde { use super::*; - pub fn serialize( + pub(crate) fn serialize( dictionaries: &Option>>, serializer: S, ) -> Result @@ -250,7 +250,7 @@ mod dictionaries_serde { } } - pub fn deserialize<'de, D>( + pub(crate) fn deserialize<'de, D>( deserializer: D, ) -> Result>>, D::Error> where @@ -264,7 +264,7 @@ mod dictionaries_serde { /// List of [`ZstdDictionary`] #[cfg_attr(test, derive(PartialEq))] #[derive(Serialize, Deserialize, Deref)] -pub struct ZstdDictionaries<'a>(Vec>); +pub(crate) struct ZstdDictionaries<'a>(Vec>); impl<'a> std::fmt::Debug for ZstdDictionaries<'a> { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { @@ -274,12 +274,12 @@ impl<'a> std::fmt::Debug for ZstdDictionaries<'a> { impl<'a> ZstdDictionaries<'a> { /// Creates [`ZstdDictionaries`]. - pub fn new(raw: Vec) -> Self { + pub(crate) fn new(raw: Vec) -> Self { Self(raw.into_iter().map(ZstdDictionary::Raw).collect()) } /// Loads a list [`RawDictionary`] into a list of [`ZstdDictionary::Loaded`]. - pub fn load(raw: Vec) -> Self { + pub(crate) fn load(raw: Vec) -> Self { Self( raw.into_iter() .map(|dict| ZstdDictionary::Loaded(DecoderDictionary::copy(&dict))) @@ -288,7 +288,7 @@ impl<'a> ZstdDictionaries<'a> { } /// Creates a list of decompressors from a list of [`ZstdDictionary::Loaded`]. - pub fn decompressors(&self) -> Result>, NippyJarError> { + pub(crate) fn decompressors(&self) -> Result>, NippyJarError> { Ok(self .iter() .flat_map(|dict| { @@ -300,7 +300,7 @@ impl<'a> ZstdDictionaries<'a> { } /// Creates a list of compressors from a list of [`ZstdDictionary::Raw`]. - pub fn compressors(&self) -> Result>, NippyJarError> { + pub(crate) fn compressors(&self) -> Result>, NippyJarError> { Ok(self .iter() .flat_map(|dict| { @@ -314,14 +314,14 @@ impl<'a> ZstdDictionaries<'a> { /// A Zstd dictionary. It's created and serialized with [`ZstdDictionary::Raw`], and deserialized as /// [`ZstdDictionary::Loaded`]. -pub enum ZstdDictionary<'a> { +pub(crate) enum ZstdDictionary<'a> { Raw(RawDictionary), Loaded(DecoderDictionary<'a>), } impl<'a> ZstdDictionary<'a> { /// Returns a reference to the expected `RawDictionary` - pub fn raw(&self) -> Option<&RawDictionary> { + pub(crate) fn raw(&self) -> Option<&RawDictionary> { match self { ZstdDictionary::Raw(dict) => Some(dict), ZstdDictionary::Loaded(_) => None, @@ -329,7 +329,7 @@ impl<'a> ZstdDictionary<'a> { } /// Returns a reference to the expected `DecoderDictionary` - pub fn loaded(&self) -> Option<&DecoderDictionary<'_>> { + pub(crate) fn loaded(&self) -> Option<&DecoderDictionary<'_>> { match self { ZstdDictionary::Raw(_) => None, ZstdDictionary::Loaded(dict) => Some(dict), diff --git a/crates/storage/nippy-jar/src/lib.rs b/crates/storage/nippy-jar/src/lib.rs index daeb1a4a6eb5..471e771e25d7 100644 --- a/crates/storage/nippy-jar/src/lib.rs +++ b/crates/storage/nippy-jar/src/lib.rs @@ -5,8 +5,7 @@ html_favicon_url = "https://avatars0.githubusercontent.com/u/97369466?s=256", issue_tracker_base_url = "https://github.com/paradigmxyz/reth/issues/" )] -// TODO(danipopes): add these warnings -// #![warn(missing_debug_implementations, missing_docs, unreachable_pub, rustdoc::all)] +#![warn(missing_debug_implementations, unreachable_pub, rustdoc::all)] // TODO(danipopes): missing_docs #![deny(unused_must_use, rust_2018_idioms)] #![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))] @@ -438,12 +437,12 @@ impl PerfectHashingFunction for NippyJar { #[derive(Debug)] pub struct DataReader { /// Data file descriptor. Needs to be kept alive as long as `data_mmap` handle. - #[allow(unused)] + #[allow(dead_code)] data_file: File, /// Mmap handle for data. data_mmap: Mmap, /// Offset file descriptor. Needs to be kept alive as long as `offset_mmap` handle. - #[allow(unused)] + #[allow(dead_code)] offset_file: File, /// Mmap handle for offsets. offset_mmap: Mmap, @@ -805,7 +804,7 @@ mod tests { let block_start = 500; #[derive(Serialize, Deserialize, Debug)] - pub struct BlockJarHeader { + struct BlockJarHeader { block_start: usize, } diff --git a/crates/storage/nippy-jar/src/writer.rs b/crates/storage/nippy-jar/src/writer.rs index 38427674f083..b8de6454c27d 100644 --- a/crates/storage/nippy-jar/src/writer.rs +++ b/crates/storage/nippy-jar/src/writer.rs @@ -1,6 +1,7 @@ use crate::{compression::Compression, ColumnResult, NippyJar, NippyJarError, NippyJarHeader}; use std::{ cmp::Ordering, + fmt, fs::{File, OpenOptions}, io::{Read, Seek, SeekFrom, Write}, path::Path, @@ -40,6 +41,12 @@ pub struct NippyJarWriter<'a, H> { column: usize, } +impl fmt::Debug for NippyJarWriter<'_, H> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("NippyJarWriter").finish_non_exhaustive() + } +} + impl<'a, H: NippyJarHeader> NippyJarWriter<'a, H> { pub fn new(jar: &'a mut NippyJar) -> Result { let (data_file, offsets_file, is_created) = diff --git a/crates/storage/provider/src/providers/chain_info.rs b/crates/storage/provider/src/providers/chain_info.rs index 2532ad866deb..7bde9ac24587 100644 --- a/crates/storage/provider/src/providers/chain_info.rs +++ b/crates/storage/provider/src/providers/chain_info.rs @@ -71,7 +71,7 @@ impl ChainInfoTracker { } /// Returns the canonical head of the chain. - #[allow(unused)] + #[allow(dead_code)] pub(crate) fn get_canonical_num_hash(&self) -> BlockNumHash { self.inner.canonical_head.read().num_hash() } @@ -82,14 +82,14 @@ impl ChainInfoTracker { } /// Returns the safe header of the chain. - #[allow(unused)] + #[allow(dead_code)] pub(crate) fn get_safe_num_hash(&self) -> Option { let h = self.inner.safe_block.read(); h.as_ref().map(|h| h.num_hash()) } /// Returns the finalized header of the chain. - #[allow(unused)] + #[allow(dead_code)] pub(crate) fn get_finalized_num_hash(&self) -> Option { let h = self.inner.finalized_block.read(); h.as_ref().map(|h| h.num_hash()) diff --git a/crates/storage/provider/src/providers/database/provider.rs b/crates/storage/provider/src/providers/database/provider.rs index 48563cf3c67c..bce17cccc7c2 100644 --- a/crates/storage/provider/src/providers/database/provider.rs +++ b/crates/storage/provider/src/providers/database/provider.rs @@ -103,7 +103,7 @@ pub struct DatabaseProvider { /// Chain spec chain_spec: Arc, /// Snapshot provider - #[allow(unused)] + #[allow(dead_code)] snapshot_provider: Option>, } diff --git a/crates/storage/provider/src/providers/state/historical.rs b/crates/storage/provider/src/providers/state/historical.rs index 7d7fabe2018e..85460498d393 100644 --- a/crates/storage/provider/src/providers/state/historical.rs +++ b/crates/storage/provider/src/providers/state/historical.rs @@ -353,7 +353,7 @@ mod tests { const STORAGE: B256 = b256!("0000000000000000000000000000000000000000000000000000000000000001"); fn assert_state_provider() {} - #[allow(unused)] + #[allow(dead_code)] fn assert_historical_state_provider() { assert_state_provider::>(); } diff --git a/crates/storage/provider/src/providers/state/latest.rs b/crates/storage/provider/src/providers/state/latest.rs index a12eef46305b..a1f1ae1349cc 100644 --- a/crates/storage/provider/src/providers/state/latest.rs +++ b/crates/storage/provider/src/providers/state/latest.rs @@ -129,7 +129,7 @@ mod tests { use super::*; fn assert_state_provider() {} - #[allow(unused)] + #[allow(dead_code)] fn assert_latest_state_provider() { assert_state_provider::>(); } diff --git a/crates/transaction-pool/src/blobstore/mod.rs b/crates/transaction-pool/src/blobstore/mod.rs index 6dee69d4b33f..6cbe755ef3aa 100644 --- a/crates/transaction-pool/src/blobstore/mod.rs +++ b/crates/transaction-pool/src/blobstore/mod.rs @@ -118,7 +118,7 @@ impl BlobStoreSize { mod tests { use super::*; - #[allow(unused)] + #[allow(dead_code)] struct DynStore { store: Box, } diff --git a/crates/transaction-pool/src/identifier.rs b/crates/transaction-pool/src/identifier.rs index c2c73c7b49a8..feefd57aeaa8 100644 --- a/crates/transaction-pool/src/identifier.rs +++ b/crates/transaction-pool/src/identifier.rs @@ -18,7 +18,7 @@ pub(crate) struct SenderIdentifiers { impl SenderIdentifiers { /// Returns the address for the given identifier. - #[allow(unused)] + #[allow(dead_code)] pub(crate) fn address(&self, id: &SenderId) -> Option<&Address> { self.sender_to_address.get(id) } diff --git a/crates/transaction-pool/src/pool/blob.rs b/crates/transaction-pool/src/pool/blob.rs index 040462639110..aea5d3e7d28f 100644 --- a/crates/transaction-pool/src/pool/blob.rs +++ b/crates/transaction-pool/src/pool/blob.rs @@ -1,4 +1,3 @@ -#![allow(dead_code, unused)] use crate::{ identifier::TransactionId, pool::size::SizeTracker, traits::BestTransactionsAttributes, PoolTransaction, SubPoolLimit, ValidPoolTransaction, @@ -85,7 +84,7 @@ impl BlobTransactions { /// Returns all transactions that satisfy the given basefee and blob_fee. pub(crate) fn satisfy_attributes( &self, - best_transactions_attributes: BestTransactionsAttributes, + _best_transactions_attributes: BestTransactionsAttributes, ) -> Vec>> { Vec::new() } @@ -102,7 +101,7 @@ impl BlobTransactions { /// Returns whether the pool is empty #[cfg(test)] - #[allow(unused)] + #[allow(dead_code)] pub(crate) fn is_empty(&self) -> bool { self.by_id.is_empty() } diff --git a/crates/transaction-pool/src/pool/parked.rs b/crates/transaction-pool/src/pool/parked.rs index e64770927bc4..d34aa96a1783 100644 --- a/crates/transaction-pool/src/pool/parked.rs +++ b/crates/transaction-pool/src/pool/parked.rs @@ -207,7 +207,7 @@ impl ParkedPool { /// Returns whether the pool is empty #[cfg(test)] - #[allow(unused)] + #[allow(dead_code)] pub(crate) fn is_empty(&self) -> bool { self.by_id.is_empty() } diff --git a/crates/transaction-pool/src/pool/txpool.rs b/crates/transaction-pool/src/pool/txpool.rs index 90174dbf6138..7d3594965698 100644 --- a/crates/transaction-pool/src/pool/txpool.rs +++ b/crates/transaction-pool/src/pool/txpool.rs @@ -833,7 +833,7 @@ impl Drop for TxPool { // Additional test impls #[cfg(any(test, feature = "test-utils"))] -#[allow(missing_docs)] +#[allow(dead_code)] impl TxPool { pub(crate) fn pending(&self) -> &PendingPool { &self.pending_pool @@ -897,7 +897,7 @@ impl AllTransactions { } /// Returns an iterator over all _unique_ hashes in the pool - #[allow(unused)] + #[allow(dead_code)] pub(crate) fn hashes_iter(&self) -> impl Iterator + '_ { self.by_hash.keys().copied() } @@ -1142,7 +1142,7 @@ impl AllTransactions { /// Returns a mutable iterator over all transactions for the given sender, starting with the /// lowest nonce #[cfg(test)] - #[allow(unused)] + #[allow(dead_code)] pub(crate) fn txs_iter_mut( &mut self, sender: SenderId, @@ -1656,7 +1656,7 @@ pub(crate) type InsertResult = Result, InsertErr>; pub(crate) enum InsertErr { /// Attempted to replace existing transaction, but was underpriced Underpriced { - #[allow(unused)] + #[allow(dead_code)] transaction: Arc>, existing: TxHash, }, @@ -1691,7 +1691,7 @@ pub(crate) struct InsertOk { /// Where to move the transaction to. move_to: SubPool, /// Current state of the inserted tx. - #[allow(unused)] + #[allow(dead_code)] state: TxState, /// The transaction that was replaced by this. replaced_tx: Option<(Arc>, SubPool)>, diff --git a/crates/transaction-pool/src/test_utils/gen.rs b/crates/transaction-pool/src/test_utils/gen.rs index 1f2a44bcdaf5..337d604a4e16 100644 --- a/crates/transaction-pool/src/test_utils/gen.rs +++ b/crates/transaction-pool/src/test_utils/gen.rs @@ -1,9 +1,6 @@ #![allow(missing_docs)] -use crate::{ - test_utils::{MockTransactionFactory, MockValidTx}, - EthPooledTransaction, -}; +use crate::EthPooledTransaction; use rand::Rng; use reth_primitives::{ constants::MIN_PROTOCOL_BASE_FEE, sign_message, AccessList, Address, Bytes, @@ -26,7 +23,7 @@ impl TransactionGenerator { } /// Generates random random signers - pub fn with_num_signers(mut rng: R, num_signers: usize) -> Self { + pub fn with_num_signers(rng: R, num_signers: usize) -> Self { let mut signer_keys = Vec::with_capacity(num_signers); for _ in 0..num_signers { signer_keys.push(B256::random()); @@ -118,10 +115,10 @@ impl TransactionBuilder { nonce, gas_limit, max_fee_per_gas, - max_priority_fee_per_gas, + max_priority_fee_per_gas: _, to, value, - access_list, + access_list: _, input, } = self; let tx: Transaction = TxLegacy { diff --git a/crates/transaction-pool/src/test_utils/mock.rs b/crates/transaction-pool/src/test_utils/mock.rs index 5f0f80a06bc5..0137676e1f9d 100644 --- a/crates/transaction-pool/src/test_utils/mock.rs +++ b/crates/transaction-pool/src/test_utils/mock.rs @@ -1,4 +1,6 @@ -//! Mock Types +//! Mock types. + +#![allow(dead_code, unused_macros)] use crate::{ identifier::{SenderIdentifiers, TransactionId}, @@ -13,11 +15,11 @@ use rand::{ }; use reth_primitives::{ constants::{eip4844::DATA_GAS_PER_BLOB, MIN_PROTOCOL_BASE_FEE}, - hex, AccessList, Address, BlobTransactionSidecar, Bytes, FromRecoveredPooledTransaction, + AccessList, Address, BlobTransactionSidecar, Bytes, FromRecoveredPooledTransaction, FromRecoveredTransaction, IntoRecoveredTransaction, PooledTransactionsElementEcRecovered, Signature, Transaction, TransactionKind, TransactionSigned, TransactionSignedEcRecovered, - TxEip1559, TxEip2930, TxEip4844, TxHash, TxLegacy, TxType, TxValue, B256, EIP1559_TX_TYPE_ID, - EIP2930_TX_TYPE_ID, EIP4844_TX_TYPE_ID, LEGACY_TX_TYPE_ID, U128, U256, + TxEip1559, TxEip2930, TxEip4844, TxHash, TxLegacy, TxType, B256, EIP1559_TX_TYPE_ID, + EIP2930_TX_TYPE_ID, EIP4844_TX_TYPE_ID, LEGACY_TX_TYPE_ID, U256, }; use std::{ops::Range, sync::Arc, time::Instant}; @@ -88,6 +90,7 @@ macro_rules! set_value { *$field = new_value; } #[cfg(feature = "optimism")] + #[allow(unused_variables)] MockTransaction::Deposit(ref mut tx) => { op_set_value!(tx, $this, new_value); } @@ -104,6 +107,7 @@ macro_rules! get_value { MockTransaction::Eip4844 { $field, .. } => $field.clone(), MockTransaction::Eip2930 { $field, .. } => $field.clone(), #[cfg(feature = "optimism")] + #[allow(unused_variables)] MockTransaction::Deposit(tx) => { op_get_value!(tx, $field) } @@ -311,8 +315,8 @@ impl MockTransaction { /// Sets the priority fee for dynamic fee transactions (EIP-1559 and EIP-4844) pub fn set_priority_fee(&mut self, val: u128) -> &mut Self { - if let (MockTransaction::Eip1559 { max_priority_fee_per_gas, .. } | - MockTransaction::Eip4844 { max_priority_fee_per_gas, .. }) = self + if let MockTransaction::Eip1559 { max_priority_fee_per_gas, .. } | + MockTransaction::Eip4844 { max_priority_fee_per_gas, .. } = self { *max_priority_fee_per_gas = val; } @@ -325,8 +329,8 @@ impl MockTransaction { } pub fn get_priority_fee(&self) -> Option { - if let (MockTransaction::Eip1559 { max_priority_fee_per_gas, .. } | - MockTransaction::Eip4844 { max_priority_fee_per_gas, .. }) = self + if let MockTransaction::Eip1559 { max_priority_fee_per_gas, .. } | + MockTransaction::Eip4844 { max_priority_fee_per_gas, .. } = self { Some(*max_priority_fee_per_gas) } else { @@ -335,8 +339,8 @@ impl MockTransaction { } pub fn set_max_fee(&mut self, val: u128) -> &mut Self { - if let (MockTransaction::Eip1559 { max_fee_per_gas, .. } | - MockTransaction::Eip4844 { max_fee_per_gas, .. }) = self + if let MockTransaction::Eip1559 { max_fee_per_gas, .. } | + MockTransaction::Eip4844 { max_fee_per_gas, .. } = self { *max_fee_per_gas = val; } @@ -349,8 +353,8 @@ impl MockTransaction { } pub fn get_max_fee(&self) -> Option { - if let (MockTransaction::Eip1559 { max_fee_per_gas, .. } | - MockTransaction::Eip4844 { max_fee_per_gas, .. }) = self + if let MockTransaction::Eip1559 { max_fee_per_gas, .. } | + MockTransaction::Eip4844 { max_fee_per_gas, .. } = self { Some(*max_fee_per_gas) } else { @@ -439,30 +443,30 @@ impl MockTransaction { /// Returns a clone with a decreased nonce pub fn prev(&self) -> Self { - let mut next = self.clone().with_hash(B256::random()); + let next = self.clone().with_hash(B256::random()); next.with_nonce(self.get_nonce() - 1) } /// Returns a clone with an increased nonce pub fn next(&self) -> Self { - let mut next = self.clone().with_hash(B256::random()); + let next = self.clone().with_hash(B256::random()); next.with_nonce(self.get_nonce() + 1) } /// Returns a clone with an increased nonce pub fn skip(&self, skip: u64) -> Self { - let mut next = self.clone().with_hash(B256::random()); + let next = self.clone().with_hash(B256::random()); next.with_nonce(self.get_nonce() + skip + 1) } /// Returns a clone with incremented nonce - pub fn inc_nonce(mut self) -> Self { + pub fn inc_nonce(self) -> Self { let nonce = self.get_nonce() + 1; self.with_nonce(nonce) } /// Sets a new random hash - pub fn rng_hash(mut self) -> Self { + pub fn rng_hash(self) -> Self { self.with_hash(B256::random()) } @@ -473,7 +477,7 @@ impl MockTransaction { /// Returns a new transaction with a higher gas price pub fn inc_price_by(&self, value: u128) -> Self { - let mut next = self.clone(); + let next = self.clone(); let gas = self.get_gas_price().checked_add(value).unwrap(); next.with_gas_price(gas) } @@ -485,21 +489,21 @@ impl MockTransaction { /// Returns a new transaction with a lower gas price pub fn decr_price_by(&self, value: u128) -> Self { - let mut next = self.clone(); + let next = self.clone(); let gas = self.get_gas_price().checked_sub(value).unwrap(); next.with_gas_price(gas) } /// Returns a new transaction with a higher value pub fn inc_value(&self) -> Self { - let mut next = self.clone(); + let next = self.clone(); let val = self.get_value().checked_add(U256::from(1)).unwrap(); next.with_value(val) } /// Returns a new transaction with a higher gas limit pub fn inc_limit(&self) -> Self { - let mut next = self.clone(); + let next = self.clone(); let gas = self.get_gas_limit() + 1; next.with_gas_limit(gas) } @@ -850,8 +854,8 @@ impl From for Transaction { fn from(mock: MockTransaction) -> Self { match mock { MockTransaction::Legacy { - hash, - sender, + hash: _, + sender: _, nonce, gas_price, gas_limit, @@ -868,8 +872,8 @@ impl From for Transaction { input: input.clone(), }), MockTransaction::Eip1559 { - hash, - sender, + hash: _, + sender: _, nonce, max_fee_per_gas, max_priority_fee_per_gas, @@ -891,7 +895,7 @@ impl From for Transaction { }), MockTransaction::Eip4844 { hash, - sender, + sender: _, nonce, max_fee_per_gas, max_priority_fee_per_gas, @@ -901,7 +905,7 @@ impl From for Transaction { value, accesslist, input, - sidecar, + sidecar: _, } => Self::Eip4844(TxEip4844 { chain_id: 1, nonce, @@ -916,8 +920,8 @@ impl From for Transaction { input, }), MockTransaction::Eip2930 { - hash, - sender, + hash: _, + sender: _, nonce, to, gas_limit, @@ -1025,6 +1029,7 @@ impl proptest::arbitrary::Arbitrary for MockTransaction { // performance just use a default sidecar sidecar: BlobTransactionSidecar::default(), }, + #[allow(unreachable_patterns)] _ => unimplemented!(), }) .boxed() @@ -1155,7 +1160,7 @@ impl MockTransactionSet { let mut txs = Vec::with_capacity(tx_count); let mut curr_tx = MockTransaction::new_from_type(tx_type).with_nonce(from_nonce); for i in 0..tx_count { - let nonce = from_nonce + i as u64; + let _nonce = from_nonce + i as u64; curr_tx = curr_tx.next().with_sender(sender); txs.push(curr_tx.clone()); } diff --git a/crates/transaction-pool/src/test_utils/mod.rs b/crates/transaction-pool/src/test_utils/mod.rs index 05879c0cbf18..74823e85afc6 100644 --- a/crates/transaction-pool/src/test_utils/mod.rs +++ b/crates/transaction-pool/src/test_utils/mod.rs @@ -1,17 +1,16 @@ //! Internal helpers for testing. -#![allow(missing_docs, unused, missing_debug_implementations, unreachable_pub)] -mod gen; -mod mock; -mod pool; +#![allow(missing_docs, missing_debug_implementations)] -use crate::{ - blobstore::InMemoryBlobStore, noop::MockTransactionValidator, Pool, PoolTransaction, - TransactionOrigin, TransactionValidationOutcome, TransactionValidator, -}; +use crate::{blobstore::InMemoryBlobStore, noop::MockTransactionValidator, Pool}; + +mod gen; pub use gen::*; + +mod mock; pub use mock::*; -use std::{marker::PhantomData, sync::Arc}; + +mod pool; /// A [Pool] used for testing pub type TestPool = @@ -21,6 +20,7 @@ pub type TestPool = pub fn testing_pool() -> TestPool { testing_pool_with_validator(MockTransactionValidator::default()) } + /// Returns a new [Pool] used for testing purposes pub fn testing_pool_with_validator( validator: MockTransactionValidator, diff --git a/crates/transaction-pool/src/test_utils/pool.rs b/crates/transaction-pool/src/test_utils/pool.rs index 545b90d0cf79..9a6deb8c00d5 100644 --- a/crates/transaction-pool/src/test_utils/pool.rs +++ b/crates/transaction-pool/src/test_utils/pool.rs @@ -1,24 +1,22 @@ //! Test helpers for mocking an entire pool. +#![allow(dead_code)] + use crate::{ - error::PoolResult, pool::{txpool::TxPool, AddedTransaction}, - test_utils::{ - MockOrdering, MockTransaction, MockTransactionDistribution, MockTransactionFactory, - }, + test_utils::{MockOrdering, MockTransactionDistribution, MockTransactionFactory}, TransactionOrdering, }; use rand::Rng; -use reth_primitives::{Address, U128, U256}; +use reth_primitives::{Address, U256}; use serde::{Deserialize, Serialize}; use std::{ collections::HashMap, ops::{Deref, DerefMut}, - sync::Arc, }; /// A wrapped `TxPool` with additional helpers for testing -pub struct MockPool { +pub(crate) struct MockPool { // The wrapped pool. pool: TxPool, } @@ -60,7 +58,7 @@ impl DerefMut for MockPool { } /// Simulates transaction execution. -pub struct MockTransactionSimulator { +pub(crate) struct MockTransactionSimulator { /// The pending base fee base_fee: u128, /// Generator for transactions @@ -83,7 +81,7 @@ pub struct MockTransactionSimulator { impl MockTransactionSimulator { /// Returns a new mock instance - pub fn new(mut rng: R, config: MockSimulatorConfig) -> Self { + pub(crate) fn new(mut rng: R, config: MockSimulatorConfig) -> Self { let senders = config.addresses(&mut rng); let nonces = senders.iter().copied().map(|a| (a, 0)).collect(); let balances = senders.iter().copied().map(|a| (a, config.balance)).collect(); @@ -113,7 +111,7 @@ impl MockTransactionSimulator { } /// Executes the next scenario and applies it to the pool - pub fn next(&mut self, pool: &mut MockPool) { + pub(crate) fn next(&mut self, pool: &mut MockPool) { let sender = self.rng_address(); let scenario = self.rng_scenario(); let on_chain_nonce = self.nonces[&sender]; @@ -152,30 +150,29 @@ impl MockTransactionSimulator { } /// How to configure a new mock transaction stream -pub struct MockSimulatorConfig { +pub(crate) struct MockSimulatorConfig { /// How many senders to generate. - pub num_senders: usize, + pub(crate) num_senders: usize, // TODO(mattsse): add a way to generate different balances - pub balance: U256, + pub(crate) balance: U256, /// Scenarios to test - pub scenarios: Vec, + pub(crate) scenarios: Vec, /// The start base fee - pub base_fee: u128, + pub(crate) base_fee: u128, /// generator for transactions - pub tx_generator: MockTransactionDistribution, + pub(crate) tx_generator: MockTransactionDistribution, } impl MockSimulatorConfig { /// Generates a set of random addresses - pub fn addresses(&self, rng: &mut impl rand::Rng) -> Vec
{ - let _ = rng.gen::(); // TODO(dani): ::random_with - std::iter::repeat_with(Address::random).take(self.num_senders).collect() + pub(crate) fn addresses(&self, rng: &mut impl rand::Rng) -> Vec
{ + std::iter::repeat_with(|| Address::random_with(rng)).take(self.num_senders).collect() } } /// Represents #[derive(Debug, Clone, Serialize, Deserialize)] -pub enum ScenarioType { +pub(crate) enum ScenarioType { OnchainNonce, HigherNonce { skip: u64 }, } @@ -186,7 +183,7 @@ pub enum ScenarioType { /// /// An executed scenario can affect previous executed transactions #[derive(Debug, Clone, Serialize, Deserialize)] -pub enum Scenario { +pub(crate) enum Scenario { /// Send a tx with the same nonce as on chain. OnchainNonce { nonce: u64 }, /// Send a tx with a higher nonce that what the sender has on chain @@ -199,7 +196,7 @@ pub enum Scenario { /// Represents an executed scenario #[derive(Debug, Clone, Serialize, Deserialize)] -pub struct ExecutedScenario { +pub(crate) struct ExecutedScenario { /// balance at the time of execution balance: U256, /// nonce at the time of execution @@ -210,7 +207,7 @@ pub struct ExecutedScenario { /// All executed scenarios by a sender #[derive(Debug, Clone, Serialize, Deserialize)] -pub struct ExecutedScenarios { +pub(crate) struct ExecutedScenarios { sender: Address, scenarios: Vec, } diff --git a/docs/crates/network.md b/docs/crates/network.md index 8da43254f4b4..fea2556d7712 100644 --- a/docs/crates/network.md +++ b/docs/crates/network.md @@ -461,7 +461,7 @@ pub struct EthRequestHandler { /// The client type that can interact with the chain. client: Arc, /// Used for reporting peers. - #[allow(unused)] + #[allow(dead_code)] peers: PeersHandle, /// Incoming request from the [NetworkManager](crate::NetworkManager). incoming_requests: UnboundedReceiverStream, From 365acf08dd57d342662d7751a63114a515020f21 Mon Sep 17 00:00:00 2001 From: Thomas Coratger <60488569+tcoratger@users.noreply.github.com> Date: Thu, 28 Dec 2023 17:39:16 +0100 Subject: [PATCH 216/277] fix: better conversion error handling in `block_hash_ref` (#5870) Co-authored-by: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Co-authored-by: Matthias Seitz --- crates/interfaces/src/provider.rs | 5 ++++- crates/revm/src/database.rs | 20 +++++++++++++++----- 2 files changed, 19 insertions(+), 6 deletions(-) diff --git a/crates/interfaces/src/provider.rs b/crates/interfaces/src/provider.rs index adbd8d1db930..5e8a6a02431f 100644 --- a/crates/interfaces/src/provider.rs +++ b/crates/interfaces/src/provider.rs @@ -1,6 +1,6 @@ use reth_primitives::{ Address, BlockHash, BlockHashOrNumber, BlockNumber, GotExpected, SnapshotSegment, - TxHashOrNumber, TxNumber, B256, + TxHashOrNumber, TxNumber, B256, U256, }; use std::path::PathBuf; use thiserror::Error; @@ -122,6 +122,9 @@ pub enum ProviderError { /// Snapshot file is not found for requested transaction. #[error("not able to find {0} snapshot file for transaction id {1}")] MissingSnapshotTx(SnapshotSegment, TxNumber), + /// Error encountered when the block number conversion from U256 to u64 causes an overflow. + #[error("failed to convert block number U256 to u64: {0}")] + BlockNumberOverflow(U256), } impl From for ProviderError { diff --git a/crates/revm/src/database.rs b/crates/revm/src/database.rs index 455188c634e7..1eaded23b6ad 100644 --- a/crates/revm/src/database.rs +++ b/crates/revm/src/database.rs @@ -74,8 +74,13 @@ impl Database for StateProviderDatabase { /// Returns `Ok` with the block hash if found, or the default hash otherwise. /// Note: It safely casts the `number` to `u64`. fn block_hash(&mut self, number: U256) -> Result { - // The `number` represents the block number, so it is safe to cast it to u64. - Ok(self.0.block_hash(number.try_into().unwrap())?.unwrap_or_default()) + // Attempt to convert U256 to u64 + let block_number = match number.try_into() { + Ok(value) => value, + Err(_) => return Err(Self::Error::BlockNumberOverflow(number)), + }; + + Ok(self.0.block_hash(block_number)?.unwrap_or_default()) } } @@ -112,9 +117,14 @@ impl DatabaseRef for StateProviderDatabase { /// Retrieves the block hash for a given block number. /// /// Returns `Ok` with the block hash if found, or the default hash otherwise. - /// Note: This operation may potentially be unsafe due to the unwrap operation. fn block_hash_ref(&self, number: U256) -> Result { - // Note: this unwrap is potentially unsafe - Ok(self.0.block_hash(number.try_into().unwrap())?.unwrap_or_default()) + // Attempt to convert U256 to u64 + let block_number = match number.try_into() { + Ok(value) => value, + Err(_) => return Err(Self::Error::BlockNumberOverflow(number)), + }; + + // Get the block hash or default hash + Ok(self.0.block_hash(block_number)?.unwrap_or_default()) } } From e5ecd4af065027bd999f0c8ebd876394a3bbef99 Mon Sep 17 00:00:00 2001 From: Matthias Seitz Date: Thu, 28 Dec 2023 20:57:07 +0100 Subject: [PATCH 217/277] refactor: move ethereum and op builder into separate crates (#5876) --- Cargo.lock | 30 ++ Cargo.toml | 3 + bin/reth/Cargo.toml | 6 +- bin/reth/src/cli/ext.rs | 9 +- bin/reth/src/debug_cmd/build_block.rs | 14 +- crates/payload/basic/Cargo.toml | 11 +- crates/payload/basic/src/lib.rs | 504 +++++--------------------- crates/payload/basic/src/optimism.rs | 326 ----------------- crates/payload/ethereum/Cargo.toml | 29 ++ crates/payload/ethereum/src/lib.rs | 306 ++++++++++++++++ crates/payload/optimism/Cargo.toml | 35 ++ crates/payload/optimism/src/lib.rs | 407 +++++++++++++++++++++ 12 files changed, 921 insertions(+), 759 deletions(-) delete mode 100644 crates/payload/basic/src/optimism.rs create mode 100644 crates/payload/ethereum/Cargo.toml create mode 100644 crates/payload/ethereum/src/lib.rs create mode 100644 crates/payload/optimism/Cargo.toml create mode 100644 crates/payload/optimism/src/lib.rs diff --git a/Cargo.lock b/Cargo.lock index c5482ac9ff3b..db68da191c04 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5624,12 +5624,14 @@ dependencies = [ "reth-db", "reth-discv4", "reth-downloaders", + "reth-ethereum-payload-builder", "reth-interfaces", "reth-metrics", "reth-net-nat", "reth-network", "reth-network-api", "reth-nippy-jar", + "reth-optimism-payload-builder", "reth-payload-builder", "reth-payload-validator", "reth-primitives", @@ -6005,6 +6007,20 @@ dependencies = [ "thiserror", ] +[[package]] +name = "reth-ethereum-payload-builder" +version = "0.1.0-alpha.13" +dependencies = [ + "reth-basic-payload-builder", + "reth-payload-builder", + "reth-primitives", + "reth-provider", + "reth-revm", + "reth-transaction-pool", + "revm", + "tracing", +] + [[package]] name = "reth-interfaces" version = "0.1.0-alpha.13" @@ -6206,6 +6222,20 @@ dependencies = [ "zstd 0.12.4", ] +[[package]] +name = "reth-optimism-payload-builder" +version = "0.1.0-alpha.13" +dependencies = [ + "reth-basic-payload-builder", + "reth-payload-builder", + "reth-primitives", + "reth-provider", + "reth-revm", + "reth-transaction-pool", + "revm", + "tracing", +] + [[package]] name = "reth-payload-builder" version = "0.1.0-alpha.13" diff --git a/Cargo.toml b/Cargo.toml index 1a7fb4a5c161..4c32de962819 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -21,6 +21,8 @@ members = [ "crates/net/network-api/", "crates/payload/basic/", "crates/payload/builder/", + "crates/payload/ethereum/", + "crates/payload/optimism/", "crates/payload/validator/", "crates/primitives/", "crates/prune/", @@ -105,6 +107,7 @@ reth-downloaders = { path = "crates/net/downloaders" } reth-ecies = { path = "crates/net/ecies" } reth-eth-wire = { path = "crates/net/eth-wire" } reth-ethereum-forks = { path = "crates/ethereum-forks" } +reth-ethereum-payload-builder = { path = "crates/payload/ethereum" } reth-interfaces = { path = "crates/interfaces" } reth-ipc = { path = "crates/rpc/ipc" } reth-libmdbx = { path = "crates/storage/libmdbx-rs" } diff --git a/bin/reth/Cargo.toml b/bin/reth/Cargo.toml index d1747b069f2b..281fe9e3bd03 100644 --- a/bin/reth/Cargo.toml +++ b/bin/reth/Cargo.toml @@ -43,6 +43,8 @@ reth-downloaders = { workspace = true, features = ["test-utils"] } reth-tracing.workspace = true reth-tasks.workspace = true reth-net-nat.workspace = true +reth-optimism-payload-builder = { path = "../../crates/payload/optimism", optional = true } +reth-ethereum-payload-builder.workspace = true reth-payload-builder.workspace = true reth-payload-validator.workspace = true reth-basic-payload-builder.workspace = true @@ -134,10 +136,12 @@ optimism = [ "reth-provider/optimism", "reth-beacon-consensus/optimism", "reth-auto-seal-consensus/optimism", - "reth-basic-payload-builder/optimism", "reth-network/optimism", "reth-network-api/optimism", "reth-blockchain-tree/optimism", + "reth-payload-builder/optimism", + "reth-optimism-payload-builder/optimism", + "reth-ethereum-payload-builder/optimism", ] # no-op feature flag for switching between the `optimism` and default functionality in CI matrices ethereum = [] diff --git a/bin/reth/src/cli/ext.rs b/bin/reth/src/cli/ext.rs index 939c11ae549e..d72db048b3f2 100644 --- a/bin/reth/src/cli/ext.rs +++ b/bin/reth/src/cli/ext.rs @@ -140,17 +140,18 @@ pub trait RethNodeCommandConfig: fmt::Debug { .extradata(conf.extradata_rlp_bytes()) .max_gas_limit(conf.max_gas_limit()); + // no extradata for optimism #[cfg(feature = "optimism")] - let payload_job_config = - payload_job_config.compute_pending_block(conf.compute_pending_block()); + let payload_job_config = payload_job_config.extradata(Default::default()); // The default payload builder is implemented on the unit type. #[cfg(not(feature = "optimism"))] - let payload_builder = reth_basic_payload_builder::EthereumPayloadBuilder::default(); + let payload_builder = reth_ethereum_payload_builder::EthereumPayloadBuilder::default(); // Optimism's payload builder is implemented on the OptimismPayloadBuilder type. #[cfg(feature = "optimism")] - let payload_builder = reth_basic_payload_builder::OptimismPayloadBuilder::default(); + let payload_builder = reth_optimism_payload_builder::OptimismPayloadBuilder::default() + .set_compute_pending_block(conf.compute_pending_block()); let payload_generator = BasicPayloadJobGenerator::with_builder( components.provider(), diff --git a/bin/reth/src/debug_cmd/build_block.rs b/bin/reth/src/debug_cmd/build_block.rs index 29728692b4b5..c4fe2b52fb63 100644 --- a/bin/reth/src/debug_cmd/build_block.rs +++ b/bin/reth/src/debug_cmd/build_block.rs @@ -12,7 +12,7 @@ use alloy_rlp::Decodable; use clap::Parser; use eyre::Context; use reth_basic_payload_builder::{ - default_payload_builder, BuildArguments, BuildOutcome, Cancelled, PayloadConfig, + BuildArguments, BuildOutcome, Cancelled, PayloadBuilder, PayloadConfig, }; use reth_beacon_consensus::BeaconConsensus; use reth_blockchain_tree::{ @@ -244,8 +244,6 @@ impl Command { Bytes::default(), PayloadBuilderAttributes::try_new(best_block.hash, payload_attrs)?, self.chain.clone(), - #[cfg(feature = "optimism")] - true, ); let args = BuildArguments::new( blockchain_db.clone(), @@ -255,7 +253,15 @@ impl Command { Cancelled::default(), None, ); - match default_payload_builder(args)? { + + #[cfg(feature = "optimism")] + let payload_builder = reth_optimism_payload_builder::OptimismPayloadBuilder::default() + .compute_pending_block(); + + #[cfg(not(feature = "optimism"))] + let payload_builder = reth_ethereum_payload_builder::EthereumPayloadBuilder::default(); + + match payload_builder.try_build(args)? { BuildOutcome::Better { payload, .. } => { let block = payload.block(); debug!(target: "reth::cli", ?block, "Built new payload"); diff --git a/crates/payload/basic/Cargo.toml b/crates/payload/basic/Cargo.toml index 6d06aa4b032f..4ecbaa698b4e 100644 --- a/crates/payload/basic/Cargo.toml +++ b/crates/payload/basic/Cargo.toml @@ -32,13 +32,4 @@ reth-metrics.workspace = true metrics.workspace = true # misc -tracing.workspace = true - -[features] -optimism = [ - "reth-primitives/optimism", - "reth-revm/optimism", - "reth-transaction-pool/optimism", - "reth-provider/optimism", - "reth-payload-builder/optimism" -] +tracing.workspace = true \ No newline at end of file diff --git a/crates/payload/basic/src/lib.rs b/crates/payload/basic/src/lib.rs index 2b5c397f2be5..3f07ca28fcd3 100644 --- a/crates/payload/basic/src/lib.rs +++ b/crates/payload/basic/src/lib.rs @@ -5,14 +5,37 @@ html_favicon_url = "https://avatars0.githubusercontent.com/u/97369466?s=256", issue_tracker_base_url = "https://github.com/paradigmxyz/reth/issues/" )] -#![warn(missing_debug_implementations, missing_docs, unreachable_pub, rustdoc::all)] +#![warn( + missing_debug_implementations, + missing_docs, + unused_crate_dependencies, + unreachable_pub, + rustdoc::all +)] #![deny(unused_must_use, rust_2018_idioms)] #![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))] -use crate::metrics::PayloadBuilderMetrics; use alloy_rlp::Encodable; use futures_core::ready; use futures_util::FutureExt; +use revm::{ + db::states::bundle_state::BundleRetention, + primitives::{BlockEnv, CfgEnv, Env}, + Database, DatabaseCommit, State, +}; +use std::{ + future::Future, + pin::Pin, + sync::{atomic::AtomicBool, Arc}, + task::{Context, Poll}, + time::{Duration, SystemTime, UNIX_EPOCH}, +}; +use tokio::{ + sync::{oneshot, Semaphore}, + time::{Interval, Sleep}, +}; +use tracing::{debug, trace, warn}; + use reth_interfaces::RethResult; use reth_payload_builder::{ database::CachedReads, error::PayloadBuilderError, BuiltPayload, KeepPayloadJobAlive, @@ -21,14 +44,11 @@ use reth_payload_builder::{ use reth_primitives::{ bytes::BytesMut, constants::{ - eip4844::MAX_DATA_GAS_PER_BLOCK, BEACON_NONCE, EMPTY_RECEIPTS, EMPTY_TRANSACTIONS, - EMPTY_WITHDRAWALS, ETHEREUM_BLOCK_GAS_LIMIT, RETH_CLIENT_VERSION, SLOT_DURATION, + BEACON_NONCE, EMPTY_RECEIPTS, EMPTY_TRANSACTIONS, EMPTY_WITHDRAWALS, + ETHEREUM_BLOCK_GAS_LIMIT, RETH_CLIENT_VERSION, SLOT_DURATION, }, - eip4844::calculate_excess_blob_gas, - proofs, - revm::{compat::into_reth_log, env::tx_env_with_recovered}, - Block, BlockNumberOrTag, Bytes, ChainSpec, Header, IntoRecoveredTransaction, Receipt, Receipts, - SealedBlock, Withdrawal, B256, EMPTY_OMMER_ROOT_HASH, U256, + proofs, Block, BlockNumberOrTag, Bytes, ChainSpec, Header, Receipts, SealedBlock, Withdrawal, + B256, EMPTY_OMMER_ROOT_HASH, U256, }; use reth_provider::{ BlockReaderIdExt, BlockSource, BundleStateWithReceipts, ProviderError, StateProviderFactory, @@ -39,39 +59,14 @@ use reth_revm::{ }; use reth_tasks::TaskSpawner; use reth_transaction_pool::TransactionPool; -use revm::{ - db::states::bundle_state::BundleRetention, - primitives::{BlockEnv, CfgEnv, EVMError, Env, InvalidTransaction, ResultAndState}, - Database, DatabaseCommit, State, -}; -use std::{ - future::Future, - pin::Pin, - sync::{atomic::AtomicBool, Arc}, - task::{Context, Poll}, - time::{Duration, SystemTime, UNIX_EPOCH}, -}; -use tokio::{ - sync::{oneshot, Semaphore}, - time::{Interval, Sleep}, -}; -use tracing::{debug, trace, warn}; - -mod metrics; -#[cfg(feature = "optimism")] -mod optimism; -#[cfg(feature = "optimism")] -pub use optimism::OptimismPayloadBuilder; +use crate::metrics::PayloadBuilderMetrics; -/// Ethereum payload builder -#[derive(Debug, Clone, Copy, Default)] -#[non_exhaustive] -pub struct EthereumPayloadBuilder; +mod metrics; /// The [`PayloadJobGenerator`] that creates [`BasicPayloadJob`]s. #[derive(Debug)] -pub struct BasicPayloadJobGenerator { +pub struct BasicPayloadJobGenerator { /// The client that can interact with the chain. client: Client, /// txpool @@ -92,26 +87,6 @@ pub struct BasicPayloadJobGenerator BasicPayloadJobGenerator { - /// Creates a new [BasicPayloadJobGenerator] with the given config. - pub fn new( - client: Client, - pool: Pool, - executor: Tasks, - config: BasicPayloadJobGeneratorConfig, - chain_spec: Arc, - ) -> Self { - BasicPayloadJobGenerator::with_builder( - client, - pool, - executor, - config, - chain_spec, - EthereumPayloadBuilder, - ) - } -} - impl BasicPayloadJobGenerator { /// Creates a new [BasicPayloadJobGenerator] with the given config and custom [PayloadBuilder] pub fn with_builder( @@ -195,8 +170,6 @@ where self.config.extradata.clone(), attributes, Arc::clone(&self.chain_spec), - #[cfg(feature = "optimism")] - self.config.compute_pending_block, ); let until = self.job_deadline(config.attributes.timestamp); @@ -246,9 +219,6 @@ pub struct BasicPayloadJobGeneratorConfig { deadline: Duration, /// Maximum number of tasks to spawn for building a payload. max_payload_tasks: usize, - /// The rollup's compute pending block configuration option. - #[cfg(feature = "optimism")] - compute_pending_block: bool, } // === impl BasicPayloadJobGeneratorConfig === @@ -292,15 +262,6 @@ impl BasicPayloadJobGeneratorConfig { self.max_gas_limit = max_gas_limit; self } - - /// Sets the compute pending block configuration option. - /// - /// Defaults to `false`. - #[cfg(feature = "optimism")] - pub fn compute_pending_block(mut self, compute_pending_block: bool) -> Self { - self.compute_pending_block = compute_pending_block; - self - } } impl Default for BasicPayloadJobGeneratorConfig { @@ -314,8 +275,6 @@ impl Default for BasicPayloadJobGeneratorConfig { // 12s slot time deadline: SLOT_DURATION, max_payload_tasks: 3, - #[cfg(feature = "optimism")] - compute_pending_block: false, } } } @@ -344,7 +303,7 @@ pub struct BasicPayloadJob { /// Caches all disk reads for the state the new payloads builds on /// /// This is used to avoid reading the same state over and over again when new attempts are - /// triggerd, because during the building process we'll repeatedly execute the transactions. + /// triggered, because during the building process we'll repeatedly execute the transactions. cached_reads: Option, /// metrics for this type metrics: PayloadBuilderMetrics, @@ -478,6 +437,22 @@ where if best_payload.is_none() { debug!(target: "payload_builder", id=%self.config.payload_id(), "no best payload yet to resolve, building empty payload"); + let args = BuildArguments { + client: self.client.clone(), + pool: self.pool.clone(), + cached_reads: self.cached_reads.take().unwrap_or_default(), + config: self.config.clone(), + cancel: Cancelled::default(), + best_payload: None, + }; + + if let Some(payload) = self.builder.on_missing_payload(args) { + return ( + ResolveBestPayload { best_payload: Some(payload), maybe_better, empty_payload }, + KeepPayloadJobAlive::Yes, + ) + } + // if no payload has been built yet self.metrics.inc_requested_empty_payload(); // no payload built yet, so we need to return an empty payload @@ -489,41 +464,6 @@ where let _ = tx.send(res); })); - // In Optimism, the PayloadAttributes can specify a `no_tx_pool` option that implies we - // should not pull transactions from the tx pool. In this case, we build the payload - // upfront with the list of transactions sent in the attributes without caring about - // the results of the polling job, if a best payload has not already been built. - #[cfg(feature = "optimism")] - { - if self.config.chain_spec.is_optimism() && - self.config.attributes.optimism_payload_attributes.no_tx_pool - { - let args = BuildArguments { - client: self.client.clone(), - pool: self.pool.clone(), - cached_reads: self.cached_reads.take().unwrap_or_default(), - config: self.config.clone(), - cancel: Cancelled::default(), - best_payload: None, - }; - if let Ok(BuildOutcome::Better { payload, cached_reads }) = - self.builder.try_build(args) - { - self.cached_reads = Some(cached_reads); - trace!(target: "payload_builder", "[OPTIMISM] Forced best payload"); - let payload = Arc::new(payload); - return ( - ResolveBestPayload { - best_payload: Some(payload), - maybe_better, - empty_payload, - }, - KeepPayloadJobAlive::Yes, - ) - } - } - } - empty_payload = Some(rx); } @@ -636,35 +576,27 @@ impl Drop for Cancelled { #[derive(Clone, Debug)] pub struct PayloadConfig { /// Pre-configured block environment. - initialized_block_env: BlockEnv, + pub initialized_block_env: BlockEnv, /// Configuration for the environment. - initialized_cfg: CfgEnv, + pub initialized_cfg: CfgEnv, /// The parent block. - parent_block: Arc, + pub parent_block: Arc, /// Block extra data. - extra_data: Bytes, + pub extra_data: Bytes, /// Requested attributes for the payload. - attributes: PayloadBuilderAttributes, + pub attributes: PayloadBuilderAttributes, /// The chain spec. - chain_spec: Arc, - /// The rollup's compute pending block configuration option. - // TODO(clabby): Implement this feature. - #[cfg(feature = "optimism")] - #[allow(dead_code)] - compute_pending_block: bool, + pub chain_spec: Arc, } impl PayloadConfig { /// Returns an owned instance of the [PayloadConfig]'s extra_data bytes. - pub(crate) fn extra_data(&self) -> reth_primitives::Bytes { - #[cfg(feature = "optimism")] - if self.chain_spec.is_optimism() { - return Default::default() - } + pub fn extra_data(&self) -> Bytes { self.extra_data.clone() } - pub(crate) fn payload_id(&self) -> PayloadId { + /// Returns the payload id. + pub fn payload_id(&self) -> PayloadId { self.attributes.id } } @@ -676,7 +608,6 @@ impl PayloadConfig { extra_data: Bytes, attributes: PayloadBuilderAttributes, chain_spec: Arc, - #[cfg(feature = "optimism")] compute_pending_block: bool, ) -> Self { // configure evm env based on parent block let (initialized_cfg, initialized_block_env) = @@ -689,8 +620,6 @@ impl PayloadConfig { extra_data, attributes, chain_spec, - #[cfg(feature = "optimism")] - compute_pending_block, } } } @@ -723,12 +652,18 @@ pub enum BuildOutcome { /// payload configuration, cancellation status, and the best payload achieved so far. #[derive(Debug)] pub struct BuildArguments { - client: Client, - pool: Pool, - cached_reads: CachedReads, - config: PayloadConfig, - cancel: Cancelled, - best_payload: Option>, + /// How to interact with the chain. + pub client: Client, + /// The transaction pool. + pub pool: Pool, + /// Previously cached disk reads + pub cached_reads: CachedReads, + /// How to configure the payload. + pub config: PayloadConfig, + /// A marker that can be used to cancel the job. + pub cancel: Cancelled, + /// The best payload achieved so far. + pub best_payload: Option>, } impl BuildArguments { @@ -770,278 +705,16 @@ pub trait PayloadBuilder: Send + Sync + Clone { &self, args: BuildArguments, ) -> Result; -} - -// Default implementation of [PayloadBuilder] for unit type -impl PayloadBuilder for EthereumPayloadBuilder -where - Client: StateProviderFactory, - Pool: TransactionPool, -{ - fn try_build( - &self, - args: BuildArguments, - ) -> Result { - default_payload_builder(args) - } -} - -/// Constructs an Ethereum transaction payload using the best transactions from the pool. -/// -/// Given build arguments including an Ethereum client, transaction pool, -/// and configuration, this function creates a transaction payload. Returns -/// a result indicating success with the payload or an error in case of failure. -#[inline] -pub fn default_payload_builder( - args: BuildArguments, -) -> Result -where - Client: StateProviderFactory, - Pool: TransactionPool, -{ - let BuildArguments { client, pool, mut cached_reads, config, cancel, best_payload } = args; - let state_provider = client.state_by_block_hash(config.parent_block.hash)?; - let state = StateProviderDatabase::new(&state_provider); - let mut db = - State::builder().with_database_ref(cached_reads.as_db(&state)).with_bundle_update().build(); - let extra_data = config.extra_data(); - let PayloadConfig { - initialized_block_env, - initialized_cfg, - parent_block, - attributes, - chain_spec, - .. - } = config; - - debug!(target: "payload_builder", id=%attributes.id, parent_hash = ?parent_block.hash, parent_number = parent_block.number, "building new payload"); - let mut cumulative_gas_used = 0; - let mut sum_blob_gas_used = 0; - let block_gas_limit: u64 = initialized_block_env.gas_limit.try_into().unwrap_or(u64::MAX); - let base_fee = initialized_block_env.basefee.to::(); - - let mut executed_txs = Vec::new(); - let mut best_txs = pool.best_transactions_with_base_fee(base_fee); - - let mut total_fees = U256::ZERO; - - let block_number = initialized_block_env.number.to::(); - - // apply eip-4788 pre block contract call - pre_block_beacon_root_contract_call( - &mut db, - &chain_spec, - block_number, - &initialized_cfg, - &initialized_block_env, - &attributes, - )?; - - let mut receipts = Vec::new(); - while let Some(pool_tx) = best_txs.next() { - // ensure we still have capacity for this transaction - if cumulative_gas_used + pool_tx.gas_limit() > block_gas_limit { - // we can't fit this transaction into the block, so we need to mark it as invalid - // which also removes all dependent transaction from the iterator before we can - // continue - best_txs.mark_invalid(&pool_tx); - continue - } - - // check if the job was cancelled, if so we can exit early - if cancel.is_cancelled() { - return Ok(BuildOutcome::Cancelled) - } - - // convert tx to a signed transaction - let tx = pool_tx.to_recovered_transaction(); - - // There's only limited amount of blob space available per block, so we need to check if the - // EIP-4844 can still fit in the block - if let Some(blob_tx) = tx.transaction.as_eip4844() { - let tx_blob_gas = blob_tx.blob_gas(); - if sum_blob_gas_used + tx_blob_gas > MAX_DATA_GAS_PER_BLOCK { - // we can't fit this _blob_ transaction into the block, so we mark it as invalid, - // which removes its dependent transactions from the iterator. This is similar to - // the gas limit condition for regular transactions above. - trace!(target: "payload_builder", tx=?tx.hash, ?sum_blob_gas_used, ?tx_blob_gas, "skipping blob transaction because it would exceed the max data gas per block"); - best_txs.mark_invalid(&pool_tx); - continue - } - } - - // Configure the environment for the block. - let env = Env { - cfg: initialized_cfg.clone(), - block: initialized_block_env.clone(), - tx: tx_env_with_recovered(&tx), - }; - - let mut evm = revm::EVM::with_env(env); - evm.database(&mut db); - - let ResultAndState { result, state } = match evm.transact() { - Ok(res) => res, - Err(err) => { - match err { - EVMError::Transaction(err) => { - if matches!(err, InvalidTransaction::NonceTooLow { .. }) { - // if the nonce is too low, we can skip this transaction - trace!(target: "payload_builder", ?err, ?tx, "skipping nonce too low transaction"); - } else { - // if the transaction is invalid, we can skip it and all of its - // descendants - trace!(target: "payload_builder", ?err, ?tx, "skipping invalid transaction and its descendants"); - best_txs.mark_invalid(&pool_tx); - } - - continue - } - err => { - // this is an error that we should treat as fatal for this attempt - return Err(PayloadBuilderError::EvmExecutionError(err)) - } - } - } - }; - - // commit changes - db.commit(state); - - // add to the total blob gas used if the transaction successfully executed - if let Some(blob_tx) = tx.transaction.as_eip4844() { - let tx_blob_gas = blob_tx.blob_gas(); - sum_blob_gas_used += tx_blob_gas; - - // if we've reached the max data gas per block, we can skip blob txs entirely - if sum_blob_gas_used == MAX_DATA_GAS_PER_BLOCK { - best_txs.skip_blobs(); - } - } - - let gas_used = result.gas_used(); - - // add gas used by the transaction to cumulative gas used, before creating the receipt - cumulative_gas_used += gas_used; - - // Push transaction changeset and calculate header bloom filter for receipt. - receipts.push(Some(Receipt { - tx_type: tx.tx_type(), - success: result.is_success(), - cumulative_gas_used, - logs: result.logs().into_iter().map(into_reth_log).collect(), - #[cfg(feature = "optimism")] - deposit_nonce: None, - #[cfg(feature = "optimism")] - deposit_receipt_version: None, - })); - - // update add to total fees - let miner_fee = tx - .effective_tip_per_gas(Some(base_fee)) - .expect("fee is always valid; execution succeeded"); - total_fees += U256::from(miner_fee) * U256::from(gas_used); - - // append transaction to the list of executed transactions - executed_txs.push(tx.into_signed()); - } - - // check if we have a better block - if !is_better_payload(best_payload.as_deref(), total_fees) { - // can skip building the block - return Ok(BuildOutcome::Aborted { fees: total_fees, cached_reads }) - } - - let WithdrawalsOutcome { withdrawals_root, withdrawals } = - commit_withdrawals(&mut db, &chain_spec, attributes.timestamp, attributes.withdrawals)?; - - // merge all transitions into bundle state, this would apply the withdrawal balance changes and - // 4788 contract call - db.merge_transitions(BundleRetention::PlainState); - - let bundle = BundleStateWithReceipts::new( - db.take_bundle(), - Receipts::from_vec(vec![receipts]), - block_number, - ); - let receipts_root = bundle - .receipts_root_slow( - block_number, - #[cfg(feature = "optimism")] - chain_spec.as_ref(), - #[cfg(feature = "optimism")] - attributes.timestamp, - ) - .expect("Number is in range"); - let logs_bloom = bundle.block_logs_bloom(block_number).expect("Number is in range"); - - // calculate the state root - let state_root = state_provider.state_root(&bundle)?; - - // create the block header - let transactions_root = proofs::calculate_transaction_root(&executed_txs); - - // initialize empty blob sidecars at first. If cancun is active then this will - let mut blob_sidecars = Vec::new(); - let mut excess_blob_gas = None; - let mut blob_gas_used = None; - - // only determine cancun fields when active - if chain_spec.is_cancun_active_at_timestamp(attributes.timestamp) { - // grab the blob sidecars from the executed txs - blob_sidecars = pool.get_all_blobs_exact( - executed_txs.iter().filter(|tx| tx.is_eip4844()).map(|tx| tx.hash).collect(), - )?; - - excess_blob_gas = if chain_spec.is_cancun_active_at_timestamp(parent_block.timestamp) { - let parent_excess_blob_gas = parent_block.excess_blob_gas.unwrap_or_default(); - let parent_blob_gas_used = parent_block.blob_gas_used.unwrap_or_default(); - Some(calculate_excess_blob_gas(parent_excess_blob_gas, parent_blob_gas_used)) - } else { - // for the first post-fork block, both parent.blob_gas_used and parent.excess_blob_gas - // are evaluated as 0 - Some(calculate_excess_blob_gas(0, 0)) - }; - - blob_gas_used = Some(sum_blob_gas_used); + /// Invoked when the payload job is being resolved and there is no payload yet. + /// + /// If this returns a payload, it will be used as the final payload for the job. + /// + /// TODO(mattsse): This needs to be refined a bit because this only exists for OP atm + fn on_missing_payload(&self, args: BuildArguments) -> Option> { + let _args = args; + None } - - let header = Header { - parent_hash: parent_block.hash, - ommers_hash: EMPTY_OMMER_ROOT_HASH, - beneficiary: initialized_block_env.coinbase, - state_root, - transactions_root, - receipts_root, - withdrawals_root, - logs_bloom, - timestamp: attributes.timestamp, - mix_hash: attributes.prev_randao, - nonce: BEACON_NONCE, - base_fee_per_gas: Some(base_fee), - number: parent_block.number + 1, - gas_limit: block_gas_limit, - difficulty: U256::ZERO, - gas_used: cumulative_gas_used, - extra_data, - parent_beacon_block_root: attributes.parent_beacon_block_root, - blob_gas_used, - excess_blob_gas, - }; - - // seal the block - let block = Block { header, body: executed_txs, ommers: vec![], withdrawals }; - - let sealed_block = block.seal_slow(); - debug!(target: "payload_builder", ?sealed_block, "sealed built block"); - - let mut payload = BuiltPayload::new(attributes.id, sealed_block, total_fees); - - // extend the payload with the blob sidecars from the executed txs - payload.extend_sidecars(blob_sidecars); - - Ok(BuildOutcome::Better { payload, cached_reads }) } /// Builds an empty payload without any transactions. @@ -1139,19 +812,22 @@ where /// Represents the outcome of committing withdrawals to the runtime database and post state. /// Pre-shanghai these are `None` values. -#[derive(Default)] -struct WithdrawalsOutcome { - withdrawals: Option>, - withdrawals_root: Option, +#[derive(Default, Debug)] +pub struct WithdrawalsOutcome { + /// committed withdrawals, if any. + pub withdrawals: Option>, + /// withdrawals root if any. + pub withdrawals_root: Option, } impl WithdrawalsOutcome { /// No withdrawals pre shanghai - fn pre_shanghai() -> Self { + pub fn pre_shanghai() -> Self { Self { withdrawals: None, withdrawals_root: None } } - fn empty() -> Self { + /// No withdrawals + pub fn empty() -> Self { Self { withdrawals: Some(vec![]), withdrawals_root: Some(EMPTY_WITHDRAWALS) } } } @@ -1161,7 +837,7 @@ impl WithdrawalsOutcome { /// Returns the withdrawals root. /// /// Returns `None` values pre shanghai -fn commit_withdrawals>( +pub fn commit_withdrawals>( db: &mut State, chain_spec: &ChainSpec, timestamp: u64, @@ -1199,7 +875,7 @@ fn commit_withdrawals>( /// /// This uses [apply_beacon_root_contract_call] to ultimately apply the beacon root contract state /// change. -fn pre_block_beacon_root_contract_call( +pub fn pre_block_beacon_root_contract_call( db: &mut DB, chain_spec: &ChainSpec, block_number: u64, @@ -1236,7 +912,7 @@ where /// /// This compares the total fees of the blocks, higher is better. #[inline(always)] -fn is_better_payload(best_payload: Option<&BuiltPayload>, new_fees: U256) -> bool { +pub fn is_better_payload(best_payload: Option<&BuiltPayload>, new_fees: U256) -> bool { if let Some(best_payload) = best_payload { new_fees > best_payload.fees() } else { diff --git a/crates/payload/basic/src/optimism.rs b/crates/payload/basic/src/optimism.rs deleted file mode 100644 index 2f27854586c2..000000000000 --- a/crates/payload/basic/src/optimism.rs +++ /dev/null @@ -1,326 +0,0 @@ -//! Optimism's [PayloadBuilder] implementation. - -use super::*; -use reth_payload_builder::error::OptimismPayloadBuilderError; -use reth_primitives::Hardfork; - -/// Constructs an Ethereum transaction payload from the transactions sent through the -/// Payload attributes by the sequencer. If the `no_tx_pool` argument is passed in -/// the payload attributes, the transaction pool will be ignored and the only transactions -/// included in the payload will be those sent through the attributes. -/// -/// Given build arguments including an Ethereum client, transaction pool, -/// and configuration, this function creates a transaction payload. Returns -/// a result indicating success with the payload or an error in case of failure. -#[inline] -pub(crate) fn optimism_payload_builder( - args: BuildArguments, -) -> Result -where - Client: StateProviderFactory, - Pool: TransactionPool, -{ - let BuildArguments { client, pool, mut cached_reads, config, cancel, best_payload } = args; - - let state_provider = client.state_by_block_hash(config.parent_block.hash)?; - let state = StateProviderDatabase::new(&state_provider); - let mut db = - State::builder().with_database_ref(cached_reads.as_db(&state)).with_bundle_update().build(); - let extra_data = config.extra_data(); - let PayloadConfig { - initialized_block_env, - initialized_cfg, - parent_block, - attributes, - chain_spec, - .. - } = config; - - debug!(target: "payload_builder", id=%attributes.id, parent_hash = ?parent_block.hash, parent_number = parent_block.number, "building new payload"); - let mut cumulative_gas_used = 0; - let block_gas_limit: u64 = attributes - .optimism_payload_attributes - .gas_limit - .unwrap_or(initialized_block_env.gas_limit.try_into().unwrap_or(u64::MAX)); - let base_fee = initialized_block_env.basefee.to::(); - - let mut executed_txs = Vec::new(); - let mut best_txs = pool.best_transactions_with_base_fee(base_fee); - - let mut total_fees = U256::ZERO; - - let block_number = initialized_block_env.number.to::(); - - let is_regolith = - chain_spec.is_fork_active_at_timestamp(Hardfork::Regolith, attributes.timestamp); - - // Ensure that the create2deployer is force-deployed at the canyon transition. Optimism - // blocks will always have at least a single transaction in them (the L1 info transaction), - // so we can safely assume that this will always be triggered upon the transition and that - // the above check for empty blocks will never be hit on OP chains. - reth_revm::optimism::ensure_create2_deployer(chain_spec.clone(), attributes.timestamp, &mut db) - .map_err(|_| { - PayloadBuilderError::Optimism(OptimismPayloadBuilderError::ForceCreate2DeployerFail) - })?; - - let mut receipts = Vec::new(); - for sequencer_tx in attributes.optimism_payload_attributes.transactions { - // Check if the job was cancelled, if so we can exit early. - if cancel.is_cancelled() { - return Ok(BuildOutcome::Cancelled) - } - - // Convert the transaction to a [TransactionSignedEcRecovered]. This is - // purely for the purposes of utilizing the [tx_env_with_recovered] function. - // Deposit transactions do not have signatures, so if the tx is a deposit, this - // will just pull in its `from` address. - let sequencer_tx = sequencer_tx.clone().try_into_ecrecovered().map_err(|_| { - PayloadBuilderError::Optimism(OptimismPayloadBuilderError::TransactionEcRecoverFailed) - })?; - - // Cache the depositor account prior to the state transition for the deposit nonce. - // - // Note that this *only* needs to be done post-regolith hardfork, as deposit nonces - // were not introduced in Bedrock. In addition, regular transactions don't have deposit - // nonces, so we don't need to touch the DB for those. - let depositor = (is_regolith && sequencer_tx.is_deposit()) - .then(|| { - db.load_cache_account(sequencer_tx.signer()) - .map(|acc| acc.account_info().unwrap_or_default()) - }) - .transpose() - .map_err(|_| { - PayloadBuilderError::Optimism(OptimismPayloadBuilderError::AccountLoadFailed( - sequencer_tx.signer(), - )) - })?; - - // Configure the environment for the block. - let env = Env { - cfg: initialized_cfg.clone(), - block: initialized_block_env.clone(), - tx: tx_env_with_recovered(&sequencer_tx), - }; - - let mut evm = revm::EVM::with_env(env); - evm.database(&mut db); - - let ResultAndState { result, state } = match evm.transact() { - Ok(res) => res, - Err(err) => { - match err { - EVMError::Transaction(err) => { - trace!(target: "optimism_payload_builder", ?err, ?sequencer_tx, "Error in sequencer transaction, skipping."); - continue - } - err => { - // this is an error that we should treat as fatal for this attempt - return Err(PayloadBuilderError::EvmExecutionError(err)) - } - } - } - }; - - // commit changes - db.commit(state); - - let gas_used = result.gas_used(); - - // add gas used by the transaction to cumulative gas used, before creating the receipt - cumulative_gas_used += gas_used; - - // Push transaction changeset and calculate header bloom filter for receipt. - receipts.push(Some(Receipt { - tx_type: sequencer_tx.tx_type(), - success: result.is_success(), - cumulative_gas_used, - logs: result.logs().into_iter().map(into_reth_log).collect(), - #[cfg(feature = "optimism")] - deposit_nonce: depositor.map(|account| account.nonce), - // The deposit receipt version was introduced in Canyon to indicate an update to how - // receipt hashes should be computed when set. The state transition process - // ensures this is only set for post-Canyon deposit transactions. - #[cfg(feature = "optimism")] - deposit_receipt_version: chain_spec - .is_fork_active_at_timestamp(Hardfork::Canyon, attributes.timestamp) - .then_some(1), - })); - - // append transaction to the list of executed transactions - executed_txs.push(sequencer_tx.into_signed()); - } - - if !attributes.optimism_payload_attributes.no_tx_pool { - while let Some(pool_tx) = best_txs.next() { - // ensure we still have capacity for this transaction - if cumulative_gas_used + pool_tx.gas_limit() > block_gas_limit { - // we can't fit this transaction into the block, so we need to mark it as invalid - // which also removes all dependent transaction from the iterator before we can - // continue - best_txs.mark_invalid(&pool_tx); - continue - } - - // check if the job was cancelled, if so we can exit early - if cancel.is_cancelled() { - return Ok(BuildOutcome::Cancelled) - } - - // convert tx to a signed transaction - let tx = pool_tx.to_recovered_transaction(); - - // Configure the environment for the block. - let env = Env { - cfg: initialized_cfg.clone(), - block: initialized_block_env.clone(), - tx: tx_env_with_recovered(&tx), - }; - - let mut evm = revm::EVM::with_env(env); - evm.database(&mut db); - - let ResultAndState { result, state } = match evm.transact() { - Ok(res) => res, - Err(err) => { - match err { - EVMError::Transaction(err) => { - if matches!(err, InvalidTransaction::NonceTooLow { .. }) { - // if the nonce is too low, we can skip this transaction - trace!(target: "payload_builder", ?err, ?tx, "skipping nonce too low transaction"); - } else { - // if the transaction is invalid, we can skip it and all of its - // descendants - trace!(target: "payload_builder", ?err, ?tx, "skipping invalid transaction and its descendants"); - best_txs.mark_invalid(&pool_tx); - } - - continue - } - err => { - // this is an error that we should treat as fatal for this attempt - return Err(PayloadBuilderError::EvmExecutionError(err)) - } - } - } - }; - - // commit changes - db.commit(state); - - let gas_used = result.gas_used(); - - // add gas used by the transaction to cumulative gas used, before creating the receipt - cumulative_gas_used += gas_used; - - // Push transaction changeset and calculate header bloom filter for receipt. - receipts.push(Some(Receipt { - tx_type: tx.tx_type(), - success: result.is_success(), - cumulative_gas_used, - logs: result.logs().into_iter().map(into_reth_log).collect(), - #[cfg(feature = "optimism")] - deposit_nonce: None, - #[cfg(feature = "optimism")] - deposit_receipt_version: None, - })); - - // update add to total fees - let miner_fee = tx - .effective_tip_per_gas(Some(base_fee)) - .expect("fee is always valid; execution succeeded"); - total_fees += U256::from(miner_fee) * U256::from(gas_used); - - // append transaction to the list of executed transactions - executed_txs.push(tx.into_signed()); - } - } - - // check if we have a better block - if !is_better_payload(best_payload.as_deref(), total_fees) { - // can skip building the block - return Ok(BuildOutcome::Aborted { fees: total_fees, cached_reads }) - } - - let WithdrawalsOutcome { withdrawals_root, withdrawals } = - commit_withdrawals(&mut db, &chain_spec, attributes.timestamp, attributes.withdrawals)?; - - // merge all transitions into bundle state, this would apply the withdrawal balance changes - // and 4788 contract call - db.merge_transitions(BundleRetention::PlainState); - - let bundle = BundleStateWithReceipts::new( - db.take_bundle(), - Receipts::from_vec(vec![receipts]), - block_number, - ); - let receipts_root = bundle - .receipts_root_slow(block_number, chain_spec.as_ref(), attributes.timestamp) - .expect("Number is in range"); - let logs_bloom = bundle.block_logs_bloom(block_number).expect("Number is in range"); - - // calculate the state root - let state_root = state_provider.state_root(&bundle)?; - - // create the block header - let transactions_root = proofs::calculate_transaction_root(&executed_txs); - - // Cancun is not yet active on Optimism chains. - let blob_sidecars = Vec::new(); - let excess_blob_gas = None; - let blob_gas_used = None; - - let header = Header { - parent_hash: parent_block.hash, - ommers_hash: EMPTY_OMMER_ROOT_HASH, - beneficiary: initialized_block_env.coinbase, - state_root, - transactions_root, - receipts_root, - withdrawals_root, - logs_bloom, - timestamp: attributes.timestamp, - mix_hash: attributes.prev_randao, - nonce: BEACON_NONCE, - base_fee_per_gas: Some(base_fee), - number: parent_block.number + 1, - gas_limit: block_gas_limit, - difficulty: U256::ZERO, - gas_used: cumulative_gas_used, - extra_data, - parent_beacon_block_root: attributes.parent_beacon_block_root, - blob_gas_used, - excess_blob_gas, - }; - - // seal the block - let block = Block { header, body: executed_txs, ommers: vec![], withdrawals }; - - let sealed_block = block.seal_slow(); - debug!(target: "payload_builder", ?sealed_block, "sealed built block"); - - let mut payload = BuiltPayload::new(attributes.id, sealed_block, total_fees); - - // extend the payload with the blob sidecars from the executed txs - payload.extend_sidecars(blob_sidecars); - - Ok(BuildOutcome::Better { payload, cached_reads }) -} - -/// Optimism's payload builder -#[derive(Debug, Clone, Copy, Default)] -#[non_exhaustive] -pub struct OptimismPayloadBuilder; - -/// Implementation of the [PayloadBuilder] trait for [OptimismPayloadBuilder]. -impl PayloadBuilder for OptimismPayloadBuilder -where - Client: StateProviderFactory, - Pool: TransactionPool, -{ - fn try_build( - &self, - args: BuildArguments, - ) -> Result { - optimism_payload_builder(args) - } -} diff --git a/crates/payload/ethereum/Cargo.toml b/crates/payload/ethereum/Cargo.toml new file mode 100644 index 000000000000..91e2410f226a --- /dev/null +++ b/crates/payload/ethereum/Cargo.toml @@ -0,0 +1,29 @@ +[package] +name = "reth-ethereum-payload-builder" +version.workspace = true +edition.workspace = true +rust-version.workspace = true +license.workspace = true +homepage.workspace = true +repository.workspace = true +description = "A basic ethereum payload builder for reth that uses the txpool API to build payloads." + +[dependencies] +# reth +reth-primitives.workspace = true +reth-revm.workspace = true +reth-transaction-pool.workspace = true +reth-provider.workspace = true +reth-payload-builder.workspace = true +reth-basic-payload-builder.workspace = true + +# ethereum +revm.workspace = true + +# misc +tracing.workspace = true + +[features] +# This is a workaround for reth-cli crate to allow this as mandatory dependency without breaking the build even if unused. +# This makes managing features and testing workspace easier because clippy always builds all members if --workspace is provided +optimism = [] \ No newline at end of file diff --git a/crates/payload/ethereum/src/lib.rs b/crates/payload/ethereum/src/lib.rs new file mode 100644 index 000000000000..5738b51c3961 --- /dev/null +++ b/crates/payload/ethereum/src/lib.rs @@ -0,0 +1,306 @@ +//! A basic Ethereum payload builder implementation. + +#![doc( + html_logo_url = "https://raw.githubusercontent.com/paradigmxyz/reth/main/assets/reth-docs.png", + html_favicon_url = "https://avatars0.githubusercontent.com/u/97369466?s=256", + issue_tracker_base_url = "https://github.com/paradigmxyz/reth/issues/" +)] +#![warn(missing_debug_implementations, missing_docs, unreachable_pub, rustdoc::all)] +#![deny(unused_must_use, rust_2018_idioms)] +#![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))] + +#[cfg(not(feature = "optimism"))] +pub use builder::*; + +#[cfg(not(feature = "optimism"))] +mod builder { + use reth_basic_payload_builder::{ + commit_withdrawals, is_better_payload, pre_block_beacon_root_contract_call, BuildArguments, + BuildOutcome, PayloadBuilder, PayloadConfig, WithdrawalsOutcome, + }; + use reth_payload_builder::{error::PayloadBuilderError, BuiltPayload}; + use reth_primitives::{ + constants::{eip4844::MAX_DATA_GAS_PER_BLOCK, BEACON_NONCE}, + eip4844::calculate_excess_blob_gas, + proofs, + revm::{compat::into_reth_log, env::tx_env_with_recovered}, + Block, Header, IntoRecoveredTransaction, Receipt, Receipts, EMPTY_OMMER_ROOT_HASH, U256, + }; + use reth_provider::{BundleStateWithReceipts, StateProviderFactory}; + use reth_revm::database::StateProviderDatabase; + use reth_transaction_pool::TransactionPool; + use revm::{ + db::states::bundle_state::BundleRetention, + primitives::{EVMError, Env, InvalidTransaction, ResultAndState}, + DatabaseCommit, State, + }; + use tracing::{debug, trace}; + + /// Ethereum payload builder + #[derive(Debug, Clone, Copy, PartialEq, Eq, Default)] + #[non_exhaustive] + pub struct EthereumPayloadBuilder; + + // Default implementation of [PayloadBuilder] for unit type + impl PayloadBuilder for EthereumPayloadBuilder + where + Client: StateProviderFactory, + Pool: TransactionPool, + { + fn try_build( + &self, + args: BuildArguments, + ) -> Result { + default_ethereum_payload_builder(args) + } + } + + /// Constructs an Ethereum transaction payload using the best transactions from the pool. + /// + /// Given build arguments including an Ethereum client, transaction pool, + /// and configuration, this function creates a transaction payload. Returns + /// a result indicating success with the payload or an error in case of failure. + #[inline] + pub fn default_ethereum_payload_builder( + args: BuildArguments, + ) -> Result + where + Client: StateProviderFactory, + Pool: TransactionPool, + { + let BuildArguments { client, pool, mut cached_reads, config, cancel, best_payload } = args; + + let state_provider = client.state_by_block_hash(config.parent_block.hash)?; + let state = StateProviderDatabase::new(&state_provider); + let mut db = State::builder() + .with_database_ref(cached_reads.as_db(&state)) + .with_bundle_update() + .build(); + let extra_data = config.extra_data(); + let PayloadConfig { + initialized_block_env, + initialized_cfg, + parent_block, + attributes, + chain_spec, + .. + } = config; + + debug!(target: "payload_builder", id=%attributes.id, parent_hash = ?parent_block.hash, parent_number = parent_block.number, "building new payload"); + let mut cumulative_gas_used = 0; + let mut sum_blob_gas_used = 0; + let block_gas_limit: u64 = initialized_block_env.gas_limit.try_into().unwrap_or(u64::MAX); + let base_fee = initialized_block_env.basefee.to::(); + + let mut executed_txs = Vec::new(); + let mut best_txs = pool.best_transactions_with_base_fee(base_fee); + + let mut total_fees = U256::ZERO; + + let block_number = initialized_block_env.number.to::(); + + // apply eip-4788 pre block contract call + pre_block_beacon_root_contract_call( + &mut db, + &chain_spec, + block_number, + &initialized_cfg, + &initialized_block_env, + &attributes, + )?; + + let mut receipts = Vec::new(); + while let Some(pool_tx) = best_txs.next() { + // ensure we still have capacity for this transaction + if cumulative_gas_used + pool_tx.gas_limit() > block_gas_limit { + // we can't fit this transaction into the block, so we need to mark it as invalid + // which also removes all dependent transaction from the iterator before we can + // continue + best_txs.mark_invalid(&pool_tx); + continue + } + + // check if the job was cancelled, if so we can exit early + if cancel.is_cancelled() { + return Ok(BuildOutcome::Cancelled) + } + + // convert tx to a signed transaction + let tx = pool_tx.to_recovered_transaction(); + + // There's only limited amount of blob space available per block, so we need to check if + // the EIP-4844 can still fit in the block + if let Some(blob_tx) = tx.transaction.as_eip4844() { + let tx_blob_gas = blob_tx.blob_gas(); + if sum_blob_gas_used + tx_blob_gas > MAX_DATA_GAS_PER_BLOCK { + // we can't fit this _blob_ transaction into the block, so we mark it as + // invalid, which removes its dependent transactions from + // the iterator. This is similar to the gas limit condition + // for regular transactions above. + trace!(target: "payload_builder", tx=?tx.hash, ?sum_blob_gas_used, ?tx_blob_gas, "skipping blob transaction because it would exceed the max data gas per block"); + best_txs.mark_invalid(&pool_tx); + continue + } + } + + // Configure the environment for the block. + let env = Env { + cfg: initialized_cfg.clone(), + block: initialized_block_env.clone(), + tx: tx_env_with_recovered(&tx), + }; + + let mut evm = revm::EVM::with_env(env); + evm.database(&mut db); + + let ResultAndState { result, state } = match evm.transact() { + Ok(res) => res, + Err(err) => { + match err { + EVMError::Transaction(err) => { + if matches!(err, InvalidTransaction::NonceTooLow { .. }) { + // if the nonce is too low, we can skip this transaction + trace!(target: "payload_builder", ?err, ?tx, "skipping nonce too low transaction"); + } else { + // if the transaction is invalid, we can skip it and all of its + // descendants + trace!(target: "payload_builder", ?err, ?tx, "skipping invalid transaction and its descendants"); + best_txs.mark_invalid(&pool_tx); + } + + continue + } + err => { + // this is an error that we should treat as fatal for this attempt + return Err(PayloadBuilderError::EvmExecutionError(err)) + } + } + } + }; + + // commit changes + db.commit(state); + + // add to the total blob gas used if the transaction successfully executed + if let Some(blob_tx) = tx.transaction.as_eip4844() { + let tx_blob_gas = blob_tx.blob_gas(); + sum_blob_gas_used += tx_blob_gas; + + // if we've reached the max data gas per block, we can skip blob txs entirely + if sum_blob_gas_used == MAX_DATA_GAS_PER_BLOCK { + best_txs.skip_blobs(); + } + } + + let gas_used = result.gas_used(); + + // add gas used by the transaction to cumulative gas used, before creating the receipt + cumulative_gas_used += gas_used; + + // Push transaction changeset and calculate header bloom filter for receipt. + receipts.push(Some(Receipt { + tx_type: tx.tx_type(), + success: result.is_success(), + cumulative_gas_used, + logs: result.logs().into_iter().map(into_reth_log).collect(), + })); + + // update add to total fees + let miner_fee = tx + .effective_tip_per_gas(Some(base_fee)) + .expect("fee is always valid; execution succeeded"); + total_fees += U256::from(miner_fee) * U256::from(gas_used); + + // append transaction to the list of executed transactions + executed_txs.push(tx.into_signed()); + } + + // check if we have a better block + if !is_better_payload(best_payload.as_deref(), total_fees) { + // can skip building the block + return Ok(BuildOutcome::Aborted { fees: total_fees, cached_reads }) + } + + let WithdrawalsOutcome { withdrawals_root, withdrawals } = + commit_withdrawals(&mut db, &chain_spec, attributes.timestamp, attributes.withdrawals)?; + + // merge all transitions into bundle state, this would apply the withdrawal balance changes + // and 4788 contract call + db.merge_transitions(BundleRetention::PlainState); + + let bundle = BundleStateWithReceipts::new( + db.take_bundle(), + Receipts::from_vec(vec![receipts]), + block_number, + ); + let receipts_root = bundle.receipts_root_slow(block_number).expect("Number is in range"); + let logs_bloom = bundle.block_logs_bloom(block_number).expect("Number is in range"); + + // calculate the state root + let state_root = state_provider.state_root(&bundle)?; + + // create the block header + let transactions_root = proofs::calculate_transaction_root(&executed_txs); + + // initialize empty blob sidecars at first. If cancun is active then this will + let mut blob_sidecars = Vec::new(); + let mut excess_blob_gas = None; + let mut blob_gas_used = None; + + // only determine cancun fields when active + if chain_spec.is_cancun_active_at_timestamp(attributes.timestamp) { + // grab the blob sidecars from the executed txs + blob_sidecars = pool.get_all_blobs_exact( + executed_txs.iter().filter(|tx| tx.is_eip4844()).map(|tx| tx.hash).collect(), + )?; + + excess_blob_gas = if chain_spec.is_cancun_active_at_timestamp(parent_block.timestamp) { + let parent_excess_blob_gas = parent_block.excess_blob_gas.unwrap_or_default(); + let parent_blob_gas_used = parent_block.blob_gas_used.unwrap_or_default(); + Some(calculate_excess_blob_gas(parent_excess_blob_gas, parent_blob_gas_used)) + } else { + // for the first post-fork block, both parent.blob_gas_used and + // parent.excess_blob_gas are evaluated as 0 + Some(calculate_excess_blob_gas(0, 0)) + }; + + blob_gas_used = Some(sum_blob_gas_used); + } + + let header = Header { + parent_hash: parent_block.hash, + ommers_hash: EMPTY_OMMER_ROOT_HASH, + beneficiary: initialized_block_env.coinbase, + state_root, + transactions_root, + receipts_root, + withdrawals_root, + logs_bloom, + timestamp: attributes.timestamp, + mix_hash: attributes.prev_randao, + nonce: BEACON_NONCE, + base_fee_per_gas: Some(base_fee), + number: parent_block.number + 1, + gas_limit: block_gas_limit, + difficulty: U256::ZERO, + gas_used: cumulative_gas_used, + extra_data, + parent_beacon_block_root: attributes.parent_beacon_block_root, + blob_gas_used, + excess_blob_gas, + }; + + // seal the block + let block = Block { header, body: executed_txs, ommers: vec![], withdrawals }; + + let sealed_block = block.seal_slow(); + debug!(target: "payload_builder", ?sealed_block, "sealed built block"); + + let mut payload = BuiltPayload::new(attributes.id, sealed_block, total_fees); + + // extend the payload with the blob sidecars from the executed txs + payload.extend_sidecars(blob_sidecars); + + Ok(BuildOutcome::Better { payload, cached_reads }) + } +} diff --git a/crates/payload/optimism/Cargo.toml b/crates/payload/optimism/Cargo.toml new file mode 100644 index 000000000000..388cb4c40821 --- /dev/null +++ b/crates/payload/optimism/Cargo.toml @@ -0,0 +1,35 @@ +[package] +name = "reth-optimism-payload-builder" +version.workspace = true +edition.workspace = true +rust-version.workspace = true +license.workspace = true +homepage.workspace = true +repository.workspace = true +description = "A payload builder for op-reth that builds optimistic payloads." + +[dependencies] +# reth +reth-primitives.workspace = true +reth-revm.workspace = true +reth-transaction-pool.workspace = true +reth-provider.workspace = true +reth-payload-builder.workspace = true +reth-basic-payload-builder.workspace = true + +# ethereum +revm.workspace = true + +# misc +tracing.workspace = true + +[features] +# This is a workaround for reth-cli crate to allow this as mandatory dependency without breaking the build even if unused. +# This makes managing features and testing workspace easier because clippy always builds all members if --workspace is provided +optimism = [ + "reth-primitives/optimism", + "reth-revm/optimism", + "reth-transaction-pool/optimism", + "reth-provider/optimism", + "reth-payload-builder/optimism", +] \ No newline at end of file diff --git a/crates/payload/optimism/src/lib.rs b/crates/payload/optimism/src/lib.rs new file mode 100644 index 000000000000..11b36e9876ea --- /dev/null +++ b/crates/payload/optimism/src/lib.rs @@ -0,0 +1,407 @@ +//! Optimism's payload builder implementation. + +#![doc( + html_logo_url = "https://raw.githubusercontent.com/paradigmxyz/reth/main/assets/reth-docs.png", + html_favicon_url = "https://avatars0.githubusercontent.com/u/97369466?s=256", + issue_tracker_base_url = "https://github.com/paradigmxyz/reth/issues/" +)] +#![warn(missing_debug_implementations, missing_docs, unreachable_pub, rustdoc::all)] +#![deny(unused_must_use, rust_2018_idioms)] +#![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))] + +#[cfg(feature = "optimism")] +pub use builder::*; + +#[cfg(feature = "optimism")] +mod builder { + use reth_basic_payload_builder::*; + use reth_payload_builder::{ + error::{OptimismPayloadBuilderError, PayloadBuilderError}, + BuiltPayload, + }; + use reth_primitives::{ + constants::BEACON_NONCE, + proofs, + revm::{compat::into_reth_log, env::tx_env_with_recovered}, + Block, Hardfork, Header, IntoRecoveredTransaction, Receipt, Receipts, + EMPTY_OMMER_ROOT_HASH, U256, + }; + use reth_provider::{BundleStateWithReceipts, StateProviderFactory}; + use reth_revm::database::StateProviderDatabase; + use reth_transaction_pool::TransactionPool; + use revm::{ + db::states::bundle_state::BundleRetention, + primitives::{EVMError, Env, InvalidTransaction, ResultAndState}, + DatabaseCommit, State, + }; + use std::sync::Arc; + use tracing::{debug, trace}; + + /// Optimism's payload builder + #[derive(Debug, Clone, Copy, PartialEq, Eq, Default)] + #[non_exhaustive] + pub struct OptimismPayloadBuilder { + /// The rollup's compute pending block configuration option. + // TODO(clabby): Implement this feature. + compute_pending_block: bool, + } + + impl OptimismPayloadBuilder { + /// Sets the rollup's compute pending block configuration option. + pub fn set_compute_pending_block(mut self, compute_pending_block: bool) -> Self { + self.compute_pending_block = compute_pending_block; + self + } + + /// Enables the rollup's compute pending block configuration option. + pub fn compute_pending_block(self) -> Self { + self.set_compute_pending_block(true) + } + + /// Returns the rollup's compute pending block configuration option. + pub fn is_compute_pending_block(&self) -> bool { + self.compute_pending_block + } + } + + /// Implementation of the [PayloadBuilder] trait for [OptimismPayloadBuilder]. + impl PayloadBuilder for OptimismPayloadBuilder + where + Client: StateProviderFactory, + Pool: TransactionPool, + { + fn try_build( + &self, + args: BuildArguments, + ) -> Result { + optimism_payload_builder(args, self.compute_pending_block) + } + + fn on_missing_payload( + &self, + args: BuildArguments, + ) -> Option> { + // In Optimism, the PayloadAttributes can specify a `no_tx_pool` option that implies we + // should not pull transactions from the tx pool. In this case, we build the payload + // upfront with the list of transactions sent in the attributes without caring about + // the results of the polling job, if a best payload has not already been built. + if args.config.attributes.optimism_payload_attributes.no_tx_pool { + if let Ok(BuildOutcome::Better { payload, .. }) = self.try_build(args) { + trace!(target: "payload_builder", "[OPTIMISM] Forced best payload"); + let payload = Arc::new(payload); + return Some(payload) + } + } + + None + } + } + + /// Constructs an Ethereum transaction payload from the transactions sent through the + /// Payload attributes by the sequencer. If the `no_tx_pool` argument is passed in + /// the payload attributes, the transaction pool will be ignored and the only transactions + /// included in the payload will be those sent through the attributes. + /// + /// Given build arguments including an Ethereum client, transaction pool, + /// and configuration, this function creates a transaction payload. Returns + /// a result indicating success with the payload or an error in case of failure. + #[inline] + pub(crate) fn optimism_payload_builder( + args: BuildArguments, + _compute_pending_block: bool, + ) -> Result + where + Client: StateProviderFactory, + Pool: TransactionPool, + { + let BuildArguments { client, pool, mut cached_reads, config, cancel, best_payload } = args; + + let state_provider = client.state_by_block_hash(config.parent_block.hash)?; + let state = StateProviderDatabase::new(&state_provider); + let mut db = State::builder() + .with_database_ref(cached_reads.as_db(&state)) + .with_bundle_update() + .build(); + let extra_data = config.extra_data(); + let PayloadConfig { + initialized_block_env, + initialized_cfg, + parent_block, + attributes, + chain_spec, + .. + } = config; + + debug!(target: "payload_builder", id=%attributes.id, parent_hash = ?parent_block.hash, parent_number = parent_block.number, "building new payload"); + let mut cumulative_gas_used = 0; + let block_gas_limit: u64 = attributes + .optimism_payload_attributes + .gas_limit + .unwrap_or(initialized_block_env.gas_limit.try_into().unwrap_or(u64::MAX)); + let base_fee = initialized_block_env.basefee.to::(); + + let mut executed_txs = Vec::new(); + let mut best_txs = pool.best_transactions_with_base_fee(base_fee); + + let mut total_fees = U256::ZERO; + + let block_number = initialized_block_env.number.to::(); + + let is_regolith = + chain_spec.is_fork_active_at_timestamp(Hardfork::Regolith, attributes.timestamp); + + // Ensure that the create2deployer is force-deployed at the canyon transition. Optimism + // blocks will always have at least a single transaction in them (the L1 info transaction), + // so we can safely assume that this will always be triggered upon the transition and that + // the above check for empty blocks will never be hit on OP chains. + reth_revm::optimism::ensure_create2_deployer( + chain_spec.clone(), + attributes.timestamp, + &mut db, + ) + .map_err(|_| { + PayloadBuilderError::Optimism(OptimismPayloadBuilderError::ForceCreate2DeployerFail) + })?; + + let mut receipts = Vec::new(); + for sequencer_tx in attributes.optimism_payload_attributes.transactions { + // Check if the job was cancelled, if so we can exit early. + if cancel.is_cancelled() { + return Ok(BuildOutcome::Cancelled) + } + + // Convert the transaction to a [TransactionSignedEcRecovered]. This is + // purely for the purposes of utilizing the [tx_env_with_recovered] function. + // Deposit transactions do not have signatures, so if the tx is a deposit, this + // will just pull in its `from` address. + let sequencer_tx = sequencer_tx.clone().try_into_ecrecovered().map_err(|_| { + PayloadBuilderError::Optimism( + OptimismPayloadBuilderError::TransactionEcRecoverFailed, + ) + })?; + + // Cache the depositor account prior to the state transition for the deposit nonce. + // + // Note that this *only* needs to be done post-regolith hardfork, as deposit nonces + // were not introduced in Bedrock. In addition, regular transactions don't have deposit + // nonces, so we don't need to touch the DB for those. + let depositor = (is_regolith && sequencer_tx.is_deposit()) + .then(|| { + db.load_cache_account(sequencer_tx.signer()) + .map(|acc| acc.account_info().unwrap_or_default()) + }) + .transpose() + .map_err(|_| { + PayloadBuilderError::Optimism(OptimismPayloadBuilderError::AccountLoadFailed( + sequencer_tx.signer(), + )) + })?; + + // Configure the environment for the block. + let env = Env { + cfg: initialized_cfg.clone(), + block: initialized_block_env.clone(), + tx: tx_env_with_recovered(&sequencer_tx), + }; + + let mut evm = revm::EVM::with_env(env); + evm.database(&mut db); + + let ResultAndState { result, state } = match evm.transact() { + Ok(res) => res, + Err(err) => { + match err { + EVMError::Transaction(err) => { + trace!(target: "optimism_payload_builder", ?err, ?sequencer_tx, "Error in sequencer transaction, skipping."); + continue + } + err => { + // this is an error that we should treat as fatal for this attempt + return Err(PayloadBuilderError::EvmExecutionError(err)) + } + } + } + }; + + // commit changes + db.commit(state); + + let gas_used = result.gas_used(); + + // add gas used by the transaction to cumulative gas used, before creating the receipt + cumulative_gas_used += gas_used; + + // Push transaction changeset and calculate header bloom filter for receipt. + receipts.push(Some(Receipt { + tx_type: sequencer_tx.tx_type(), + success: result.is_success(), + cumulative_gas_used, + logs: result.logs().into_iter().map(into_reth_log).collect(), + deposit_nonce: depositor.map(|account| account.nonce), + // The deposit receipt version was introduced in Canyon to indicate an update to how + // receipt hashes should be computed when set. The state transition process + // ensures this is only set for post-Canyon deposit transactions. + deposit_receipt_version: chain_spec + .is_fork_active_at_timestamp(Hardfork::Canyon, attributes.timestamp) + .then_some(1), + })); + + // append transaction to the list of executed transactions + executed_txs.push(sequencer_tx.into_signed()); + } + + if !attributes.optimism_payload_attributes.no_tx_pool { + while let Some(pool_tx) = best_txs.next() { + // ensure we still have capacity for this transaction + if cumulative_gas_used + pool_tx.gas_limit() > block_gas_limit { + // we can't fit this transaction into the block, so we need to mark it as + // invalid which also removes all dependent transaction from + // the iterator before we can continue + best_txs.mark_invalid(&pool_tx); + continue + } + + // check if the job was cancelled, if so we can exit early + if cancel.is_cancelled() { + return Ok(BuildOutcome::Cancelled) + } + + // convert tx to a signed transaction + let tx = pool_tx.to_recovered_transaction(); + + // Configure the environment for the block. + let env = Env { + cfg: initialized_cfg.clone(), + block: initialized_block_env.clone(), + tx: tx_env_with_recovered(&tx), + }; + + let mut evm = revm::EVM::with_env(env); + evm.database(&mut db); + + let ResultAndState { result, state } = match evm.transact() { + Ok(res) => res, + Err(err) => { + match err { + EVMError::Transaction(err) => { + if matches!(err, InvalidTransaction::NonceTooLow { .. }) { + // if the nonce is too low, we can skip this transaction + trace!(target: "payload_builder", ?err, ?tx, "skipping nonce too low transaction"); + } else { + // if the transaction is invalid, we can skip it and all of its + // descendants + trace!(target: "payload_builder", ?err, ?tx, "skipping invalid transaction and its descendants"); + best_txs.mark_invalid(&pool_tx); + } + + continue + } + err => { + // this is an error that we should treat as fatal for this attempt + return Err(PayloadBuilderError::EvmExecutionError(err)) + } + } + } + }; + + // commit changes + db.commit(state); + + let gas_used = result.gas_used(); + + // add gas used by the transaction to cumulative gas used, before creating the + // receipt + cumulative_gas_used += gas_used; + + // Push transaction changeset and calculate header bloom filter for receipt. + receipts.push(Some(Receipt { + tx_type: tx.tx_type(), + success: result.is_success(), + cumulative_gas_used, + logs: result.logs().into_iter().map(into_reth_log).collect(), + deposit_nonce: None, + deposit_receipt_version: None, + })); + + // update add to total fees + let miner_fee = tx + .effective_tip_per_gas(Some(base_fee)) + .expect("fee is always valid; execution succeeded"); + total_fees += U256::from(miner_fee) * U256::from(gas_used); + + // append transaction to the list of executed transactions + executed_txs.push(tx.into_signed()); + } + } + + // check if we have a better block + if !is_better_payload(best_payload.as_deref(), total_fees) { + // can skip building the block + return Ok(BuildOutcome::Aborted { fees: total_fees, cached_reads }) + } + + let WithdrawalsOutcome { withdrawals_root, withdrawals } = + commit_withdrawals(&mut db, &chain_spec, attributes.timestamp, attributes.withdrawals)?; + + // merge all transitions into bundle state, this would apply the withdrawal balance changes + // and 4788 contract call + db.merge_transitions(BundleRetention::PlainState); + + let bundle = BundleStateWithReceipts::new( + db.take_bundle(), + Receipts::from_vec(vec![receipts]), + block_number, + ); + let receipts_root = bundle + .receipts_root_slow(block_number, chain_spec.as_ref(), attributes.timestamp) + .expect("Number is in range"); + let logs_bloom = bundle.block_logs_bloom(block_number).expect("Number is in range"); + + // calculate the state root + let state_root = state_provider.state_root(&bundle)?; + + // create the block header + let transactions_root = proofs::calculate_transaction_root(&executed_txs); + + // Cancun is not yet active on Optimism chains. + let blob_sidecars = Vec::new(); + let excess_blob_gas = None; + let blob_gas_used = None; + + let header = Header { + parent_hash: parent_block.hash, + ommers_hash: EMPTY_OMMER_ROOT_HASH, + beneficiary: initialized_block_env.coinbase, + state_root, + transactions_root, + receipts_root, + withdrawals_root, + logs_bloom, + timestamp: attributes.timestamp, + mix_hash: attributes.prev_randao, + nonce: BEACON_NONCE, + base_fee_per_gas: Some(base_fee), + number: parent_block.number + 1, + gas_limit: block_gas_limit, + difficulty: U256::ZERO, + gas_used: cumulative_gas_used, + extra_data, + parent_beacon_block_root: attributes.parent_beacon_block_root, + blob_gas_used, + excess_blob_gas, + }; + + // seal the block + let block = Block { header, body: executed_txs, ommers: vec![], withdrawals }; + + let sealed_block = block.seal_slow(); + debug!(target: "payload_builder", ?sealed_block, "sealed built block"); + + let mut payload = BuiltPayload::new(attributes.id, sealed_block, total_fees); + + // extend the payload with the blob sidecars from the executed txs + payload.extend_sidecars(blob_sidecars); + + Ok(BuildOutcome::Better { payload, cached_reads }) + } +} From cd08ba825f63dd60ecb64aa72ae47074e87d24e6 Mon Sep 17 00:00:00 2001 From: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Date: Thu, 28 Dec 2023 21:03:04 +0100 Subject: [PATCH 218/277] fix: ::bytes re-export (#5885) --- Cargo.lock | 25 +++++++++++++++++++++++-- crates/primitives/src/lib.rs | 9 +++++---- 2 files changed, 28 insertions(+), 6 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index db68da191c04..5ab534d80ab7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -170,9 +170,9 @@ dependencies = [ [[package]] name = "alloy-primitives" -version = "0.5.3" +version = "0.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c0e5e60ff0e0c34c553822dabcfe0f5fece5a8c52f08a915be8c737de4b03fa" +checksum = "9c234f92024707f224510ff82419b2be0e1d8e1fd911defcac5a085cd7f83898" dependencies = [ "alloy-rlp", "arbitrary", @@ -185,6 +185,7 @@ dependencies = [ "getrandom 0.2.11", "hex-literal", "itoa", + "keccak-asm", "proptest", "proptest-derive", "rand 0.8.5", @@ -4005,6 +4006,16 @@ dependencies = [ "cpufeatures", ] +[[package]] +name = "keccak-asm" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb8515fff80ed850aea4a1595f2e519c003e2a00a82fe168ebf5269196caf444" +dependencies = [ + "digest 0.10.7", + "sha3-asm", +] + [[package]] name = "lazy_static" version = "1.4.0" @@ -7425,6 +7436,16 @@ dependencies = [ "keccak", ] +[[package]] +name = "sha3-asm" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bac61da6b35ad76b195eb4771210f947734321a8d81d7738e1580d953bc7a15e" +dependencies = [ + "cc", + "cfg-if", +] + [[package]] name = "sharded-slab" version = "0.1.7" diff --git a/crates/primitives/src/lib.rs b/crates/primitives/src/lib.rs index 37445e1dfb1b..988c82ecf410 100644 --- a/crates/primitives/src/lib.rs +++ b/crates/primitives/src/lib.rs @@ -50,7 +50,6 @@ pub use block::{ Block, BlockBody, BlockHashOrNumber, BlockId, BlockNumHash, BlockNumberOrTag, BlockWithSenders, ForkBlock, RpcBlockHash, SealedBlock, SealedBlockWithSenders, }; -pub use bytes::{self, Buf, BufMut, BytesMut}; pub use chain::{ AllGenesisFormats, BaseFeeParams, BaseFeeParamsKind, Chain, ChainInfo, ChainSpec, ChainSpecBuilder, DisplayHardforks, ForkBaseFeeParams, ForkCondition, ForkTimestamps, @@ -100,9 +99,11 @@ pub use withdrawal::Withdrawal; // Re-exports pub use self::ruint::UintTryTo; pub use alloy_primitives::{ - self, address, b256, bloom, bytes, eip191_hash_message, hex, hex_literal, keccak256, ruint, - Address, BlockHash, BlockNumber, Bloom, BloomInput, Bytes, ChainId, Selector, StorageKey, - StorageValue, TxHash, TxIndex, TxNumber, B128, B256, B512, B64, U128, U256, U64, U8, + self, address, b256, bloom, bytes, + bytes::{Buf, BufMut, BytesMut}, + eip191_hash_message, hex, hex_literal, keccak256, ruint, Address, BlockHash, BlockNumber, + Bloom, BloomInput, Bytes, ChainId, Selector, StorageKey, StorageValue, TxHash, TxIndex, + TxNumber, B128, B256, B512, B64, U128, U256, U64, U8, }; pub use reth_ethereum_forks::*; pub use revm_primitives::{self, JumpMap}; From 903f033513613216d6021c4d04d54e105f79926a Mon Sep 17 00:00:00 2001 From: Matthias Seitz Date: Fri, 29 Dec 2023 11:35:05 +0100 Subject: [PATCH 219/277] chore: ignore reth-ethereum-payload-builder in udeps (#5890) --- Cargo.toml | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 4c32de962819..2fe5902233d9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -142,7 +142,7 @@ reth-transaction-pool = { path = "crates/transaction-pool" } reth-trie = { path = "crates/trie" } # revm -revm = { git = "https://github.com/bluealloy/revm", branch = "reth_freeze", features = ["std", "secp256k1"], default-features = false } +revm = { git = "https://github.com/bluealloy/revm", branch = "reth_freeze", features = ["std", "secp256k1"], default-features = false } revm-primitives = { git = "https://github.com/bluealloy/revm", branch = "reth_freeze", features = ["std"], default-features = false } # eth @@ -227,3 +227,8 @@ pprof = "0.13" proptest = "1.4" proptest-derive = "0.4" serial_test = "2" + + +[workspace.metadata.cargo-udeps.ignore] +# ignored because this is mutually exclusive with the optimism payload builder via feature flags +normal = ["reth-ethereum-payload-builder"] \ No newline at end of file From f2bac81b92e7d5eb85d6899fa4a67c881e043d3c Mon Sep 17 00:00:00 2001 From: Thomas Coratger <60488569+tcoratger@users.noreply.github.com> Date: Fri, 29 Dec 2023 13:40:06 +0100 Subject: [PATCH 220/277] doc: improve documentation for trie crate (#5872) Co-authored-by: Roman Krasiuk --- crates/trie/src/node_iter.rs | 49 +++++++++++++++++-- .../trie/src/trie_cursor/database_cursors.rs | 9 ++++ crates/trie/src/trie_cursor/noop.rs | 16 ++++-- crates/trie/src/trie_cursor/subnode.rs | 7 +++ crates/trie/src/walker.rs | 1 + 5 files changed, 72 insertions(+), 10 deletions(-) diff --git a/crates/trie/src/node_iter.rs b/crates/trie/src/node_iter.rs index 0e1eb862c7fe..90e79cb82165 100644 --- a/crates/trie/src/node_iter.rs +++ b/crates/trie/src/node_iter.rs @@ -6,28 +6,39 @@ use crate::{ }; use reth_primitives::{trie::Nibbles, Account, StorageEntry, B256, U256}; +/// Represents a branch node in the trie. #[derive(Debug)] pub(crate) struct TrieBranchNode { + /// The key associated with the node. pub(crate) key: Nibbles, + /// The value associated with the node. pub(crate) value: B256, + /// Indicates whether children are in the trie. pub(crate) children_are_in_trie: bool, } impl TrieBranchNode { + /// Creates a new `TrieBranchNode`. pub(crate) fn new(key: Nibbles, value: B256, children_are_in_trie: bool) -> Self { Self { key, value, children_are_in_trie } } } +/// Represents a variant of an account node. #[derive(Debug)] pub(crate) enum AccountNode { + /// Branch node. Branch(TrieBranchNode), + /// Leaf node. Leaf(B256, Account), } +/// Represents a variant of a storage node. #[derive(Debug)] pub(crate) enum StorageNode { + /// Branch node. Branch(TrieBranchNode), + /// Leaf node. Leaf(B256, U256), } @@ -49,6 +60,7 @@ pub(crate) struct AccountNodeIter { } impl AccountNodeIter { + /// Creates a new `AccountNodeIter`. pub(crate) fn new(walker: TrieWalker, hashed_account_cursor: H) -> Self { Self { walker, @@ -59,6 +71,8 @@ impl AccountNodeIter { } } + /// Sets the last iterated account key and returns the modified `AccountNodeIter`. + /// This is used to resume iteration from the last checkpoint. pub(crate) fn with_last_account_key(mut self, previous_account_key: B256) -> Self { self.previous_account_key = Some(previous_account_key); self @@ -83,9 +97,12 @@ where /// NOTE: The iteration will start from the key of the previous hashed entry if it was supplied. pub(crate) fn try_next(&mut self) -> Result, StateRootError> { loop { + // If the walker has a key... if let Some(key) = self.walker.key() { + // Check if the current walker key is unchecked and there's no previous account key if !self.current_walker_key_checked && self.previous_account_key.is_none() { self.current_walker_key_checked = true; + // If it's possible to skip the current node in the walker, return a branch node if self.walker.can_skip_current_node { return Ok(Some(AccountNode::Branch(TrieBranchNode::new( key.clone(), @@ -96,22 +113,30 @@ where } } + // If there's a hashed address and account... if let Some((hashed_address, account)) = self.current_hashed_entry.take() { + // If the walker's key is less than the unpacked hashed address, reset the checked + // status and continue if self.walker.key().map_or(false, |key| key < &Nibbles::unpack(hashed_address)) { self.current_walker_key_checked = false; continue } + // Set the next hashed entry as a leaf node and return self.current_hashed_entry = self.hashed_account_cursor.next()?; return Ok(Some(AccountNode::Leaf(hashed_address, account))) } + // Handle seeking and advancing based on the previous account key match self.previous_account_key.take() { Some(account_key) => { + // Seek to the previous account key and get the next hashed entry self.hashed_account_cursor.seek(account_key)?; self.current_hashed_entry = self.hashed_account_cursor.next()?; } None => { + // Get the seek key and set the current hashed entry based on walker's next + // unprocessed key let seek_key = match self.walker.next_unprocessed_key() { Some(key) => key, None => break, // no more keys @@ -142,6 +167,7 @@ pub(crate) struct StorageNodeIter { } impl StorageNodeIter { + /// Creates a new instance of StorageNodeIter. pub(crate) fn new( walker: TrieWalker, hashed_storage_cursor: H, @@ -173,10 +199,14 @@ where /// 5. Repeat. pub(crate) fn try_next(&mut self) -> Result, StorageRootError> { loop { + // Check if there's a key in the walker. if let Some(key) = self.walker.key() { + // Check if the walker key hasn't been checked yet. if !self.current_walker_key_checked { self.current_walker_key_checked = true; + // Check if the current node can be skipped in the walker. if self.walker.can_skip_current_node { + // Return a branch node based on the walker's properties. return Ok(Some(StorageNode::Branch(TrieBranchNode::new( key.clone(), self.walker.hash().unwrap(), @@ -186,23 +216,32 @@ where } } + // Check for a current hashed storage entry. if let Some(StorageEntry { key: hashed_key, value }) = self.current_hashed_entry.take() { + // Compare keys and proceed accordingly. if self.walker.key().map_or(false, |key| key < &Nibbles::unpack(hashed_key)) { self.current_walker_key_checked = false; continue } + // Move to the next hashed storage entry and return the corresponding leaf node. self.current_hashed_entry = self.hashed_storage_cursor.next()?; return Ok(Some(StorageNode::Leaf(hashed_key, value))) } - let Some(seek_key) = self.walker.next_unprocessed_key() else { break }; - self.current_hashed_entry = - self.hashed_storage_cursor.seek(self.hashed_address, seek_key)?; - self.walker.advance()?; + // Attempt to get the next unprocessed key from the walker. + if let Some(seek_key) = self.walker.next_unprocessed_key() { + // Seek and update the current hashed entry based on the new seek key. + self.current_hashed_entry = + self.hashed_storage_cursor.seek(self.hashed_address, seek_key)?; + self.walker.advance()?; + } else { + // No more keys to process, break the loop. + break + } } - Ok(None) + Ok(None) // Return None if no more nodes are available. } } diff --git a/crates/trie/src/trie_cursor/database_cursors.rs b/crates/trie/src/trie_cursor/database_cursors.rs index 317285cd33db..ff0668cc9685 100644 --- a/crates/trie/src/trie_cursor/database_cursors.rs +++ b/crates/trie/src/trie_cursor/database_cursors.rs @@ -45,8 +45,10 @@ impl TrieCursor for DatabaseAccountTrieCursor where C: DbCursorRO, { + /// The type of key used by this cursor. type Key = StoredNibbles; + /// Seeks an exact match for the provided key in the account trie. fn seek_exact( &mut self, key: Self::Key, @@ -54,6 +56,7 @@ where Ok(self.0.seek_exact(key)?.map(|value| (value.0 .0.to_vec(), value.1 .0))) } + /// Seeks a key in the account trie that matches or is greater than the provided key. fn seek( &mut self, key: Self::Key, @@ -61,6 +64,7 @@ where Ok(self.0.seek(key)?.map(|value| (value.0 .0.to_vec(), value.1 .0))) } + /// Retrieves the current key in the cursor. fn current(&mut self) -> Result, DatabaseError> { Ok(self.0.current()?.map(|(k, _)| TrieKey::AccountNode(k))) } @@ -71,6 +75,7 @@ where pub struct DatabaseStorageTrieCursor { /// The underlying cursor. pub cursor: C, + /// Hashed address used for cursor positioning. hashed_address: B256, } @@ -85,8 +90,10 @@ impl TrieCursor for DatabaseStorageTrieCursor where C: DbDupCursorRO + DbCursorRO, { + /// Defines the type for keys used in the storage trie cursor. type Key = StoredNibblesSubKey; + /// Seeks an exact match for the given key in the storage trie. fn seek_exact( &mut self, key: Self::Key, @@ -98,6 +105,7 @@ where .map(|value| (value.nibbles.to_vec(), value.node))) } + /// Seeks the given key in the storage trie. fn seek( &mut self, key: Self::Key, @@ -108,6 +116,7 @@ where .map(|value| (value.nibbles.to_vec(), value.node))) } + /// Retrieves the current value in the storage trie cursor. fn current(&mut self) -> Result, DatabaseError> { Ok(self.cursor.current()?.map(|(k, v)| TrieKey::StorageNode(k, v.nibbles))) } diff --git a/crates/trie/src/trie_cursor/noop.rs b/crates/trie/src/trie_cursor/noop.rs index 32487c063857..363af8f2aa57 100644 --- a/crates/trie/src/trie_cursor/noop.rs +++ b/crates/trie/src/trie_cursor/noop.rs @@ -1,22 +1,22 @@ +use super::{TrieCursor, TrieCursorFactory}; +use crate::updates::TrieKey; use reth_db::DatabaseError; use reth_primitives::trie::{BranchNodeCompact, StoredNibbles, StoredNibblesSubKey}; -use crate::updates::TrieKey; - -use super::{TrieCursor, TrieCursorFactory}; - /// Noop trie cursor factory. #[derive(Default, Debug)] #[non_exhaustive] pub struct NoopTrieCursorFactory; impl TrieCursorFactory for NoopTrieCursorFactory { + /// Generates a Noop account trie cursor. fn account_trie_cursor( &self, ) -> Result + '_>, DatabaseError> { Ok(Box::::default()) } + /// Generates a Noop storage trie cursor. fn storage_tries_cursor( &self, _hashed_address: reth_primitives::B256, @@ -33,6 +33,7 @@ pub struct NoopAccountTrieCursor; impl TrieCursor for NoopAccountTrieCursor { type Key = StoredNibbles; + /// Seeks within the account trie. fn seek( &mut self, _key: Self::Key, @@ -40,6 +41,7 @@ impl TrieCursor for NoopAccountTrieCursor { Ok(None) } + /// Seeks an exact match within the account trie. fn seek_exact( &mut self, _key: Self::Key, @@ -47,12 +49,13 @@ impl TrieCursor for NoopAccountTrieCursor { Ok(None) } + /// Retrieves the current cursor position within the account trie. fn current(&mut self) -> Result, DatabaseError> { Ok(None) } } -/// Noop account trie cursor. +/// Noop storage trie cursor. #[derive(Default, Debug)] #[non_exhaustive] pub struct NoopStorageTrieCursor; @@ -60,6 +63,7 @@ pub struct NoopStorageTrieCursor; impl TrieCursor for NoopStorageTrieCursor { type Key = StoredNibblesSubKey; + /// Seeks a key in storage tries. fn seek( &mut self, _key: Self::Key, @@ -67,6 +71,7 @@ impl TrieCursor for NoopStorageTrieCursor { Ok(None) } + /// Seeks an exact match in storage tries. fn seek_exact( &mut self, _key: Self::Key, @@ -74,6 +79,7 @@ impl TrieCursor for NoopStorageTrieCursor { Ok(None) } + /// Retrieves the current cursor position within storage tries. fn current(&mut self) -> Result, DatabaseError> { Ok(None) } diff --git a/crates/trie/src/trie_cursor/subnode.rs b/crates/trie/src/trie_cursor/subnode.rs index 39afd975d7f3..e1aa3ce8fff5 100644 --- a/crates/trie/src/trie_cursor/subnode.rs +++ b/crates/trie/src/trie_cursor/subnode.rs @@ -35,7 +35,12 @@ impl std::fmt::Debug for CursorSubNode { } } +/// Implements conversion from `StoredSubNode` to `CursorSubNode`. impl From for CursorSubNode { + /// Converts a `StoredSubNode` into a `CursorSubNode`. + /// + /// Extracts necessary values from the `StoredSubNode` and constructs + /// a corresponding `CursorSubNode`. fn from(value: StoredSubNode) -> Self { let nibble = match value.nibble { Some(n) => n as i8, @@ -141,6 +146,7 @@ impl CursorSubNode { } } +/// Constructs a full key from the given `Nibbles` and `nibble`. #[inline] fn full_key(mut key: Nibbles, nibble: i8) -> Nibbles { if nibble >= 0 { @@ -149,6 +155,7 @@ fn full_key(mut key: Nibbles, nibble: i8) -> Nibbles { key } +/// Updates the key by replacing or appending a nibble based on the old and new nibble values. #[inline] fn update_full_key(key: &mut Nibbles, old_nibble: i8, new_nibble: i8) { if new_nibble >= 0 { diff --git a/crates/trie/src/walker.rs b/crates/trie/src/walker.rs index df0886140747..ee93063f0b70 100644 --- a/crates/trie/src/walker.rs +++ b/crates/trie/src/walker.rs @@ -233,6 +233,7 @@ impl TrieWalker { }) } + /// Updates the skip node flag based on the walker's current state. fn update_skip_node(&mut self) { self.can_skip_current_node = if let Some(node) = self.stack.last() { !self.changes.contains(node.full_key()) && node.hash_flag() From 21c7e77671c9a9df22444b76b4a2a8a05609881a Mon Sep 17 00:00:00 2001 From: Roman Krasiuk Date: Fri, 29 Dec 2023 15:13:43 +0100 Subject: [PATCH 221/277] refactor(tree): block buffer (#5879) --- crates/blockchain-tree/src/block_buffer.rs | 344 +++++++++--------- crates/blockchain-tree/src/blockchain_tree.rs | 36 +- crates/blockchain-tree/src/state.rs | 2 +- 3 files changed, 189 insertions(+), 193 deletions(-) diff --git a/crates/blockchain-tree/src/block_buffer.rs b/crates/blockchain-tree/src/block_buffer.rs index 1132791159c6..0c71cd570893 100644 --- a/crates/blockchain-tree/src/block_buffer.rs +++ b/crates/blockchain-tree/src/block_buffer.rs @@ -1,42 +1,38 @@ +use crate::metrics::BlockBufferMetrics; use lru::LruCache; -use reth_primitives::{BlockHash, BlockNumHash, BlockNumber, SealedBlockWithSenders}; +use reth_primitives::{BlockHash, BlockNumber, SealedBlockWithSenders}; use std::{ - collections::{btree_map::Entry, hash_map, BTreeMap, HashMap, HashSet}, + collections::{btree_map, hash_map, BTreeMap, HashMap, HashSet}, num::NonZeroUsize, }; -use crate::metrics::BlockBufferMetrics; -/// Type that contains blocks by number and hash. -pub type BufferedBlocks = BTreeMap>; - -/// Contains the Tree of pending blocks that are not executed but buffered -/// It allows us to store unconnected blocks for potential inclusion. +/// Contains the tree of pending blocks that cannot be executed due to missing parent. +/// It allows to store unconnected blocks for potential future inclusion. /// -/// It has three main functionality: +/// The buffer has three main functionalities: /// * [BlockBuffer::insert_block] for inserting blocks inside the buffer. -/// * [BlockBuffer::remove_with_children] for connecting blocks if the parent gets received and -/// inserted. -/// * [BlockBuffer::clean_old_blocks] to clear old blocks that are below finalized line. +/// * [BlockBuffer::remove_block_with_children] for connecting blocks if the parent gets received +/// and inserted. +/// * [BlockBuffer::remove_old_blocks] to remove old blocks that precede the finalized number. /// /// Note: Buffer is limited by number of blocks that it can contain and eviction of the block /// is done by last recently used block. #[derive(Debug)] pub struct BlockBuffer { - /// Blocks ordered by block number inside the BTreeMap. - /// - /// Note: BTreeMap is used so that we can remove the finalized old blocks - /// from the buffer - pub(crate) blocks: BufferedBlocks, - /// Needed for removal of the blocks. and to connect the potential unconnected block - /// to the connected one. - pub(crate) parent_to_child: HashMap>, - /// Helper map for fetching the block num from the block hash. - pub(crate) hash_to_num: HashMap, + /// All blocks in the buffer stored by their block hash. + pub(crate) blocks: HashMap, + /// Map of any parent block hash (even the ones not currently in the buffer) + /// to the buffered children. + /// Allows connecting buffered blocks by parent. + pub(crate) parent_to_child: HashMap>, + /// BTreeMap tracking the earliest blocks by block number. + /// Used for removal of old blocks that precede finalization. + pub(crate) earliest_blocks: BTreeMap>, /// LRU used for tracing oldest inserted blocks that are going to be /// first in line for evicting if `max_blocks` limit is hit. /// /// Used as counter of amount of blocks inside buffer. - pub(crate) lru: LruCache, + pub(crate) lru: LruCache, /// Various metrics for the block buffer. pub(crate) metrics: BlockBufferMetrics, } @@ -47,30 +43,47 @@ impl BlockBuffer { Self { blocks: Default::default(), parent_to_child: Default::default(), - hash_to_num: Default::default(), + earliest_blocks: Default::default(), lru: LruCache::new(NonZeroUsize::new(limit).unwrap()), metrics: Default::default(), } } + /// Return reference to buffered blocks + pub fn blocks(&self) -> &HashMap { + &self.blocks + } + + /// Return reference to the requested block. + pub fn block(&self, hash: &BlockHash) -> Option<&SealedBlockWithSenders> { + self.blocks.get(hash) + } + + /// Return a reference to the lowest ancestor of the given block in the buffer. + pub fn lowest_ancestor(&self, hash: &BlockHash) -> Option<&SealedBlockWithSenders> { + let mut current_block = self.blocks.get(hash)?; + while let Some(parent) = self.blocks.get(¤t_block.parent_hash) { + current_block = parent; + } + Some(current_block) + } + /// Insert a correct block inside the buffer. pub fn insert_block(&mut self, block: SealedBlockWithSenders) { - let num_hash = block.num_hash(); + let hash = block.hash; - self.parent_to_child.entry(block.parent_hash).or_default().insert(block.num_hash()); - self.hash_to_num.insert(block.hash, block.number); - self.blocks.entry(block.number).or_default().insert(block.hash, block); + self.parent_to_child.entry(block.parent_hash).or_default().insert(hash); + self.earliest_blocks.entry(block.number).or_default().insert(hash); + self.blocks.insert(hash, block); - if let Some((evicted_num_hash, _)) = - self.lru.push(num_hash, ()).filter(|(b, _)| *b != num_hash) - { + if let Some((evicted_hash, _)) = self.lru.push(hash, ()).filter(|(b, _)| *b != hash) { // evict the block if limit is hit - if let Some(evicted_block) = self.remove_from_blocks(&evicted_num_hash) { + if let Some(evicted_block) = self.remove_block(&evicted_hash) { // evict the block if limit is hit - self.remove_from_parent(evicted_block.parent_hash, &evicted_num_hash); + self.remove_from_parent(evicted_block.parent_hash, &evicted_hash); } } - self.metrics.blocks.set(self.len() as f64); + self.metrics.blocks.set(self.blocks.len() as f64); } /// Removes the given block from the buffer and also all the children of the block. @@ -79,134 +92,97 @@ impl BlockBuffer { /// /// Note: that order of returned blocks is important and the blocks with lower block number /// in the chain will come first so that they can be executed in the correct order. - pub fn remove_with_children(&mut self, parent: BlockNumHash) -> Vec { + pub fn remove_block_with_children( + &mut self, + parent_hash: &BlockHash, + ) -> Vec { // remove parent block if present - let mut taken = Vec::new(); - if let Some(block) = self.remove_from_blocks(&parent) { - taken.push(block); + let mut removed = Vec::new(); + if let Some(block) = self.remove_block(parent_hash) { + removed.push(block); } - taken.extend(self.remove_children(vec![parent])); - self.metrics.blocks.set(self.len() as f64); - taken + removed.extend(self.remove_children(vec![*parent_hash])); + self.metrics.blocks.set(self.blocks.len() as f64); + removed } - /// Clean up the old blocks from the buffer as blocks before finalization are not needed - /// anymore. We can discard them from the buffer. - pub fn clean_old_blocks(&mut self, finalized_number: BlockNumber) { - let mut remove_parent_children = Vec::new(); + /// Discard all blocks that precede finalized block number from the buffer. + pub fn remove_old_blocks(&mut self, finalized_number: BlockNumber) { + let mut block_hashes_to_remove = Vec::new(); // discard all blocks that are before the finalized number. - while let Some(entry) = self.blocks.first_entry() { + while let Some(entry) = self.earliest_blocks.first_entry() { if *entry.key() > finalized_number { break } - let blocks = entry.remove(); - remove_parent_children.extend( - blocks.into_iter().map(|(hash, block)| BlockNumHash::new(block.number, hash)), - ); + let block_hashes = entry.remove(); + block_hashes_to_remove.extend(block_hashes); } - // remove from lru - for block in remove_parent_children.iter() { - self.lru.pop(block); - } - - self.remove_children(remove_parent_children); - self.metrics.blocks.set(self.len() as f64); - } - - /// Return reference to buffered blocks - pub fn blocks(&self) -> &BufferedBlocks { - &self.blocks - } - - /// Return reference to the asked block. - pub fn block(&self, block: BlockNumHash) -> Option<&SealedBlockWithSenders> { - self.blocks.get(&block.number)?.get(&block.hash) - } - - /// Return reference to the asked block by hash. - pub fn block_by_hash(&self, hash: &BlockHash) -> Option<&SealedBlockWithSenders> { - let num = self.hash_to_num.get(hash)?; - self.blocks.get(num)?.get(hash) - } - /// Return a reference to the lowest ancestor of the given block in the buffer. - pub fn lowest_ancestor(&self, hash: &BlockHash) -> Option<&SealedBlockWithSenders> { - let mut current_block = self.block_by_hash(hash)?; - while let Some(block) = self - .blocks - .get(&(current_block.number - 1)) - .and_then(|blocks| blocks.get(¤t_block.parent_hash)) - { - current_block = block; + // remove from other collections. + for block_hash in &block_hashes_to_remove { + // It's fine to call + self.remove_block(block_hash); } - Some(current_block) - } - /// Return number of blocks inside buffer. - pub fn len(&self) -> usize { - self.lru.len() + self.remove_children(block_hashes_to_remove); + self.metrics.blocks.set(self.blocks.len() as f64); } - /// Return if buffer is empty. - pub fn is_empty(&self) -> bool { - self.lru.is_empty() - } - - /// Remove from the hash to num map. - fn remove_from_hash_to_num(&mut self, hash: &BlockHash) { - self.hash_to_num.remove(hash); - } - - /// Remove from parent child connection. Dont touch childrens. - fn remove_from_parent(&mut self, parent: BlockHash, block: &BlockNumHash) { - self.remove_from_hash_to_num(&parent); - - // remove from parent to child connection, but only for this block parent. - if let hash_map::Entry::Occupied(mut entry) = self.parent_to_child.entry(parent) { - entry.get_mut().remove(block); - // if set is empty remove block entry. + /// Remove block entry + fn remove_from_earliest_blocks(&mut self, number: BlockNumber, hash: &BlockHash) { + if let btree_map::Entry::Occupied(mut entry) = self.earliest_blocks.entry(number) { + entry.get_mut().remove(hash); if entry.get().is_empty() { entry.remove(); } - }; + } } - /// Remove block from `self.blocks`, This will also remove block from `self.lru`. - /// - /// Note: This function will not remove block from the `self.parent_to_child` connection. - fn remove_from_blocks(&mut self, block: &BlockNumHash) -> Option { - self.remove_from_hash_to_num(&block.hash); - - if let Entry::Occupied(mut entry) = self.blocks.entry(block.number) { - let ret = entry.get_mut().remove(&block.hash); + /// Remove from parent child connection. This method does not remove children. + fn remove_from_parent(&mut self, parent_hash: BlockHash, hash: &BlockHash) { + // remove from parent to child connection, but only for this block parent. + if let hash_map::Entry::Occupied(mut entry) = self.parent_to_child.entry(parent_hash) { + entry.get_mut().remove(hash); // if set is empty remove block entry. if entry.get().is_empty() { entry.remove(); } - self.lru.pop(block); - return ret }; - None + } + + /// Removes block from inner collections. + /// This method will only remove the block if it's present inside `self.blocks`. + /// The block might be missing from other collections, the method will only ensure that it has + /// been removed. + fn remove_block(&mut self, hash: &BlockHash) -> Option { + if let Some(block) = self.blocks.remove(hash) { + self.remove_from_earliest_blocks(block.number, hash); + self.remove_from_parent(block.parent_hash, hash); + self.lru.pop(hash); + Some(block) + } else { + None + } } /// Remove all children and their descendants for the given blocks and return them. - fn remove_children(&mut self, parent_blocks: Vec) -> Vec { + fn remove_children(&mut self, parent_hashes: Vec) -> Vec { // remove all parent child connection and all the child children blocks that are connected // to the discarded parent blocks. - let mut remove_parent_children = parent_blocks; + let mut remove_parent_children = parent_hashes; let mut removed_blocks = Vec::new(); - while let Some(parent_num_hash) = remove_parent_children.pop() { + while let Some(parent_hash) = remove_parent_children.pop() { // get this child blocks children and add them to the remove list. - if let Some(parent_childrens) = self.parent_to_child.remove(&parent_num_hash.hash) { + if let Some(parent_children) = self.parent_to_child.remove(&parent_hash) { // remove child from buffer - for child in parent_childrens.iter() { - if let Some(block) = self.remove_from_blocks(child) { + for child_hash in parent_children.iter() { + if let Some(block) = self.remove_block(child_hash) { removed_blocks.push(block); } } - remove_parent_children.extend(parent_childrens); + remove_parent_children.extend(parent_children); } } removed_blocks @@ -223,11 +199,41 @@ mod tests { use reth_primitives::{BlockHash, BlockNumHash, SealedBlockWithSenders}; use std::collections::HashMap; + /// Create random block with specified number and parent hash. fn create_block(rng: &mut R, number: u64, parent: BlockHash) -> SealedBlockWithSenders { let block = random_block(rng, number, Some(parent), None, None); block.seal_with_senders().unwrap() } + /// Assert that all buffer collections have the same data length. + fn assert_buffer_lengths(buffer: &BlockBuffer, expected: usize) { + assert_eq!(buffer.blocks.len(), expected); + assert_eq!(buffer.lru.len(), expected); + assert_eq!( + buffer.parent_to_child.iter().fold(0, |acc, (_, hashes)| acc + hashes.len()), + expected + ); + assert_eq!( + buffer.earliest_blocks.iter().fold(0, |acc, (_, hashes)| acc + hashes.len()), + expected + ); + } + + /// Assert that the block was removed from all buffer collections. + fn assert_block_removal(buffer: &BlockBuffer, block: &SealedBlockWithSenders) { + assert!(buffer.blocks.get(&block.hash).is_none()); + assert!(buffer + .parent_to_child + .get(&block.parent_hash) + .and_then(|p| p.get(&block.hash)) + .is_none()); + assert!(buffer + .earliest_blocks + .get(&block.number) + .and_then(|hashes| hashes.get(&block.hash)) + .is_none()); + } + #[test] fn simple_insertion() { let mut rng = generators::rng(); @@ -236,17 +242,16 @@ mod tests { let mut buffer = BlockBuffer::new(3); buffer.insert_block(block1.clone()); - assert_eq!(buffer.len(), 1); - assert_eq!(buffer.block(block1.num_hash()), Some(&block1)); - assert_eq!(buffer.block_by_hash(&block1.hash), Some(&block1)); + assert_buffer_lengths(&buffer, 1); + assert_eq!(buffer.block(&block1.hash), Some(&block1)); } #[test] - fn take_all_chain_of_childrens() { + fn take_entire_chain_of_children() { let mut rng = generators::rng(); - let main_parent = BlockNumHash::new(9, rng.gen()); - let block1 = create_block(&mut rng, 10, main_parent.hash); + let main_parent_hash = rng.gen(); + let block1 = create_block(&mut rng, 10, main_parent_hash); let block2 = create_block(&mut rng, 11, block1.hash); let block3 = create_block(&mut rng, 12, block2.hash); let parent4 = rng.gen(); @@ -259,24 +264,27 @@ mod tests { buffer.insert_block(block3.clone()); buffer.insert_block(block4.clone()); - assert_eq!(buffer.len(), 4); - assert_eq!(buffer.block_by_hash(&block4.hash), Some(&block4)); - assert_eq!(buffer.block_by_hash(&block2.hash), Some(&block2)); - assert_eq!(buffer.block_by_hash(&main_parent.hash), None); + assert_buffer_lengths(&buffer, 4); + assert_eq!(buffer.block(&block4.hash), Some(&block4)); + assert_eq!(buffer.block(&block2.hash), Some(&block2)); + assert_eq!(buffer.block(&main_parent_hash), None); assert_eq!(buffer.lowest_ancestor(&block4.hash), Some(&block4)); assert_eq!(buffer.lowest_ancestor(&block3.hash), Some(&block1)); assert_eq!(buffer.lowest_ancestor(&block1.hash), Some(&block1)); - assert_eq!(buffer.remove_with_children(main_parent), vec![block1, block2, block3]); - assert_eq!(buffer.len(), 1); + assert_eq!( + buffer.remove_block_with_children(&main_parent_hash), + vec![block1, block2, block3] + ); + assert_buffer_lengths(&buffer, 1); } #[test] - fn take_all_multi_level_childrens() { + fn take_all_multi_level_children() { let mut rng = generators::rng(); - let main_parent = BlockNumHash::new(9, rng.gen()); - let block1 = create_block(&mut rng, 10, main_parent.hash); + let main_parent_hash = rng.gen(); + let block1 = create_block(&mut rng, 10, main_parent_hash); let block2 = create_block(&mut rng, 11, block1.hash); let block3 = create_block(&mut rng, 11, block1.hash); let block4 = create_block(&mut rng, 12, block2.hash); @@ -288,10 +296,10 @@ mod tests { buffer.insert_block(block3.clone()); buffer.insert_block(block4.clone()); - assert_eq!(buffer.len(), 4); + assert_buffer_lengths(&buffer, 4); assert_eq!( buffer - .remove_with_children(main_parent) + .remove_block_with_children(&main_parent_hash) .into_iter() .map(|b| (b.hash, b)) .collect::>(), @@ -302,11 +310,11 @@ mod tests { (block4.hash, block4) ]) ); - assert_eq!(buffer.len(), 0); + assert_buffer_lengths(&buffer, 0); } #[test] - fn take_self_with_childs() { + fn take_block_with_children() { let mut rng = generators::rng(); let main_parent = BlockNumHash::new(9, rng.gen()); @@ -322,10 +330,10 @@ mod tests { buffer.insert_block(block3.clone()); buffer.insert_block(block4.clone()); - assert_eq!(buffer.len(), 4); + assert_buffer_lengths(&buffer, 4); assert_eq!( buffer - .remove_with_children(block1.num_hash()) + .remove_block_with_children(&block1.hash) .into_iter() .map(|b| (b.hash, b)) .collect::>(), @@ -336,11 +344,11 @@ mod tests { (block4.hash, block4) ]) ); - assert_eq!(buffer.len(), 0); + assert_buffer_lengths(&buffer, 0); } #[test] - fn clean_chain_of_children() { + fn remove_chain_of_children() { let mut rng = generators::rng(); let main_parent = BlockNumHash::new(9, rng.gen()); @@ -357,13 +365,13 @@ mod tests { buffer.insert_block(block3); buffer.insert_block(block4); - assert_eq!(buffer.len(), 4); - buffer.clean_old_blocks(block1.number); - assert_eq!(buffer.len(), 1); + assert_buffer_lengths(&buffer, 4); + buffer.remove_old_blocks(block1.number); + assert_buffer_lengths(&buffer, 1); } #[test] - fn clean_all_multi_level_childrens() { + fn remove_all_multi_level_children() { let mut rng = generators::rng(); let main_parent = BlockNumHash::new(9, rng.gen()); @@ -379,13 +387,13 @@ mod tests { buffer.insert_block(block3); buffer.insert_block(block4); - assert_eq!(buffer.len(), 4); - buffer.clean_old_blocks(block1.number); - assert_eq!(buffer.len(), 0); + assert_buffer_lengths(&buffer, 4); + buffer.remove_old_blocks(block1.number); + assert_buffer_lengths(&buffer, 0); } #[test] - fn clean_multi_chains() { + fn remove_multi_chains() { let mut rng = generators::rng(); let main_parent = BlockNumHash::new(9, rng.gen()); @@ -423,19 +431,9 @@ mod tests { assert_eq!(buffer.lowest_ancestor(&block1a.hash), Some(&block1a)); assert_eq!(buffer.lowest_ancestor(&block1.hash), Some(&block1)); - assert_eq!(buffer.len(), 7); - buffer.clean_old_blocks(10); - assert_eq!(buffer.len(), 2); - } - - fn assert_block_existance(buffer: &BlockBuffer, block: &SealedBlockWithSenders) { - assert!(buffer.blocks.get(&block.number).and_then(|t| t.get(&block.hash)).is_none()); - assert!(buffer - .parent_to_child - .get(&block.parent_hash) - .and_then(|p| p.get(&block.num_hash())) - .is_none()); - assert!(buffer.hash_to_num.get(&block.hash).is_none()); + assert_buffer_lengths(&buffer, 7); + buffer.remove_old_blocks(10); + assert_buffer_lengths(&buffer, 2); } #[test] @@ -465,14 +463,14 @@ mod tests { assert_eq!(buffer.lowest_ancestor(&block4.hash), Some(&block4)); // block1 gets evicted - assert_block_existance(&buffer, &block1); + assert_block_removal(&buffer, &block1); // check lowest ancestor results post eviction assert_eq!(buffer.lowest_ancestor(&block3.hash), Some(&block2)); assert_eq!(buffer.lowest_ancestor(&block2.hash), Some(&block2)); assert_eq!(buffer.lowest_ancestor(&block1.hash), None); - assert_eq!(buffer.len(), 3); + assert_buffer_lengths(&buffer, 3); } #[test] @@ -494,8 +492,8 @@ mod tests { buffer.insert_block(block4); // block3 gets evicted - assert_block_existance(&buffer, &block1); + assert_block_removal(&buffer, &block1); - assert_eq!(buffer.len(), 3); + assert_buffer_lengths(&buffer, 3); } } diff --git a/crates/blockchain-tree/src/blockchain_tree.rs b/crates/blockchain-tree/src/blockchain_tree.rs index d10e79e545eb..1a60342cdf75 100644 --- a/crates/blockchain-tree/src/blockchain_tree.rs +++ b/crates/blockchain-tree/src/blockchain_tree.rs @@ -172,7 +172,7 @@ impl BlockchainTree { } // check if block is disconnected - if let Some(block) = self.state.buffered_blocks.block(block) { + if let Some(block) = self.state.buffered_blocks.block(&block.hash) { return Ok(Some(BlockStatus::Disconnected { missing_ancestor: block.parent_num_hash(), })); @@ -328,7 +328,7 @@ impl BlockchainTree { } // if there is a parent inside the buffer, validate against it. - if let Some(buffered_parent) = self.state.buffered_blocks.block(parent) { + if let Some(buffered_parent) = self.state.buffered_blocks.block(&parent.hash) { self.externals .consensus .validate_header_against_parent(&block, buffered_parent) @@ -804,7 +804,7 @@ impl BlockchainTree { } } // clean block buffer. - self.state.buffered_blocks.clean_old_blocks(finalized_block); + self.state.buffered_blocks.remove_old_blocks(finalized_block); } /// Reads the last `N` canonical hashes from the database and updates the block indices of the @@ -890,7 +890,7 @@ impl BlockchainTree { trace!(target: "blockchain_tree", ?new_block, "try_connect_buffered_blocks"); // first remove all the children of the new block from the buffer - let include_blocks = self.state.buffered_blocks.remove_with_children(new_block); + let include_blocks = self.state.buffered_blocks.remove_block_with_children(&new_block.hash); // then try to reinsert them into the tree for block in include_blocks.into_iter() { // dont fail on error, just ignore the block. @@ -1282,7 +1282,6 @@ impl BlockchainTree { #[cfg(test)] mod tests { use super::*; - use crate::block_buffer::BufferedBlocks; use assert_matches::assert_matches; use linked_hash_set::LinkedHashSet; use reth_db::{tables, test_utils::TempDatabase, transaction::DbTxMut, DatabaseEnv}; @@ -1368,7 +1367,7 @@ mod tests { /// Pending blocks pending_blocks: Option<(BlockNumber, HashSet)>, /// Buffered blocks - buffered_blocks: Option, + buffered_blocks: Option>, } impl TreeTester { @@ -1376,10 +1375,12 @@ mod tests { self.chain_num = Some(chain_num); self } + fn with_block_to_chain(mut self, block_to_chain: HashMap) -> Self { self.block_to_chain = Some(block_to_chain); self } + fn with_fork_to_child( mut self, fork_to_child: HashMap>, @@ -1388,7 +1389,10 @@ mod tests { self } - fn with_buffered_blocks(mut self, buffered_blocks: BufferedBlocks) -> Self { + fn with_buffered_blocks( + mut self, + buffered_blocks: HashMap, + ) -> Self { self.buffered_blocks = Some(buffered_blocks); self } @@ -1659,10 +1663,7 @@ mod tests { // | TreeTester::default() - .with_buffered_blocks(BTreeMap::from([( - block2.number, - HashMap::from([(block2.hash(), block2.clone())]), - )])) + .with_buffered_blocks(HashMap::from([(block2.hash(), block2.clone())])) .assert(&tree); assert_eq!( @@ -1959,10 +1960,7 @@ mod tests { ); TreeTester::default() - .with_buffered_blocks(BTreeMap::from([( - block2b.number, - HashMap::from([(block2b.hash(), block2b.clone())]), - )])) + .with_buffered_blocks(HashMap::from([(block2b.hash(), block2b.clone())])) .assert(&tree); // update canonical block to b2, this would make b2a be removed @@ -1979,10 +1977,10 @@ mod tests { // | TreeTester::default() .with_chain_num(0) - .with_block_to_chain(HashMap::from([])) - .with_fork_to_child(HashMap::from([])) - .with_pending_blocks((block2.number + 1, HashSet::from([]))) - .with_buffered_blocks(BTreeMap::from([])) + .with_block_to_chain(HashMap::default()) + .with_fork_to_child(HashMap::default()) + .with_pending_blocks((block2.number + 1, HashSet::default())) + .with_buffered_blocks(HashMap::default()) .assert(&tree); } } diff --git a/crates/blockchain-tree/src/state.rs b/crates/blockchain-tree/src/state.rs index 8c4c58229414..f741df8ec20a 100644 --- a/crates/blockchain-tree/src/state.rs +++ b/crates/blockchain-tree/src/state.rs @@ -99,7 +99,7 @@ impl TreeState { /// Checks the block buffer for the given block. pub(crate) fn get_buffered_block(&self, hash: &BlockHash) -> Option<&SealedBlockWithSenders> { - self.buffered_blocks.block_by_hash(hash) + self.buffered_blocks.block(hash) } /// Gets the lowest ancestor for the given block in the block buffer. From 9821f77078309934761b94cb20a3c0e126dbd83b Mon Sep 17 00:00:00 2001 From: Dan Cline <6798349+Rjected@users.noreply.github.com> Date: Fri, 29 Dec 2023 12:01:39 -0500 Subject: [PATCH 222/277] chore: move reth commands to separate folder (#5886) --- bin/reth/src/chain/mod.rs | 7 ------- bin/reth/src/cli/ext.rs | 7 ++++--- bin/reth/src/cli/mod.rs | 13 ++++++------- bin/reth/src/cli/node_builder.rs | 2 +- bin/reth/src/{config.rs => commands/config_cmd.rs} | 0 bin/reth/src/{ => commands}/db/clear.rs | 0 bin/reth/src/{ => commands}/db/diff.rs | 0 bin/reth/src/{ => commands}/db/get.rs | 0 bin/reth/src/{ => commands}/db/list.rs | 0 bin/reth/src/{ => commands}/db/mod.rs | 0 bin/reth/src/{ => commands}/db/snapshots/bench.rs | 0 bin/reth/src/{ => commands}/db/snapshots/headers.rs | 0 bin/reth/src/{ => commands}/db/snapshots/mod.rs | 0 .../src/{ => commands}/db/snapshots/receipts.rs | 0 .../src/{ => commands}/db/snapshots/transactions.rs | 0 bin/reth/src/{ => commands}/db/tui.rs | 0 .../src/{ => commands}/debug_cmd/build_block.rs | 0 bin/reth/src/{ => commands}/debug_cmd/execution.rs | 2 +- .../{ => commands}/debug_cmd/in_memory_merkle.rs | 0 bin/reth/src/{ => commands}/debug_cmd/merkle.rs | 0 bin/reth/src/{ => commands}/debug_cmd/mod.rs | 0 bin/reth/src/{chain => commands}/import.rs | 4 +++- .../src/{chain/init.rs => commands/init_cmd.rs} | 2 ++ bin/reth/src/commands/mod.rs | 12 ++++++++++++ bin/reth/src/{ => commands}/node/cl_events.rs | 0 bin/reth/src/{ => commands}/node/events.rs | 4 ++-- bin/reth/src/{ => commands}/node/mod.rs | 0 bin/reth/src/{ => commands}/p2p/mod.rs | 0 bin/reth/src/{ => commands}/recover/mod.rs | 0 .../src/{ => commands}/recover/storage_tries.rs | 0 bin/reth/src/{ => commands}/stage/drop.rs | 0 bin/reth/src/{ => commands}/stage/dump/execution.rs | 0 .../{ => commands}/stage/dump/hashing_account.rs | 0 .../{ => commands}/stage/dump/hashing_storage.rs | 0 bin/reth/src/{ => commands}/stage/dump/merkle.rs | 0 bin/reth/src/{ => commands}/stage/dump/mod.rs | 0 bin/reth/src/{ => commands}/stage/mod.rs | 0 bin/reth/src/{ => commands}/stage/run.rs | 0 bin/reth/src/{ => commands}/stage/unwind.rs | 0 bin/reth/src/{ => commands}/test_vectors/mod.rs | 0 bin/reth/src/{ => commands}/test_vectors/tables.rs | 0 bin/reth/src/lib.rs | 10 +--------- crates/consensus/auto-seal/tests/it/auto_mine.rs | 2 +- 43 files changed, 33 insertions(+), 32 deletions(-) delete mode 100644 bin/reth/src/chain/mod.rs rename bin/reth/src/{config.rs => commands/config_cmd.rs} (100%) rename bin/reth/src/{ => commands}/db/clear.rs (100%) rename bin/reth/src/{ => commands}/db/diff.rs (100%) rename bin/reth/src/{ => commands}/db/get.rs (100%) rename bin/reth/src/{ => commands}/db/list.rs (100%) rename bin/reth/src/{ => commands}/db/mod.rs (100%) rename bin/reth/src/{ => commands}/db/snapshots/bench.rs (100%) rename bin/reth/src/{ => commands}/db/snapshots/headers.rs (100%) rename bin/reth/src/{ => commands}/db/snapshots/mod.rs (100%) rename bin/reth/src/{ => commands}/db/snapshots/receipts.rs (100%) rename bin/reth/src/{ => commands}/db/snapshots/transactions.rs (100%) rename bin/reth/src/{ => commands}/db/tui.rs (100%) rename bin/reth/src/{ => commands}/debug_cmd/build_block.rs (100%) rename bin/reth/src/{ => commands}/debug_cmd/execution.rs (99%) rename bin/reth/src/{ => commands}/debug_cmd/in_memory_merkle.rs (100%) rename bin/reth/src/{ => commands}/debug_cmd/merkle.rs (100%) rename bin/reth/src/{ => commands}/debug_cmd/mod.rs (100%) rename bin/reth/src/{chain => commands}/import.rs (98%) rename bin/reth/src/{chain/init.rs => commands/init_cmd.rs} (97%) create mode 100644 bin/reth/src/commands/mod.rs rename bin/reth/src/{ => commands}/node/cl_events.rs (100%) rename bin/reth/src/{ => commands}/node/events.rs (99%) rename bin/reth/src/{ => commands}/node/mod.rs (100%) rename bin/reth/src/{ => commands}/p2p/mod.rs (100%) rename bin/reth/src/{ => commands}/recover/mod.rs (100%) rename bin/reth/src/{ => commands}/recover/storage_tries.rs (100%) rename bin/reth/src/{ => commands}/stage/drop.rs (100%) rename bin/reth/src/{ => commands}/stage/dump/execution.rs (100%) rename bin/reth/src/{ => commands}/stage/dump/hashing_account.rs (100%) rename bin/reth/src/{ => commands}/stage/dump/hashing_storage.rs (100%) rename bin/reth/src/{ => commands}/stage/dump/merkle.rs (100%) rename bin/reth/src/{ => commands}/stage/dump/mod.rs (100%) rename bin/reth/src/{ => commands}/stage/mod.rs (100%) rename bin/reth/src/{ => commands}/stage/run.rs (100%) rename bin/reth/src/{ => commands}/stage/unwind.rs (100%) rename bin/reth/src/{ => commands}/test_vectors/mod.rs (100%) rename bin/reth/src/{ => commands}/test_vectors/tables.rs (100%) diff --git a/bin/reth/src/chain/mod.rs b/bin/reth/src/chain/mod.rs deleted file mode 100644 index 202f9392a6f4..000000000000 --- a/bin/reth/src/chain/mod.rs +++ /dev/null @@ -1,7 +0,0 @@ -//! Command line utilities for initializing a chain. - -mod import; -mod init; - -pub use import::ImportCommand; -pub use init::InitCommand; diff --git a/bin/reth/src/cli/ext.rs b/bin/reth/src/cli/ext.rs index d72db048b3f2..de1d0170b2ee 100644 --- a/bin/reth/src/cli/ext.rs +++ b/bin/reth/src/cli/ext.rs @@ -29,7 +29,7 @@ impl RethCliExt for () { } /// A trait that allows for extending and customizing parts of the node command -/// [NodeCommand](crate::node::NodeCommand). +/// [NodeCommand](crate::commands::node::NodeCommand). /// /// The functions are invoked during the initialization of the node command in the following order: /// @@ -177,7 +177,8 @@ pub trait RethNodeCommandExt: RethNodeCommandConfig + fmt::Debug + clap::Args {} // blanket impl for all types that implement the required traits. impl RethNodeCommandExt for T where T: RethNodeCommandConfig + fmt::Debug + clap::Args {} -/// The default configuration for the reth node command [Command](crate::node::NodeCommand). +/// The default configuration for the reth node command +/// [Command](crate::commands::node::NodeCommand). /// /// This is a convenience type for [NoArgs<()>]. #[derive(Debug, Clone, Copy, Default, Args)] @@ -200,7 +201,7 @@ impl RethCliExt for NoArgsCliExt { /// additional CLI arguments. /// /// Note: This type must be manually filled with a [RethNodeCommandConfig] manually before executing -/// the [NodeCommand](crate::node::NodeCommand). +/// the [NodeCommand](crate::commands::node::NodeCommand). #[derive(Debug, Clone, Copy, Default, Args)] pub struct NoArgs { #[clap(skip)] diff --git a/bin/reth/src/cli/mod.rs b/bin/reth/src/cli/mod.rs index 090172263def..5eb5788462ca 100644 --- a/bin/reth/src/cli/mod.rs +++ b/bin/reth/src/cli/mod.rs @@ -2,13 +2,12 @@ use crate::{ args::utils::{chain_help, genesis_value_parser, SUPPORTED_CHAINS}, - chain, cli::ext::RethCliExt, - db, debug_cmd, + commands::{ + config_cmd, db, debug_cmd, import, init_cmd, node, p2p, recover, stage, test_vectors, + }, dirs::{LogsDir, PlatformPath}, - node, p2p, recover, runner::CliRunner, - stage, test_vectors, version::{LONG_VERSION, SHORT_VERSION}, }; use clap::{value_parser, ArgAction, Args, Parser, Subcommand, ValueEnum}; @@ -140,10 +139,10 @@ pub enum Commands { Node(node::NodeCommand), /// Initialize the database from a genesis file. #[command(name = "init")] - Init(chain::InitCommand), + Init(init_cmd::InitCommand), /// This syncs RLP encoded blocks from a file. #[command(name = "import")] - Import(chain::ImportCommand), + Import(import::ImportCommand), /// Database debugging utilities #[command(name = "db")] Db(db::Command), @@ -158,7 +157,7 @@ pub enum Commands { TestVectors(test_vectors::Command), /// Write config to stdout #[command(name = "config")] - Config(crate::config::Command), + Config(config_cmd::Command), /// Various debug routines #[command(name = "debug")] Debug(debug_cmd::Command), diff --git a/bin/reth/src/cli/node_builder.rs b/bin/reth/src/cli/node_builder.rs index 5e0ea2d7457f..c8daa406479c 100644 --- a/bin/reth/src/cli/node_builder.rs +++ b/bin/reth/src/cli/node_builder.rs @@ -11,9 +11,9 @@ use crate::{ db_type::{DatabaseBuilder, DatabaseInstance}, ext::{RethCliExt, RethNodeCommandConfig}, }, + commands::node::{cl_events::ConsensusLayerHealthEvents, events}, dirs::{ChainPath, DataDirPath, MaybePlatformPath}, init::init_genesis, - node::{cl_events::ConsensusLayerHealthEvents, events}, prometheus_exporter, utils::{get_single_header, write_peers_to_file}, version::SHORT_VERSION, diff --git a/bin/reth/src/config.rs b/bin/reth/src/commands/config_cmd.rs similarity index 100% rename from bin/reth/src/config.rs rename to bin/reth/src/commands/config_cmd.rs diff --git a/bin/reth/src/db/clear.rs b/bin/reth/src/commands/db/clear.rs similarity index 100% rename from bin/reth/src/db/clear.rs rename to bin/reth/src/commands/db/clear.rs diff --git a/bin/reth/src/db/diff.rs b/bin/reth/src/commands/db/diff.rs similarity index 100% rename from bin/reth/src/db/diff.rs rename to bin/reth/src/commands/db/diff.rs diff --git a/bin/reth/src/db/get.rs b/bin/reth/src/commands/db/get.rs similarity index 100% rename from bin/reth/src/db/get.rs rename to bin/reth/src/commands/db/get.rs diff --git a/bin/reth/src/db/list.rs b/bin/reth/src/commands/db/list.rs similarity index 100% rename from bin/reth/src/db/list.rs rename to bin/reth/src/commands/db/list.rs diff --git a/bin/reth/src/db/mod.rs b/bin/reth/src/commands/db/mod.rs similarity index 100% rename from bin/reth/src/db/mod.rs rename to bin/reth/src/commands/db/mod.rs diff --git a/bin/reth/src/db/snapshots/bench.rs b/bin/reth/src/commands/db/snapshots/bench.rs similarity index 100% rename from bin/reth/src/db/snapshots/bench.rs rename to bin/reth/src/commands/db/snapshots/bench.rs diff --git a/bin/reth/src/db/snapshots/headers.rs b/bin/reth/src/commands/db/snapshots/headers.rs similarity index 100% rename from bin/reth/src/db/snapshots/headers.rs rename to bin/reth/src/commands/db/snapshots/headers.rs diff --git a/bin/reth/src/db/snapshots/mod.rs b/bin/reth/src/commands/db/snapshots/mod.rs similarity index 100% rename from bin/reth/src/db/snapshots/mod.rs rename to bin/reth/src/commands/db/snapshots/mod.rs diff --git a/bin/reth/src/db/snapshots/receipts.rs b/bin/reth/src/commands/db/snapshots/receipts.rs similarity index 100% rename from bin/reth/src/db/snapshots/receipts.rs rename to bin/reth/src/commands/db/snapshots/receipts.rs diff --git a/bin/reth/src/db/snapshots/transactions.rs b/bin/reth/src/commands/db/snapshots/transactions.rs similarity index 100% rename from bin/reth/src/db/snapshots/transactions.rs rename to bin/reth/src/commands/db/snapshots/transactions.rs diff --git a/bin/reth/src/db/tui.rs b/bin/reth/src/commands/db/tui.rs similarity index 100% rename from bin/reth/src/db/tui.rs rename to bin/reth/src/commands/db/tui.rs diff --git a/bin/reth/src/debug_cmd/build_block.rs b/bin/reth/src/commands/debug_cmd/build_block.rs similarity index 100% rename from bin/reth/src/debug_cmd/build_block.rs rename to bin/reth/src/commands/debug_cmd/build_block.rs diff --git a/bin/reth/src/debug_cmd/execution.rs b/bin/reth/src/commands/debug_cmd/execution.rs similarity index 99% rename from bin/reth/src/debug_cmd/execution.rs rename to bin/reth/src/commands/debug_cmd/execution.rs index 4a9ae96982c8..97ff323f6f42 100644 --- a/bin/reth/src/debug_cmd/execution.rs +++ b/bin/reth/src/commands/debug_cmd/execution.rs @@ -6,9 +6,9 @@ use crate::{ utils::{chain_help, genesis_value_parser, SUPPORTED_CHAINS}, DatabaseArgs, NetworkArgs, }, + commands::node::events, dirs::{DataDirPath, MaybePlatformPath}, init::init_genesis, - node::events, runner::CliContext, utils::get_single_header, }; diff --git a/bin/reth/src/debug_cmd/in_memory_merkle.rs b/bin/reth/src/commands/debug_cmd/in_memory_merkle.rs similarity index 100% rename from bin/reth/src/debug_cmd/in_memory_merkle.rs rename to bin/reth/src/commands/debug_cmd/in_memory_merkle.rs diff --git a/bin/reth/src/debug_cmd/merkle.rs b/bin/reth/src/commands/debug_cmd/merkle.rs similarity index 100% rename from bin/reth/src/debug_cmd/merkle.rs rename to bin/reth/src/commands/debug_cmd/merkle.rs diff --git a/bin/reth/src/debug_cmd/mod.rs b/bin/reth/src/commands/debug_cmd/mod.rs similarity index 100% rename from bin/reth/src/debug_cmd/mod.rs rename to bin/reth/src/commands/debug_cmd/mod.rs diff --git a/bin/reth/src/chain/import.rs b/bin/reth/src/commands/import.rs similarity index 98% rename from bin/reth/src/chain/import.rs rename to bin/reth/src/commands/import.rs index 602fd0f8ed60..f1e6741526da 100644 --- a/bin/reth/src/chain/import.rs +++ b/bin/reth/src/commands/import.rs @@ -1,11 +1,13 @@ +//! Command that initializes the node by importing a chain from a file. + use crate::{ args::{ utils::{chain_help, genesis_value_parser, SUPPORTED_CHAINS}, DatabaseArgs, }, + commands::node::events::{handle_events, NodeEvent}, dirs::{DataDirPath, MaybePlatformPath}, init::init_genesis, - node::events::{handle_events, NodeEvent}, version::SHORT_VERSION, }; use clap::Parser; diff --git a/bin/reth/src/chain/init.rs b/bin/reth/src/commands/init_cmd.rs similarity index 97% rename from bin/reth/src/chain/init.rs rename to bin/reth/src/commands/init_cmd.rs index c54b38cad0f0..3d972035894a 100644 --- a/bin/reth/src/chain/init.rs +++ b/bin/reth/src/commands/init_cmd.rs @@ -1,3 +1,5 @@ +//! Command that initializes the node from a genesis file. + use crate::{ args::{ utils::{chain_help, genesis_value_parser, SUPPORTED_CHAINS}, diff --git a/bin/reth/src/commands/mod.rs b/bin/reth/src/commands/mod.rs new file mode 100644 index 000000000000..f5584120a592 --- /dev/null +++ b/bin/reth/src/commands/mod.rs @@ -0,0 +1,12 @@ +//! This contains all of the `reth` commands + +pub mod config_cmd; +pub mod db; +pub mod debug_cmd; +pub mod import; +pub mod init_cmd; +pub mod node; +pub mod p2p; +pub mod recover; +pub mod stage; +pub mod test_vectors; diff --git a/bin/reth/src/node/cl_events.rs b/bin/reth/src/commands/node/cl_events.rs similarity index 100% rename from bin/reth/src/node/cl_events.rs rename to bin/reth/src/commands/node/cl_events.rs diff --git a/bin/reth/src/node/events.rs b/bin/reth/src/commands/node/events.rs similarity index 99% rename from bin/reth/src/node/events.rs rename to bin/reth/src/commands/node/events.rs index 0929c17071b2..841108312385 100644 --- a/bin/reth/src/node/events.rs +++ b/bin/reth/src/commands/node/events.rs @@ -1,6 +1,6 @@ //! Support for handling events emitted by node components. -use crate::node::cl_events::ConsensusLayerHealthEvent; +use crate::commands::node::cl_events::ConsensusLayerHealthEvent; use futures::Stream; use reth_beacon_consensus::BeaconConsensusEngineEvent; use reth_db::{database::Database, database_metrics::DatabaseMetadata}; @@ -466,7 +466,7 @@ impl Display for Eta { #[cfg(test)] mod tests { - use crate::node::events::Eta; + use crate::commands::node::events::Eta; use std::time::{Duration, Instant}; #[test] diff --git a/bin/reth/src/node/mod.rs b/bin/reth/src/commands/node/mod.rs similarity index 100% rename from bin/reth/src/node/mod.rs rename to bin/reth/src/commands/node/mod.rs diff --git a/bin/reth/src/p2p/mod.rs b/bin/reth/src/commands/p2p/mod.rs similarity index 100% rename from bin/reth/src/p2p/mod.rs rename to bin/reth/src/commands/p2p/mod.rs diff --git a/bin/reth/src/recover/mod.rs b/bin/reth/src/commands/recover/mod.rs similarity index 100% rename from bin/reth/src/recover/mod.rs rename to bin/reth/src/commands/recover/mod.rs diff --git a/bin/reth/src/recover/storage_tries.rs b/bin/reth/src/commands/recover/storage_tries.rs similarity index 100% rename from bin/reth/src/recover/storage_tries.rs rename to bin/reth/src/commands/recover/storage_tries.rs diff --git a/bin/reth/src/stage/drop.rs b/bin/reth/src/commands/stage/drop.rs similarity index 100% rename from bin/reth/src/stage/drop.rs rename to bin/reth/src/commands/stage/drop.rs diff --git a/bin/reth/src/stage/dump/execution.rs b/bin/reth/src/commands/stage/dump/execution.rs similarity index 100% rename from bin/reth/src/stage/dump/execution.rs rename to bin/reth/src/commands/stage/dump/execution.rs diff --git a/bin/reth/src/stage/dump/hashing_account.rs b/bin/reth/src/commands/stage/dump/hashing_account.rs similarity index 100% rename from bin/reth/src/stage/dump/hashing_account.rs rename to bin/reth/src/commands/stage/dump/hashing_account.rs diff --git a/bin/reth/src/stage/dump/hashing_storage.rs b/bin/reth/src/commands/stage/dump/hashing_storage.rs similarity index 100% rename from bin/reth/src/stage/dump/hashing_storage.rs rename to bin/reth/src/commands/stage/dump/hashing_storage.rs diff --git a/bin/reth/src/stage/dump/merkle.rs b/bin/reth/src/commands/stage/dump/merkle.rs similarity index 100% rename from bin/reth/src/stage/dump/merkle.rs rename to bin/reth/src/commands/stage/dump/merkle.rs diff --git a/bin/reth/src/stage/dump/mod.rs b/bin/reth/src/commands/stage/dump/mod.rs similarity index 100% rename from bin/reth/src/stage/dump/mod.rs rename to bin/reth/src/commands/stage/dump/mod.rs diff --git a/bin/reth/src/stage/mod.rs b/bin/reth/src/commands/stage/mod.rs similarity index 100% rename from bin/reth/src/stage/mod.rs rename to bin/reth/src/commands/stage/mod.rs diff --git a/bin/reth/src/stage/run.rs b/bin/reth/src/commands/stage/run.rs similarity index 100% rename from bin/reth/src/stage/run.rs rename to bin/reth/src/commands/stage/run.rs diff --git a/bin/reth/src/stage/unwind.rs b/bin/reth/src/commands/stage/unwind.rs similarity index 100% rename from bin/reth/src/stage/unwind.rs rename to bin/reth/src/commands/stage/unwind.rs diff --git a/bin/reth/src/test_vectors/mod.rs b/bin/reth/src/commands/test_vectors/mod.rs similarity index 100% rename from bin/reth/src/test_vectors/mod.rs rename to bin/reth/src/commands/test_vectors/mod.rs diff --git a/bin/reth/src/test_vectors/tables.rs b/bin/reth/src/commands/test_vectors/tables.rs similarity index 100% rename from bin/reth/src/test_vectors/tables.rs rename to bin/reth/src/commands/test_vectors/tables.rs diff --git a/bin/reth/src/lib.rs b/bin/reth/src/lib.rs index 973f988c0f9e..62ffb3a4af50 100644 --- a/bin/reth/src/lib.rs +++ b/bin/reth/src/lib.rs @@ -29,20 +29,12 @@ #![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))] pub mod args; -pub mod chain; pub mod cli; -pub mod config; -pub mod db; -pub mod debug_cmd; +pub mod commands; pub mod dirs; pub mod init; -pub mod node; -pub mod p2p; pub mod prometheus_exporter; -pub mod recover; pub mod runner; -pub mod stage; -pub mod test_vectors; pub mod utils; pub mod version; diff --git a/crates/consensus/auto-seal/tests/it/auto_mine.rs b/crates/consensus/auto-seal/tests/it/auto_mine.rs index ae3159bb358c..7417f830b9f9 100644 --- a/crates/consensus/auto-seal/tests/it/auto_mine.rs +++ b/crates/consensus/auto-seal/tests/it/auto_mine.rs @@ -7,7 +7,7 @@ use reth::{ components::RethNodeComponents, ext::{NoArgs, NoArgsCliExt, RethNodeCommandConfig}, }, - node::NodeCommand, + commands::node::NodeCommand, runner::CliRunner, tasks::TaskSpawner, }; From 5cc01b63a96f1b4d40933b0f1a2c7b4271cfd44e Mon Sep 17 00:00:00 2001 From: Matthias Seitz Date: Sat, 30 Dec 2023 10:36:20 +0100 Subject: [PATCH 223/277] chore: move builder to own module (#5899) --- bin/reth/src/{cli/node_builder.rs => builder/mod.rs} | 10 +++++----- bin/reth/src/cli/mod.rs | 1 - bin/reth/src/commands/node/mod.rs | 3 ++- bin/reth/src/lib.rs | 1 + 4 files changed, 8 insertions(+), 7 deletions(-) rename bin/reth/src/{cli/node_builder.rs => builder/mod.rs} (99%) diff --git a/bin/reth/src/cli/node_builder.rs b/bin/reth/src/builder/mod.rs similarity index 99% rename from bin/reth/src/cli/node_builder.rs rename to bin/reth/src/builder/mod.rs index c8daa406479c..a4de50a74d06 100644 --- a/bin/reth/src/cli/node_builder.rs +++ b/bin/reth/src/builder/mod.rs @@ -1,5 +1,5 @@ //! Support for customizing the node -use super::{components::RethRpcServerHandles, ext::DefaultRethNodeCommandConfig}; +use super::cli::{components::RethRpcServerHandles, ext::DefaultRethNodeCommandConfig}; use crate::{ args::{ get_secret_key, DatabaseArgs, DebugArgs, DevArgs, NetworkArgs, PayloadBuilderArgs, @@ -113,8 +113,8 @@ pub static PROMETHEUS_RECORDER_HANDLE: Lazy = /// ```rust /// # use reth_tasks::{TaskManager, TaskSpawner}; /// # use reth::{ +/// # builder::NodeConfig, /// # cli::{ -/// # node_builder::NodeConfig, /// # ext::DefaultRethNodeCommandConfig, /// # }, /// # args::RpcServerArgs, @@ -147,8 +147,8 @@ pub static PROMETHEUS_RECORDER_HANDLE: Lazy = /// ```rust /// # use reth_tasks::{TaskManager, TaskSpawner}; /// # use reth::{ +/// # builder::NodeConfig, /// # cli::{ -/// # node_builder::NodeConfig, /// # ext::DefaultRethNodeCommandConfig, /// # }, /// # args::RpcServerArgs, @@ -369,8 +369,8 @@ impl NodeConfig { /// # Example /// ```rust /// # use reth_tasks::{TaskManager, TaskSpawner}; + /// # use reth::builder::NodeConfig; /// # use reth::cli::{ - /// # node_builder::NodeConfig, /// # ext::DefaultRethNodeCommandConfig, /// # }; /// # use tokio::runtime::Handle; @@ -1348,7 +1348,7 @@ impl NodeHandle { /// # Example /// ``` /// # use reth::{ -/// # cli::node_builder::{NodeConfig, spawn_node}, +/// # builder::{NodeConfig, spawn_node}, /// # args::RpcServerArgs, /// # }; /// async fn t() { diff --git a/bin/reth/src/cli/mod.rs b/bin/reth/src/cli/mod.rs index 5eb5788462ca..8a0b18b1c03a 100644 --- a/bin/reth/src/cli/mod.rs +++ b/bin/reth/src/cli/mod.rs @@ -23,7 +23,6 @@ pub mod components; pub mod config; pub mod db_type; pub mod ext; -pub mod node_builder; /// Default [directives](Directive) for [EnvFilter] which disables high-frequency debug logs from /// `hyper` and `trust-dns` diff --git a/bin/reth/src/commands/node/mod.rs b/bin/reth/src/commands/node/mod.rs index 23cfc896a631..23840e55758c 100644 --- a/bin/reth/src/commands/node/mod.rs +++ b/bin/reth/src/commands/node/mod.rs @@ -8,7 +8,8 @@ use crate::{ DatabaseArgs, DebugArgs, DevArgs, NetworkArgs, PayloadBuilderArgs, PruningArgs, RpcServerArgs, TxPoolArgs, }, - cli::{db_type::DatabaseBuilder, ext::RethCliExt, node_builder::NodeConfig}, + builder::NodeConfig, + cli::{db_type::DatabaseBuilder, ext::RethCliExt}, dirs::{DataDirPath, MaybePlatformPath}, runner::CliContext, }; diff --git a/bin/reth/src/lib.rs b/bin/reth/src/lib.rs index 62ffb3a4af50..3a6e0fce53da 100644 --- a/bin/reth/src/lib.rs +++ b/bin/reth/src/lib.rs @@ -29,6 +29,7 @@ #![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))] pub mod args; +pub mod builder; pub mod cli; pub mod commands; pub mod dirs; From 92ecc2f83568592e44b5101b4fddacc5ae9f21c6 Mon Sep 17 00:00:00 2001 From: Matthias Seitz Date: Sat, 30 Dec 2023 11:39:45 +0100 Subject: [PATCH 224/277] chore: move LogArgs to own mod (#5901) --- bin/reth/src/args/log_args.rs | 134 ++++++++++++++++++++++++++++++ bin/reth/src/args/mod.rs | 4 + bin/reth/src/cli/mod.rs | 148 ++++------------------------------ 3 files changed, 152 insertions(+), 134 deletions(-) create mode 100644 bin/reth/src/args/log_args.rs diff --git a/bin/reth/src/args/log_args.rs b/bin/reth/src/args/log_args.rs new file mode 100644 index 000000000000..6c4b736b44e6 --- /dev/null +++ b/bin/reth/src/args/log_args.rs @@ -0,0 +1,134 @@ +//! clap [Args](clap::Args) for logging configuration. + +use crate::dirs::{LogsDir, PlatformPath}; +use clap::{Args, ValueEnum}; +use reth_tracing::{ + tracing_subscriber::{registry::LookupSpan, EnvFilter}, + BoxedLayer, FileWorkerGuard, +}; +use std::{fmt, fmt::Display}; +use tracing::Subscriber; + +/// Default [directives](reth_tracing::tracing_subscriber::filter::Directive) for [EnvFilter] which +/// disables high-frequency debug logs from `hyper` and `trust-dns` +const DEFAULT_ENV_FILTER_DIRECTIVES: [&str; 3] = + ["hyper::proto::h1=off", "trust_dns_proto=off", "atrust_dns_resolver=off"]; + +/// Constant to convert megabytes to bytes +const MB_TO_BYTES: u64 = 1024 * 1024; + +/// The log configuration. +#[derive(Debug, Args)] +#[command(next_help_heading = "Logging")] +pub struct LogArgs { + /// The path to put log files in. + #[arg(long = "log.file.directory", value_name = "PATH", global = true, default_value_t)] + pub log_file_directory: PlatformPath, + + /// The maximum size (in MB) of one log file. + #[arg(long = "log.file.max-size", value_name = "SIZE", global = true, default_value_t = 200)] + pub log_file_max_size: u64, + + /// The maximum amount of log files that will be stored. If set to 0, background file logging + /// is disabled. + #[arg(long = "log.file.max-files", value_name = "COUNT", global = true, default_value_t = 5)] + pub log_file_max_files: usize, + + /// The filter to use for logs written to the log file. + #[arg(long = "log.file.filter", value_name = "FILTER", global = true, default_value = "debug")] + pub log_file_filter: String, + + /// Write logs to journald. + #[arg(long = "log.journald", global = true)] + pub journald: bool, + + /// The filter to use for logs written to journald. + #[arg( + long = "log.journald.filter", + value_name = "FILTER", + global = true, + default_value = "error" + )] + pub journald_filter: String, + + /// Sets whether or not the formatter emits ANSI terminal escape codes for colors and other + /// text formatting. + #[arg( + long, + value_name = "COLOR", + global = true, + default_value_t = ColorMode::Always + )] + pub color: ColorMode, +} + +impl LogArgs { + /// Builds tracing layers from the current log options. + pub fn layers(&self) -> eyre::Result<(Vec>, Option)> + where + S: Subscriber, + for<'a> S: LookupSpan<'a>, + { + let mut layers = Vec::new(); + + // Function to create a new EnvFilter with environment (from `RUST_LOG` env var), default + // (from `DEFAULT_DIRECTIVES`) and additional directives. + let create_env_filter = |additional_directives: &str| -> eyre::Result { + let env_filter = EnvFilter::builder().from_env_lossy(); + + DEFAULT_ENV_FILTER_DIRECTIVES + .into_iter() + .chain(additional_directives.split(',')) + .try_fold(env_filter, |env_filter, directive| { + Ok(env_filter.add_directive(directive.parse()?)) + }) + }; + + // Create and add the journald layer if enabled + if self.journald { + let journald_filter = create_env_filter(&self.journald_filter)?; + layers.push( + reth_tracing::journald(journald_filter).expect("Could not connect to journald"), + ); + } + + // Create and add the file logging layer if enabled + let file_guard = if self.log_file_max_files > 0 { + let file_filter = create_env_filter(&self.log_file_filter)?; + let (layer, guard) = reth_tracing::file( + file_filter, + &self.log_file_directory, + "reth.log", + self.log_file_max_size * MB_TO_BYTES, + self.log_file_max_files, + ); + layers.push(layer); + Some(guard) + } else { + None + }; + + Ok((layers, file_guard)) + } +} + +/// The color mode for the cli. +#[derive(Debug, Copy, Clone, ValueEnum, Eq, PartialEq)] +pub enum ColorMode { + /// Colors on + Always, + /// Colors on + Auto, + /// Colors off + Never, +} + +impl Display for ColorMode { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + ColorMode::Always => write!(f, "always"), + ColorMode::Auto => write!(f, "auto"), + ColorMode::Never => write!(f, "never"), + } + } +} diff --git a/bin/reth/src/args/mod.rs b/bin/reth/src/args/mod.rs index 0e41b9443675..abbaea400205 100644 --- a/bin/reth/src/args/mod.rs +++ b/bin/reth/src/args/mod.rs @@ -20,6 +20,10 @@ pub use debug_args::DebugArgs; mod database_args; pub use database_args::DatabaseArgs; +/// LogArgs struct for configuring the logger +mod log_args; +pub use log_args::{ColorMode, LogArgs}; + mod secret_key; pub use secret_key::{get_secret_key, SecretKeyError}; diff --git a/bin/reth/src/cli/mod.rs b/bin/reth/src/cli/mod.rs index 8a0b18b1c03a..cb631394bc4b 100644 --- a/bin/reth/src/cli/mod.rs +++ b/bin/reth/src/cli/mod.rs @@ -1,34 +1,31 @@ //! CLI definition and entrypoint to executable use crate::{ - args::utils::{chain_help, genesis_value_parser, SUPPORTED_CHAINS}, + args::{ + utils::{chain_help, genesis_value_parser, SUPPORTED_CHAINS}, + LogArgs, + }, cli::ext::RethCliExt, commands::{ config_cmd, db, debug_cmd, import, init_cmd, node, p2p, recover, stage, test_vectors, }, - dirs::{LogsDir, PlatformPath}, runner::CliRunner, version::{LONG_VERSION, SHORT_VERSION}, }; -use clap::{value_parser, ArgAction, Args, Parser, Subcommand, ValueEnum}; +use clap::{value_parser, ArgAction, Args, Parser, Subcommand}; use reth_primitives::ChainSpec; use reth_tracing::{ - tracing::{metadata::LevelFilter, Level, Subscriber}, - tracing_subscriber::{filter::Directive, registry::LookupSpan, EnvFilter}, - BoxedLayer, FileWorkerGuard, + tracing::{metadata::LevelFilter, Level}, + tracing_subscriber::filter::Directive, + FileWorkerGuard, }; -use std::{fmt, fmt::Display, sync::Arc}; +use std::sync::Arc; pub mod components; pub mod config; pub mod db_type; pub mod ext; -/// Default [directives](Directive) for [EnvFilter] which disables high-frequency debug logs from -/// `hyper` and `trust-dns` -const DEFAULT_ENV_FILTER_DIRECTIVES: [&str; 3] = - ["hyper::proto::h1=off", "trust_dns_proto=off", "atrust_dns_resolver=off"]; - /// The main reth cli interface. /// /// This is the entrypoint to the executable. @@ -69,7 +66,7 @@ pub struct Cli { instance: u16, #[clap(flatten)] - logs: Logs, + logs: LogArgs, #[clap(flatten)] verbosity: Verbosity, @@ -176,104 +173,6 @@ impl Commands { } } -/// The log configuration. -#[derive(Debug, Args)] -#[command(next_help_heading = "Logging")] -pub struct Logs { - /// The path to put log files in. - #[arg(long = "log.file.directory", value_name = "PATH", global = true, default_value_t)] - log_file_directory: PlatformPath, - - /// The maximum size (in MB) of one log file. - #[arg(long = "log.file.max-size", value_name = "SIZE", global = true, default_value_t = 200)] - log_file_max_size: u64, - - /// The maximum amount of log files that will be stored. If set to 0, background file logging - /// is disabled. - #[arg(long = "log.file.max-files", value_name = "COUNT", global = true, default_value_t = 5)] - log_file_max_files: usize, - - /// The filter to use for logs written to the log file. - #[arg(long = "log.file.filter", value_name = "FILTER", global = true, default_value = "debug")] - log_file_filter: String, - - /// Write logs to journald. - #[arg(long = "log.journald", global = true)] - journald: bool, - - /// The filter to use for logs written to journald. - #[arg( - long = "log.journald.filter", - value_name = "FILTER", - global = true, - default_value = "error" - )] - journald_filter: String, - - /// Sets whether or not the formatter emits ANSI terminal escape codes for colors and other - /// text formatting. - #[arg( - long, - value_name = "COLOR", - global = true, - default_value_t = ColorMode::Always - )] - color: ColorMode, -} - -/// Constant to convert megabytes to bytes -const MB_TO_BYTES: u64 = 1024 * 1024; - -impl Logs { - /// Builds tracing layers from the current log options. - pub fn layers(&self) -> eyre::Result<(Vec>, Option)> - where - S: Subscriber, - for<'a> S: LookupSpan<'a>, - { - let mut layers = Vec::new(); - - // Function to create a new EnvFilter with environment (from `RUST_LOG` env var), default - // (from `DEFAULT_DIRECTIVES`) and additional directives. - let create_env_filter = |additional_directives: &str| -> eyre::Result { - let env_filter = EnvFilter::builder().from_env_lossy(); - - DEFAULT_ENV_FILTER_DIRECTIVES - .into_iter() - .chain(additional_directives.split(',')) - .try_fold(env_filter, |env_filter, directive| { - Ok(env_filter.add_directive(directive.parse()?)) - }) - }; - - // Create and add the journald layer if enabled - if self.journald { - let journald_filter = create_env_filter(&self.journald_filter)?; - layers.push( - reth_tracing::journald(journald_filter).expect("Could not connect to journald"), - ); - } - - // Create and add the file logging layer if enabled - let file_guard = if self.log_file_max_files > 0 { - let file_filter = create_env_filter(&self.log_file_filter)?; - let (layer, guard) = reth_tracing::file( - file_filter, - &self.log_file_directory, - "reth.log", - self.log_file_max_size * MB_TO_BYTES, - self.log_file_max_files, - ); - layers.push(layer); - Some(guard) - } else { - None - }; - - Ok((layers, file_guard)) - } -} - /// The verbosity settings for the cli. #[derive(Debug, Copy, Clone, Args)] #[command(next_help_heading = "Display")] @@ -313,33 +212,14 @@ impl Verbosity { } } -/// The color mode for the cli. -#[derive(Debug, Copy, Clone, ValueEnum, Eq, PartialEq)] -pub enum ColorMode { - /// Colors on - Always, - /// Colors on - Auto, - /// Colors off - Never, -} - -impl Display for ColorMode { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self { - ColorMode::Always => write!(f, "always"), - ColorMode::Auto => write!(f, "auto"), - ColorMode::Never => write!(f, "never"), - } - } -} - #[cfg(test)] mod tests { - use super::*; - use crate::args::utils::SUPPORTED_CHAINS; use clap::CommandFactory; + use crate::args::{utils::SUPPORTED_CHAINS, ColorMode}; + + use super::*; + #[test] fn parse_color_mode() { let reth = Cli::<()>::try_parse_from(["reth", "node", "--color", "always"]).unwrap(); From 5b9817ae4445e49220ab31f12bc03603968ee85e Mon Sep 17 00:00:00 2001 From: Matthias Seitz Date: Sat, 30 Dec 2023 16:16:53 +0100 Subject: [PATCH 225/277] chore: rm redundant serde default (#5902) --- .../rpc/rpc-types/src/eth/engine/payload.rs | 33 +++++++++---------- 1 file changed, 16 insertions(+), 17 deletions(-) diff --git a/crates/rpc/rpc-types/src/eth/engine/payload.rs b/crates/rpc/rpc-types/src/eth/engine/payload.rs index 3f0ff5c870a2..aa3ca4223dda 100644 --- a/crates/rpc/rpc-types/src/eth/engine/payload.rs +++ b/crates/rpc/rpc-types/src/eth/engine/payload.rs @@ -66,7 +66,7 @@ pub struct ExecutionPayloadInputV2 { #[serde(flatten)] pub execution_payload: ExecutionPayloadV1, /// The payload withdrawals - #[serde(default, skip_serializing_if = "Option::is_none")] + #[serde(skip_serializing_if = "Option::is_none")] pub withdrawals: Option>, } @@ -220,12 +220,6 @@ impl ssz::Encode for ExecutionPayloadV2 { false } - fn ssz_bytes_len(&self) -> usize { - ::ssz_bytes_len(&self.payload_inner) + - ssz::BYTES_PER_LENGTH_OFFSET + - self.withdrawals.ssz_bytes_len() - } - fn ssz_append(&self, buf: &mut Vec) { let offset = ::ssz_fixed_len() * 5 +
::ssz_fixed_len() + @@ -254,6 +248,12 @@ impl ssz::Encode for ExecutionPayloadV2 { encoder.finalize(); } + + fn ssz_bytes_len(&self) -> usize { + ::ssz_bytes_len(&self.payload_inner) + + ssz::BYTES_PER_LENGTH_OFFSET + + self.withdrawals.ssz_bytes_len() + } } /// This structure maps on the ExecutionPayloadV3 structure of the beacon chain spec. @@ -349,11 +349,6 @@ impl ssz::Encode for ExecutionPayloadV3 { false } - fn ssz_bytes_len(&self) -> usize { - ::ssz_bytes_len(&self.payload_inner) + - ::ssz_fixed_len() * 2 - } - fn ssz_append(&self, buf: &mut Vec) { let offset = ::ssz_fixed_len() * 5 +
::ssz_fixed_len() + @@ -384,6 +379,11 @@ impl ssz::Encode for ExecutionPayloadV3 { encoder.finalize(); } + + fn ssz_bytes_len(&self) -> usize { + ::ssz_bytes_len(&self.payload_inner) + + ::ssz_fixed_len() * 2 + } } /// This includes all bundled blob related data of an executed payload. @@ -616,12 +616,12 @@ pub struct PayloadAttributes { pub suggested_fee_recipient: Address, /// Array of [`Withdrawal`] enabled with V2 /// See - #[serde(default, skip_serializing_if = "Option::is_none")] + #[serde(skip_serializing_if = "Option::is_none")] pub withdrawals: Option>, /// Root of the parent beacon block enabled with V3. /// /// See also - #[serde(default, skip_serializing_if = "Option::is_none")] + #[serde(skip_serializing_if = "Option::is_none")] pub parent_beacon_block_root: Option, /// Optimism Payload Attributes #[cfg(feature = "optimism")] @@ -635,15 +635,14 @@ pub struct PayloadAttributes { #[cfg(feature = "optimism")] pub struct OptimismPayloadAttributes { /// Transactions is a field for rollups: the transactions list is forced into the block - #[serde(default, skip_serializing_if = "Option::is_none")] + #[serde(skip_serializing_if = "Option::is_none")] pub transactions: Option>, /// If true, the no transactions are taken out of the tx-pool, only transactions from the above /// Transactions list will be included. - #[serde(default, skip_serializing_if = "Option::is_none")] + #[serde(skip_serializing_if = "Option::is_none")] pub no_tx_pool: Option, /// If set, this sets the exact gas limit the block produced with. #[serde( - default, skip_serializing_if = "Option::is_none", deserialize_with = "crate::serde_helpers::u64_hex_opt::deserialize" )] From 4567252a72beba50a6fef54a164722f70bbf6296 Mon Sep 17 00:00:00 2001 From: Matthias Seitz Date: Sun, 31 Dec 2023 04:07:18 +0100 Subject: [PATCH 226/277] feat: add TaskManager::current (#5900) --- crates/tasks/src/lib.rs | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/crates/tasks/src/lib.rs b/crates/tasks/src/lib.rs index 02fe964de1d2..fa8634073579 100644 --- a/crates/tasks/src/lib.rs +++ b/crates/tasks/src/lib.rs @@ -162,6 +162,16 @@ pub struct TaskManager { // === impl TaskManager === impl TaskManager { + /// Returns a a [TaskManager] over the currently running Runtime. + /// + /// # Panics + /// + /// This will panic if called outside the context of a Tokio runtime. + pub fn current() -> Self { + let handle = Handle::current(); + Self::new(handle) + } + /// Create a new instance connected to the given handle's tokio runtime. pub fn new(handle: Handle) -> Self { let (panicked_tasks_tx, panicked_tasks_rx) = unbounded_channel(); From b318c57236311c761d537b00d092103c5c354b47 Mon Sep 17 00:00:00 2001 From: Siyuan Han <47173566+hsyodyssey@users.noreply.github.com> Date: Sun, 31 Dec 2023 13:54:34 +0800 Subject: [PATCH 227/277] feat(chain): enable Holesky testnet dns with test (#5898) --- crates/primitives/src/chain/mod.rs | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/crates/primitives/src/chain/mod.rs b/crates/primitives/src/chain/mod.rs index 14986532359a..f377674ea5a9 100644 --- a/crates/primitives/src/chain/mod.rs +++ b/crates/primitives/src/chain/mod.rs @@ -194,7 +194,7 @@ impl Chain { let named: NamedChain = self.try_into().ok()?; - if matches!(named, C::Mainnet | C::Goerli | C::Sepolia | C::Ropsten | C::Rinkeby) { + if matches!(named, C::Mainnet | C::Goerli | C::Sepolia | C::Holesky) { return Some(format!("{DNS_PREFIX}all.{}.ethdisco.net", named.as_ref().to_lowercase())) } None @@ -453,9 +453,16 @@ mod tests { } #[test] - fn test_dns_network() { + fn test_dns_main_network() { let s = "enrtree://AKA3AM6LPBYEUDMVNU3BSVQJ5AD45Y7YPOHJLEF6W26QOE4VTUDPE@all.mainnet.ethdisco.net"; let chain: Chain = NamedChain::Mainnet.into(); assert_eq!(s, chain.public_dns_network_protocol().unwrap().as_str()); } + + #[test] + fn test_dns_holesky_network() { + let s = "enrtree://AKA3AM6LPBYEUDMVNU3BSVQJ5AD45Y7YPOHJLEF6W26QOE4VTUDPE@all.holesky.ethdisco.net"; + let chain: Chain = NamedChain::Holesky.into(); + assert_eq!(s, chain.public_dns_network_protocol().unwrap().as_str()); + } } From 5bfd9a6c57aabfe660cb89be0bea63f70a78d937 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Sun, 31 Dec 2023 07:06:47 +0100 Subject: [PATCH 228/277] chore(deps): weekly `cargo update` (#5905) Co-authored-by: github-merge-queue --- Cargo.lock | 412 ++++++++++++++++++++++++++--------------------------- 1 file changed, 201 insertions(+), 211 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 5ab534d80ab7..546fadf361d8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -88,9 +88,9 @@ dependencies = [ [[package]] name = "ahash" -version = "0.8.6" +version = "0.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91429305e9f0a25f6205c5b8e0d2db09e0708a7a6df0f42212bb56c32c8ac97a" +checksum = "77c3a9648d43b9cd48db467b3f87fdd6e146bcc88ab0180006cef2179fe11d01" dependencies = [ "cfg-if", "getrandom 0.2.11", @@ -140,9 +140,9 @@ checksum = "0942ffc6dcaadf03badf6e6a2d0228460359d5e34b57ccdc720b7382dfbd5ec5" [[package]] name = "alloy-dyn-abi" -version = "0.5.3" +version = "0.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "74ab9cc043cd4b0a806f79e32624c148efecd9c9395e4a75000d51fdc9726be0" +checksum = "7cb9c4b008d0004a0518ba69f3cd9e94795f3c23b71a80e1ef3bf499f67fba23" dependencies = [ "alloy-json-abi", "alloy-primitives", @@ -158,9 +158,9 @@ dependencies = [ [[package]] name = "alloy-json-abi" -version = "0.5.3" +version = "0.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf8889c85658aae27e96515ff2c9200cb8d8c78baefad5aee088e49b47f5f6f3" +checksum = "838228983f74f30e4efbd8d42d25bfe1b5bf6450ca71ee9d7628f134fbe8ae8e" dependencies = [ "alloy-primitives", "alloy-sol-type-parser", @@ -196,32 +196,31 @@ dependencies = [ [[package]] name = "alloy-rlp" -version = "0.3.3" +version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc0fac0fc16baf1f63f78b47c3d24718f3619b0714076f6a02957d808d52cbef" +checksum = "8d58d9f5da7b40e9bfff0b7e7816700be4019db97d4b6359fe7f94a9e22e42ac" dependencies = [ "alloy-rlp-derive", "arrayvec", "bytes", - "smol_str", ] [[package]] name = "alloy-rlp-derive" -version = "0.3.3" +version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0391754c09fab4eae3404d19d0d297aa1c670c1775ab51d8a5312afeca23157" +checksum = "1a047897373be4bbb0224c1afdabca92648dc57a9c9ef6e7b0be3aff7a859c83" dependencies = [ "proc-macro2", "quote", - "syn 2.0.41", + "syn 2.0.43", ] [[package]] name = "alloy-sol-macro" -version = "0.5.3" +version = "0.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8c9d43ca0a56b356f35775deecc8f660ac99e34cbf4a33462d4bd8addd9ab6f" +checksum = "970e5cf1ca089e964d4f7f7afc7c9ad642bfb1bdc695a20b0cba3b3c28954774" dependencies = [ "const-hex", "dunce", @@ -230,25 +229,25 @@ dependencies = [ "proc-macro-error", "proc-macro2", "quote", - "syn 2.0.41", + "syn 2.0.43", "syn-solidity", "tiny-keccak", ] [[package]] name = "alloy-sol-type-parser" -version = "0.5.3" +version = "0.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41cdf1064e9b5160ae47b5190171a0655c8b4966b9657b04f48ff5d868684ade" +checksum = "c82c1ed2d61e982cef4c4d709f4aeef5f39a6a6a7c59b6e54c9ed4f3f7e3741b" dependencies = [ "winnow", ] [[package]] name = "alloy-sol-types" -version = "0.5.3" +version = "0.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c169266a4b5ecf6f471947be10690f0aa295063774853b50540708b267a96e51" +checksum = "2a059d4d2c78f8f21e470772c75f9abd9ac6d48c2aaf6b278d1ead06ed9ac664" dependencies = [ "alloy-primitives", "alloy-sol-macro", @@ -346,9 +345,9 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.75" +version = "1.0.78" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4668cab20f66d8d020e1fbc0ebe47217433c1b6c8f2040faf858554e394ace6" +checksum = "ca87830a3e3fb156dc96cfbd31cb620265dd053be734723f22b760d6cc3c3051" [[package]] name = "aquamarine" @@ -361,7 +360,7 @@ dependencies = [ "proc-macro-error", "proc-macro2", "quote", - "syn 2.0.41", + "syn 2.0.43", ] [[package]] @@ -578,18 +577,18 @@ checksum = "16e62a023e7c117e27523144c5d2459f4397fcc3cab0085af8e2224f643a0193" dependencies = [ "proc-macro2", "quote", - "syn 2.0.41", + "syn 2.0.43", ] [[package]] name = "async-trait" -version = "0.1.74" +version = "0.1.76" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a66537f1bb974b254c98ed142ff995236e81b9d0fe4db0575f46612cb15eb0f9" +checksum = "531b97fb4cd3dfdce92c35dedbfdc1f0b9d8091c8ca943d6dae340ef5012d514" dependencies = [ "proc-macro2", "quote", - "syn 2.0.41", + "syn 2.0.43", ] [[package]] @@ -770,7 +769,7 @@ dependencies = [ "regex", "rustc-hash", "shlex", - "syn 2.0.41", + "syn 2.0.43", "which", ] @@ -791,7 +790,7 @@ dependencies = [ "regex", "rustc-hash", "shlex", - "syn 2.0.41", + "syn 2.0.43", ] [[package]] @@ -833,9 +832,9 @@ dependencies = [ [[package]] name = "bitm" -version = "0.2.3" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7becd9fb525c1c507eb025ec37129a0d9320aee17c841085a48101f4f18c0d27" +checksum = "1c963a97cda30f51eb4f343d0f7924d491bdf30273d806ca1775239e37ab6bad" dependencies = [ "dyn_size_of", ] @@ -995,7 +994,7 @@ checksum = "005fa0c5bd20805466dda55eb34cd709bb31a2592bb26927b47714eeed6914d8" dependencies = [ "proc-macro2", "quote", - "syn 2.0.41", + "syn 2.0.43", "synstructure", ] @@ -1140,9 +1139,9 @@ dependencies = [ [[package]] name = "cargo-platform" -version = "0.1.5" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e34637b3140142bdf929fb439e8aa4ebad7651ebf7b1080b3930aa16ac1459ff" +checksum = "ceed8ef69d8518a5dda55c07425450b58a4e1946f4951eab6d7191ee86c2443d" dependencies = [ "serde", ] @@ -1272,9 +1271,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.4.11" +version = "4.4.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfaff671f6b22ca62406885ece523383b9b64022e341e53e009a62ebc47a45f2" +checksum = "dcfab8ba68f3668e89f6ff60f5b205cea56aa7b769451a59f34b8682f51c056d" dependencies = [ "clap_builder", "clap_derive", @@ -1282,9 +1281,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.4.11" +version = "4.4.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a216b506622bb1d316cd51328dce24e07bdff4a6128a47c7e7fad11878d5adbb" +checksum = "fb7fb5e4e979aec3be7791562fcba452f94ad85e954da024396433e0e25a79e9" dependencies = [ "anstream", "anstyle", @@ -1301,7 +1300,7 @@ dependencies = [ "heck", "proc-macro2", "quote", - "syn 2.0.41", + "syn 2.0.43", ] [[package]] @@ -1335,7 +1334,7 @@ dependencies = [ "proc-macro2", "quote", "serde", - "syn 2.0.41", + "syn 2.0.43", ] [[package]] @@ -1585,9 +1584,9 @@ checksum = "7059fff8937831a9ae6f0fe4d658ffabf58f2ca96aa9dec1c889f936f705f216" [[package]] name = "crossbeam-channel" -version = "0.5.9" +version = "0.5.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "14c3242926edf34aec4ac3a77108ad4854bffaa2e4ddc1824124ce59231302d5" +checksum = "82a9b73a36529d9c47029b9fb3a6f0ea3cc916a261195352ba19e770fc1748b2" dependencies = [ "cfg-if", "crossbeam-utils", @@ -1606,21 +1605,20 @@ dependencies = [ [[package]] name = "crossbeam-epoch" -version = "0.9.16" +version = "0.9.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d2fe95351b870527a5d09bf563ed3c97c0cffb87cf1c78a591bf48bb218d9aa" +checksum = "0e3681d554572a651dda4186cd47240627c3d0114d45a95f6ad27f2f22e7548d" dependencies = [ "autocfg", "cfg-if", "crossbeam-utils", - "memoffset", ] [[package]] name = "crossbeam-utils" -version = "0.8.17" +version = "0.8.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c06d96137f14f244c37f989d9fff8f95e6c18b918e71f36638f8c49112e4c78f" +checksum = "c3a430a770ebd84726f584a90ee7f020d28db52c6d02138900f22341f866d39c" dependencies = [ "cfg-if", ] @@ -1760,7 +1758,7 @@ checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3" dependencies = [ "proc-macro2", "quote", - "syn 2.0.41", + "syn 2.0.43", ] [[package]] @@ -1832,7 +1830,7 @@ dependencies = [ "proc-macro2", "quote", "strsim 0.10.0", - "syn 2.0.41", + "syn 2.0.43", ] [[package]] @@ -1865,7 +1863,7 @@ checksum = "836a9bbc7ad63342d6d6e7b815ccab164bc77a2d95d84bc3117a8c0d5c98e2d5" dependencies = [ "darling_core 0.20.3", "quote", - "syn 2.0.41", + "syn 2.0.43", ] [[package]] @@ -1924,9 +1922,9 @@ dependencies = [ [[package]] name = "deranged" -version = "0.3.10" +version = "0.3.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8eb30d70a07a3b04884d2677f06bec33509dc67ca60d92949e5535352d3191dc" +checksum = "b42b6fa04a440b495c8b04d0e71b707c585f83cb9cb28cf8cd0d976c315e31b4" dependencies = [ "powerfmt", "serde", @@ -1951,7 +1949,7 @@ checksum = "67e77553c4162a157adbf834ebae5b415acbecbeafc7a74b0e886657506a7611" dependencies = [ "proc-macro2", "quote", - "syn 2.0.41", + "syn 2.0.43", ] [[package]] @@ -2125,7 +2123,7 @@ checksum = "487585f4d0c6655fe74905e2504d8ad6908e4db67f744eb140876906c2f3175d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.41", + "syn 2.0.43", ] [[package]] @@ -2326,7 +2324,7 @@ dependencies = [ "heck", "proc-macro2", "quote", - "syn 2.0.41", + "syn 2.0.43", ] [[package]] @@ -2339,7 +2337,7 @@ dependencies = [ "num-traits", "proc-macro2", "quote", - "syn 2.0.41", + "syn 2.0.43", ] [[package]] @@ -2350,7 +2348,7 @@ checksum = "c2ad8cef1d801a4686bfd8919f0b30eac4c8e48968c437a6405ded4fb5272d2b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.41", + "syn 2.0.43", ] [[package]] @@ -2498,7 +2496,7 @@ dependencies = [ "regex", "serde", "serde_json", - "syn 2.0.41", + "syn 2.0.43", "toml 0.8.2", "walkdir", ] @@ -2516,7 +2514,7 @@ dependencies = [ "proc-macro2", "quote", "serde_json", - "syn 2.0.41", + "syn 2.0.43", ] [[package]] @@ -2542,7 +2540,7 @@ dependencies = [ "serde", "serde_json", "strum", - "syn 2.0.41", + "syn 2.0.43", "tempfile", "thiserror", "tiny-keccak", @@ -2802,9 +2800,9 @@ checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c" [[package]] name = "futures" -version = "0.3.29" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da0290714b38af9b4a7b094b8a37086d1b4e61f2df9122c3cad2577669145335" +checksum = "645c6916888f6cb6350d2550b80fb63e734897a8498abe35cfb732b6487804b0" dependencies = [ "futures-channel", "futures-core", @@ -2817,9 +2815,9 @@ dependencies = [ [[package]] name = "futures-channel" -version = "0.3.29" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff4dd66668b557604244583e3e1e1eada8c5c2e96a6d0d6653ede395b78bbacb" +checksum = "eac8f7d7865dcb88bd4373ab671c8cf4508703796caa2b1985a9ca867b3fcb78" dependencies = [ "futures-core", "futures-sink", @@ -2827,15 +2825,15 @@ dependencies = [ [[package]] name = "futures-core" -version = "0.3.29" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb1d22c66e66d9d72e1758f0bd7d4fd0bee04cad842ee34587d68c07e45d088c" +checksum = "dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d" [[package]] name = "futures-executor" -version = "0.3.29" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f4fb8693db0cf099eadcca0efe2a5a22e4550f98ed16aba6c48700da29597bc" +checksum = "a576fc72ae164fca6b9db127eaa9a9dda0d61316034f33a0a0d4eda41f02b01d" dependencies = [ "futures-core", "futures-task", @@ -2844,9 +2842,9 @@ dependencies = [ [[package]] name = "futures-io" -version = "0.3.29" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8bf34a163b5c4c52d0478a4d757da8fb65cabef42ba90515efee0f6f9fa45aaa" +checksum = "a44623e20b9681a318efdd71c299b6b222ed6f231972bfe2f224ebad6311f0c1" [[package]] name = "futures-lite" @@ -2875,26 +2873,26 @@ dependencies = [ [[package]] name = "futures-macro" -version = "0.3.29" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53b153fd91e4b0147f4aced87be237c98248656bb01050b96bf3ee89220a8ddb" +checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" dependencies = [ "proc-macro2", "quote", - "syn 2.0.41", + "syn 2.0.43", ] [[package]] name = "futures-sink" -version = "0.3.29" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e36d3378ee38c2a36ad710c5d30c2911d752cb941c00c72dbabfb786a7970817" +checksum = "9fb8e00e87438d937621c1c6269e53f536c14d3fbd6a042bb24879e57d474fb5" [[package]] name = "futures-task" -version = "0.3.29" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "efd193069b0ddadc69c46389b740bbccdd97203899b48d09c5f7969591d6bae2" +checksum = "38d84fa142264698cdce1a9f9172cf383a0c82de1bddcf3092901442c4097004" [[package]] name = "futures-timer" @@ -2908,9 +2906,9 @@ dependencies = [ [[package]] name = "futures-util" -version = "0.3.29" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a19526d624e703a3179b3d322efec918b6246ea0fa51d41124525f00f1cc8104" +checksum = "3d6401deb83407ab3da39eba7e33987a73c3df0c82b4bb5813ee871c19c41d48" dependencies = [ "futures-channel", "futures-core", @@ -3302,9 +3300,9 @@ dependencies = [ [[package]] name = "hyper" -version = "0.14.27" +version = "0.14.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ffb1cfd654a8219eaef89881fdb3bb3b1cdc5fa75ded05d6933b2b382e395468" +checksum = "bf96e135eb83a2a8ddf766e426a841d8ddd7449d5f00d34ea02b41d2f19eef80" dependencies = [ "bytes", "futures-channel", @@ -3317,7 +3315,7 @@ dependencies = [ "httpdate", "itoa", "pin-project-lite", - "socket2 0.4.10", + "socket2 0.5.5", "tokio", "tower-service", "tracing", @@ -3362,9 +3360,9 @@ checksum = "71a816c97c42258aa5834d07590b718b4c9a598944cd39a52dc25b351185d678" [[package]] name = "iana-time-zone" -version = "0.1.58" +version = "0.1.59" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8326b86b6cff230b97d0d312a6c40a60726df3332e721f72a1b035f451663b20" +checksum = "b6a67363e2aa4443928ce15e57ebae94fd8949958fd1223c4cfc0cd473ad7539" dependencies = [ "android_system_properties", "core-foundation-sys", @@ -3717,13 +3715,13 @@ dependencies = [ [[package]] name = "is-terminal" -version = "0.4.9" +version = "0.4.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb0889898416213fab133e1d33a0e5858a48177452750691bde3666d0fdbaf8b" +checksum = "0bad00257d07be169d870ab665980b06cdb366d792ad690bf2e76876dc503455" dependencies = [ "hermit-abi", "rustix", - "windows-sys 0.48.0", + "windows-sys 0.52.0", ] [[package]] @@ -4153,9 +4151,9 @@ checksum = "3ea9b256699eda7b0387ffbc776dd625e28bde3918446381781245b7a50349d8" [[package]] name = "mach2" -version = "0.4.1" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d0d1830bcd151a6fc4aea1369af235b36c1528fe976b8ff678683c9995eade8" +checksum = "19b955cdeb2a02b9117f121ce63aa52d08ade45de53e48fe6a38b39c10f6f709" dependencies = [ "libc", ] @@ -4199,9 +4197,9 @@ checksum = "2532096657941c2fea9c289d370a250971c689d4f143798ff67113ec042024a5" [[package]] name = "memchr" -version = "2.6.4" +version = "2.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f665ee40bc4a3c5590afb1e9677db74a508659dfd71e126420da8274909a0167" +checksum = "523dc4f511e55ab87b694dc30d0f820d60906ef06413f93d4d7a1385599cc149" [[package]] name = "memmap2" @@ -4214,30 +4212,31 @@ dependencies = [ [[package]] name = "memmap2" -version = "0.9.1" +version = "0.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f850157af41022bbb1b04ed15c011ce4d59520be82a4e3718b10c34b02cb85e" +checksum = "45fd3a57831bf88bc63f8cebc0cf956116276e97fef3966103e96416209f7c92" dependencies = [ "libc", ] [[package]] -name = "memoffset" -version = "0.9.0" +name = "metrics" +version = "0.21.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a634b1c61a95585bd15607c6ab0c4e5b226e695ff2800ba0cdccddf208c406c" +checksum = "fde3af1a009ed76a778cb84fdef9e7dbbdf5775ae3e4cc1f434a6a307f6f76c5" dependencies = [ - "autocfg", + "ahash", + "metrics-macros", + "portable-atomic", ] [[package]] name = "metrics" -version = "0.21.1" +version = "0.22.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fde3af1a009ed76a778cb84fdef9e7dbbdf5775ae3e4cc1f434a6a307f6f76c5" +checksum = "77b9e10a211c839210fd7f99954bda26e5f8e26ec686ad68da6a32df7c80e782" dependencies = [ "ahash", - "metrics-macros", "portable-atomic", ] @@ -4251,7 +4250,7 @@ dependencies = [ "hyper", "indexmap 1.9.3", "ipnet", - "metrics", + "metrics 0.21.1", "metrics-util", "quanta", "thiserror", @@ -4261,24 +4260,24 @@ dependencies = [ [[package]] name = "metrics-macros" -version = "0.7.0" +version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ddece26afd34c31585c74a4db0630c376df271c285d682d1e55012197830b6df" +checksum = "38b4faf00617defe497754acde3024865bc143d44a86799b24e191ecff91354f" dependencies = [ "proc-macro2", "quote", - "syn 2.0.41", + "syn 2.0.43", ] [[package]] name = "metrics-process" -version = "1.0.13" +version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2674a02f6ad51326c2106d9aa5a07d1f759695b655c06df0bba5d5fb338ac0a4" +checksum = "397ca18f9d62947522bd73ea09f437168f50da0ae22fb577d7f39bb2eee64907" dependencies = [ "libproc", "mach2", - "metrics", + "metrics 0.22.0", "once_cell", "procfs", "rlimit", @@ -4296,7 +4295,7 @@ dependencies = [ "crossbeam-utils", "hashbrown 0.13.2", "indexmap 1.9.3", - "metrics", + "metrics 0.21.1", "num_cpus", "ordered-float", "quanta", @@ -4592,7 +4591,7 @@ dependencies = [ "proc-macro-crate 1.3.1", "proc-macro2", "quote", - "syn 2.0.41", + "syn 2.0.43", ] [[package]] @@ -4604,7 +4603,7 @@ dependencies = [ "proc-macro-crate 2.0.1", "proc-macro2", "quote", - "syn 2.0.41", + "syn 2.0.43", ] [[package]] @@ -4632,9 +4631,9 @@ dependencies = [ [[package]] name = "object" -version = "0.32.1" +version = "0.32.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9cf5f9dd3933bd50a9e1f149ec995f39ae2c496d31fd772c1fd45ebc27e902b0" +checksum = "a6a622008b6e321afc04970976f62ee297fdbaa6f95318ca343e3eebb9648441" dependencies = [ "memchr", ] @@ -4877,9 +4876,9 @@ dependencies = [ [[package]] name = "ph" -version = "0.8.1" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "88c6e62e083483e2812a9d2a6eff6b97871302ef4166b5182c6da30624b7e991" +checksum = "c8267f3f34640e69a448dcf1ae8c76ae49a573cd9665b7f5d50b26ae169f361c" dependencies = [ "binout", "bitm", @@ -4928,7 +4927,7 @@ dependencies = [ "phf_shared", "proc-macro2", "quote", - "syn 2.0.41", + "syn 2.0.43", ] [[package]] @@ -4957,7 +4956,7 @@ checksum = "4359fd9c9171ec6e8c62926d6faaf553a8dc3f64e1507e76da7911b4f6a04405" dependencies = [ "proc-macro2", "quote", - "syn 2.0.41", + "syn 2.0.43", ] [[package]] @@ -4984,9 +4983,9 @@ dependencies = [ [[package]] name = "pkg-config" -version = "0.3.27" +version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26072860ba924cbfa98ea39c8c19b4dd6a4a25423dbdf219c1eca91aa0cf6964" +checksum = "69d3587f8a9e599cc7ec2c00e331f71c4e69a5f9a4b8a6efd5b07466b9736f9a" [[package]] name = "plain_hasher" @@ -4999,9 +4998,9 @@ dependencies = [ [[package]] name = "platforms" -version = "3.2.0" +version = "3.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "14e6ab3f592e6fb464fc9712d8d6e6912de6473954635fd76a589d832cffcbb0" +checksum = "626dec3cac7cc0e1577a2ec3fc496277ec2baa084bebad95bb6fdbfae235f84c" [[package]] name = "plotters" @@ -5148,7 +5147,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ae005bd773ab59b4725093fd7df83fd7892f7d8eafb48dbd7de6e024e4215f9d" dependencies = [ "proc-macro2", - "syn 2.0.41", + "syn 2.0.43", ] [[package]] @@ -5211,9 +5210,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.70" +version = "1.0.71" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "39278fbbf5fb4f646ce651690877f89d1c5811a3d4acb27700c1cb3cdb78fd3b" +checksum = "75cb1540fadbd5b8fbccc4dddad2734eba435053f725621c070711a14bb5f4b8" dependencies = [ "unicode-ident", ] @@ -5546,9 +5545,9 @@ dependencies = [ [[package]] name = "reqwest" -version = "0.11.22" +version = "0.11.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "046cd98826c46c2ac8ddecae268eb5c2e58628688a5fc7a2643704a73faba95b" +checksum = "37b1ae8d9ac08420c66222fb9096fc5de435c3c48542bc5336c51892cffafb41" dependencies = [ "base64 0.21.5", "bytes", @@ -5615,7 +5614,7 @@ dependencies = [ "itertools 0.12.0", "jemalloc-ctl", "jemallocator", - "metrics", + "metrics 0.21.1", "metrics-exporter-prometheus", "metrics-process", "metrics-util", @@ -5705,7 +5704,7 @@ dependencies = [ "alloy-rlp", "futures-core", "futures-util", - "metrics", + "metrics 0.21.1", "reth-interfaces", "reth-metrics", "reth-payload-builder", @@ -5726,7 +5725,7 @@ dependencies = [ "assert_matches", "cfg-if", "futures", - "metrics", + "metrics 0.21.1", "reth-blockchain-tree", "reth-consensus-common", "reth-db", @@ -5761,7 +5760,7 @@ dependencies = [ "assert_matches", "linked_hash_set", "lru 0.11.1", - "metrics", + "metrics 0.21.1", "parking_lot 0.12.1", "reth-db", "reth-interfaces", @@ -5832,7 +5831,7 @@ dependencies = [ "heapless", "iai", "itertools 0.12.0", - "metrics", + "metrics 0.21.1", "modular-bitfield", "page_size", "parity-scale-codec", @@ -5918,7 +5917,7 @@ dependencies = [ "futures", "futures-util", "itertools 0.12.0", - "metrics", + "metrics 0.21.1", "pin-project", "rayon", "reth-config", @@ -5979,7 +5978,7 @@ dependencies = [ "derive_more", "ethers-core", "futures", - "metrics", + "metrics 0.21.1", "pin-project", "proptest", "proptest-derive", @@ -6104,7 +6103,7 @@ name = "reth-metrics" version = "0.1.0-alpha.13" dependencies = [ "futures", - "metrics", + "metrics 0.21.1", "reth-metrics-derive", "tokio", "tokio-util", @@ -6114,13 +6113,13 @@ dependencies = [ name = "reth-metrics-derive" version = "0.1.0-alpha.13" dependencies = [ - "metrics", + "metrics 0.21.1", "once_cell", "proc-macro2", "quote", "regex", "serial_test", - "syn 2.0.41", + "syn 2.0.43", "trybuild", ] @@ -6166,7 +6165,7 @@ dependencies = [ "humantime-serde", "linked-hash-map", "linked_hash_set", - "metrics", + "metrics 0.21.1", "parking_lot 0.12.1", "pin-project", "pprof", @@ -6253,7 +6252,7 @@ version = "0.1.0-alpha.13" dependencies = [ "alloy-rlp", "futures-util", - "metrics", + "metrics 0.21.1", "reth-interfaces", "reth-metrics", "reth-primitives", @@ -6338,7 +6337,7 @@ dependencies = [ "auto_impl", "dashmap", "itertools 0.12.0", - "metrics", + "metrics 0.21.1", "parking_lot 0.12.1", "pin-project", "rand 0.8.5", @@ -6363,7 +6362,7 @@ version = "0.1.0-alpha.13" dependencies = [ "assert_matches", "itertools 0.12.0", - "metrics", + "metrics 0.21.1", "rayon", "reth-config", "reth-db", @@ -6429,7 +6428,7 @@ dependencies = [ "jsonrpsee", "jsonwebtoken", "lazy_static", - "metrics", + "metrics 0.21.1", "pin-project", "rand 0.8.5", "rayon", @@ -6494,7 +6493,7 @@ version = "0.1.0-alpha.13" dependencies = [ "hyper", "jsonrpsee", - "metrics", + "metrics 0.21.1", "reth-beacon-consensus", "reth-interfaces", "reth-ipc", @@ -6530,7 +6529,7 @@ dependencies = [ "async-trait", "jsonrpsee-core", "jsonrpsee-types", - "metrics", + "metrics 0.21.1", "reth-beacon-consensus", "reth-interfaces", "reth-metrics", @@ -6610,7 +6609,7 @@ dependencies = [ "criterion", "futures-util", "itertools 0.12.0", - "metrics", + "metrics 0.21.1", "num-traits", "paste", "pin-project", @@ -6644,7 +6643,7 @@ version = "0.1.0-alpha.13" dependencies = [ "dyn-clone", "futures-util", - "metrics", + "metrics 0.21.1", "reth-metrics", "thiserror", "tokio", @@ -6684,7 +6683,7 @@ dependencies = [ "criterion", "fnv", "futures-util", - "metrics", + "metrics 0.21.1", "parking_lot 0.12.1", "paste", "proptest", @@ -7094,11 +7093,11 @@ dependencies = [ [[package]] name = "schannel" -version = "0.1.22" +version = "0.1.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c3733bf4cf7ea0880754e19cb5a462007c4a8c1914bff372ccc95b464f1df88" +checksum = "fbc91545643bcf3a0bbb6569265615222618bdf33ce4ffbbd13c4bbd4c093534" dependencies = [ - "windows-sys 0.48.0", + "windows-sys 0.52.0", ] [[package]] @@ -7265,9 +7264,9 @@ dependencies = [ [[package]] name = "serde_bytes" -version = "0.11.12" +version = "0.11.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab33ec92f677585af6d88c65593ae2375adde54efdbf16d597f2cbc7a6d368ff" +checksum = "8bb1879ea93538b78549031e2d54da3e901fd7e75f2e4dc758d760937b123d10" dependencies = [ "serde", ] @@ -7280,7 +7279,7 @@ checksum = "43576ca501357b9b071ac53cdc7da8ef0cbd9493d8df094cd821777ea6e894d3" dependencies = [ "proc-macro2", "quote", - "syn 2.0.41", + "syn 2.0.43", ] [[package]] @@ -7307,9 +7306,9 @@ dependencies = [ [[package]] name = "serde_spanned" -version = "0.6.4" +version = "0.6.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12022b835073e5b11e90a14f86838ceb1c8fb0325b72416845c487ac0fa95e80" +checksum = "eb3622f419d1296904700073ea6cc23ad690adbd66f13ea683df73298736f0c1" dependencies = [ "serde", ] @@ -7352,7 +7351,7 @@ dependencies = [ "darling 0.20.3", "proc-macro2", "quote", - "syn 2.0.41", + "syn 2.0.43", ] [[package]] @@ -7377,7 +7376,7 @@ checksum = "91d129178576168c589c9ec973feedf7d3126c01ac2bf08795109aa35b69fb8f" dependencies = [ "proc-macro2", "quote", - "syn 2.0.41", + "syn 2.0.43", ] [[package]] @@ -7512,9 +7511,9 @@ dependencies = [ [[package]] name = "similar" -version = "2.3.0" +version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2aeaf503862c419d66959f5d7ca015337d864e9c49485d771b732e2a20453597" +checksum = "32fea41aca09ee824cc9724996433064c89f7777e60762749a4170a14abbfa21" dependencies = [ "bstr", "unicode-segmentation", @@ -7573,15 +7572,6 @@ dependencies = [ "serde", ] -[[package]] -name = "smol_str" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "74212e6bbe9a4352329b2f68ba3130c15a3f26fe88ff22dbdc6cdd58fa85e99c" -dependencies = [ - "serde", -] - [[package]] name = "snap" version = "1.1.1" @@ -7704,7 +7694,7 @@ dependencies = [ "proc-macro2", "quote", "rustversion", - "syn 2.0.41", + "syn 2.0.43", ] [[package]] @@ -7752,7 +7742,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1cccfffbc6bb3bb2d3a26cd2077f4d055f6808d266f9d4d158797a4c60510dfe" dependencies = [ "debugid", - "memmap2 0.9.1", + "memmap2 0.9.3", "stable_deref_trait", "uuid 1.6.1", ] @@ -7781,9 +7771,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.41" +version = "2.0.43" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44c8b28c477cc3bf0e7966561e3460130e1255f7a1cf71931075f1c5e7a7e269" +checksum = "ee659fb5f3d355364e1f3e5bc10fb82068efbf824a1e9d1c9504244a6469ad53" dependencies = [ "proc-macro2", "quote", @@ -7792,14 +7782,14 @@ dependencies = [ [[package]] name = "syn-solidity" -version = "0.5.3" +version = "0.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4e95b65f5854377a31ebfa69d71b87c9d0d9922fddbfeb91a8eda4a0c5868eb" +checksum = "91ede2e5b2c6bfac4bc0ff4499957a11725dc12a7ddb86270e827ef575892553" dependencies = [ "paste", "proc-macro2", "quote", - "syn 2.0.41", + "syn 2.0.43", ] [[package]] @@ -7810,7 +7800,7 @@ checksum = "285ba80e733fac80aa4270fbcdf83772a79b80aa35c97075320abfee4a915b06" dependencies = [ "proc-macro2", "quote", - "syn 2.0.41", + "syn 2.0.43", "unicode-xid", ] @@ -7843,15 +7833,15 @@ checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" [[package]] name = "tempfile" -version = "3.8.1" +version = "3.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ef1adac450ad7f4b3c28589471ade84f25f731a7a0fe30d71dfa9f60fd808e5" +checksum = "01ce4141aa927a6d1bd34a041795abd0db1cccba5d5f24b009f694bdf3a1f3fa" dependencies = [ "cfg-if", "fastrand 2.0.1", "redox_syscall 0.4.1", "rustix", - "windows-sys 0.48.0", + "windows-sys 0.52.0", ] [[package]] @@ -7905,7 +7895,7 @@ dependencies = [ "prettyplease", "proc-macro2", "quote", - "syn 2.0.41", + "syn 2.0.43", ] [[package]] @@ -7929,22 +7919,22 @@ checksum = "a38c90d48152c236a3ab59271da4f4ae63d678c5d7ad6b7714d7cb9760be5e4b" [[package]] name = "thiserror" -version = "1.0.51" +version = "1.0.53" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f11c217e1416d6f036b870f14e0413d480dbf28edbee1f877abaf0206af43bb7" +checksum = "b2cd5904763bad08ad5513ddbb12cf2ae273ca53fa9f68e843e236ec6dfccc09" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.51" +version = "1.0.53" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "01742297787513b79cf8e29d1056ede1313e2420b7b3b15d0a768b4921f549df" +checksum = "3dcf4a824cce0aeacd6f38ae6f24234c8e80d68632338ebaa1443b5df9e29e19" dependencies = [ "proc-macro2", "quote", - "syn 2.0.41", + "syn 2.0.43", ] [[package]] @@ -7968,9 +7958,9 @@ dependencies = [ [[package]] name = "time" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4a34ab300f2dee6e562c10a046fc05e358b29f9bf92277f30c3c8d82275f6f5" +checksum = "f657ba42c3f86e7680e53c8cd3af8abbe56b5491790b46e22e19c0d57463583e" dependencies = [ "deranged", "itoa", @@ -7990,9 +7980,9 @@ checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3" [[package]] name = "time-macros" -version = "0.2.15" +version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ad70d68dba9e1f8aceda7aa6711965dfec1cac869f311a51bd08b3a2ccbce20" +checksum = "26197e33420244aeb70c3e8c78376ca46571bc4e701e4791c2cd9f57dcb3a43f" dependencies = [ "time-core", ] @@ -8044,9 +8034,9 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.35.0" +version = "1.35.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "841d45b238a16291a4e1584e61820b8ae57d696cc5015c459c229ccc6990cc1c" +checksum = "c89b4efa943be685f629b149f53829423f8f5531ea21249408e8e2f8671ec104" dependencies = [ "backtrace", "bytes", @@ -8069,7 +8059,7 @@ checksum = "5b8a1e28f2deaa14e508979454cb3a223b10b938b45af148bc0986de36f1923b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.41", + "syn 2.0.43", ] [[package]] @@ -8283,7 +8273,7 @@ checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.41", + "syn 2.0.43", ] [[package]] @@ -8457,9 +8447,9 @@ checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" [[package]] name = "trybuild" -version = "1.0.85" +version = "1.0.86" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "196a58260a906cedb9bf6d8034b6379d0c11f552416960452f267402ceeddff1" +checksum = "8419ecd263363827c5730386f418715766f584e2f874d32c23c5b00bd9727e7e" dependencies = [ "basic-toml", "glob", @@ -8748,7 +8738,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn 2.0.41", + "syn 2.0.43", "wasm-bindgen-shared", ] @@ -8782,7 +8772,7 @@ checksum = "f0eb82fcb7930ae6219a7ecfd55b217f5f0893484b7a13022ebb2b2bf20b5283" dependencies = [ "proc-macro2", "quote", - "syn 2.0.41", + "syn 2.0.43", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -8879,21 +8869,21 @@ checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" [[package]] name = "windows" -version = "0.51.1" +version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca229916c5ee38c2f2bc1e9d8f04df975b4bd93f9955dc69fabb5d91270045c9" +checksum = "e48a53791691ab099e5e2ad123536d0fff50652600abaf43bbf952894110d0be" dependencies = [ "windows-core", - "windows-targets 0.48.5", + "windows-targets 0.52.0", ] [[package]] name = "windows-core" -version = "0.51.1" +version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1f8cf84f35d2db49a46868f947758c7a1138116f7fac3bc844f43ade1292e64" +checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" dependencies = [ - "windows-targets 0.48.5", + "windows-targets 0.52.0", ] [[package]] @@ -9096,9 +9086,9 @@ checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04" [[package]] name = "winnow" -version = "0.5.28" +version = "0.5.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c830786f7720c2fd27a1a0e27a709dbd3c4d009b56d098fc742d4f4eab91fe2" +checksum = "97a4882e6b134d6c28953a387571f1acdd3496830d5e36c5e3a1075580ea641c" dependencies = [ "memchr", ] @@ -9203,28 +9193,28 @@ checksum = "9e6936f0cce458098a201c245a11bef556c6a0181129c7034d10d76d1ec3a2b8" dependencies = [ "proc-macro2", "quote", - "syn 2.0.41", + "syn 2.0.43", "synstructure", ] [[package]] name = "zerocopy" -version = "0.7.31" +version = "0.7.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c4061bedbb353041c12f413700357bec76df2c7e2ca8e4df8bac24c6bf68e3d" +checksum = "74d4d3961e53fa4c9a25a8637fc2bfaf2595b3d3ae34875568a5cf64787716be" dependencies = [ "zerocopy-derive", ] [[package]] name = "zerocopy-derive" -version = "0.7.31" +version = "0.7.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b3c129550b3e6de3fd0ba67ba5c81818f9805e58b8d7fee80a3a59d2c9fc601a" +checksum = "9ce1b18ccd8e73a9321186f97e46f9f04b778851177567b1975109d26a08d2a6" dependencies = [ "proc-macro2", "quote", - "syn 2.0.41", + "syn 2.0.43", ] [[package]] @@ -9244,7 +9234,7 @@ checksum = "e6a647510471d372f2e6c2e6b7219e44d8c574d24fdc11c610a61455782f18c3" dependencies = [ "proc-macro2", "quote", - "syn 2.0.41", + "syn 2.0.43", "synstructure", ] @@ -9265,7 +9255,7 @@ checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" dependencies = [ "proc-macro2", "quote", - "syn 2.0.41", + "syn 2.0.43", ] [[package]] @@ -9288,7 +9278,7 @@ checksum = "7a4a1638a1934450809c2266a70362bfc96cd90550c073f5b8a55014d1010157" dependencies = [ "proc-macro2", "quote", - "syn 2.0.41", + "syn 2.0.43", ] [[package]] From 876a837d947c6a0aeba3f8f36e493339725cbd24 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Urbanek?= Date: Sun, 31 Dec 2023 10:44:35 +0100 Subject: [PATCH 229/277] Fix prune docs typo (#5911) --- book/run/config.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/book/run/config.md b/book/run/config.md index f62bad26a26b..1885c1d146c6 100644 --- a/book/run/config.md +++ b/book/run/config.md @@ -357,7 +357,7 @@ block_interval = 5 [prune.parts] # Sender Recovery pruning configuration -sender_recovery = { distance = 100_000 } # Prune all transaction senders before the block `head-128`, i.e. keep transaction senders for the last 129 blocks +sender_recovery = { distance = 100_000 } # Prune all transaction senders before the block `head-100000`, i.e. keep transaction senders for the last 100001 blocks # Transaction Lookup pruning configuration transaction_lookup = "full" # Prune all TxNumber => TxHash mappings @@ -366,10 +366,10 @@ transaction_lookup = "full" # Prune all TxNumber => TxHash mappings receipts = { before = 1920000 } # Prune all receipts from transactions before the block 1920000, i.e. keep receipts from the block 1920000 # Account History pruning configuration -account_history = { distance = 100_000 } # Prune all historical account states before the block `head-128` +account_history = { distance = 100_000 } # Prune all historical account states before the block `head-100000` # Storage History pruning configuration -storage_history = { distance = 100_000 } # Prune all historical storage states before the block `head-128` +storage_history = { distance = 100_000 } # Prune all historical storage states before the block `head-100000` ``` We can also prune receipts more granular, using the logs filtering: From f16a22d979260c919b05ac03244fe32afd01db53 Mon Sep 17 00:00:00 2001 From: Thomas Coratger <60488569+tcoratger@users.noreply.github.com> Date: Sun, 31 Dec 2023 12:53:06 +0100 Subject: [PATCH 230/277] feat: add `nonce_after_all_transactions` implementation for `TxPool` (#5904) --- crates/transaction-pool/src/pool/txpool.rs | 48 +++++++++++++++++++++- 1 file changed, 47 insertions(+), 1 deletion(-) diff --git a/crates/transaction-pool/src/pool/txpool.rs b/crates/transaction-pool/src/pool/txpool.rs index 7d3594965698..5875987914b4 100644 --- a/crates/transaction-pool/src/pool/txpool.rs +++ b/crates/transaction-pool/src/pool/txpool.rs @@ -90,6 +90,20 @@ impl TxPool { } } + /// Retrieves the highest nonce for a specific sender from the transaction pool. + pub fn get_highest_nonce_by_sender(&self, sender: SenderId) -> Option { + self.all().txs_iter(sender).last().map(|(_, tx)| tx.transaction.nonce()) + } + + /// Retrieves the highest transaction (wrapped in an `Arc`) for a specific sender from the + /// transaction pool. + pub fn get_highest_transaction_by_sender( + &self, + sender: SenderId, + ) -> Option>> { + self.all().txs_iter(sender).last().map(|(_, tx)| Arc::clone(&tx.transaction)) + } + /// Returns access to the [`AllTransactions`] container. pub(crate) fn all(&self) -> &AllTransactions { &self.all_transactions @@ -1624,7 +1638,7 @@ impl Default for AllTransactions { by_hash: Default::default(), txs: Default::default(), tx_counter: Default::default(), - last_seen_block_number: 0, + last_seen_block_number: Default::default(), last_seen_block_hash: Default::default(), pending_fees: Default::default(), price_bumps: Default::default(), @@ -2547,6 +2561,38 @@ mod tests { assert_eq!(pool.all_transactions.txs.get(&id).unwrap().subpool, SubPool::BaseFee) } + #[test] + fn get_highest_transaction_by_sender_and_nonce() { + // Set up a mock transaction factory and a new transaction pool. + let mut f = MockTransactionFactory::default(); + let mut pool = TxPool::new(MockOrdering::default(), Default::default()); + + // Create a mock transaction and add it to the pool. + let tx = MockTransaction::eip1559(); + pool.add_transaction(f.validated(tx.clone()), U256::from(1_000), 0).unwrap(); + + // Create another mock transaction with an incremented price. + let tx1 = tx.inc_price().next().clone(); + + // Validate the second mock transaction and add it to the pool. + let tx1_validated = f.validated(tx1.clone()); + pool.add_transaction(tx1_validated, U256::from(1_000), 0).unwrap(); + + // Ensure that the calculated next nonce for the sender matches the expected value. + assert_eq!( + pool.get_highest_nonce_by_sender(f.ids.sender_id(&tx.sender()).unwrap()), + Some(1) + ); + + // Retrieve the highest transaction by sender. + let highest_tx = pool + .get_highest_transaction_by_sender(f.ids.sender_id(&tx.sender()).unwrap()) + .expect("Failed to retrieve highest transaction"); + + // Validate that the retrieved highest transaction matches the expected transaction. + assert_eq!(highest_tx.as_ref().transaction, tx1); + } + #[test] fn discard_nonce_too_low() { let mut f = MockTransactionFactory::default(); From cdfdadd6c37a179da314c71692ad7ce20dd9a089 Mon Sep 17 00:00:00 2001 From: Matthias Seitz Date: Sun, 31 Dec 2023 13:57:15 +0100 Subject: [PATCH 231/277] docs: include s/2 value in docs (#5914) --- crates/primitives/src/transaction/signature.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/crates/primitives/src/transaction/signature.rs b/crates/primitives/src/transaction/signature.rs index 6d051633b55c..9df9a29535d1 100644 --- a/crates/primitives/src/transaction/signature.rs +++ b/crates/primitives/src/transaction/signature.rs @@ -7,6 +7,8 @@ use serde::{Deserialize, Serialize}; /// The order of the secp256k1 curve, divided by two. Signatures that should be checked according /// to EIP-2 should have an S value less than or equal to this. +/// +/// `57896044618658097711785492504343953926418782139537452191302581570759080747168` const SECP256K1N_HALF: U256 = U256::from_be_bytes([ 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x5D, 0x57, 0x6E, 0x73, 0x57, 0xA4, 0x50, 0x1D, 0xDF, 0xE9, 0x2F, 0x46, 0x68, 0x1B, 0x20, 0xA0, From 069ca8268ec50e5c4a87f682627d94da5a4102fb Mon Sep 17 00:00:00 2001 From: Matthias Seitz Date: Sun, 31 Dec 2023 23:22:17 +0100 Subject: [PATCH 232/277] fix: also adjust ipc path (#5909) --- bin/reth/src/args/rpc_server_args.rs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/bin/reth/src/args/rpc_server_args.rs b/bin/reth/src/args/rpc_server_args.rs index 64db2104e63f..1f0846b1faf7 100644 --- a/bin/reth/src/args/rpc_server_args.rs +++ b/bin/reth/src/args/rpc_server_args.rs @@ -195,6 +195,7 @@ impl RpcServerArgs { /// * The `auth_port` is scaled by a factor of `instance * 100` /// * The `http_port` is scaled by a factor of `-instance` /// * The `ws_port` is scaled by a factor of `instance * 2` + /// * The `ipcpath` is appended with the instance number: `/tmp/reth.ipc-` /// /// # Panics /// Warning: if `instance` is zero in debug mode, this will panic. @@ -212,6 +213,10 @@ impl RpcServerArgs { self.http_port -= instance - 1; // ws port is scaled by a factor of instance * 2 self.ws_port += instance * 2 - 2; + + // also adjust the ipc path by appending the instance number to the path used for the + // endpoint + self.ipcpath = format!("{}-{}", self.ipcpath, instance); } /// Configures and launches _all_ servers. From 8dff31e3accf63521f746dc797ce0c3da2ea86bf Mon Sep 17 00:00:00 2001 From: Matthias Seitz Date: Sun, 31 Dec 2023 23:22:52 +0100 Subject: [PATCH 233/277] chore: improve ipc error message (#5908) --- crates/rpc/ipc/src/server/mod.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/crates/rpc/ipc/src/server/mod.rs b/crates/rpc/ipc/src/server/mod.rs index 8d3c8f38b177..3a412883a146 100644 --- a/crates/rpc/ipc/src/server/mod.rs +++ b/crates/rpc/ipc/src/server/mod.rs @@ -122,6 +122,7 @@ where let connection_guard = ConnectionGuard::new(self.cfg.max_connections as usize); let mut connections = FutureDriver::default(); + let endpoint_path = self.endpoint.path().to_string(); let incoming = match self.endpoint.incoming() { Ok(connections) => { #[cfg(windows)] @@ -129,7 +130,8 @@ where Incoming::new(connections) } Err(err) => { - on_ready.send(Err(err.to_string())).ok(); + let msg = format!("failed to listen on ipc endpoint `{endpoint_path}`: {err}"); + on_ready.send(Err(msg)).ok(); return Err(err) } }; From 4cd30bdc9b28f3cc9de943c9a415512c3bb35397 Mon Sep 17 00:00:00 2001 From: Thomas Coratger <60488569+tcoratger@users.noreply.github.com> Date: Tue, 2 Jan 2024 14:39:59 +0100 Subject: [PATCH 234/277] feat(tx-pool): add `get_transactions_by_sender_and_nonce` for `TransactionPool` trait (#5912) --- Cargo.toml | 3 ++- crates/transaction-pool/src/lib.rs | 16 +++++++++++++++- crates/transaction-pool/src/noop.rs | 8 ++++++++ crates/transaction-pool/src/pool/mod.rs | 7 ++++++- crates/transaction-pool/src/pool/txpool.rs | 1 - crates/transaction-pool/src/traits.rs | 7 +++++++ 6 files changed, 38 insertions(+), 4 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 2fe5902233d9..05598c1b9c92 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -184,6 +184,7 @@ once_cell = "1.17" syn = "2.0" ahash = "0.8.6" + # proc-macros proc-macro2 = "1.0" quote = "1.0" @@ -231,4 +232,4 @@ serial_test = "2" [workspace.metadata.cargo-udeps.ignore] # ignored because this is mutually exclusive with the optimism payload builder via feature flags -normal = ["reth-ethereum-payload-builder"] \ No newline at end of file +normal = ["reth-ethereum-payload-builder"] diff --git a/crates/transaction-pool/src/lib.rs b/crates/transaction-pool/src/lib.rs index 1621c6ffe179..ab7b73debb88 100644 --- a/crates/transaction-pool/src/lib.rs +++ b/crates/transaction-pool/src/lib.rs @@ -144,7 +144,7 @@ #![deny(unused_must_use, rust_2018_idioms)] #![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))] -use crate::pool::PoolInner; +use crate::{identifier::TransactionId, pool::PoolInner}; use aquamarine as _; use reth_primitives::{Address, BlobTransactionSidecar, PooledTransactionsElement, TxHash, U256}; use reth_provider::StateProviderFactory; @@ -473,6 +473,20 @@ where self.pool.get_transactions_by_sender(sender) } + fn get_transactions_by_sender_and_nonce( + &self, + sender: Address, + nonce: u64, + ) -> Option>> { + let transaction_id = TransactionId::new(self.pool.get_sender_id(sender), nonce); + + self.inner() + .get_pool_data() + .all() + .get(&transaction_id) + .map(|tx| Arc::clone(&tx.transaction)) + } + fn get_transactions_by_origin( &self, origin: TransactionOrigin, diff --git a/crates/transaction-pool/src/noop.rs b/crates/transaction-pool/src/noop.rs index a1cadb5f6597..c1a322edd37b 100644 --- a/crates/transaction-pool/src/noop.rs +++ b/crates/transaction-pool/src/noop.rs @@ -192,6 +192,14 @@ impl TransactionPool for NoopTransactionPool { vec![] } + fn get_transactions_by_sender_and_nonce( + &self, + _sender: Address, + _nonce: u64, + ) -> Option>> { + None + } + fn unique_senders(&self) -> HashSet
{ Default::default() } diff --git a/crates/transaction-pool/src/pool/mod.rs b/crates/transaction-pool/src/pool/mod.rs index 1b7717a8fc5e..778445334f1b 100644 --- a/crates/transaction-pool/src/pool/mod.rs +++ b/crates/transaction-pool/src/pool/mod.rs @@ -81,7 +81,7 @@ use crate::{ CanonicalStateUpdate, ChangedAccount, PoolConfig, TransactionOrdering, TransactionValidator, }; use best::BestTransactions; -use parking_lot::{Mutex, RwLock}; +use parking_lot::{Mutex, RwLock, RwLockReadGuard}; use reth_primitives::{ Address, BlobTransaction, BlobTransactionSidecar, IntoRecoveredTransaction, PooledTransactionsElement, TransactionSigned, TxHash, B256, @@ -279,6 +279,11 @@ where self.event_listener.write().subscribe_all() } + /// Returns a read lock to the pool's data. + pub(crate) fn get_pool_data(&self) -> RwLockReadGuard<'_, TxPool> { + self.pool.read() + } + /// Returns hashes of _all_ transactions in the pool. pub(crate) fn pooled_transactions_hashes(&self) -> Vec { let pool = self.pool.read(); diff --git a/crates/transaction-pool/src/pool/txpool.rs b/crates/transaction-pool/src/pool/txpool.rs index 5875987914b4..c58a9e7fb8ec 100644 --- a/crates/transaction-pool/src/pool/txpool.rs +++ b/crates/transaction-pool/src/pool/txpool.rs @@ -929,7 +929,6 @@ impl AllTransactions { } /// Returns the internal transaction with additional metadata - #[cfg(test)] pub(crate) fn get(&self, id: &TransactionId) -> Option<&PoolInternalTransaction> { self.txs.get(id) } diff --git a/crates/transaction-pool/src/traits.rs b/crates/transaction-pool/src/traits.rs index 3888b1078666..70875cd0fd0f 100644 --- a/crates/transaction-pool/src/traits.rs +++ b/crates/transaction-pool/src/traits.rs @@ -306,6 +306,13 @@ pub trait TransactionPool: Send + Sync + Clone { sender: Address, ) -> Vec>>; + /// Returns a transaction sent by a given user with a given nonce + fn get_transactions_by_sender_and_nonce( + &self, + sender: Address, + nonce: u64, + ) -> Option>>; + /// Returns all transactions that where submitted with the given [TransactionOrigin] fn get_transactions_by_origin( &self, From 7fa4cddc7235c058048834a37592b2828fce8aed Mon Sep 17 00:00:00 2001 From: Nil Medvedev Date: Tue, 2 Jan 2024 14:11:41 +0000 Subject: [PATCH 235/277] feat: restore tx pool after reboot (#5665) Co-authored-by: Matthias Seitz --- bin/reth/src/builder/mod.rs | 19 ++- bin/reth/src/dirs.rs | 7 + crates/primitives/src/fs.rs | 6 + crates/tasks/src/lib.rs | 28 ++++ crates/transaction-pool/src/lib.rs | 12 +- crates/transaction-pool/src/maintain.rs | 197 +++++++++++++++++++++++- 6 files changed, 259 insertions(+), 10 deletions(-) diff --git a/bin/reth/src/builder/mod.rs b/bin/reth/src/builder/mod.rs index a4de50a74d06..40fbe5203e25 100644 --- a/bin/reth/src/builder/mod.rs +++ b/bin/reth/src/builder/mod.rs @@ -536,6 +536,7 @@ impl NodeConfig { blockchain_db: &BlockchainProvider, head: Head, executor: &TaskExecutor, + data_dir: &ChainPath, ) -> eyre::Result, InMemoryBlobStore>> where DB: Database + Unpin + Clone + 'static, @@ -555,12 +556,28 @@ impl NodeConfig { let transaction_pool = reth_transaction_pool::Pool::eth_pool(validator, blob_store, self.txpool.pool_config()); info!(target: "reth::cli", "Transaction pool initialized"); + let transactions_path = data_dir.txpool_transactions_path(); // spawn txpool maintenance task { let pool = transaction_pool.clone(); let chain_events = blockchain_db.canonical_state_stream(); let client = blockchain_db.clone(); + let transactions_backup_config = + reth_transaction_pool::maintain::LocalTransactionBackupConfig::with_local_txs_backup(transactions_path); + + executor.spawn_critical_with_graceful_shutdown_signal( + "local transactions backup task", + |shutdown| { + reth_transaction_pool::maintain::backup_local_transactions_task( + shutdown, + pool.clone(), + transactions_backup_config, + ) + }, + ); + + // spawn the maintenance task executor.spawn_critical( "txpool maintenance task", reth_transaction_pool::maintain::maintain_transaction_pool_future( @@ -1055,7 +1072,7 @@ impl NodeBuilderWit // build transaction pool let transaction_pool = - self.config.build_and_spawn_txpool(&blockchain_db, head, &executor)?; + self.config.build_and_spawn_txpool(&blockchain_db, head, &executor, &self.data_dir)?; // build network let (network_client, mut network_builder) = self diff --git a/bin/reth/src/dirs.rs b/bin/reth/src/dirs.rs index bf07b8285f94..1a0bee8cbcdd 100644 --- a/bin/reth/src/dirs.rs +++ b/bin/reth/src/dirs.rs @@ -312,6 +312,13 @@ impl ChainPath { self.0.join("blobstore").into() } + /// Returns the path to the local transactions backup file + /// + /// `//txpool-transactions-backup.rlp` + pub fn txpool_transactions_path(&self) -> PathBuf { + self.0.join("txpool-transactions-backup.rlp").into() + } + /// Returns the path to the config file for this chain. /// /// `//reth.toml` diff --git a/crates/primitives/src/fs.rs b/crates/primitives/src/fs.rs index 23b7ee320999..5a23465dbcb8 100644 --- a/crates/primitives/src/fs.rs +++ b/crates/primitives/src/fs.rs @@ -129,6 +129,12 @@ pub fn remove_dir_all(path: impl AsRef) -> Result<()> { fs::remove_dir_all(path).map_err(|err| FsPathError::remove_dir(err, path)) } +/// Wrapper for `std::fs::remove_file` +pub fn remove_file(path: impl AsRef) -> Result<()> { + let path = path.as_ref(); + fs::remove_file(path).map_err(|err| FsPathError::remove_file(err, path)) +} + /// Wrapper for `std::fs::create_dir_all` pub fn create_dir_all(path: impl AsRef) -> Result<()> { let path = path.as_ref(); diff --git a/crates/tasks/src/lib.rs b/crates/tasks/src/lib.rs index fa8634073579..7ba3fd170a26 100644 --- a/crates/tasks/src/lib.rs +++ b/crates/tasks/src/lib.rs @@ -535,6 +535,34 @@ impl TaskSpawner for TaskExecutor { } } +/// TaskSpawner with extended behaviour +pub trait TaskSpawnerExt: Send + Sync + Unpin + std::fmt::Debug + DynClone { + /// This spawns a critical task onto the runtime. + /// + /// If this task panics, the [TaskManager] is notified. + /// The [TaskManager] will wait until the given future has completed before shutting down. + fn spawn_critical_with_graceful_shutdown_signal( + &self, + name: &'static str, + f: impl FnOnce(GracefulShutdown) -> F, + ) -> JoinHandle<()> + where + F: Future + Send + 'static; +} + +impl TaskSpawnerExt for TaskExecutor { + fn spawn_critical_with_graceful_shutdown_signal( + &self, + name: &'static str, + f: impl FnOnce(GracefulShutdown) -> F, + ) -> JoinHandle<()> + where + F: Future + Send + 'static, + { + TaskExecutor::spawn_critical_with_graceful_shutdown_signal(self, name, f) + } +} + /// Determines how a task is spawned enum TaskKind { /// Spawn the task to the default executor [Handle::spawn] diff --git a/crates/transaction-pool/src/lib.rs b/crates/transaction-pool/src/lib.rs index ab7b73debb88..16158c0d98ab 100644 --- a/crates/transaction-pool/src/lib.rs +++ b/crates/transaction-pool/src/lib.rs @@ -110,22 +110,28 @@ //! use reth_primitives::MAINNET; //! use reth_provider::{BlockReaderIdExt, CanonStateNotification, ChainSpecProvider, StateProviderFactory}; //! use reth_tasks::TokioTaskExecutor; +//! use reth_tasks::TaskSpawner; +//! use reth_tasks::TaskManager; //! use reth_transaction_pool::{TransactionValidationTaskExecutor, Pool}; //! use reth_transaction_pool::blobstore::InMemoryBlobStore; -//! use reth_transaction_pool::maintain::maintain_transaction_pool_future; +//! use reth_transaction_pool::maintain::{maintain_transaction_pool_future}; +//! //! async fn t(client: C, stream: St) //! where C: StateProviderFactory + BlockReaderIdExt + ChainSpecProvider + Clone + 'static, //! St: Stream + Send + Unpin + 'static, //! { //! let blob_store = InMemoryBlobStore::default(); +//! let rt = tokio::runtime::Runtime::new().unwrap(); +//! let manager = TaskManager::new(rt.handle().clone()); +//! let executor = manager.executor(); //! let pool = Pool::eth_pool( -//! TransactionValidationTaskExecutor::eth(client.clone(), MAINNET.clone(), blob_store.clone(), TokioTaskExecutor::default()), +//! TransactionValidationTaskExecutor::eth(client.clone(), MAINNET.clone(), blob_store.clone(), executor.clone()), //! blob_store, //! Default::default(), //! ); //! //! // spawn a task that listens for new blocks and updates the pool's transactions, mined transactions etc.. -//! tokio::task::spawn( maintain_transaction_pool_future(client, pool, stream, TokioTaskExecutor::default(), Default::default())); +//! tokio::task::spawn(maintain_transaction_pool_future(client, pool, stream, executor.clone(), Default::default())); //! //! # } //! ``` diff --git a/crates/transaction-pool/src/maintain.rs b/crates/transaction-pool/src/maintain.rs index db74f50f2f8b..828d7dca40fc 100644 --- a/crates/transaction-pool/src/maintain.rs +++ b/crates/transaction-pool/src/maintain.rs @@ -2,17 +2,19 @@ use crate::{ blobstore::{BlobStoreCanonTracker, BlobStoreUpdates}, + error::PoolError, metrics::MaintainPoolMetrics, - traits::{CanonicalStateUpdate, ChangedAccount, TransactionPoolExt}, - BlockInfo, TransactionPool, + traits::{CanonicalStateUpdate, ChangedAccount, TransactionPool, TransactionPoolExt}, + BlockInfo, }; use futures_util::{ future::{BoxFuture, Fuse, FusedFuture}, FutureExt, Stream, StreamExt, }; use reth_primitives::{ - Address, BlockHash, BlockNumber, BlockNumberOrTag, FromRecoveredPooledTransaction, - FromRecoveredTransaction, PooledTransactionsElementEcRecovered, + fs::FsPathError, Address, BlockHash, BlockNumber, BlockNumberOrTag, + FromRecoveredPooledTransaction, FromRecoveredTransaction, IntoRecoveredTransaction, + PooledTransactionsElementEcRecovered, TransactionSigned, }; use reth_provider::{ BlockReaderIdExt, BundleStateWithReceipts, CanonStateNotification, ChainSpecProvider, @@ -23,9 +25,10 @@ use std::{ borrow::Borrow, collections::HashSet, hash::{Hash, Hasher}, + path::{Path, PathBuf}, }; use tokio::sync::oneshot; -use tracing::{debug, trace}; +use tracing::{debug, error, info, trace, warn}; /// Additional settings for maintaining the transaction pool #[derive(Debug, Clone, Copy, PartialEq, Eq)] @@ -47,6 +50,20 @@ impl Default for MaintainPoolConfig { } } +/// Settings for local transaction backup task +#[derive(Debug, Clone, Default)] +pub struct LocalTransactionBackupConfig { + /// Path to transactions backup file + pub transactions_path: Option, +} + +impl LocalTransactionBackupConfig { + /// Receive path to transactions backup and return initialized config + pub fn with_local_txs_backup(transactions_path: PathBuf) -> Self { + Self { transactions_path: Some(transactions_path) } + } +} + /// Returns a spawnable future for maintaining the state of the transaction pool. pub fn maintain_transaction_pool_future( client: Client, @@ -83,7 +100,7 @@ pub async fn maintain_transaction_pool( Tasks: TaskSpawner + 'static, { let metrics = MaintainPoolMetrics::default(); - let MaintainPoolConfig { max_update_depth, max_reload_accounts } = config; + let MaintainPoolConfig { max_update_depth, max_reload_accounts, .. } = config; // ensure the pool points to latest state if let Ok(Some(latest)) = client.header_by_number_or_tag(BlockNumberOrTag::Latest) { let latest = latest.seal_slow(); @@ -535,9 +552,124 @@ fn changed_accounts_iter( .map(|(address, acc)| ChangedAccount { address, nonce: acc.nonce, balance: acc.balance }) } +/// Loads transactions from a file, decodes them from the RLP format, and inserts them +/// into the transaction pool on node boot up. +/// The file is removed after the transactions have been successfully processed. +async fn load_and_reinsert_transactions

( + pool: P, + file_path: &Path, +) -> Result<(), TransactionsBackupError> +where + P: TransactionPool, +{ + if !file_path.exists() { + return Ok(()) + } + + debug!(target: "txpool", txs_file =?file_path, "Check local persistent storage for saved transactions"); + let data = reth_primitives::fs::read(file_path)?; + + if data.is_empty() { + return Ok(()) + } + + let txs_signed: Vec = alloy_rlp::Decodable::decode(&mut data.as_slice())?; + + let pool_transactions = txs_signed + .into_iter() + .filter_map(|tx| tx.try_ecrecovered().map(::from_recovered_transaction)) + .collect::>(); + let outcome = pool.add_transactions(crate::TransactionOrigin::Local, pool_transactions).await?; + + info!(target: "txpool", txs_file =?file_path, num_txs=%outcome.len(), "Successfully reinserted local transactions from file"); + reth_primitives::fs::remove_file(file_path)?; + Ok(()) +} + +fn save_local_txs_backup

(pool: P, file_path: &Path) +where + P: TransactionPool, +{ + let local_transactions = pool.get_local_transactions(); + if local_transactions.is_empty() { + trace!(target: "txpool", "no local transactions to save"); + return + } + + let local_transactions = local_transactions + .into_iter() + .map(|tx| tx.to_recovered_transaction().into_signed()) + .collect::>(); + + let num_txs = local_transactions.len(); + let mut buf = alloy_rlp::BytesMut::new(); + alloy_rlp::encode_list(&local_transactions, &mut buf); + info!(target: "txpool", txs_file =?file_path, num_txs=%num_txs, "Saving current local transactions"); + let parent_dir = file_path.parent().map(std::fs::create_dir_all).transpose(); + + match parent_dir.map(|_| reth_primitives::fs::write(file_path, buf)) { + Ok(_) => { + info!(target: "txpool", txs_file=?file_path, "Wrote local transactions to file"); + } + Err(err) => { + warn!(target: "txpool", %err, txs_file=?file_path, "Failed to write local transactions to file"); + } + } +} + +/// Errors possible during txs backup load and decode +#[derive(thiserror::Error, Debug)] +pub enum TransactionsBackupError { + /// Error during RLP decoding of transactions + #[error("failed to apply transactions backup. Encountered RLP decode error: {0}")] + Decode(#[from] alloy_rlp::Error), + /// Error during file upload + #[error("failed to apply transactions backup. Encountered file error: {0}")] + FsPath(#[from] FsPathError), + /// Error adding transactions to the transaction pool + #[error("failed to insert transactions to the transactions pool. Encountered pool error: {0}")] + Pool(#[from] PoolError), +} + +/// Task which manages saving local transactions to the persistent file in case of shutdown. +/// Reloads the transactions from the file on the boot up and inserts them into the pool. +pub async fn backup_local_transactions_task

( + shutdown: reth_tasks::shutdown::GracefulShutdown, + pool: P, + config: LocalTransactionBackupConfig, +) where + P: TransactionPool + Clone, +{ + let Some(transactions_path) = config.transactions_path else { + // nothing to do + return + }; + + if let Err(err) = load_and_reinsert_transactions(pool.clone(), &transactions_path).await { + error!(target: "txpool", "{}", err) + } + + let graceful_guard = shutdown.await; + + // write transactions to disk + save_local_txs_backup(pool, &transactions_path); + + drop(graceful_guard) +} + +#[cfg(not(feature = "optimism"))] #[cfg(test)] mod tests { use super::*; + use crate::{ + blobstore::InMemoryBlobStore, validate::EthTransactionValidatorBuilder, + CoinbaseTipOrdering, EthPooledTransaction, Pool, PoolTransaction, TransactionOrigin, + }; + use reth_primitives::{ + fs, hex, FromRecoveredPooledTransaction, PooledTransactionsElement, MAINNET, U256, + }; + use reth_provider::test_utils::{ExtendedAccount, MockEthProvider}; + use reth_tasks::TaskManager; #[test] fn changed_acc_entry() { @@ -546,4 +678,57 @@ mod tests { copy.nonce = 10; assert!(changed_acc.eq(&ChangedAccountEntry(copy))); } + + const EXTENSION: &str = "rlp"; + const FILENAME: &str = "test_transactions_backup"; + + #[tokio::test(flavor = "multi_thread")] + async fn test_save_local_txs_backup() { + let temp_dir = tempfile::tempdir().unwrap(); + let transactions_path = temp_dir.path().join(FILENAME).with_extension(EXTENSION); + let tx_bytes = hex!("02f87201830655c2808505ef61f08482565f94388c818ca8b9251b393131c08a736a67ccb192978801049e39c4b5b1f580c001a01764ace353514e8abdfb92446de356b260e3c1225b73fc4c8876a6258d12a129a04f02294aa61ca7676061cd99f29275491218b4754b46a0248e5e42bc5091f507"); + let tx = PooledTransactionsElement::decode_enveloped(tx_bytes.into()).unwrap(); + let provider = MockEthProvider::default(); + let transaction = EthPooledTransaction::from_recovered_pooled_transaction( + tx.try_into_ecrecovered().unwrap(), + ); + let tx_to_cmp = transaction.clone(); + let sender = hex!("1f9090aaE28b8a3dCeaDf281B0F12828e676c326").into(); + provider.add_account(sender, ExtendedAccount::new(42, U256::MAX)); + let blob_store = InMemoryBlobStore::default(); + let validator = EthTransactionValidatorBuilder::new(MAINNET.clone()) + .build(provider, blob_store.clone()); + + let txpool = Pool::new( + validator.clone(), + CoinbaseTipOrdering::default(), + blob_store.clone(), + Default::default(), + ); + + txpool.add_transaction(TransactionOrigin::Local, transaction.clone()).await.unwrap(); + + let handle = tokio::runtime::Handle::current(); + let manager = TaskManager::new(handle); + let config = LocalTransactionBackupConfig::with_local_txs_backup(transactions_path.clone()); + manager.executor().spawn_critical_with_graceful_shutdown_signal("test task", |shutdown| { + backup_local_transactions_task(shutdown, txpool.clone(), config) + }); + + let mut txns = txpool.get_local_transactions(); + let tx_on_finish = txns.pop().expect("there should be 1 transaction"); + + assert_eq!(*tx_to_cmp.hash(), *tx_on_finish.hash()); + + // shutdown the executor + manager.graceful_shutdown(); + + let data = fs::read(transactions_path).unwrap(); + + let txs: Vec = + alloy_rlp::Decodable::decode(&mut data.as_slice()).unwrap(); + assert_eq!(txs.len(), 1); + + temp_dir.close().unwrap(); + } } From 962809e199345824b1bda82a277518f6f51799e6 Mon Sep 17 00:00:00 2001 From: Thomas Coratger <60488569+tcoratger@users.noreply.github.com> Date: Tue, 2 Jan 2024 15:33:29 +0100 Subject: [PATCH 236/277] feat(tx-pool): refactor `PoolInner` implementation using `get_pool_data` util method (#5920) --- crates/transaction-pool/src/pool/mod.rs | 52 ++++++++++++------------- 1 file changed, 26 insertions(+), 26 deletions(-) diff --git a/crates/transaction-pool/src/pool/mod.rs b/crates/transaction-pool/src/pool/mod.rs index 778445334f1b..e9287786989b 100644 --- a/crates/transaction-pool/src/pool/mod.rs +++ b/crates/transaction-pool/src/pool/mod.rs @@ -182,12 +182,12 @@ where /// Returns stats about the size of the pool. pub(crate) fn size(&self) -> PoolSize { - self.pool.read().size() + self.get_pool_data().size() } /// Returns the currently tracked block pub(crate) fn block_info(&self) -> BlockInfo { - self.pool.read().block_info() + self.get_pool_data().block_info() } /// Returns the currently tracked block pub(crate) fn set_block_info(&self, info: BlockInfo) { @@ -201,7 +201,7 @@ where /// Returns all senders in the pool pub(crate) fn unique_senders(&self) -> HashSet

{ - self.pool.read().unique_senders() + self.get_pool_data().unique_senders() } /// Converts the changed accounts to a map of sender ids to sender info (internal identifier @@ -264,12 +264,9 @@ where &self, tx_hash: TxHash, ) -> Option { - let pool = self.pool.read(); - if pool.contains(&tx_hash) { - Some(self.event_listener.write().subscribe(tx_hash)) - } else { - None - } + self.get_pool_data() + .contains(&tx_hash) + .then(|| self.event_listener.write().subscribe(tx_hash)) } /// Adds a listener for all transaction events. @@ -286,14 +283,17 @@ where /// Returns hashes of _all_ transactions in the pool. pub(crate) fn pooled_transactions_hashes(&self) -> Vec { - let pool = self.pool.read(); - pool.all().transactions_iter().filter(|tx| tx.propagate).map(|tx| *tx.hash()).collect() + self.get_pool_data() + .all() + .transactions_iter() + .filter(|tx| tx.propagate) + .map(|tx| *tx.hash()) + .collect() } /// Returns _all_ transactions in the pool. pub(crate) fn pooled_transactions(&self) -> Vec>> { - let pool = self.pool.read(); - pool.all().transactions_iter().filter(|tx| tx.propagate).collect() + self.get_pool_data().all().transactions_iter().filter(|tx| tx.propagate).collect() } /// Returns the [BlobTransaction] for the given transaction if the sidecar exists. @@ -617,7 +617,7 @@ where /// Returns an iterator that yields transactions that are ready to be included in the block. pub(crate) fn best_transactions(&self) -> BestTransactions { - self.pool.read().best_transactions() + self.get_pool_data().best_transactions() } /// Returns an iterator that yields transactions that are ready to be included in the block with @@ -627,7 +627,7 @@ where base_fee: u64, ) -> Box>>> { - self.pool.read().best_transactions_with_base_fee(base_fee) + self.get_pool_data().best_transactions_with_base_fee(base_fee) } /// Returns an iterator that yields transactions that are ready to be included in the block with @@ -637,22 +637,22 @@ where best_transactions_attributes: BestTransactionsAttributes, ) -> Box>>> { - self.pool.read().best_transactions_with_attributes(best_transactions_attributes) + self.get_pool_data().best_transactions_with_attributes(best_transactions_attributes) } /// Returns all transactions from the pending sub-pool pub(crate) fn pending_transactions(&self) -> Vec>> { - self.pool.read().pending_transactions() + self.get_pool_data().pending_transactions() } /// Returns all transactions from parked pools pub(crate) fn queued_transactions(&self) -> Vec>> { - self.pool.read().queued_transactions() + self.get_pool_data().queued_transactions() } /// Returns all transactions in the pool pub(crate) fn all_transactions(&self) -> AllPoolTransactions { - let pool = self.pool.read(); + let pool = self.get_pool_data(); AllPoolTransactions { pending: pool.pending_transactions(), queued: pool.queued_transactions(), @@ -681,7 +681,7 @@ where if hashes.is_empty() { return } - let pool = self.pool.read(); + let pool = self.get_pool_data(); hashes.retain(|tx| !pool.contains(tx)) } @@ -690,7 +690,7 @@ where &self, tx_hash: &TxHash, ) -> Option>> { - self.pool.read().get(tx_hash) + self.get_pool_data().get(tx_hash) } /// Returns all transactions of the address @@ -699,7 +699,7 @@ where sender: Address, ) -> Vec>> { let sender_id = self.get_sender_id(sender); - self.pool.read().get_transactions_by_sender(sender_id) + self.get_pool_data().get_transactions_by_sender(sender_id) } /// Returns all transactions that where submitted with the given [TransactionOrigin] @@ -707,7 +707,7 @@ where &self, origin: TransactionOrigin, ) -> Vec>> { - self.pool.read().all().transactions_iter().filter(|tx| tx.origin == origin).collect() + self.get_pool_data().all().transactions_iter().filter(|tx| tx.origin == origin).collect() } /// Returns all the transactions belonging to the hashes. @@ -720,7 +720,7 @@ where if txs.is_empty() { return Vec::new() } - self.pool.read().get_all(txs).collect() + self.get_pool_data().get_all(txs).collect() } /// Notify about propagated transactions. @@ -735,12 +735,12 @@ where /// Number of transactions in the entire pool pub(crate) fn len(&self) -> usize { - self.pool.read().len() + self.get_pool_data().len() } /// Whether the pool is empty pub(crate) fn is_empty(&self) -> bool { - self.pool.read().is_empty() + self.get_pool_data().is_empty() } /// Enforces the size limits of pool and returns the discarded transactions if violated. From 5f53545f45246c5b1b414ff5ecb83e67bc49f5aa Mon Sep 17 00:00:00 2001 From: Matthias Seitz Date: Tue, 2 Jan 2024 16:00:53 +0100 Subject: [PATCH 237/277] fix: use unchecked sender recovery when loading from disk (#5919) --- crates/primitives/src/block.rs | 29 ++++++++++++++++--- crates/primitives/src/transaction/mod.rs | 18 +++++++++++- .../src/providers/database/provider.rs | 9 +++++- 3 files changed, 50 insertions(+), 6 deletions(-) diff --git a/crates/primitives/src/block.rs b/crates/primitives/src/block.rs index 37cb5954bf50..ee458137f6f3 100644 --- a/crates/primitives/src/block.rs +++ b/crates/primitives/src/block.rs @@ -64,16 +64,37 @@ impl Block { /// /// If the number of senders does not match the number of transactions in the block /// and the signer recovery for one of the transactions fails. + /// + /// Note: this is expected to be called with blocks read from disk. + #[track_caller] + pub fn with_senders_unchecked(self, senders: Vec
) -> BlockWithSenders { + self.try_with_senders_unchecked(senders).expect("stored block is valid") + } + + /// Transform into a [`BlockWithSenders`] using the given senders. + /// + /// If the number of senders does not match the number of transactions in the block, this falls + /// back to manually recovery, but _without ensuring that the signature has a low `s` value_. + /// See also [TransactionSigned::recover_signer_unchecked] + /// + /// Returns an error if a signature is invalid. #[track_caller] - pub fn with_senders(self, senders: Vec
) -> BlockWithSenders { + pub fn try_with_senders_unchecked( + self, + senders: Vec
, + ) -> Result { let senders = if self.body.len() == senders.len() { senders } else { - TransactionSigned::recover_signers(&self.body, self.body.len()) - .expect("stored block is valid") + let Some(senders) = + TransactionSigned::recover_signers_unchecked(&self.body, self.body.len()) + else { + return Err(self); + }; + senders }; - BlockWithSenders { block: self, senders } + Ok(BlockWithSenders { block: self, senders }) } /// **Expensive**. Transform into a [`BlockWithSenders`] by recovering senders in the contained diff --git a/crates/primitives/src/transaction/mod.rs b/crates/primitives/src/transaction/mod.rs index 448852582baf..82b08a863548 100644 --- a/crates/primitives/src/transaction/mod.rs +++ b/crates/primitives/src/transaction/mod.rs @@ -996,7 +996,7 @@ impl TransactionSigned { self.signature.recover_signer_unchecked(signature_hash) } - /// Recovers a list of signers from a transaction list iterator + /// Recovers a list of signers from a transaction list iterator. /// /// Returns `None`, if some transaction's signature is invalid, see also /// [Self::recover_signer]. @@ -1011,6 +1011,22 @@ impl TransactionSigned { } } + /// Recovers a list of signers from a transaction list iterator _without ensuring that the + /// signature has a low `s` value_. + /// + /// Returns `None`, if some transaction's signature is invalid, see also + /// [Self::recover_signer_unchecked]. + pub fn recover_signers_unchecked<'a, T>(txes: T, num_txes: usize) -> Option> + where + T: IntoParallelIterator + IntoIterator + Send, + { + if num_txes < *PARALLEL_SENDER_RECOVERY_THRESHOLD { + txes.into_iter().map(|tx| tx.recover_signer_unchecked()).collect() + } else { + txes.into_par_iter().map(|tx| tx.recover_signer_unchecked()).collect() + } + } + /// Returns the [TransactionSignedEcRecovered] transaction with the given sender. #[inline] pub const fn with_signer(self, signer: Address) -> TransactionSignedEcRecovered { diff --git a/crates/storage/provider/src/providers/database/provider.rs b/crates/storage/provider/src/providers/database/provider.rs index bce17cccc7c2..52f675873239 100644 --- a/crates/storage/provider/src/providers/database/provider.rs +++ b/crates/storage/provider/src/providers/database/provider.rs @@ -1348,7 +1348,14 @@ impl BlockReader for DatabaseProvider { }) .collect(); - Ok(Some(Block { header, body, ommers, withdrawals }.with_senders(senders))) + let block = Block { header, body, ommers, withdrawals }; + let block = block + // Note: we're using unchecked here because we know the block contains valid txs wrt to + // its height and can ignore the s value check so pre EIP-2 txs are allowed + .try_with_senders_unchecked(senders) + .map_err(|_| ProviderError::SenderRecoveryError)?; + + Ok(Some(block)) } fn block_range(&self, range: RangeInclusive) -> ProviderResult> { From dcf1d5090fe34f0a7f04e08b16d7ca68f6fa7889 Mon Sep 17 00:00:00 2001 From: Dan Cline <6798349+Rjected@users.noreply.github.com> Date: Tue, 2 Jan 2024 14:02:41 -0500 Subject: [PATCH 238/277] docs: improve rlp transaction encoding docs (#5922) --- crates/net/eth-wire/src/disconnect.rs | 8 +- crates/net/eth-wire/src/p2pstream.rs | 20 ++-- crates/net/eth-wire/src/types/message.rs | 8 +- crates/primitives/src/transaction/eip1559.rs | 10 ++ crates/primitives/src/transaction/eip2930.rs | 8 ++ crates/primitives/src/transaction/eip4844.rs | 6 ++ crates/primitives/src/transaction/legacy.rs | 14 +++ crates/primitives/src/transaction/mod.rs | 102 ++++++++++++------ crates/primitives/src/transaction/pooled.rs | 16 ++- crates/rpc/rpc-types/src/eth/block.rs | 6 +- .../rpc-types/src/eth/transaction/typed.rs | 24 +++++ 11 files changed, 168 insertions(+), 54 deletions(-) diff --git a/crates/net/eth-wire/src/disconnect.rs b/crates/net/eth-wire/src/disconnect.rs index e03f99f07f9f..b3d5867bfa12 100644 --- a/crates/net/eth-wire/src/disconnect.rs +++ b/crates/net/eth-wire/src/disconnect.rs @@ -104,9 +104,9 @@ impl TryFrom for DisconnectReason { } } -/// The [`Encodable`] implementation for [`DisconnectReason`] encodes the disconnect reason in a -/// single-element RLP list. impl Encodable for DisconnectReason { + /// The [`Encodable`] implementation for [`DisconnectReason`] encodes the disconnect reason in + /// a single-element RLP list. fn encode(&self, out: &mut dyn BufMut) { vec![*self as u8].encode(out); } @@ -115,9 +115,9 @@ impl Encodable for DisconnectReason { } } -/// The [`Decodable`] implementation for [`DisconnectReason`] supports either a disconnect reason -/// encoded a single byte or a RLP list containing the disconnect reason. impl Decodable for DisconnectReason { + /// The [`Decodable`] implementation for [`DisconnectReason`] supports either a disconnect + /// reason encoded a single byte or a RLP list containing the disconnect reason. fn decode(buf: &mut &[u8]) -> alloy_rlp::Result { if buf.is_empty() { return Err(RlpError::InputTooShort) diff --git a/crates/net/eth-wire/src/p2pstream.rs b/crates/net/eth-wire/src/p2pstream.rs index 5809de260284..d46ece6836cf 100644 --- a/crates/net/eth-wire/src/p2pstream.rs +++ b/crates/net/eth-wire/src/p2pstream.rs @@ -643,11 +643,11 @@ impl P2PMessage { } } -/// The [`Encodable`] implementation for [`P2PMessage::Ping`] and [`P2PMessage::Pong`] encodes the -/// message as RLP, and prepends a snappy header to the RLP bytes for all variants except the -/// [`P2PMessage::Hello`] variant, because the hello message is never compressed in the `p2p` -/// subprotocol. impl Encodable for P2PMessage { + /// The [`Encodable`] implementation for [`P2PMessage::Ping`] and [`P2PMessage::Pong`] encodes + /// the message as RLP, and prepends a snappy header to the RLP bytes for all variants except + /// the [`P2PMessage::Hello`] variant, because the hello message is never compressed in the + /// `p2p` subprotocol. fn encode(&self, out: &mut dyn BufMut) { (self.message_id() as u8).encode(out); match self { @@ -680,13 +680,13 @@ impl Encodable for P2PMessage { } } -/// The [`Decodable`] implementation for [`P2PMessage`] assumes that each of the message variants -/// are snappy compressed, except for the [`P2PMessage::Hello`] variant since the hello message is -/// never compressed in the `p2p` subprotocol. -/// -/// The [`Decodable`] implementation for [`P2PMessage::Ping`] and [`P2PMessage::Pong`] expects a -/// snappy encoded payload, see [`Encodable`] implementation. impl Decodable for P2PMessage { + /// The [`Decodable`] implementation for [`P2PMessage`] assumes that each of the message + /// variants are snappy compressed, except for the [`P2PMessage::Hello`] variant since the + /// hello message is never compressed in the `p2p` subprotocol. + /// + /// The [`Decodable`] implementation for [`P2PMessage::Ping`] and [`P2PMessage::Pong`] expects + /// a snappy encoded payload, see [`Encodable`] implementation. fn decode(buf: &mut &[u8]) -> alloy_rlp::Result { /// Removes the snappy prefix from the Ping/Pong buffer fn advance_snappy_ping_pong_payload(buf: &mut &[u8]) -> alloy_rlp::Result<()> { diff --git a/crates/net/eth-wire/src/types/message.rs b/crates/net/eth-wire/src/types/message.rs index fe247c0a892b..bf9d0bdcbf62 100644 --- a/crates/net/eth-wire/src/types/message.rs +++ b/crates/net/eth-wire/src/types/message.rs @@ -105,9 +105,9 @@ impl ProtocolMessage { } } -/// Encodes the protocol message into bytes. -/// The message type is encoded as a single byte and prepended to the message. impl Encodable for ProtocolMessage { + /// Encodes the protocol message into bytes. The message type is encoded as a single byte and + /// prepended to the message. fn encode(&self, out: &mut dyn BufMut) { self.message_type.encode(out); self.message.encode(out); @@ -130,9 +130,9 @@ pub struct ProtocolBroadcastMessage { pub message: EthBroadcastMessage, } -/// Encodes the protocol message into bytes. -/// The message type is encoded as a single byte and prepended to the message. impl Encodable for ProtocolBroadcastMessage { + /// Encodes the protocol message into bytes. The message type is encoded as a single byte and + /// prepended to the message. fn encode(&self, out: &mut dyn BufMut) { self.message_type.encode(out); self.message.encode(out); diff --git a/crates/primitives/src/transaction/eip1559.rs b/crates/primitives/src/transaction/eip1559.rs index 43f9e0db682b..89f78e72364c 100644 --- a/crates/primitives/src/transaction/eip1559.rs +++ b/crates/primitives/src/transaction/eip1559.rs @@ -138,6 +138,10 @@ impl TxEip1559 { /// Inner encoding function that is used for both rlp [`Encodable`] trait and for calculating /// hash that for eip2718 does not require rlp header + /// + /// This encodes the transaction as: + /// `rlp(chain_id, nonce, max_priority_fee_per_gas, max_fee_per_gas, gas_limit to, value, input, + /// access_list, y_parity, r, s)` pub(crate) fn encode_with_signature( &self, signature: &Signature, @@ -192,6 +196,12 @@ impl TxEip1559 { } /// Encodes the legacy transaction in RLP for signing. + /// + /// This encodes the transaction as: + /// `tx_type || rlp(chain_id, nonce, max_priority_fee_per_gas, max_fee_per_gas, gas_limit, to, + /// value, input, access_list)` + /// + /// Note that there is no rlp header before the transaction type byte. pub(crate) fn encode_for_signing(&self, out: &mut dyn bytes::BufMut) { out.put_u8(self.tx_type() as u8); Header { list: true, payload_length: self.fields_len() }.encode(out); diff --git a/crates/primitives/src/transaction/eip2930.rs b/crates/primitives/src/transaction/eip2930.rs index 5cd4d7803473..1e8c51f47b73 100644 --- a/crates/primitives/src/transaction/eip2930.rs +++ b/crates/primitives/src/transaction/eip2930.rs @@ -117,6 +117,9 @@ impl TxEip2930 { /// Inner encoding function that is used for both rlp [`Encodable`] trait and for calculating /// hash that for eip2718 does not require rlp header + /// + /// This encodes the transaction as: + /// `rlp(nonce, gas_price, gas_limit, to, value, input, access_list, y_parity, r, s)` pub(crate) fn encode_with_signature( &self, signature: &Signature, @@ -157,6 +160,11 @@ impl TxEip2930 { } /// Encodes the legacy transaction in RLP for signing. + /// + /// This encodes the transaction as: + /// `tx_type || rlp(chain_id, nonce, gas_price, gas_limit, to, value, input, access_list)` + /// + /// Note that there is no rlp header before the transaction type byte. pub(crate) fn encode_for_signing(&self, out: &mut dyn bytes::BufMut) { out.put_u8(self.tx_type() as u8); Header { list: true, payload_length: self.fields_len() }.encode(out); diff --git a/crates/primitives/src/transaction/eip4844.rs b/crates/primitives/src/transaction/eip4844.rs index 87d9cee9e05d..bafe7c0196ea 100644 --- a/crates/primitives/src/transaction/eip4844.rs +++ b/crates/primitives/src/transaction/eip4844.rs @@ -295,6 +295,12 @@ impl TxEip4844 { } /// Encodes the legacy transaction in RLP for signing. + /// + /// This encodes the transaction as: + /// `tx_type || rlp(chain_id, nonce, max_priority_fee_per_gas, max_fee_per_gas, gas_limit, to, + /// value, input, access_list, max_fee_per_blob_gas, blob_versioned_hashes)` + /// + /// Note that there is no rlp header before the transaction type byte. pub(crate) fn encode_for_signing(&self, out: &mut dyn bytes::BufMut) { out.put_u8(self.tx_type() as u8); Header { list: true, payload_length: self.fields_len() }.encode(out); diff --git a/crates/primitives/src/transaction/legacy.rs b/crates/primitives/src/transaction/legacy.rs index 98153c6bd6da..2a5b0b19a9ea 100644 --- a/crates/primitives/src/transaction/legacy.rs +++ b/crates/primitives/src/transaction/legacy.rs @@ -81,6 +81,11 @@ impl TxLegacy { /// Inner encoding function that is used for both rlp [`Encodable`] trait and for calculating /// hash. + /// + /// This encodes the transaction as: + /// `rlp(nonce, gas_price, gas_limit, to, value, input, v, r, s)` + /// + /// The `v` value is encoded according to EIP-155 if the `chain_id` is not `None`. pub(crate) fn encode_with_signature(&self, signature: &Signature, out: &mut dyn bytes::BufMut) { let payload_length = self.fields_len() + signature.payload_len_with_eip155_chain_id(self.chain_id); @@ -105,6 +110,9 @@ impl TxLegacy { /// Encodes EIP-155 arguments into the desired buffer. Only encodes values for legacy /// transactions. + /// + /// If a `chain_id` is `Some`, this encodes the `chain_id`, followed by two zeroes, as defined + /// by [EIP-155](https://eips.ethereum.org/EIPS/eip-155). pub(crate) fn encode_eip155_fields(&self, out: &mut dyn bytes::BufMut) { // if this is a legacy transaction without a chain ID, it must be pre-EIP-155 // and does not need to encode the chain ID for the signature hash encoding @@ -131,6 +139,12 @@ impl TxLegacy { } /// Encodes the legacy transaction in RLP for signing, including the EIP-155 fields if possible. + /// + /// If a `chain_id` is `Some`, this encodes the transaction as: + /// `rlp(nonce, gas_price, gas_limit, to, value, input, chain_id, 0, 0)` + /// + /// Otherwise, this encodes the transaction as: + /// `rlp(nonce, gas_price, gas_limit, to, value, input)` pub(crate) fn encode_for_signing(&self, out: &mut dyn bytes::BufMut) { Header { list: true, payload_length: self.fields_len() + self.eip155_fields_len() } .encode(out); diff --git a/crates/primitives/src/transaction/mod.rs b/crates/primitives/src/transaction/mod.rs index 82b08a863548..01bf2e82d11c 100644 --- a/crates/primitives/src/transaction/mod.rs +++ b/crates/primitives/src/transaction/mod.rs @@ -669,9 +669,9 @@ impl Default for Transaction { } } -/// This encodes the transaction _without_ the signature, and is only suitable for creating a hash -/// intended for signing. impl Encodable for Transaction { + /// This encodes the transaction _without_ the signature, and is only suitable for creating a + /// hash intended for signing. fn encode(&self, out: &mut dyn bytes::BufMut) { match self { Transaction::Legacy(legacy_tx) => { @@ -771,12 +771,19 @@ impl Compact for TransactionKind { } impl Encodable for TransactionKind { + /// This encodes the `to` field of a transaction request. + /// If the [TransactionKind] is a [TransactionKind::Call] it will encode the inner address: + /// `rlp(address)` + /// + /// If the [TransactionKind] is a [TransactionKind::Create] it will encode an empty list: + /// `rlp([])`, which is also fn encode(&self, out: &mut dyn alloy_rlp::BufMut) { match self { TransactionKind::Call(to) => to.encode(out), TransactionKind::Create => out.put_u8(EMPTY_STRING_CODE), } } + fn length(&self) -> usize { match self { TransactionKind::Call(to) => to.length(), @@ -1092,9 +1099,10 @@ impl TransactionSigned { /// Encodes the transaction into the "raw" format (e.g. `eth_sendRawTransaction`). /// This format is also referred to as "binary" encoding. /// - /// For legacy transactions, it encodes the RLP of the transaction into the buffer: `rlp(tx)` + /// For legacy transactions, it encodes the RLP of the transaction into the buffer: + /// `rlp(tx-data)` /// For EIP-2718 typed it encodes the type of the transaction followed by the rlp of the - /// transaction: `type || rlp(tx)` + /// transaction: `tx-type || rlp(tx-data)` pub fn encode_enveloped(&self, out: &mut dyn bytes::BufMut) { self.encode_inner(out, false) } @@ -1211,12 +1219,12 @@ impl TransactionSigned { /// /// This should be used _only_ be used internally in general transaction decoding methods, /// which have already ensured that the input is a typed transaction with the following format: - /// `tx_type || rlp(tx)` + /// `tx-type || rlp(tx-data)` /// /// Note that this format does not start with any RLP header, and instead starts with a single /// byte indicating the transaction type. /// - /// CAUTION: this expects that `data` is `tx_type || rlp(tx)` + /// CAUTION: this expects that `data` is `tx-type || rlp(tx-data)` pub fn decode_enveloped_typed_transaction( data: &mut &[u8], ) -> alloy_rlp::Result { @@ -1276,13 +1284,13 @@ impl TransactionSigned { /// /// A raw transaction is either a legacy transaction or EIP-2718 typed transaction. /// - /// For legacy transactions, the format is encoded as: `rlp(tx)`. This format will start with a - /// RLP list header. + /// For legacy transactions, the format is encoded as: `rlp(tx-data)`. This format will start + /// with a RLP list header. /// /// For EIP-2718 typed transactions, the format is encoded as the type of the transaction - /// followed by the rlp of the transaction: `type || rlp(tx)`. + /// followed by the rlp of the transaction: `type || rlp(tx-data)`. /// - /// To decode EIP-4844 transactions in `eth_sendRawTransaction`, use + /// To decode EIP-4844 transactions from `eth_sendRawTransaction`, use /// [PooledTransactionsElement::decode_enveloped]. pub fn decode_enveloped(data: &mut &[u8]) -> alloy_rlp::Result { if data.is_empty() { @@ -1325,6 +1333,14 @@ impl From for TransactionSigned { } impl Encodable for TransactionSigned { + /// This encodes the transaction _with_ the signature, and an rlp header. + /// + /// For legacy transactions, it encodes the transaction data: + /// `rlp(tx-data)` + /// + /// For EIP-2718 typed transactions, it encodes the transaction type followed by the rlp of the + /// transaction: + /// `rlp(tx-type || rlp(tx-data))` fn encode(&self, out: &mut dyn bytes::BufMut) { self.encode_inner(out, true); } @@ -1334,29 +1350,37 @@ impl Encodable for TransactionSigned { } } -/// This `Decodable` implementation only supports decoding rlp encoded transactions as it's used by -/// p2p. -/// -/// The p2p encoding format always includes an RLP header, although the type RLP header depends on -/// whether or not the transaction is a legacy transaction. -/// -/// If the transaction is a legacy transaction, it is just encoded as a RLP list: `rlp(tx)`. -/// -/// If the transaction is a typed transaction, it is encoded as a RLP string: -/// `rlp(type || rlp(tx))` -/// -/// This cannot be used for decoding EIP-4844 transactions in p2p, since the EIP-4844 variant of -/// [TransactionSigned] does not include the blob sidecar. For a general purpose decoding method -/// suitable for decoding transactions from p2p, see [PooledTransactionsElement]. -/// -/// CAUTION: Due to a quirk in [Header::decode], this method will succeed even if a typed -/// transaction is encoded in the RPC format, and does not start with a RLP header. This is because -/// [Header::decode] does not advance the buffer, and returns a length-1 string header if the first -/// byte is less than `0xf7`. This causes this decode implementation to pass unaltered buffer to -/// [TransactionSigned::decode_enveloped_typed_transaction], which expects the RPC format. Despite -/// this quirk, this should **not** be used for RPC methods that accept raw transactions. impl Decodable for TransactionSigned { + /// This `Decodable` implementation only supports decoding rlp encoded transactions as it's used + /// by p2p. + /// + /// The p2p encoding format always includes an RLP header, although the type RLP header depends + /// on whether or not the transaction is a legacy transaction. + /// + /// If the transaction is a legacy transaction, it is just encoded as a RLP list: + /// `rlp(tx-data)`. + /// + /// If the transaction is a typed transaction, it is encoded as a RLP string: + /// `rlp(tx-type || rlp(tx-data))` + /// + /// This can be used for decoding all signed transactions in p2p `BlockBodies` responses. + /// + /// This cannot be used for decoding EIP-4844 transactions in p2p `PooledTransactions`, since + /// the EIP-4844 variant of [TransactionSigned] does not include the blob sidecar. + /// + /// For a method suitable for decoding pooled transactions, see [PooledTransactionsElement]. + /// + /// CAUTION: Due to a quirk in [Header::decode], this method will succeed even if a typed + /// transaction is encoded in this format, and does not start with a RLP header: + /// `tx-type || rlp(tx-data)`. + /// + /// This is because [Header::decode] does not advance the buffer, and returns a length-1 string + /// header if the first byte is less than `0xf7`. fn decode(buf: &mut &[u8]) -> alloy_rlp::Result { + if buf.is_empty() { + return Err(RlpError::InputTooShort) + } + // decode header let mut original_encoding = *buf; let header = Header::decode(buf)?; @@ -1480,6 +1504,9 @@ impl TransactionSignedEcRecovered { } impl Encodable for TransactionSignedEcRecovered { + /// This encodes the transaction _with_ the signature, and an rlp header. + /// + /// Refer to docs for [TransactionSigned::encode] for details on the exact format. fn encode(&self, out: &mut dyn bytes::BufMut) { self.signed_transaction.encode(out) } @@ -1569,6 +1596,19 @@ mod tests { assert_eq!(RlpError::InputTooShort, res); } + #[test] + fn raw_kind_encoding_sanity() { + // check the 0x80 encoding for Create + let mut buf = Vec::new(); + TransactionKind::Create.encode(&mut buf); + assert_eq!(buf, vec![0x80]); + + // check decoding + let buf = [0x80]; + let decoded = TransactionKind::decode(&mut &buf[..]).unwrap(); + assert_eq!(decoded, TransactionKind::Create); + } + #[test] fn test_decode_create_goerli() { // test that an example create tx from goerli decodes properly diff --git a/crates/primitives/src/transaction/pooled.rs b/crates/primitives/src/transaction/pooled.rs index bff710968902..72b0e1c8838d 100644 --- a/crates/primitives/src/transaction/pooled.rs +++ b/crates/primitives/src/transaction/pooled.rs @@ -321,17 +321,27 @@ impl PooledTransactionsElement { impl Encodable for PooledTransactionsElement { /// Encodes an enveloped post EIP-4844 [PooledTransactionsElement]. + /// + /// For legacy transactions, this encodes the transaction as `rlp(tx-data)`. + /// + /// For EIP-2718 transactions, this encodes the transaction as `rlp(tx_type || rlp(tx-data)))`. fn encode(&self, out: &mut dyn bytes::BufMut) { + // The encoding of `tx-data` depends on the transaction type. Refer to these docs for more + // information on the exact format: + // - Legacy: TxLegacy::encode_with_signature + // - EIP-2930: TxEip2930::encode_with_signature + // - EIP-1559: TxEip1559::encode_with_signature + // - EIP-4844: BlobTransaction::encode_with_type_inner match self { Self::Legacy { transaction, signature, .. } => { transaction.encode_with_signature(signature, out) } Self::Eip2930 { transaction, signature, .. } => { - // encodes with header + // encodes with string header transaction.encode_with_signature(signature, out, true) } Self::Eip1559 { transaction, signature, .. } => { - // encodes with header + // encodes with string header transaction.encode_with_signature(signature, out, true) } Self::BlobTransaction(blob_tx) => { @@ -377,7 +387,7 @@ impl Encodable for PooledTransactionsElement { impl Decodable for PooledTransactionsElement { /// Decodes an enveloped post EIP-4844 [PooledTransactionsElement]. /// - /// CAUTION: this expects that `buf` is `[id, rlp(tx)]` + /// CAUTION: this expects that `buf` is `rlp(tx_type || rlp(tx-data))` fn decode(buf: &mut &[u8]) -> alloy_rlp::Result { // From the EIP-4844 spec: // Blob transactions have two network representations. During transaction gossip responses diff --git a/crates/rpc/rpc-types/src/eth/block.rs b/crates/rpc/rpc-types/src/eth/block.rs index 124a2a322f63..91370b7cf70e 100644 --- a/crates/rpc/rpc-types/src/eth/block.rs +++ b/crates/rpc/rpc-types/src/eth/block.rs @@ -646,8 +646,8 @@ impl From for BlockHashOrNumber { } } -/// Allows for RLP encoding of either a block hash or block number 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), @@ -662,8 +662,10 @@ impl Encodable for BlockHashOrNumber { } } -/// Allows for RLP decoding of a block hash or block number 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 diff --git a/crates/rpc/rpc-types/src/eth/transaction/typed.rs b/crates/rpc/rpc-types/src/eth/transaction/typed.rs index 6dd0314cbe62..65c577680105 100644 --- a/crates/rpc/rpc-types/src/eth/transaction/typed.rs +++ b/crates/rpc/rpc-types/src/eth/transaction/typed.rs @@ -107,6 +107,12 @@ impl TransactionKind { } impl Encodable for TransactionKind { + /// This encodes the `to` field of a transaction request. + /// If the [TransactionKind] is a [TransactionKind::Call] it will encode the inner address: + /// `rlp(address)` + /// + /// If the [TransactionKind] is a [TransactionKind::Create] it will encode an empty list: + /// `rlp([])`, which is also fn encode(&self, out: &mut dyn BufMut) { match self { TransactionKind::Call(to) => to.encode(out), @@ -148,3 +154,21 @@ pub struct BlobTransactionSidecar { /// The blob proofs. pub proofs: Vec, } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn raw_kind_encoding_sanity() { + // check the 0x80 encoding for Create + let mut buf = Vec::new(); + TransactionKind::Create.encode(&mut buf); + assert_eq!(buf, vec![0x80]); + + // check decoding + let buf = [0x80]; + let decoded = TransactionKind::decode(&mut &buf[..]).unwrap(); + assert_eq!(decoded, TransactionKind::Create); + } +} From 32a75dd26516904ae52da1a629c6b789aa3b74ec Mon Sep 17 00:00:00 2001 From: David Kulman <77690992+Kuly14@users.noreply.github.com> Date: Wed, 3 Jan 2024 00:03:55 +0100 Subject: [PATCH 239/277] Update broken links to `docs/crates/discv4.md` (#5923) --- docs/crates/network.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/crates/network.md b/docs/crates/network.md index fea2556d7712..7d43589f4e2d 100644 --- a/docs/crates/network.md +++ b/docs/crates/network.md @@ -8,7 +8,7 @@ Reth's P2P networking consists primarily of 4 ongoing tasks: - **ETH Requests**: Responds to incoming requests for headers and bodies - **Network Management**: Handles incoming & outgoing connections with peers, and routes requests between peers and the other tasks -We'll leave most of the discussion of the discovery task for the [discv4](../discv4/README.md) chapter, and will focus on the other three here. +We'll leave most of the discussion of the discovery task for the [discv4](./discv4.md) chapter, and will focus on the other three here. Let's take a look at how the main Reth CLI (i.e., a default-configured full node) makes use of the P2P layer to explore the primary interfaces and entrypoints into the `network` crate. @@ -168,7 +168,7 @@ The `Swarm` struct glues together incoming connections from peers, managing sess We'll touch more on the `NetworkManager` shortly! It's perhaps the most important struct in this crate. -More information about the discovery task can be found in the [discv4](../discv4/README.md) chapter. +More information about the discovery task can be found in the [discv4](./discv4.md) chapter. The ETH requests and transactions task will be explained in their own sections, following this one. From 62d2cedff2b6333038b83568b3369ac8b9b85eed Mon Sep 17 00:00:00 2001 From: Alexey Shekhirin Date: Wed, 3 Jan 2024 11:24:24 +0000 Subject: [PATCH 240/277] chore(storage): warning log about long read db transaction (#5929) --- crates/storage/db/src/implementation/mdbx/tx.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/storage/db/src/implementation/mdbx/tx.rs b/crates/storage/db/src/implementation/mdbx/tx.rs index 00e78c53b290..ffa2a0fe2467 100644 --- a/crates/storage/db/src/implementation/mdbx/tx.rs +++ b/crates/storage/db/src/implementation/mdbx/tx.rs @@ -13,7 +13,7 @@ use crate::{ use parking_lot::RwLock; use reth_interfaces::db::{DatabaseWriteError, DatabaseWriteOperation}; use reth_libmdbx::{ffi::DBI, CommitLatency, Transaction, TransactionKind, WriteFlags, RW}; -use reth_tracing::tracing::debug; +use reth_tracing::tracing::warn; use std::{ backtrace::Backtrace, marker::PhantomData, @@ -192,7 +192,7 @@ impl MetricsHandler { self.backtrace_recorded.store(true, Ordering::Relaxed); let backtrace = Backtrace::force_capture(); - debug!( + warn!( target: "storage::db::mdbx", ?open_duration, ?backtrace, From bfb3953231993d413c86bc7a130e3443b4cf061f Mon Sep 17 00:00:00 2001 From: Dan Cline <6798349+Rjected@users.noreply.github.com> Date: Wed, 3 Jan 2024 06:27:15 -0500 Subject: [PATCH 241/277] chore: improve forkchoiceUpdated doc comments (#5925) --- crates/rpc/rpc-api/src/engine.rs | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/crates/rpc/rpc-api/src/engine.rs b/crates/rpc/rpc-api/src/engine.rs index ee04b2ee8eee..94563267ce73 100644 --- a/crates/rpc/rpc-api/src/engine.rs +++ b/crates/rpc/rpc-api/src/engine.rs @@ -35,7 +35,7 @@ pub trait EngineApi { /// See also /// - /// Caution: This should not accept the `withdrawals` field + /// Caution: This should not accept the `withdrawals` field in the payload attributes. #[method(name = "forkchoiceUpdatedV1")] async fn fork_choice_updated_v1( &self, @@ -43,7 +43,15 @@ pub trait EngineApi { payload_attributes: Option, ) -> RpcResult; + /// Post Shanghai forkchoice update handler + /// + /// This is the same as `forkchoiceUpdatedV1`, but expects an additional `withdrawals` field in + /// the [PayloadAttributes], if payload attributes are provided. + /// /// See also + /// + /// Caution: This should not accept the `parentBeaconBlockRoot` field in the payload + /// attributes. #[method(name = "forkchoiceUpdatedV2")] async fn fork_choice_updated_v2( &self, @@ -51,7 +59,11 @@ pub trait EngineApi { payload_attributes: Option, ) -> RpcResult; - /// Same as `forkchoiceUpdatedV2` but supports additional [PayloadAttributes] field. + /// Post Cancun forkchoice update handler + /// + /// This is the same as `forkchoiceUpdatedV2`, but expects an additional + /// `parentBeaconBlockRoot` field in the the [PayloadAttributes], if payload attributes are + /// provided. /// /// See also #[method(name = "forkchoiceUpdatedV3")] From 1c32340b9bf2d42f2d3c2f7cc2c766cbbcbcd880 Mon Sep 17 00:00:00 2001 From: Dan Cline <6798349+Rjected@users.noreply.github.com> Date: Wed, 3 Jan 2024 08:02:37 -0500 Subject: [PATCH 242/277] chore: update licenses to use 2024 (#5926) --- LICENSE-APACHE | 2 +- LICENSE-MIT | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/LICENSE-APACHE b/LICENSE-APACHE index 16fe87b06e80..e154ad895ce6 100644 --- a/LICENSE-APACHE +++ b/LICENSE-APACHE @@ -186,7 +186,7 @@ APPENDIX: How to apply the Apache License to your work. same "printed page" as the copyright notice for easier identification within third-party archives. -Copyright [yyyy] [name of copyright owner] +Copyright 2022-2024 Reth Contributors Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/LICENSE-MIT b/LICENSE-MIT index c60ccae718cc..23dead2c53f8 100644 --- a/LICENSE-MIT +++ b/LICENSE-MIT @@ -1,6 +1,6 @@ The MIT License (MIT) -Copyright (c) 2022-2023 Reth Contributors +Copyright (c) 2022-2024 Reth Contributors Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal From d5975a329101b0c6f59306aa65c61961c843353e Mon Sep 17 00:00:00 2001 From: Dan Cline <6798349+Rjected@users.noreply.github.com> Date: Wed, 3 Jan 2024 08:02:56 -0500 Subject: [PATCH 243/277] fix: merge_ws doc comment (#5924) --- crates/rpc/rpc-builder/src/lib.rs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/crates/rpc/rpc-builder/src/lib.rs b/crates/rpc/rpc-builder/src/lib.rs index 7d848c1eea08..92df2ae70dbc 100644 --- a/crates/rpc/rpc-builder/src/lib.rs +++ b/crates/rpc/rpc-builder/src/lib.rs @@ -1782,11 +1782,11 @@ impl TransportRpcModules { &self.config } - /// Merge the given Methods in the configured http methods. + /// Merge the given [Methods] in the configured http methods. /// /// Fails if any of the methods in other is present already. /// - /// Returns Ok(false) if no http transport is configured. + /// Returns [Ok(false)] if no http transport is configured. pub fn merge_http( &mut self, other: impl Into, @@ -1797,11 +1797,11 @@ impl TransportRpcModules { Ok(false) } - /// Merge the given Methods in the configured ws methods. + /// Merge the given [Methods] in the configured ws methods. /// /// Fails if any of the methods in other is present already. /// - /// Returns Ok(false) if no http transport is configured. + /// Returns [Ok(false)] if no ws transport is configured. pub fn merge_ws( &mut self, other: impl Into, @@ -1812,11 +1812,11 @@ impl TransportRpcModules { Ok(false) } - /// Merge the given Methods in the configured ipc methods. + /// Merge the given [Methods] in the configured ipc methods. /// /// Fails if any of the methods in other is present already. /// - /// Returns Ok(false) if no ipc transport is configured. + /// Returns [Ok(false)] if no ipc transport is configured. pub fn merge_ipc( &mut self, other: impl Into, @@ -1827,7 +1827,7 @@ impl TransportRpcModules { Ok(false) } - /// Merge the given Methods in all configured methods. + /// Merge the given [Methods] in all configured methods. /// /// Fails if any of the methods in other is present already. pub fn merge_configured( From 22ba0adae08019c95db25264b39c6fc135875da6 Mon Sep 17 00:00:00 2001 From: yjh Date: Wed, 3 Jan 2024 21:12:24 +0800 Subject: [PATCH 244/277] chore: simplify some features for env (#5928) --- crates/primitives/src/revm/env.rs | 17 +++-------------- crates/revm/src/processor.rs | 2 -- 2 files changed, 3 insertions(+), 16 deletions(-) diff --git a/crates/primitives/src/revm/env.rs b/crates/primitives/src/revm/env.rs index 3d890bc71e5b..6741d191c0f8 100644 --- a/crates/primitives/src/revm/env.rs +++ b/crates/primitives/src/revm/env.rs @@ -246,9 +246,6 @@ pub fn fill_tx_env( tx_env.access_list.clear(); tx_env.blob_hashes.clear(); tx_env.max_fee_per_blob_gas.take(); - - #[cfg(feature = "optimism")] - fill_op_tx_env(tx_env, transaction, envelope); } Transaction::Eip2930(tx) => { tx_env.gas_limit = tx.gas_limit; @@ -272,9 +269,6 @@ pub fn fill_tx_env( .collect(); tx_env.blob_hashes.clear(); tx_env.max_fee_per_blob_gas.take(); - - #[cfg(feature = "optimism")] - fill_op_tx_env(tx_env, transaction, envelope); } Transaction::Eip1559(tx) => { tx_env.gas_limit = tx.gas_limit; @@ -298,9 +292,6 @@ pub fn fill_tx_env( .collect(); tx_env.blob_hashes.clear(); tx_env.max_fee_per_blob_gas.take(); - - #[cfg(feature = "optimism")] - fill_op_tx_env(tx_env, transaction, envelope); } Transaction::Eip4844(tx) => { tx_env.gas_limit = tx.gas_limit; @@ -324,9 +315,6 @@ pub fn fill_tx_env( .collect(); tx_env.blob_hashes = tx.blob_versioned_hashes.clone(); tx_env.max_fee_per_blob_gas = Some(U256::from(tx.max_fee_per_blob_gas)); - - #[cfg(feature = "optimism")] - fill_op_tx_env(tx_env, transaction, envelope); } #[cfg(feature = "optimism")] Transaction::Deposit(tx) => { @@ -342,10 +330,11 @@ pub fn fill_tx_env( tx_env.data = tx.input.clone(); tx_env.chain_id = None; tx_env.nonce = None; - - fill_op_tx_env(tx_env, transaction, envelope); } } + + #[cfg(feature = "optimism")] + fill_op_tx_env(tx_env, transaction, envelope); } #[cfg(feature = "optimism")] diff --git a/crates/revm/src/processor.rs b/crates/revm/src/processor.rs index a63e493daa09..594327da7a7f 100644 --- a/crates/revm/src/processor.rs +++ b/crates/revm/src/processor.rs @@ -462,8 +462,6 @@ impl<'a> BlockExecutor for EVMProcessor<'a> { cumulative_gas_used, // convert to reth log logs: result.into_logs().into_iter().map(into_reth_log).collect(), - #[cfg(feature = "optimism")] - deposit_nonce: None, }); } From ae4d48730557444c21bb788a75654a100dcda97e Mon Sep 17 00:00:00 2001 From: rakita Date: Thu, 4 Jan 2024 00:05:47 +0100 Subject: [PATCH 245/277] fix(BlockchainTree): remove forked chain receipts/reverts (#5921) --- crates/blockchain-tree/src/blockchain_tree.rs | 144 ++++++++++++++---- crates/blockchain-tree/src/chain.rs | 10 +- .../bundle_state_with_receipts.rs | 12 +- 3 files changed, 135 insertions(+), 31 deletions(-) diff --git a/crates/blockchain-tree/src/blockchain_tree.rs b/crates/blockchain-tree/src/blockchain_tree.rs index 1a60342cdf75..c02e15d8290a 100644 --- a/crates/blockchain-tree/src/blockchain_tree.rs +++ b/crates/blockchain-tree/src/blockchain_tree.rs @@ -147,35 +147,33 @@ impl BlockchainTree { if block.number <= last_finalized_block { // check if block is canonical if self.is_block_hash_canonical(&block.hash)? { - return Ok(Some(BlockStatus::Valid)); + return Ok(Some(BlockStatus::Valid)) } // check if block is inside database if self.externals.provider_factory.provider()?.block_number(block.hash)?.is_some() { - return Ok(Some(BlockStatus::Valid)); + return Ok(Some(BlockStatus::Valid)) } return Err(BlockchainTreeError::PendingBlockIsFinalized { last_finalized: last_finalized_block, } - .into()); + .into()) } // check if block is part of canonical chain if self.is_block_hash_canonical(&block.hash)? { - return Ok(Some(BlockStatus::Valid)); + return Ok(Some(BlockStatus::Valid)) } // is block inside chain if let Some(status) = self.is_block_inside_chain(&block) { - return Ok(Some(status)); + return Ok(Some(status)) } // check if block is disconnected if let Some(block) = self.state.buffered_blocks.block(&block.hash) { - return Ok(Some(BlockStatus::Disconnected { - missing_ancestor: block.parent_num_hash(), - })); + return Ok(Some(BlockStatus::Disconnected { missing_ancestor: block.parent_num_hash() })) } Ok(None) @@ -266,7 +264,7 @@ impl BlockchainTree { // get canonical fork. let canonical_fork = self.canonical_fork(chain_id)?; - return Some(BundleStateData { state, parent_block_hashed, canonical_fork }); + return Some(BundleStateData { state, parent_block_hashed, canonical_fork }) } // check if there is canonical block @@ -276,7 +274,7 @@ impl BlockchainTree { canonical_fork: ForkBlock { number: canonical_number, hash: block_hash }, state: BundleStateWithReceipts::default(), parent_block_hashed: self.canonical_chain().inner().clone(), - }); + }) } None @@ -299,7 +297,7 @@ impl BlockchainTree { // check if block parent can be found in any side chain. if let Some(chain_id) = self.block_indices().get_blocks_chain_id(&parent.hash) { // found parent in side tree, try to insert there - return self.try_insert_block_into_side_chain(block, chain_id, block_validation_kind); + return self.try_insert_block_into_side_chain(block, chain_id, block_validation_kind) } // if not found, check if the parent can be found inside canonical chain. @@ -307,7 +305,7 @@ impl BlockchainTree { .is_block_hash_canonical(&parent.hash) .map_err(|err| InsertBlockError::new(block.block.clone(), err.into()))? { - return self.try_append_canonical_chain(block, block_validation_kind); + return self.try_append_canonical_chain(block, block_validation_kind) } // this is another check to ensure that if the block points to a canonical block its block @@ -323,7 +321,7 @@ impl BlockchainTree { block_number: block.number, }, block.block, - )); + )) } } @@ -400,7 +398,7 @@ impl BlockchainTree { return Err(InsertBlockError::execution_error( BlockValidationError::BlockPreMerge { hash: block.hash }.into(), block.block, - )); + )) } let parent_header = provider @@ -563,7 +561,7 @@ impl BlockchainTree { } else { // if there is no fork block that point to other chains, break the loop. // it means that this fork joins to canonical block. - break; + break } } hashes @@ -584,9 +582,9 @@ impl BlockchainTree { // get fork block chain if let Some(fork_chain_id) = self.block_indices().get_blocks_chain_id(&fork.hash) { chain_id = fork_chain_id; - continue; + continue } - break; + break } (self.block_indices().canonical_hash(&fork.number) == Some(fork.hash)).then_some(fork) } @@ -693,7 +691,7 @@ impl BlockchainTree { pub fn buffer_block(&mut self, block: SealedBlockWithSenders) -> Result<(), InsertBlockError> { // validate block consensus rules if let Err(err) = self.validate_block(&block) { - return Err(InsertBlockError::consensus_error(err, block.block)); + return Err(InsertBlockError::consensus_error(err, block.block)) } self.state.buffered_blocks.insert_block(block); @@ -710,17 +708,17 @@ impl BlockchainTree { ?block, "Failed to validate total difficulty for block {}: {e:?}", block.header.hash ); - return Err(e); + return Err(e) } if let Err(e) = self.externals.consensus.validate_header(block) { error!(?block, "Failed to validate header {}: {e:?}", block.header.hash); - return Err(e); + return Err(e) } if let Err(e) = self.externals.consensus.validate_block(block) { error!(?block, "Failed to validate block {}: {e:?}", block.header.hash); - return Err(e); + return Err(e) } Ok(()) @@ -741,7 +739,7 @@ impl BlockchainTree { Some(BlockStatus::Valid) } else { Some(BlockStatus::Accepted) - }; + } } None } @@ -782,7 +780,7 @@ impl BlockchainTree { // validate block consensus rules if let Err(err) = self.validate_block(&block) { - return Err(InsertBlockError::consensus_error(err, block.block)); + return Err(InsertBlockError::consensus_error(err, block.block)) } Ok(InsertPayloadOk::Inserted( @@ -951,7 +949,7 @@ impl BlockchainTree { } if header.is_none() && self.is_block_hash_inside_chain(*hash) { - return Ok(None); + return Ok(None) } if header.is_none() { @@ -1006,9 +1004,9 @@ impl BlockchainTree { return Err(CanonicalError::from(BlockValidationError::BlockPreMerge { hash: *block_hash, }) - .into()); + .into()) } - return Ok(CanonicalOutcome::AlreadyCanonical { header }); + return Ok(CanonicalOutcome::AlreadyCanonical { header }) } let Some(chain_id) = self.block_indices().get_blocks_chain_id(block_hash) else { @@ -1016,7 +1014,7 @@ impl BlockchainTree { return Err(CanonicalError::from(BlockchainTreeError::BlockHashNotFoundInChain { block_hash: *block_hash, }) - .into()); + .into()) }; let chain = self.state.chains.remove(&chain_id).expect("To be present"); @@ -1180,7 +1178,7 @@ impl BlockchainTree { block_number: tip.number, block_hash: tip.hash, }, - )))); + )))) } recorder.record_relative(MakeCanonicalAction::RetrieveStateTrieUpdates); @@ -1206,7 +1204,7 @@ impl BlockchainTree { pub fn unwind(&mut self, unwind_to: BlockNumber) -> RethResult<()> { // nothing to be done if unwind_to is higher then the tip if self.block_indices().canonical_tip().number <= unwind_to { - return Ok(()); + return Ok(()) } // revert `N` blocks from current canonical chain and put them inside BlockchanTree let old_canon_chain = self.revert_canonical_from_database(unwind_to)?; @@ -1621,6 +1619,94 @@ mod tests { ); } + #[tokio::test] + async fn test_side_chain_fork() { + let data = BlockChainTestData::default_with_numbers(11, 12); + let (block1, exec1) = data.blocks[0].clone(); + let (block2, exec2) = data.blocks[1].clone(); + let genesis = data.genesis; + + // test pops execution results from vector, so order is from last to first. + let externals = setup_externals(vec![exec2.clone(), exec1.clone(), exec2, exec1]); + + // last finalized block would be number 9. + setup_genesis(&externals.provider_factory, genesis); + + // make tree + let config = BlockchainTreeConfig::new(1, 2, 3, 2); + let mut tree = BlockchainTree::new(externals, config, None).expect("failed to create tree"); + // genesis block 10 is already canonical + tree.make_canonical(&B256::ZERO).unwrap(); + + // make genesis block 10 as finalized + tree.finalize_block(10); + + assert_eq!( + tree.insert_block(block1.clone(), BlockValidationKind::Exhaustive).unwrap(), + InsertPayloadOk::Inserted(BlockStatus::Valid) + ); + + assert_eq!( + tree.insert_block(block2.clone(), BlockValidationKind::Exhaustive).unwrap(), + InsertPayloadOk::Inserted(BlockStatus::Valid) + ); + + // we have one chain that has two blocks. + // Trie state: + // b2 (pending block) + // | + // | + // b1 (pending block) + // / + // / + // g1 (canonical blocks) + // | + TreeTester::default() + .with_chain_num(1) + .with_block_to_chain(HashMap::from([(block1.hash, 0.into()), (block2.hash, 0.into())])) + .with_fork_to_child(HashMap::from([(block1.parent_hash, HashSet::from([block1.hash]))])) + .assert(&tree); + + let mut block2a = block2.clone(); + let block2a_hash = B256::new([0x34; 32]); + block2a.hash = block2a_hash; + + assert_eq!( + tree.insert_block(block2a.clone(), BlockValidationKind::Exhaustive).unwrap(), + InsertPayloadOk::Inserted(BlockStatus::Accepted) + ); + + // fork chain. + // Trie state: + // b2 b2a (pending blocks in tree) + // | / + // | / + // b1 + // / + // / + // g1 (canonical blocks) + // | + + TreeTester::default() + .with_chain_num(2) + .with_block_to_chain(HashMap::from([ + (block1.hash, 0.into()), + (block2.hash, 0.into()), + (block2a.hash, 1.into()), + ])) + .with_fork_to_child(HashMap::from([ + (block1.parent_hash, HashSet::from([block1.hash])), + (block2a.parent_hash, HashSet::from([block2a.hash])), + ])) + .assert(&tree); + // chain 0 has two blocks so receipts and reverts len is 2 + assert_eq!(tree.state.chains.get(&0.into()).unwrap().state().receipts().len(), 2); + assert_eq!(tree.state.chains.get(&0.into()).unwrap().state().state().reverts.len(), 2); + // chain 1 has one block so receipts and reverts len is 1 + assert_eq!(tree.state.chains.get(&1.into()).unwrap().state().receipts().len(), 1); + assert_eq!(tree.state.chains.get(&1.into()).unwrap().state().state().reverts.len(), 1); + } + #[tokio::test] async fn sanity_path() { let data = BlockChainTestData::default_with_numbers(11, 12); diff --git a/crates/blockchain-tree/src/chain.rs b/crates/blockchain-tree/src/chain.rs index 6d91282137d3..1af8f73cf2a5 100644 --- a/crates/blockchain-tree/src/chain.rs +++ b/crates/blockchain-tree/src/chain.rs @@ -172,8 +172,16 @@ impl AppendableChain { externals, ) .map_err(|err| InsertBlockError::new(block.block.clone(), err.into()))?; + // extending will also optimize few things, mostly related to selfdestruct and wiping of + // storage. state.extend(block_state); + // remove all receipts and reverts (except the last one), as they belong to the chain we + // forked from and not the new chain we are creating. + let size = state.receipts().len(); + state.receipts_mut().drain(0..size - 1); + state.state_mut().take_n_reverts(size - 1); + // If all is okay, return new chain back. Present chain is not modified. Ok(Self { chain: Chain::from_block(block, state) }) } @@ -224,7 +232,7 @@ impl AppendableChain { return Err(ConsensusError::BodyStateRootDiff( GotExpected { got: state_root, expected: block.state_root }.into(), ) - .into()); + .into()) } } diff --git a/crates/storage/provider/src/bundle_state/bundle_state_with_receipts.rs b/crates/storage/provider/src/bundle_state/bundle_state_with_receipts.rs index 0408afd090d9..958ed5cbd09b 100644 --- a/crates/storage/provider/src/bundle_state/bundle_state_with_receipts.rs +++ b/crates/storage/provider/src/bundle_state/bundle_state_with_receipts.rs @@ -95,6 +95,11 @@ impl BundleStateWithReceipts { &self.bundle } + /// Returns mutable revm bundle state. + pub fn state_mut(&mut self) -> &mut BundleState { + &mut self.bundle + } + /// Set first block. pub fn set_first_block(&mut self, first_block: BlockNumber) { self.first_block = first_block; @@ -268,11 +273,16 @@ impl BundleStateWithReceipts { self.receipts.root_slow(self.block_number_to_index(block_number)?, chain_spec, timestamp) } - /// Return reference to receipts. + /// Returns reference to receipts. pub fn receipts(&self) -> &Receipts { &self.receipts } + /// Returns mutable reference to receipts. + pub fn receipts_mut(&mut self) -> &mut Receipts { + &mut self.receipts + } + /// Return all block receipts pub fn receipts_by_block(&self, block_number: BlockNumber) -> &[Option] { let Some(index) = self.block_number_to_index(block_number) else { return &[] }; From 17b75dd403d1bffd6f10ab636f4f076e0fc4c92b Mon Sep 17 00:00:00 2001 From: Matthias Seitz Date: Thu, 4 Jan 2024 13:40:53 +0100 Subject: [PATCH 246/277] chore: move BeaconConsensus to its own crate (#5937) --- Cargo.lock | 11 ++++++++++- Cargo.toml | 2 ++ crates/consensus/beacon-core/Cargo.toml | 14 ++++++++++++++ .../src/lib.rs} | 17 +++++++++++++++++ crates/consensus/beacon/Cargo.toml | 6 ++---- crates/consensus/beacon/src/lib.rs | 11 ++++++++--- 6 files changed, 53 insertions(+), 8 deletions(-) create mode 100644 crates/consensus/beacon-core/Cargo.toml rename crates/consensus/{beacon/src/beacon_consensus.rs => beacon-core/src/lib.rs} (89%) diff --git a/Cargo.lock b/Cargo.lock index 546fadf361d8..1fea112c6b90 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5726,8 +5726,8 @@ dependencies = [ "cfg-if", "futures", "metrics 0.21.1", + "reth-beacon-consensus-core", "reth-blockchain-tree", - "reth-consensus-common", "reth-db", "reth-downloaders", "reth-interfaces", @@ -5752,6 +5752,15 @@ dependencies = [ "tracing", ] +[[package]] +name = "reth-beacon-consensus-core" +version = "0.1.0-alpha.13" +dependencies = [ + "reth-consensus-common", + "reth-interfaces", + "reth-primitives", +] + [[package]] name = "reth-blockchain-tree" version = "0.1.0-alpha.13" diff --git a/Cargo.toml b/Cargo.toml index 05598c1b9c92..84fd76b96aca 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -5,6 +5,7 @@ members = [ "crates/config/", "crates/consensus/auto-seal/", "crates/consensus/beacon/", + "crates/consensus/beacon-core/", "crates/consensus/common/", "crates/ethereum-forks/", "crates/interfaces/", @@ -96,6 +97,7 @@ reth = { path = "bin/reth" } reth-auto-seal-consensus = { path = "crates/consensus/auto-seal" } reth-basic-payload-builder = { path = "crates/payload/basic" } reth-beacon-consensus = { path = "crates/consensus/beacon" } +reth-beacon-consensus-core = { path = "crates/consensus/beacon-core" } reth-blockchain-tree = { path = "crates/blockchain-tree" } reth-codecs = { path = "crates/storage/codecs" } reth-config = { path = "crates/config" } diff --git a/crates/consensus/beacon-core/Cargo.toml b/crates/consensus/beacon-core/Cargo.toml new file mode 100644 index 000000000000..f411c2d78e9e --- /dev/null +++ b/crates/consensus/beacon-core/Cargo.toml @@ -0,0 +1,14 @@ +[package] +name = "reth-beacon-consensus-core" +version.workspace = true +edition.workspace = true +rust-version.workspace = true +license.workspace = true +homepage.workspace = true +repository.workspace = true + +[dependencies] +# reth +reth-consensus-common.workspace = true +reth-primitives.workspace = true +reth-interfaces.workspace = true \ No newline at end of file diff --git a/crates/consensus/beacon/src/beacon_consensus.rs b/crates/consensus/beacon-core/src/lib.rs similarity index 89% rename from crates/consensus/beacon/src/beacon_consensus.rs rename to crates/consensus/beacon-core/src/lib.rs index d0e8dcf002e2..d12bb4cff53c 100644 --- a/crates/consensus/beacon/src/beacon_consensus.rs +++ b/crates/consensus/beacon-core/src/lib.rs @@ -1,3 +1,20 @@ +//! Beacon consensus implementation. + +#![doc( + html_logo_url = "https://raw.githubusercontent.com/paradigmxyz/reth/main/assets/reth-docs.png", + html_favicon_url = "https://avatars0.githubusercontent.com/u/97369466?s=256", + issue_tracker_base_url = "https://github.com/paradigmxyz/reth/issues/" +)] +#![warn( + missing_debug_implementations, + missing_docs, + unused_crate_dependencies, + unreachable_pub, + rustdoc::all +)] +#![deny(unused_must_use, rust_2018_idioms)] +#![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))] + //! Consensus for ethereum network use reth_consensus_common::validation; diff --git a/crates/consensus/beacon/Cargo.toml b/crates/consensus/beacon/Cargo.toml index 6986fd63afd2..d892b6c26269 100644 --- a/crates/consensus/beacon/Cargo.toml +++ b/crates/consensus/beacon/Cargo.toml @@ -9,7 +9,7 @@ repository.workspace = true [dependencies] # reth -reth-consensus-common.workspace = true +reth-beacon-consensus-core.workspace = true reth-primitives.workspace = true reth-interfaces.workspace = true reth-stages.workspace = true @@ -21,7 +21,6 @@ reth-payload-builder.workspace = true reth-payload-validator.workspace = true reth-prune.workspace = true reth-snapshot.workspace = true -reth-rpc-types-compat.workspace = true reth-tokio-util.workspace = true # async @@ -47,6 +46,7 @@ reth-stages = { workspace = true, features = ["test-utils"] } reth-blockchain-tree = { workspace = true, features = ["test-utils"] } reth-db = { workspace = true, features = ["test-utils"] } reth-provider = { workspace = true, features = ["test-utils"] } +reth-rpc-types-compat.workspace = true reth-tracing.workspace = true reth-revm.workspace = true reth-downloaders.workspace = true @@ -55,12 +55,10 @@ assert_matches.workspace = true [features] optimism = [ - "reth-consensus-common/optimism", "reth-primitives/optimism", "reth-interfaces/optimism", "reth-provider/optimism", "reth-rpc-types/optimism", - "reth-rpc-types-compat/optimism", "reth-payload-builder/optimism", "reth-blockchain-tree/optimism", ] diff --git a/crates/consensus/beacon/src/lib.rs b/crates/consensus/beacon/src/lib.rs index d3904044f7b8..00ee920dc85c 100644 --- a/crates/consensus/beacon/src/lib.rs +++ b/crates/consensus/beacon/src/lib.rs @@ -5,12 +5,17 @@ html_favicon_url = "https://avatars0.githubusercontent.com/u/97369466?s=256", issue_tracker_base_url = "https://github.com/paradigmxyz/reth/issues/" )] -#![warn(missing_debug_implementations, missing_docs, unreachable_pub, rustdoc::all)] +#![warn( + missing_debug_implementations, + missing_docs, + unused_crate_dependencies, + unreachable_pub, + rustdoc::all +)] #![deny(unused_must_use, rust_2018_idioms)] #![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))] -mod beacon_consensus; -pub use beacon_consensus::BeaconConsensus; +pub use reth_beacon_consensus_core::BeaconConsensus; mod engine; pub use engine::*; From 9aa44093cf2db327220d03386beebd6cc653d87e Mon Sep 17 00:00:00 2001 From: Thomas Coratger <60488569+tcoratger@users.noreply.github.com> Date: Thu, 4 Jan 2024 14:38:02 +0100 Subject: [PATCH 247/277] lints: move top-level lints to [workspace.lints] manifest table (#5935) --- Cargo.toml | 8 ++++++++ bin/reth/Cargo.toml | 3 +++ bin/reth/build.rs | 2 ++ bin/reth/src/lib.rs | 3 +-- bin/reth/src/main.rs | 1 + bin/reth/src/optimism.rs | 1 + crates/blockchain-tree/Cargo.toml | 3 +++ crates/blockchain-tree/src/lib.rs | 2 -- crates/config/Cargo.toml | 3 +++ crates/config/src/lib.rs | 2 -- crates/consensus/auto-seal/Cargo.toml | 3 +++ crates/consensus/auto-seal/src/lib.rs | 4 +--- crates/consensus/auto-seal/tests/it/auto_mine.rs | 1 + crates/consensus/beacon/Cargo.toml | 3 +++ crates/consensus/beacon/src/lib.rs | 8 -------- crates/consensus/common/Cargo.toml | 5 ++++- crates/consensus/common/src/lib.rs | 2 -- crates/ethereum-forks/Cargo.toml | 3 +++ crates/ethereum-forks/src/lib.rs | 2 -- crates/interfaces/Cargo.toml | 3 +++ crates/interfaces/src/lib.rs | 9 +-------- crates/metrics/Cargo.toml | 3 +++ crates/metrics/metrics-derive/Cargo.toml | 3 +++ crates/metrics/metrics-derive/src/lib.rs | 2 -- crates/metrics/src/lib.rs | 2 -- crates/net/common/Cargo.toml | 3 +++ crates/net/common/src/lib.rs | 2 -- crates/net/discv4/Cargo.toml | 3 +++ crates/net/discv4/src/lib.rs | 4 +--- crates/net/dns/Cargo.toml | 3 +++ crates/net/dns/src/lib.rs | 4 +--- crates/net/downloaders/Cargo.toml | 3 +++ crates/net/downloaders/src/lib.rs | 2 -- crates/net/ecies/Cargo.toml | 3 +++ crates/net/ecies/src/lib.rs | 2 -- crates/net/eth-wire/Cargo.toml | 3 +++ crates/net/eth-wire/src/lib.rs | 2 -- crates/net/nat/Cargo.toml | 3 +++ crates/net/nat/src/lib.rs | 2 -- crates/net/network-api/Cargo.toml | 3 +++ crates/net/network-api/src/lib.rs | 2 -- crates/net/network/Cargo.toml | 3 +++ crates/net/network/benches/bench.rs | 1 + crates/net/network/src/lib.rs | 3 +-- crates/net/network/tests/it/clique/clique_middleware.rs | 1 + crates/net/network/tests/it/clique/geth.rs | 1 + crates/net/network/tests/it/clique/mod.rs | 1 + crates/net/network/tests/it/multiplex.rs | 1 + crates/net/network/tests/it/requests.rs | 1 + crates/payload/basic/Cargo.toml | 3 +++ crates/payload/basic/src/lib.rs | 8 -------- crates/payload/builder/Cargo.toml | 3 +++ crates/payload/builder/src/lib.rs | 2 -- crates/payload/ethereum/Cargo.toml | 5 ++++- crates/payload/ethereum/src/lib.rs | 2 -- crates/payload/optimism/Cargo.toml | 5 ++++- crates/payload/optimism/src/lib.rs | 2 -- crates/payload/validator/Cargo.toml | 5 ++++- crates/payload/validator/src/lib.rs | 9 +-------- crates/primitives/Cargo.toml | 3 +++ crates/primitives/benches/nibbles.rs | 1 + crates/primitives/benches/recover_ecdsa_crit.rs | 1 + crates/primitives/benches/trie_root.rs | 1 + crates/primitives/src/lib.rs | 2 -- crates/prune/Cargo.toml | 3 +++ crates/prune/src/lib.rs | 3 +-- crates/revm/Cargo.toml | 3 +++ crates/revm/revm-inspectors/Cargo.toml | 5 ++++- crates/revm/revm-inspectors/src/lib.rs | 2 -- crates/revm/src/lib.rs | 2 -- crates/rpc/ipc/Cargo.toml | 3 +++ crates/rpc/ipc/src/lib.rs | 2 -- crates/rpc/rpc-api/Cargo.toml | 3 +++ crates/rpc/rpc-api/src/lib.rs | 2 -- crates/rpc/rpc-builder/Cargo.toml | 3 +++ crates/rpc/rpc-builder/src/lib.rs | 2 -- crates/rpc/rpc-builder/tests/it/http.rs | 1 + crates/rpc/rpc-builder/tests/it/serde.rs | 2 +- crates/rpc/rpc-engine-api/Cargo.toml | 5 ++++- crates/rpc/rpc-engine-api/src/lib.rs | 2 -- crates/rpc/rpc-testing-util/Cargo.toml | 3 +++ crates/rpc/rpc-testing-util/src/lib.rs | 2 -- crates/rpc/rpc-types-compat/Cargo.toml | 3 +++ crates/rpc/rpc-types-compat/src/lib.rs | 2 -- crates/rpc/rpc-types/Cargo.toml | 3 +++ crates/rpc/rpc-types/src/lib.rs | 2 -- crates/rpc/rpc/Cargo.toml | 4 ++++ crates/rpc/rpc/src/lib.rs | 2 -- crates/snapshot/Cargo.toml | 3 +++ crates/snapshot/src/lib.rs | 2 -- crates/stages/Cargo.toml | 3 +++ crates/stages/benches/criterion.rs | 1 + crates/stages/benches/setup/account_hashing.rs | 1 + crates/stages/benches/setup/constants.rs | 1 + crates/stages/benches/setup/mod.rs | 1 + crates/stages/src/lib.rs | 2 -- crates/storage/codecs/Cargo.toml | 5 ++++- crates/storage/codecs/derive/Cargo.toml | 3 +++ crates/storage/codecs/derive/src/lib.rs | 6 +++--- crates/storage/codecs/src/lib.rs | 9 +-------- crates/storage/db/Cargo.toml | 3 +++ crates/storage/db/benches/criterion.rs | 1 + crates/storage/db/benches/hash_keys.rs | 1 + crates/storage/db/benches/iai.rs | 2 +- crates/storage/db/build.rs | 2 ++ crates/storage/db/src/lib.rs | 2 -- crates/storage/libmdbx-rs/Cargo.toml | 3 +++ crates/storage/libmdbx-rs/benches/cursor.rs | 1 + crates/storage/libmdbx-rs/benches/transaction.rs | 1 + crates/storage/libmdbx-rs/benches/utils.rs | 1 + crates/storage/libmdbx-rs/src/lib.rs | 3 +-- crates/storage/nippy-jar/Cargo.toml | 5 ++++- crates/storage/nippy-jar/src/lib.rs | 3 +-- crates/storage/provider/Cargo.toml | 3 +++ crates/storage/provider/src/lib.rs | 2 -- crates/tasks/Cargo.toml | 3 +++ crates/tasks/src/lib.rs | 2 -- crates/tokio-util/Cargo.toml | 3 +++ crates/tokio-util/src/lib.rs | 2 -- crates/tracing/Cargo.toml | 3 +++ crates/tracing/src/lib.rs | 2 -- crates/transaction-pool/Cargo.toml | 3 +++ crates/transaction-pool/src/lib.rs | 2 -- crates/trie/Cargo.toml | 5 ++++- crates/trie/benches/prefix_set.rs | 1 + crates/trie/src/lib.rs | 2 -- testing/ef-tests/Cargo.toml | 3 +++ testing/ef-tests/src/lib.rs | 2 -- 128 files changed, 211 insertions(+), 145 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 84fd76b96aca..6734ecabe269 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -66,6 +66,14 @@ default-members = ["bin/reth"] # https://doc.rust-lang.org/edition-guide/rust-2021/default-cargo-resolver.html resolver = "2" +[workspace.lints] +rust.missing_debug_implementations = "warn" +rust.missing_docs = "warn" +rust.unreachable_pub = "warn" +rustdoc.all = "warn" +rust.unused_must_use = "deny" +rust.rust_2018_idioms = "deny" + [workspace.package] version = "0.1.0-alpha.13" edition = "2021" diff --git a/bin/reth/Cargo.toml b/bin/reth/Cargo.toml index 281fe9e3bd03..4ad25b1cd029 100644 --- a/bin/reth/Cargo.toml +++ b/bin/reth/Cargo.toml @@ -9,6 +9,9 @@ repository.workspace = true description = "Reth node implementation" default-run = "reth" +[lints] +workspace = true + [package.metadata.cargo-udeps.ignore] normal = [ # Used for diagrams in docs diff --git a/bin/reth/build.rs b/bin/reth/build.rs index b06bdf197ce8..f24f9b22db61 100644 --- a/bin/reth/build.rs +++ b/bin/reth/build.rs @@ -1,3 +1,5 @@ +#![allow(missing_docs)] + use std::error::Error; use vergen::EmitBuilder; diff --git a/bin/reth/src/lib.rs b/bin/reth/src/lib.rs index 3a6e0fce53da..e260778e8185 100644 --- a/bin/reth/src/lib.rs +++ b/bin/reth/src/lib.rs @@ -24,8 +24,7 @@ html_favicon_url = "https://avatars0.githubusercontent.com/u/97369466?s=256", issue_tracker_base_url = "https://github.com/paradigmxyz/reth/issues/" )] -#![warn(missing_docs, unreachable_pub, rustdoc::all)] -#![deny(unused_must_use, rust_2018_idioms)] +#![allow(missing_debug_implementations)] #![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))] pub mod args; diff --git a/bin/reth/src/main.rs b/bin/reth/src/main.rs index 44c5655b84e2..ea057ee6b30a 100644 --- a/bin/reth/src/main.rs +++ b/bin/reth/src/main.rs @@ -1,4 +1,5 @@ // We use jemalloc for performance reasons +#![allow(missing_docs)] #[cfg(all(feature = "jemalloc", unix))] #[global_allocator] static ALLOC: jemallocator::Jemalloc = jemallocator::Jemalloc; diff --git a/bin/reth/src/optimism.rs b/bin/reth/src/optimism.rs index 554b91a3ae28..89ba2dd840bf 100644 --- a/bin/reth/src/optimism.rs +++ b/bin/reth/src/optimism.rs @@ -1,3 +1,4 @@ +#![allow(missing_docs, rustdoc::missing_crate_level_docs)] // We use jemalloc for performance reasons #[cfg(all(feature = "jemalloc", unix))] #[global_allocator] diff --git a/crates/blockchain-tree/Cargo.toml b/crates/blockchain-tree/Cargo.toml index c5f0c3a0c79b..fd4b97e0eda4 100644 --- a/crates/blockchain-tree/Cargo.toml +++ b/crates/blockchain-tree/Cargo.toml @@ -7,6 +7,9 @@ license.workspace = true homepage.workspace = true repository.workspace = true +[lints] +workspace = true + [package.metadata.cargo-udeps.ignore] normal = [ # Used for diagrams in docs diff --git a/crates/blockchain-tree/src/lib.rs b/crates/blockchain-tree/src/lib.rs index b825ce5beaa1..6bb3d5412b01 100644 --- a/crates/blockchain-tree/src/lib.rs +++ b/crates/blockchain-tree/src/lib.rs @@ -15,8 +15,6 @@ html_favicon_url = "https://avatars0.githubusercontent.com/u/97369466?s=256", issue_tracker_base_url = "https://github.com/paradigmxyz/reth/issues/" )] -#![warn(missing_debug_implementations, missing_docs, unreachable_pub, rustdoc::all)] -#![deny(unused_must_use, rust_2018_idioms)] #![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))] pub mod blockchain_tree; diff --git a/crates/config/Cargo.toml b/crates/config/Cargo.toml index 7eb14b5eefa3..fbea21259dff 100644 --- a/crates/config/Cargo.toml +++ b/crates/config/Cargo.toml @@ -7,6 +7,9 @@ license.workspace = true homepage.workspace = true repository.workspace = true +[lints] +workspace = true + [dependencies] # reth reth-network.workspace = true diff --git a/crates/config/src/lib.rs b/crates/config/src/lib.rs index 764397f6a2d6..362e814632e3 100644 --- a/crates/config/src/lib.rs +++ b/crates/config/src/lib.rs @@ -5,8 +5,6 @@ html_favicon_url = "https://avatars0.githubusercontent.com/u/97369466?s=256", issue_tracker_base_url = "https://github.com/paradigmxyz/reth/issues/" )] -#![warn(missing_debug_implementations, missing_docs, unreachable_pub, rustdoc::all)] -#![deny(unused_must_use, rust_2018_idioms)] #![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))] pub mod config; diff --git a/crates/consensus/auto-seal/Cargo.toml b/crates/consensus/auto-seal/Cargo.toml index 704b7a97ae10..10538ad1938e 100644 --- a/crates/consensus/auto-seal/Cargo.toml +++ b/crates/consensus/auto-seal/Cargo.toml @@ -8,6 +8,9 @@ homepage.workspace = true repository.workspace = true description = "A consensus impl for local testing purposes" +[lints] +workspace = true + [dependencies] # reth reth-beacon-consensus.workspace = true diff --git a/crates/consensus/auto-seal/src/lib.rs b/crates/consensus/auto-seal/src/lib.rs index b4cd16ff06ed..b219f083bc09 100644 --- a/crates/consensus/auto-seal/src/lib.rs +++ b/crates/consensus/auto-seal/src/lib.rs @@ -12,8 +12,6 @@ html_favicon_url = "https://avatars0.githubusercontent.com/u/97369466?s=256", issue_tracker_base_url = "https://github.com/paradigmxyz/reth/issues/" )] -#![warn(missing_debug_implementations, missing_docs, unreachable_pub, rustdoc::all)] -#![deny(unused_must_use, rust_2018_idioms)] #![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))] use reth_beacon_consensus::BeaconEngineMessage; @@ -42,7 +40,7 @@ use std::{ time::{SystemTime, UNIX_EPOCH}, }; use tokio::sync::{mpsc::UnboundedSender, RwLock, RwLockReadGuard, RwLockWriteGuard}; -use tracing::{trace, warn}; +use tracing::trace; mod client; mod mode; diff --git a/crates/consensus/auto-seal/tests/it/auto_mine.rs b/crates/consensus/auto-seal/tests/it/auto_mine.rs index 7417f830b9f9..6b4bf1544646 100644 --- a/crates/consensus/auto-seal/tests/it/auto_mine.rs +++ b/crates/consensus/auto-seal/tests/it/auto_mine.rs @@ -1,3 +1,4 @@ +#![allow(unreachable_pub)] //! auto-mine consensus integration test use clap::Parser; diff --git a/crates/consensus/beacon/Cargo.toml b/crates/consensus/beacon/Cargo.toml index d892b6c26269..9fd50ca82b2c 100644 --- a/crates/consensus/beacon/Cargo.toml +++ b/crates/consensus/beacon/Cargo.toml @@ -7,6 +7,9 @@ license.workspace = true homepage.workspace = true repository.workspace = true +[lints] +workspace = true + [dependencies] # reth reth-beacon-consensus-core.workspace = true diff --git a/crates/consensus/beacon/src/lib.rs b/crates/consensus/beacon/src/lib.rs index 00ee920dc85c..f5486915299b 100644 --- a/crates/consensus/beacon/src/lib.rs +++ b/crates/consensus/beacon/src/lib.rs @@ -5,14 +5,6 @@ html_favicon_url = "https://avatars0.githubusercontent.com/u/97369466?s=256", issue_tracker_base_url = "https://github.com/paradigmxyz/reth/issues/" )] -#![warn( - missing_debug_implementations, - missing_docs, - unused_crate_dependencies, - unreachable_pub, - rustdoc::all -)] -#![deny(unused_must_use, rust_2018_idioms)] #![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))] pub use reth_beacon_consensus_core::BeaconConsensus; diff --git a/crates/consensus/common/Cargo.toml b/crates/consensus/common/Cargo.toml index 0a32714919d3..c8690d02bbb9 100644 --- a/crates/consensus/common/Cargo.toml +++ b/crates/consensus/common/Cargo.toml @@ -7,8 +7,11 @@ license.workspace = true homepage.workspace = true repository.workspace = true +[lints] +workspace = true + [dependencies] -# reth +# reth reth-primitives.workspace = true reth-interfaces.workspace = true reth-provider.workspace = true diff --git a/crates/consensus/common/src/lib.rs b/crates/consensus/common/src/lib.rs index f1bb63a039fe..fce6d957a78e 100644 --- a/crates/consensus/common/src/lib.rs +++ b/crates/consensus/common/src/lib.rs @@ -5,8 +5,6 @@ html_favicon_url = "https://avatars0.githubusercontent.com/u/97369466?s=256", issue_tracker_base_url = "https://github.com/paradigmxyz/reth/issues/" )] -#![warn(missing_debug_implementations, missing_docs, unreachable_pub, rustdoc::all)] -#![deny(unused_must_use, rust_2018_idioms)] #![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))] /// Collection of consensus validation methods. diff --git a/crates/ethereum-forks/Cargo.toml b/crates/ethereum-forks/Cargo.toml index d4ee55f14cb4..99ecf2ef16ba 100644 --- a/crates/ethereum-forks/Cargo.toml +++ b/crates/ethereum-forks/Cargo.toml @@ -8,6 +8,9 @@ homepage.workspace = true repository.workspace = true description = "Ethereum fork types used in reth." +[lints] +workspace = true + [dependencies] # reth reth-codecs.workspace = true diff --git a/crates/ethereum-forks/src/lib.rs b/crates/ethereum-forks/src/lib.rs index c03b0f84051a..f8c8238a46f4 100644 --- a/crates/ethereum-forks/src/lib.rs +++ b/crates/ethereum-forks/src/lib.rs @@ -11,8 +11,6 @@ html_favicon_url = "https://avatars0.githubusercontent.com/u/97369466?s=256", issue_tracker_base_url = "https://github.com/paradigmxyz/reth/issues/" )] -#![warn(missing_debug_implementations, missing_docs, unreachable_pub, rustdoc::all)] -#![deny(unused_must_use, rust_2018_idioms, unused_crate_dependencies)] #![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))] mod forkid; diff --git a/crates/interfaces/Cargo.toml b/crates/interfaces/Cargo.toml index 58f70059b898..fafcdaf88e62 100644 --- a/crates/interfaces/Cargo.toml +++ b/crates/interfaces/Cargo.toml @@ -7,6 +7,9 @@ license.workspace = true homepage.workspace = true repository.workspace = true +[lints] +workspace = true + [dependencies] reth-primitives.workspace = true reth-nippy-jar.workspace = true diff --git a/crates/interfaces/src/lib.rs b/crates/interfaces/src/lib.rs index 7764bfc7906a..3e92ea592967 100644 --- a/crates/interfaces/src/lib.rs +++ b/crates/interfaces/src/lib.rs @@ -9,14 +9,7 @@ html_favicon_url = "https://avatars0.githubusercontent.com/u/97369466?s=256", issue_tracker_base_url = "https://github.com/paradigmxyz/reth/issues/" )] -#![warn( - missing_debug_implementations, - missing_docs, - unused_crate_dependencies, - unreachable_pub, - rustdoc::all -)] -#![deny(unused_must_use, rust_2018_idioms)] +#![warn(unused_crate_dependencies)] #![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))] /// Consensus traits. diff --git a/crates/metrics/Cargo.toml b/crates/metrics/Cargo.toml index dcec46cd46b1..015f24d232f8 100644 --- a/crates/metrics/Cargo.toml +++ b/crates/metrics/Cargo.toml @@ -8,6 +8,9 @@ homepage.workspace = true repository.workspace = true description = "reth metrics utilities" +[lints] +workspace = true + [dependencies] # reth reth-metrics-derive.workspace = true diff --git a/crates/metrics/metrics-derive/Cargo.toml b/crates/metrics/metrics-derive/Cargo.toml index 6f3a6ba467c5..732e3d543344 100644 --- a/crates/metrics/metrics-derive/Cargo.toml +++ b/crates/metrics/metrics-derive/Cargo.toml @@ -7,6 +7,9 @@ license.workspace = true homepage.workspace = true repository.workspace = true +[lints] +workspace = true + [lib] proc-macro = true diff --git a/crates/metrics/metrics-derive/src/lib.rs b/crates/metrics/metrics-derive/src/lib.rs index 24e7bafa0e6c..8b6c3dc7e54f 100644 --- a/crates/metrics/metrics-derive/src/lib.rs +++ b/crates/metrics/metrics-derive/src/lib.rs @@ -5,8 +5,6 @@ html_favicon_url = "https://avatars0.githubusercontent.com/u/97369466?s=256", issue_tracker_base_url = "https://github.com/paradigmxyz/reth/issues/" )] -#![warn(missing_debug_implementations, missing_docs, unreachable_pub, rustdoc::all)] -#![deny(unused_must_use, rust_2018_idioms)] #![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))] use proc_macro::TokenStream; diff --git a/crates/metrics/src/lib.rs b/crates/metrics/src/lib.rs index 71eb722530eb..285e4dc314a2 100644 --- a/crates/metrics/src/lib.rs +++ b/crates/metrics/src/lib.rs @@ -10,8 +10,6 @@ html_favicon_url = "https://avatars0.githubusercontent.com/u/97369466?s=256", issue_tracker_base_url = "https://github.com/paradigmxyz/reth/issues/" )] -#![warn(missing_debug_implementations, missing_docs, unreachable_pub, rustdoc::all)] -#![deny(unused_must_use, rust_2018_idioms)] #![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))] /// Metrics derive macro. diff --git a/crates/net/common/Cargo.toml b/crates/net/common/Cargo.toml index 0535b58be4b9..8d85fc9067b8 100644 --- a/crates/net/common/Cargo.toml +++ b/crates/net/common/Cargo.toml @@ -8,6 +8,9 @@ homepage.workspace = true repository.workspace = true description = "Types shared across network code" +[lints] +workspace = true + [dependencies] # reth reth-primitives.workspace = true diff --git a/crates/net/common/src/lib.rs b/crates/net/common/src/lib.rs index f65702aaec56..d5ad6c704648 100644 --- a/crates/net/common/src/lib.rs +++ b/crates/net/common/src/lib.rs @@ -5,8 +5,6 @@ html_favicon_url = "https://avatars0.githubusercontent.com/u/97369466?s=256", issue_tracker_base_url = "https://github.com/paradigmxyz/reth/issues/" )] -#![warn(missing_debug_implementations, missing_docs, unreachable_pub, rustdoc::all)] -#![deny(unused_must_use, rust_2018_idioms)] #![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))] pub mod ban_list; diff --git a/crates/net/discv4/Cargo.toml b/crates/net/discv4/Cargo.toml index 274a32893ea4..74b6d4786d05 100644 --- a/crates/net/discv4/Cargo.toml +++ b/crates/net/discv4/Cargo.toml @@ -8,6 +8,9 @@ homepage.workspace = true repository.workspace = true description = "Ethereum network discovery" +[lints] +workspace = true + [dependencies] # reth reth-primitives.workspace = true diff --git a/crates/net/discv4/src/lib.rs b/crates/net/discv4/src/lib.rs index 734bb2433a60..8fdfd16a9811 100644 --- a/crates/net/discv4/src/lib.rs +++ b/crates/net/discv4/src/lib.rs @@ -21,8 +21,6 @@ html_favicon_url = "https://avatars0.githubusercontent.com/u/97369466?s=256", issue_tracker_base_url = "https://github.com/paradigmxyz/reth/issues/" )] -#![warn(missing_debug_implementations, missing_docs, rustdoc::all)] -#![deny(unused_must_use, rust_2018_idioms, unreachable_pub, unused_crate_dependencies)] #![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))] use crate::{ @@ -64,7 +62,7 @@ use tokio::{ time::Interval, }; use tokio_stream::{wrappers::ReceiverStream, Stream, StreamExt}; -use tracing::{debug, trace, warn}; +use tracing::{debug, trace}; pub mod error; pub mod proto; diff --git a/crates/net/dns/Cargo.toml b/crates/net/dns/Cargo.toml index 02dd202d7bb5..3a0c2d3ec57c 100644 --- a/crates/net/dns/Cargo.toml +++ b/crates/net/dns/Cargo.toml @@ -8,6 +8,9 @@ homepage.workspace = true repository.workspace = true description = "Support for EIP-1459 Node Discovery via DNS" +[lints] +workspace = true + [dependencies] # reth reth-primitives.workspace = true diff --git a/crates/net/dns/src/lib.rs b/crates/net/dns/src/lib.rs index 878ec20240c7..f72ac4b2e1a1 100644 --- a/crates/net/dns/src/lib.rs +++ b/crates/net/dns/src/lib.rs @@ -10,8 +10,6 @@ html_favicon_url = "https://avatars0.githubusercontent.com/u/97369466?s=256", issue_tracker_base_url = "https://github.com/paradigmxyz/reth/issues/" )] -#![warn(missing_debug_implementations, missing_docs, unreachable_pub, rustdoc::all)] -#![deny(unused_must_use, rust_2018_idioms)] #![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))] pub use crate::resolver::{DnsResolver, MapResolver, Resolver}; @@ -47,7 +45,7 @@ use tokio_stream::{ wrappers::{ReceiverStream, UnboundedReceiverStream}, Stream, StreamExt, }; -use tracing::{debug, trace, warn}; +use tracing::{debug, trace}; mod config; mod error; diff --git a/crates/net/downloaders/Cargo.toml b/crates/net/downloaders/Cargo.toml index 0ed401223e69..f1cd2b2344de 100644 --- a/crates/net/downloaders/Cargo.toml +++ b/crates/net/downloaders/Cargo.toml @@ -8,6 +8,9 @@ homepage.workspace = true repository.workspace = true description = "Implementations of various block downloaders" +[lints] +workspace = true + [dependencies] # reth reth-interfaces.workspace = true diff --git a/crates/net/downloaders/src/lib.rs b/crates/net/downloaders/src/lib.rs index 042c749eaefa..02743ec26e89 100644 --- a/crates/net/downloaders/src/lib.rs +++ b/crates/net/downloaders/src/lib.rs @@ -9,8 +9,6 @@ html_favicon_url = "https://avatars0.githubusercontent.com/u/97369466?s=256", issue_tracker_base_url = "https://github.com/paradigmxyz/reth/issues/" )] -#![warn(missing_debug_implementations, missing_docs, unreachable_pub, rustdoc::all)] -#![deny(unused_must_use, rust_2018_idioms)] #![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))] /// The collection of algorithms for downloading block bodies. diff --git a/crates/net/ecies/Cargo.toml b/crates/net/ecies/Cargo.toml index f578c4f09a7f..748def55719b 100644 --- a/crates/net/ecies/Cargo.toml +++ b/crates/net/ecies/Cargo.toml @@ -7,6 +7,9 @@ license.workspace = true homepage.workspace = true repository.workspace = true +[lints] +workspace = true + [dependencies] reth-primitives.workspace = true reth-net-common.workspace = true diff --git a/crates/net/ecies/src/lib.rs b/crates/net/ecies/src/lib.rs index bd2449d03a18..54cdffb7eb10 100644 --- a/crates/net/ecies/src/lib.rs +++ b/crates/net/ecies/src/lib.rs @@ -5,8 +5,6 @@ html_favicon_url = "https://avatars0.githubusercontent.com/u/97369466?s=256", issue_tracker_base_url = "https://github.com/paradigmxyz/reth/issues/" )] -#![warn(missing_debug_implementations, missing_docs, unreachable_pub, rustdoc::all)] -#![deny(unused_must_use, rust_2018_idioms)] #![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))] pub mod algorithm; diff --git a/crates/net/eth-wire/Cargo.toml b/crates/net/eth-wire/Cargo.toml index 29e6adcf0ed6..2030a280d342 100644 --- a/crates/net/eth-wire/Cargo.toml +++ b/crates/net/eth-wire/Cargo.toml @@ -8,6 +8,9 @@ license.workspace = true homepage.workspace = true repository.workspace = true +[lints] +workspace = true + [dependencies] # reth reth-codecs.workspace = true diff --git a/crates/net/eth-wire/src/lib.rs b/crates/net/eth-wire/src/lib.rs index c090deb5f6bd..cfb2fd9fc909 100644 --- a/crates/net/eth-wire/src/lib.rs +++ b/crates/net/eth-wire/src/lib.rs @@ -10,8 +10,6 @@ html_favicon_url = "https://avatars0.githubusercontent.com/u/97369466?s=256", issue_tracker_base_url = "https://github.com/paradigmxyz/reth/issues/" )] -#![warn(missing_debug_implementations, missing_docs, unreachable_pub, rustdoc::all)] -#![deny(unused_must_use, rust_2018_idioms)] #![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))] pub mod builder; diff --git a/crates/net/nat/Cargo.toml b/crates/net/nat/Cargo.toml index 86ba73b931c5..d19023be11fc 100644 --- a/crates/net/nat/Cargo.toml +++ b/crates/net/nat/Cargo.toml @@ -8,6 +8,9 @@ homepage.workspace = true repository.workspace = true description = "Helpers for working around NAT" +[lints] +workspace = true + [dependencies] # nat diff --git a/crates/net/nat/src/lib.rs b/crates/net/nat/src/lib.rs index 080319eef24a..855cf47ff60a 100644 --- a/crates/net/nat/src/lib.rs +++ b/crates/net/nat/src/lib.rs @@ -9,8 +9,6 @@ html_favicon_url = "https://avatars0.githubusercontent.com/u/97369466?s=256", issue_tracker_base_url = "https://github.com/paradigmxyz/reth/issues/" )] -#![warn(missing_debug_implementations, missing_docs, unreachable_pub, rustdoc::all)] -#![deny(unused_must_use, rust_2018_idioms)] #![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))] use igd::aio::search_gateway; diff --git a/crates/net/network-api/Cargo.toml b/crates/net/network-api/Cargo.toml index 51bd3a58a21a..145826cd96c0 100644 --- a/crates/net/network-api/Cargo.toml +++ b/crates/net/network-api/Cargo.toml @@ -8,6 +8,9 @@ homepage.workspace = true repository.workspace = true description = "Network interfaces" +[lints] +workspace = true + [dependencies] # reth reth-primitives.workspace = true diff --git a/crates/net/network-api/src/lib.rs b/crates/net/network-api/src/lib.rs index 5d97f32068de..4afa7673f729 100644 --- a/crates/net/network-api/src/lib.rs +++ b/crates/net/network-api/src/lib.rs @@ -11,8 +11,6 @@ html_favicon_url = "https://avatars0.githubusercontent.com/u/97369466?s=256", issue_tracker_base_url = "https://github.com/paradigmxyz/reth/issues/" )] -#![warn(missing_debug_implementations, missing_docs, unreachable_pub, rustdoc::all)] -#![deny(unused_must_use, rust_2018_idioms)] #![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))] use async_trait::async_trait; diff --git a/crates/net/network/Cargo.toml b/crates/net/network/Cargo.toml index d861b2cfe836..0e363eebf202 100644 --- a/crates/net/network/Cargo.toml +++ b/crates/net/network/Cargo.toml @@ -8,6 +8,9 @@ homepage.workspace = true repository.workspace = true description = "Ethereum network support" +[lints] +workspace = true + [package.metadata.cargo-udeps.ignore] normal = [ # Used for diagrams in docs diff --git a/crates/net/network/benches/bench.rs b/crates/net/network/benches/bench.rs index 9f4fb5665997..a4bd2410b512 100644 --- a/crates/net/network/benches/bench.rs +++ b/crates/net/network/benches/bench.rs @@ -1,3 +1,4 @@ +#![allow(missing_docs)] use criterion::*; use futures::StreamExt; use pprof::criterion::{Output, PProfProfiler}; diff --git a/crates/net/network/src/lib.rs b/crates/net/network/src/lib.rs index 4c102b78f40e..526330b573fa 100644 --- a/crates/net/network/src/lib.rs +++ b/crates/net/network/src/lib.rs @@ -106,8 +106,7 @@ html_favicon_url = "https://avatars0.githubusercontent.com/u/97369466?s=256", issue_tracker_base_url = "https://github.com/paradigmxyz/reth/issues/" )] -#![warn(missing_debug_implementations, missing_docs, rustdoc::all)] // TODO(danipopes): unreachable_pub -#![deny(unused_must_use, rust_2018_idioms)] +#![allow(unreachable_pub)] #![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))] #[cfg(any(test, feature = "test-utils"))] diff --git a/crates/net/network/tests/it/clique/clique_middleware.rs b/crates/net/network/tests/it/clique/clique_middleware.rs index e79d80e9642f..4d3d1cad4014 100644 --- a/crates/net/network/tests/it/clique/clique_middleware.rs +++ b/crates/net/network/tests/it/clique/clique_middleware.rs @@ -1,3 +1,4 @@ +#![allow(unreachable_pub)] //! Helper extension traits for working with clique providers. use async_trait::async_trait; diff --git a/crates/net/network/tests/it/clique/geth.rs b/crates/net/network/tests/it/clique/geth.rs index b512efd2e44e..6534fd87db7e 100644 --- a/crates/net/network/tests/it/clique/geth.rs +++ b/crates/net/network/tests/it/clique/geth.rs @@ -1,3 +1,4 @@ +#![allow(unreachable_pub)] //! Helper struct for working with a clique geth instance. use enr::k256::ecdsa::SigningKey; diff --git a/crates/net/network/tests/it/clique/mod.rs b/crates/net/network/tests/it/clique/mod.rs index d8fb1070c663..8328234e3ea2 100644 --- a/crates/net/network/tests/it/clique/mod.rs +++ b/crates/net/network/tests/it/clique/mod.rs @@ -1,3 +1,4 @@ +#![allow(unreachable_pub)] pub mod clique_middleware; mod geth; diff --git a/crates/net/network/tests/it/multiplex.rs b/crates/net/network/tests/it/multiplex.rs index 6ed8125331ff..1e61efb4d230 100644 --- a/crates/net/network/tests/it/multiplex.rs +++ b/crates/net/network/tests/it/multiplex.rs @@ -1,3 +1,4 @@ +#![allow(unreachable_pub)] //! Testing gossiping of transactions. use crate::multiplex::proto::{PingPongProtoMessage, PingPongProtoMessageKind}; diff --git a/crates/net/network/tests/it/requests.rs b/crates/net/network/tests/it/requests.rs index 3ea46d189a44..535cf4167530 100644 --- a/crates/net/network/tests/it/requests.rs +++ b/crates/net/network/tests/it/requests.rs @@ -1,3 +1,4 @@ +#![allow(unreachable_pub)] //! Tests for eth related requests use rand::Rng; diff --git a/crates/payload/basic/Cargo.toml b/crates/payload/basic/Cargo.toml index 4ecbaa698b4e..374a6e36e457 100644 --- a/crates/payload/basic/Cargo.toml +++ b/crates/payload/basic/Cargo.toml @@ -8,6 +8,9 @@ homepage.workspace = true repository.workspace = true description = "A basic payload builder for reth that uses the txpool API to build payloads." +[lints] +workspace = true + [dependencies] # reth reth-primitives.workspace = true diff --git a/crates/payload/basic/src/lib.rs b/crates/payload/basic/src/lib.rs index 3f07ca28fcd3..21fc1ab2c0a4 100644 --- a/crates/payload/basic/src/lib.rs +++ b/crates/payload/basic/src/lib.rs @@ -5,14 +5,6 @@ html_favicon_url = "https://avatars0.githubusercontent.com/u/97369466?s=256", issue_tracker_base_url = "https://github.com/paradigmxyz/reth/issues/" )] -#![warn( - missing_debug_implementations, - missing_docs, - unused_crate_dependencies, - unreachable_pub, - rustdoc::all -)] -#![deny(unused_must_use, rust_2018_idioms)] #![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))] use alloy_rlp::Encodable; diff --git a/crates/payload/builder/Cargo.toml b/crates/payload/builder/Cargo.toml index 056629ce69dd..854b182459ed 100644 --- a/crates/payload/builder/Cargo.toml +++ b/crates/payload/builder/Cargo.toml @@ -8,6 +8,9 @@ homepage.workspace = true repository.workspace = true description = "reth payload builder" +[lints] +workspace = true + [dependencies] # reth reth-primitives.workspace = true diff --git a/crates/payload/builder/src/lib.rs b/crates/payload/builder/src/lib.rs index 0d5bdec9dae7..0aae50e49c3b 100644 --- a/crates/payload/builder/src/lib.rs +++ b/crates/payload/builder/src/lib.rs @@ -97,8 +97,6 @@ html_favicon_url = "https://avatars0.githubusercontent.com/u/97369466?s=256", issue_tracker_base_url = "https://github.com/paradigmxyz/reth/issues/" )] -#![warn(missing_debug_implementations, missing_docs, unreachable_pub, rustdoc::all)] -#![deny(unused_must_use, rust_2018_idioms)] #![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))] pub mod database; diff --git a/crates/payload/ethereum/Cargo.toml b/crates/payload/ethereum/Cargo.toml index 91e2410f226a..cb2d7baa0ab2 100644 --- a/crates/payload/ethereum/Cargo.toml +++ b/crates/payload/ethereum/Cargo.toml @@ -8,6 +8,9 @@ homepage.workspace = true repository.workspace = true description = "A basic ethereum payload builder for reth that uses the txpool API to build payloads." +[lints] +workspace = true + [dependencies] # reth reth-primitives.workspace = true @@ -26,4 +29,4 @@ tracing.workspace = true [features] # This is a workaround for reth-cli crate to allow this as mandatory dependency without breaking the build even if unused. # This makes managing features and testing workspace easier because clippy always builds all members if --workspace is provided -optimism = [] \ No newline at end of file +optimism = [] diff --git a/crates/payload/ethereum/src/lib.rs b/crates/payload/ethereum/src/lib.rs index 5738b51c3961..6de0eabe22ed 100644 --- a/crates/payload/ethereum/src/lib.rs +++ b/crates/payload/ethereum/src/lib.rs @@ -5,8 +5,6 @@ html_favicon_url = "https://avatars0.githubusercontent.com/u/97369466?s=256", issue_tracker_base_url = "https://github.com/paradigmxyz/reth/issues/" )] -#![warn(missing_debug_implementations, missing_docs, unreachable_pub, rustdoc::all)] -#![deny(unused_must_use, rust_2018_idioms)] #![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))] #[cfg(not(feature = "optimism"))] diff --git a/crates/payload/optimism/Cargo.toml b/crates/payload/optimism/Cargo.toml index 388cb4c40821..f5f9309804f1 100644 --- a/crates/payload/optimism/Cargo.toml +++ b/crates/payload/optimism/Cargo.toml @@ -8,6 +8,9 @@ homepage.workspace = true repository.workspace = true description = "A payload builder for op-reth that builds optimistic payloads." +[lints] +workspace = true + [dependencies] # reth reth-primitives.workspace = true @@ -32,4 +35,4 @@ optimism = [ "reth-transaction-pool/optimism", "reth-provider/optimism", "reth-payload-builder/optimism", -] \ No newline at end of file +] diff --git a/crates/payload/optimism/src/lib.rs b/crates/payload/optimism/src/lib.rs index 11b36e9876ea..07a45324e41d 100644 --- a/crates/payload/optimism/src/lib.rs +++ b/crates/payload/optimism/src/lib.rs @@ -5,8 +5,6 @@ html_favicon_url = "https://avatars0.githubusercontent.com/u/97369466?s=256", issue_tracker_base_url = "https://github.com/paradigmxyz/reth/issues/" )] -#![warn(missing_debug_implementations, missing_docs, unreachable_pub, rustdoc::all)] -#![deny(unused_must_use, rust_2018_idioms)] #![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))] #[cfg(feature = "optimism")] diff --git a/crates/payload/validator/Cargo.toml b/crates/payload/validator/Cargo.toml index f3003d62e309..d138001a7061 100644 --- a/crates/payload/validator/Cargo.toml +++ b/crates/payload/validator/Cargo.toml @@ -8,8 +8,11 @@ homepage.workspace = true repository.workspace = true description = "Payload validation support" +[lints] +workspace = true + [dependencies] # reth reth-primitives.workspace = true reth-rpc-types.workspace = true -reth-rpc-types-compat.workspace = true \ No newline at end of file +reth-rpc-types-compat.workspace = true diff --git a/crates/payload/validator/src/lib.rs b/crates/payload/validator/src/lib.rs index c4c7d1379498..89840c032b88 100644 --- a/crates/payload/validator/src/lib.rs +++ b/crates/payload/validator/src/lib.rs @@ -5,14 +5,7 @@ html_favicon_url = "https://avatars0.githubusercontent.com/u/97369466?s=256", issue_tracker_base_url = "https://github.com/paradigmxyz/reth/issues/" )] -#![warn( - missing_debug_implementations, - missing_docs, - unreachable_pub, - unused_crate_dependencies, - rustdoc::all -)] -#![deny(unused_must_use, rust_2018_idioms)] +#![warn(unused_crate_dependencies)] #![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))] use reth_primitives::{ChainSpec, SealedBlock}; diff --git a/crates/primitives/Cargo.toml b/crates/primitives/Cargo.toml index 87347f99506e..42b0b4e99d3e 100644 --- a/crates/primitives/Cargo.toml +++ b/crates/primitives/Cargo.toml @@ -8,6 +8,9 @@ repository.workspace = true rust-version.workspace = true description = "Commonly used types in reth." +[lints] +workspace = true + [dependencies] # reth reth-codecs.workspace = true diff --git a/crates/primitives/benches/nibbles.rs b/crates/primitives/benches/nibbles.rs index 1db4f837882d..e8e3f65f9b9a 100644 --- a/crates/primitives/benches/nibbles.rs +++ b/crates/primitives/benches/nibbles.rs @@ -1,3 +1,4 @@ +#![allow(missing_docs)] use criterion::{criterion_group, criterion_main, Criterion}; use proptest::{prelude::*, strategy::ValueTree}; use reth_primitives::trie::Nibbles; diff --git a/crates/primitives/benches/recover_ecdsa_crit.rs b/crates/primitives/benches/recover_ecdsa_crit.rs index 57893faa9f7b..e1e896dbbf8f 100644 --- a/crates/primitives/benches/recover_ecdsa_crit.rs +++ b/crates/primitives/benches/recover_ecdsa_crit.rs @@ -1,3 +1,4 @@ +#![allow(missing_docs)] use alloy_rlp::Decodable; use criterion::{criterion_group, criterion_main, Criterion}; use pprof::criterion::{Output, PProfProfiler}; diff --git a/crates/primitives/benches/trie_root.rs b/crates/primitives/benches/trie_root.rs index 6baf543194e8..ce9d41788c87 100644 --- a/crates/primitives/benches/trie_root.rs +++ b/crates/primitives/benches/trie_root.rs @@ -1,3 +1,4 @@ +#![allow(missing_docs, unreachable_pub)] use criterion::{black_box, criterion_group, criterion_main, Criterion}; use proptest::{ prelude::*, diff --git a/crates/primitives/src/lib.rs b/crates/primitives/src/lib.rs index 988c82ecf410..12117fecc809 100644 --- a/crates/primitives/src/lib.rs +++ b/crates/primitives/src/lib.rs @@ -12,8 +12,6 @@ html_favicon_url = "https://avatars0.githubusercontent.com/u/97369466?s=256", issue_tracker_base_url = "https://github.com/paradigmxyz/reth/issues/" )] -#![warn(missing_debug_implementations, missing_docs, unreachable_pub, rustdoc::all)] -#![deny(unused_must_use, rust_2018_idioms)] #![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))] mod account; diff --git a/crates/prune/Cargo.toml b/crates/prune/Cargo.toml index 70d649915d41..750cd84d6bf0 100644 --- a/crates/prune/Cargo.toml +++ b/crates/prune/Cargo.toml @@ -8,6 +8,9 @@ homepage.workspace = true repository.workspace = true description = "Pruning implementation" +[lints] +workspace = true + [dependencies] # reth reth-primitives.workspace = true diff --git a/crates/prune/src/lib.rs b/crates/prune/src/lib.rs index cee0e5dd51c7..b712f9e5b68f 100644 --- a/crates/prune/src/lib.rs +++ b/crates/prune/src/lib.rs @@ -5,8 +5,7 @@ html_favicon_url = "https://avatars0.githubusercontent.com/u/97369466?s=256", issue_tracker_base_url = "https://github.com/paradigmxyz/reth/issues/" )] -#![warn(missing_debug_implementations, unreachable_pub, rustdoc::all)] // TODO(danipopes): missing_docs -#![deny(unused_must_use, rust_2018_idioms)] +#![allow(missing_docs)] #![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))] mod builder; diff --git a/crates/revm/Cargo.toml b/crates/revm/Cargo.toml index 18f74ecf222f..6d12f754ae1d 100644 --- a/crates/revm/Cargo.toml +++ b/crates/revm/Cargo.toml @@ -8,6 +8,9 @@ homepage.workspace = true repository.workspace = true description = "reth specific revm utilities" +[lints] +workspace = true + [dependencies] # reth reth-primitives.workspace = true diff --git a/crates/revm/revm-inspectors/Cargo.toml b/crates/revm/revm-inspectors/Cargo.toml index bdfbe51f77ab..3664cbcf39fd 100644 --- a/crates/revm/revm-inspectors/Cargo.toml +++ b/crates/revm/revm-inspectors/Cargo.toml @@ -8,8 +8,11 @@ homepage.workspace = true repository.workspace = true description = "revm inspector implementations used by reth" +[lints] +workspace = true + [dependencies] -# reth +# reth reth-rpc-types.workspace = true # eth diff --git a/crates/revm/revm-inspectors/src/lib.rs b/crates/revm/revm-inspectors/src/lib.rs index 67854ba0c5ba..e524eb517de1 100644 --- a/crates/revm/revm-inspectors/src/lib.rs +++ b/crates/revm/revm-inspectors/src/lib.rs @@ -10,8 +10,6 @@ html_favicon_url = "https://avatars0.githubusercontent.com/u/97369466?s=256", issue_tracker_base_url = "https://github.com/paradigmxyz/reth/issues/" )] -#![warn(missing_debug_implementations, missing_docs, unreachable_pub, rustdoc::all)] -#![deny(unused_must_use, rust_2018_idioms, unused_crate_dependencies)] #![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))] /// An inspector implementation for an EIP2930 Accesslist diff --git a/crates/revm/src/lib.rs b/crates/revm/src/lib.rs index 116269da668b..8cc805646851 100644 --- a/crates/revm/src/lib.rs +++ b/crates/revm/src/lib.rs @@ -5,8 +5,6 @@ html_favicon_url = "https://avatars0.githubusercontent.com/u/97369466?s=256", issue_tracker_base_url = "https://github.com/paradigmxyz/reth/issues/" )] -#![warn(missing_debug_implementations, missing_docs, unreachable_pub, rustdoc::all)] -#![deny(unused_must_use, rust_2018_idioms)] #![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))] /// Contains glue code for integrating reth database into revm's [Database]. diff --git a/crates/rpc/ipc/Cargo.toml b/crates/rpc/ipc/Cargo.toml index df904112c46d..b01b689505c7 100644 --- a/crates/rpc/ipc/Cargo.toml +++ b/crates/rpc/ipc/Cargo.toml @@ -8,6 +8,9 @@ homepage.workspace = true repository.workspace = true description = "IPC support for reth" +[lints] +workspace = true + [dependencies] # async/net diff --git a/crates/rpc/ipc/src/lib.rs b/crates/rpc/ipc/src/lib.rs index 4cfc537fcd66..66dacf9be94e 100644 --- a/crates/rpc/ipc/src/lib.rs +++ b/crates/rpc/ipc/src/lib.rs @@ -9,8 +9,6 @@ html_favicon_url = "https://avatars0.githubusercontent.com/u/97369466?s=256", issue_tracker_base_url = "https://github.com/paradigmxyz/reth/issues/" )] -#![warn(missing_debug_implementations, missing_docs, unreachable_pub, rustdoc::all)] -#![deny(unused_must_use, rust_2018_idioms)] #![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))] #[cfg(unix)] diff --git a/crates/rpc/rpc-api/Cargo.toml b/crates/rpc/rpc-api/Cargo.toml index 58d0349d388a..13586f119d3c 100644 --- a/crates/rpc/rpc-api/Cargo.toml +++ b/crates/rpc/rpc-api/Cargo.toml @@ -8,6 +8,9 @@ homepage.workspace = true repository.workspace = true description = "Reth RPC interfaces" +[lints] +workspace = true + [dependencies] # reth reth-primitives.workspace = true diff --git a/crates/rpc/rpc-api/src/lib.rs b/crates/rpc/rpc-api/src/lib.rs index d414c116e6f3..e5a4b1981b67 100644 --- a/crates/rpc/rpc-api/src/lib.rs +++ b/crates/rpc/rpc-api/src/lib.rs @@ -11,8 +11,6 @@ html_favicon_url = "https://avatars0.githubusercontent.com/u/97369466?s=256", issue_tracker_base_url = "https://github.com/paradigmxyz/reth/issues/" )] -#![warn(missing_debug_implementations, missing_docs, unreachable_pub, rustdoc::all)] -#![deny(unused_must_use, rust_2018_idioms)] #![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))] mod admin; diff --git a/crates/rpc/rpc-builder/Cargo.toml b/crates/rpc/rpc-builder/Cargo.toml index 6ae2e8a1dc46..88ad4deafc29 100644 --- a/crates/rpc/rpc-builder/Cargo.toml +++ b/crates/rpc/rpc-builder/Cargo.toml @@ -8,6 +8,9 @@ homepage.workspace = true repository.workspace = true description = "Helpers for configuring RPC" +[lints] +workspace = true + [dependencies] # reth reth-primitives.workspace = true diff --git a/crates/rpc/rpc-builder/src/lib.rs b/crates/rpc/rpc-builder/src/lib.rs index 92df2ae70dbc..a559f3b8acd7 100644 --- a/crates/rpc/rpc-builder/src/lib.rs +++ b/crates/rpc/rpc-builder/src/lib.rs @@ -132,8 +132,6 @@ html_favicon_url = "https://avatars0.githubusercontent.com/u/97369466?s=256", issue_tracker_base_url = "https://github.com/paradigmxyz/reth/issues/" )] -#![warn(missing_debug_implementations, missing_docs, unreachable_pub, rustdoc::all)] -#![deny(unused_must_use, rust_2018_idioms)] #![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))] use std::{ diff --git a/crates/rpc/rpc-builder/tests/it/http.rs b/crates/rpc/rpc-builder/tests/it/http.rs index 1caf47ba8815..41b277321795 100644 --- a/crates/rpc/rpc-builder/tests/it/http.rs +++ b/crates/rpc/rpc-builder/tests/it/http.rs @@ -1,3 +1,4 @@ +#![allow(unreachable_pub)] //! Standalone http tests use crate::utils::{launch_http, launch_http_ws, launch_ws}; diff --git a/crates/rpc/rpc-builder/tests/it/serde.rs b/crates/rpc/rpc-builder/tests/it/serde.rs index 1559dd27aca5..a781e1d477e7 100644 --- a/crates/rpc/rpc-builder/tests/it/serde.rs +++ b/crates/rpc/rpc-builder/tests/it/serde.rs @@ -21,7 +21,7 @@ impl ToRpcParams for RawRpcParams { async fn test_eth_balance_serde() { let handle = launch_http(vec![RethRpcModule::Eth]).await; let s = r#"{"jsonrpc":"2.0","id":1,"method":"eth_getBalance","params":["0xaa00000000000000000000000000000000000000","0x898753d8fdd8d92c1907ca21e68c7970abd290c647a202091181deec3f30a0b2"]}"#; - let req: Request = serde_json::from_str(s).unwrap(); + let req: Request<'_> = serde_json::from_str(s).unwrap(); let client = handle.http_client().unwrap(); let params = RawRpcParams(RawValue::from_string(req.params.unwrap().to_string()).unwrap()); diff --git a/crates/rpc/rpc-engine-api/Cargo.toml b/crates/rpc/rpc-engine-api/Cargo.toml index b718862a9ccb..18f31ee85b8e 100644 --- a/crates/rpc/rpc-engine-api/Cargo.toml +++ b/crates/rpc/rpc-engine-api/Cargo.toml @@ -8,8 +8,11 @@ homepage.workspace = true repository.workspace = true description = "Implementation of Engine API" +[lints] +workspace = true + [dependencies] -# reth +# reth reth-primitives.workspace = true reth-interfaces.workspace = true reth-provider.workspace = true diff --git a/crates/rpc/rpc-engine-api/src/lib.rs b/crates/rpc/rpc-engine-api/src/lib.rs index d39025d508d0..965b0448afed 100644 --- a/crates/rpc/rpc-engine-api/src/lib.rs +++ b/crates/rpc/rpc-engine-api/src/lib.rs @@ -6,8 +6,6 @@ html_favicon_url = "https://avatars0.githubusercontent.com/u/97369466?s=256", issue_tracker_base_url = "https://github.com/paradigmxyz/reth/issues/" )] -#![warn(missing_debug_implementations, missing_docs, unreachable_pub, rustdoc::all)] -#![deny(unused_must_use, rust_2018_idioms)] #![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))] /// The Engine API implementation. diff --git a/crates/rpc/rpc-testing-util/Cargo.toml b/crates/rpc/rpc-testing-util/Cargo.toml index 53b04c7a0e99..af72a3c71d6e 100644 --- a/crates/rpc/rpc-testing-util/Cargo.toml +++ b/crates/rpc/rpc-testing-util/Cargo.toml @@ -8,6 +8,9 @@ homepage.workspace = true repository.workspace = true description = "Reth RPC testing helpers" +[lints] +workspace = true + [dependencies] # reth reth-primitives.workspace = true diff --git a/crates/rpc/rpc-testing-util/src/lib.rs b/crates/rpc/rpc-testing-util/src/lib.rs index f4a5740cb9ba..047236dc6ecd 100644 --- a/crates/rpc/rpc-testing-util/src/lib.rs +++ b/crates/rpc/rpc-testing-util/src/lib.rs @@ -5,8 +5,6 @@ html_favicon_url = "https://avatars0.githubusercontent.com/u/97369466?s=256", issue_tracker_base_url = "https://github.com/paradigmxyz/reth/issues/" )] -#![warn(missing_debug_implementations, missing_docs, unreachable_pub, rustdoc::all)] -#![deny(unused_must_use, rust_2018_idioms)] #![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))] pub mod debug; diff --git a/crates/rpc/rpc-types-compat/Cargo.toml b/crates/rpc/rpc-types-compat/Cargo.toml index e85060334f54..e9d0b862292b 100644 --- a/crates/rpc/rpc-types-compat/Cargo.toml +++ b/crates/rpc/rpc-types-compat/Cargo.toml @@ -8,6 +8,9 @@ homepage.workspace = true repository.workspace = true description = "Compatibility layer for reth-primitives and ethereum RPC types" +[lints] +workspace = true + [dependencies] reth-primitives.workspace = true reth-rpc-types.workspace = true diff --git a/crates/rpc/rpc-types-compat/src/lib.rs b/crates/rpc/rpc-types-compat/src/lib.rs index 4f4db3cef75f..115231670c02 100644 --- a/crates/rpc/rpc-types-compat/src/lib.rs +++ b/crates/rpc/rpc-types-compat/src/lib.rs @@ -7,8 +7,6 @@ html_favicon_url = "https://avatars0.githubusercontent.com/u/97369466?s=256", issue_tracker_base_url = "https://github.com/paradigmxyz/reth/issues/" )] -#![warn(missing_debug_implementations, missing_docs, unreachable_pub, rustdoc::all)] -#![deny(unused_must_use, rust_2018_idioms)] #![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))] pub mod block; diff --git a/crates/rpc/rpc-types/Cargo.toml b/crates/rpc/rpc-types/Cargo.toml index 7074686e39ec..89f972e235ca 100644 --- a/crates/rpc/rpc-types/Cargo.toml +++ b/crates/rpc/rpc-types/Cargo.toml @@ -8,6 +8,9 @@ homepage.workspace = true repository.workspace = true description = "Reth RPC types" +[lints] +workspace = true + [dependencies] # ethereum alloy-rlp = { workspace = true, features = ["arrayvec", "derive"] } diff --git a/crates/rpc/rpc-types/src/lib.rs b/crates/rpc/rpc-types/src/lib.rs index f71f7660810e..84cd0992b585 100644 --- a/crates/rpc/rpc-types/src/lib.rs +++ b/crates/rpc/rpc-types/src/lib.rs @@ -7,8 +7,6 @@ html_favicon_url = "https://avatars0.githubusercontent.com/u/97369466?s=256", issue_tracker_base_url = "https://github.com/paradigmxyz/reth/issues/" )] -#![warn(missing_debug_implementations, missing_docs, unreachable_pub, rustdoc::all)] -#![deny(unused_must_use, rust_2018_idioms)] #![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))] mod admin; diff --git a/crates/rpc/rpc/Cargo.toml b/crates/rpc/rpc/Cargo.toml index 6e50bafbde79..89aaa1e0fa5c 100644 --- a/crates/rpc/rpc/Cargo.toml +++ b/crates/rpc/rpc/Cargo.toml @@ -7,6 +7,10 @@ license.workspace = true homepage.workspace = true repository.workspace = true description = "Reth RPC implementation" + +[lints] +workspace = true + [dependencies] # reth reth-interfaces.workspace = true diff --git a/crates/rpc/rpc/src/lib.rs b/crates/rpc/rpc/src/lib.rs index 340886dab995..cb068ad78715 100644 --- a/crates/rpc/rpc/src/lib.rs +++ b/crates/rpc/rpc/src/lib.rs @@ -22,8 +22,6 @@ html_favicon_url = "https://avatars0.githubusercontent.com/u/97369466?s=256", issue_tracker_base_url = "https://github.com/paradigmxyz/reth/issues/" )] -#![warn(missing_debug_implementations, missing_docs, unreachable_pub, rustdoc::all)] -#![deny(unused_must_use, rust_2018_idioms)] #![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))] mod admin; diff --git a/crates/snapshot/Cargo.toml b/crates/snapshot/Cargo.toml index 0eed6732161b..a082c01135e3 100644 --- a/crates/snapshot/Cargo.toml +++ b/crates/snapshot/Cargo.toml @@ -8,6 +8,9 @@ homepage.workspace = true repository.workspace = true description = "Snapshotting implementation" +[lints] +workspace = true + [dependencies] # reth reth-primitives.workspace = true diff --git a/crates/snapshot/src/lib.rs b/crates/snapshot/src/lib.rs index 82f42b2d4c05..1673aa0b8e51 100644 --- a/crates/snapshot/src/lib.rs +++ b/crates/snapshot/src/lib.rs @@ -5,8 +5,6 @@ html_favicon_url = "https://avatars0.githubusercontent.com/u/97369466?s=256", issue_tracker_base_url = "https://github.com/paradigmxyz/reth/issues/" )] -#![warn(missing_debug_implementations, missing_docs, unreachable_pub, rustdoc::all)] -#![deny(unused_must_use, rust_2018_idioms)] #![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))] mod error; diff --git a/crates/stages/Cargo.toml b/crates/stages/Cargo.toml index 890bc135ed02..d3ddca5be3ae 100644 --- a/crates/stages/Cargo.toml +++ b/crates/stages/Cargo.toml @@ -8,6 +8,9 @@ homepage.workspace = true repository.workspace = true description = "Staged syncing primitives used in reth." +[lints] +workspace = true + [package.metadata.cargo-udeps.ignore] normal = [ # Used for diagrams in docs diff --git a/crates/stages/benches/criterion.rs b/crates/stages/benches/criterion.rs index 2a45c2521f4a..b8e8db7b8945 100644 --- a/crates/stages/benches/criterion.rs +++ b/crates/stages/benches/criterion.rs @@ -1,3 +1,4 @@ +#![allow(missing_docs, elided_lifetimes_in_paths)] use criterion::{ async_executor::FuturesExecutor, criterion_group, criterion_main, measurement::WallTime, BenchmarkGroup, Criterion, diff --git a/crates/stages/benches/setup/account_hashing.rs b/crates/stages/benches/setup/account_hashing.rs index 61d7662678e7..497dce2787f4 100644 --- a/crates/stages/benches/setup/account_hashing.rs +++ b/crates/stages/benches/setup/account_hashing.rs @@ -1,3 +1,4 @@ +#![allow(unreachable_pub)] use super::{constants, StageRange}; use reth_db::{ cursor::DbCursorRO, database::Database, tables, transaction::DbTx, DatabaseError as DbError, diff --git a/crates/stages/benches/setup/constants.rs b/crates/stages/benches/setup/constants.rs index 17b3f711de44..d3aba8d04285 100644 --- a/crates/stages/benches/setup/constants.rs +++ b/crates/stages/benches/setup/constants.rs @@ -1 +1,2 @@ +#![allow(unreachable_pub)] pub const ACCOUNT_HASHING_DB: &str = "ACCOUNT_HASHING_DB"; diff --git a/crates/stages/benches/setup/mod.rs b/crates/stages/benches/setup/mod.rs index a4422e23f401..f6322cc50d3f 100644 --- a/crates/stages/benches/setup/mod.rs +++ b/crates/stages/benches/setup/mod.rs @@ -1,3 +1,4 @@ +#![allow(unreachable_pub)] use itertools::concat; use reth_db::{ cursor::DbCursorRO, diff --git a/crates/stages/src/lib.rs b/crates/stages/src/lib.rs index f6ae6b8a5fc8..7c8ed234b219 100644 --- a/crates/stages/src/lib.rs +++ b/crates/stages/src/lib.rs @@ -64,8 +64,6 @@ html_favicon_url = "https://avatars0.githubusercontent.com/u/97369466?s=256", issue_tracker_base_url = "https://github.com/paradigmxyz/reth/issues/" )] -#![warn(missing_debug_implementations, missing_docs, unreachable_pub, rustdoc::all)] -#![deny(unused_must_use, rust_2018_idioms)] #![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))] mod error; diff --git a/crates/storage/codecs/Cargo.toml b/crates/storage/codecs/Cargo.toml index 05d9e7403df6..409190da5344 100644 --- a/crates/storage/codecs/Cargo.toml +++ b/crates/storage/codecs/Cargo.toml @@ -7,6 +7,9 @@ license.workspace = true homepage.workspace = true repository.workspace = true +[lints] +workspace = true + [dependencies] codecs-derive = { path = "./derive", default-features = false } @@ -33,4 +36,4 @@ compact = ["codecs-derive/compact"] scale = ["codecs-derive/scale"] postcard = ["codecs-derive/postcard"] no_codec = ["codecs-derive/no_codec"] -optimism = ["codecs-derive/optimism"] \ No newline at end of file +optimism = ["codecs-derive/optimism"] diff --git a/crates/storage/codecs/derive/Cargo.toml b/crates/storage/codecs/derive/Cargo.toml index 8d295b562e1f..1fe4a6a062d0 100644 --- a/crates/storage/codecs/derive/Cargo.toml +++ b/crates/storage/codecs/derive/Cargo.toml @@ -8,6 +8,9 @@ homepage.workspace = true repository.workspace = true readme = "../README.md" +[lints] +workspace = true + [package.metadata.cargo-udeps.ignore] normal = [ # Used in proc macros diff --git a/crates/storage/codecs/derive/src/lib.rs b/crates/storage/codecs/derive/src/lib.rs index 0b412788b094..f7334557fbb9 100644 --- a/crates/storage/codecs/derive/src/lib.rs +++ b/crates/storage/codecs/derive/src/lib.rs @@ -5,7 +5,7 @@ html_favicon_url = "https://avatars0.githubusercontent.com/u/97369466?s=256", issue_tracker_base_url = "https://github.com/paradigmxyz/reth/issues/" )] -#![deny(unused_must_use, rust_2018_idioms)] +#![allow(unreachable_pub, missing_docs)] #![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))] use proc_macro::{self, TokenStream, TokenTree}; @@ -30,14 +30,14 @@ pub fn derive_zstd(input: TokenStream) -> TokenStream { /// This code implements the main codec. If the codec supports it, it will also provide the [derive_arbitrary()] function, which automatically implements arbitrary traits and roundtrip fuzz tests. /// /// If you prefer to manually implement the arbitrary traits, you can still use the [add_arbitrary_tests()] function to add arbitrary fuzz tests. -/// +/// /// Example usage: /// * `#[main_codec(rlp)]`: will implement `derive_arbitrary(rlp)` or `derive_arbitrary(compact, rlp)`, if `compact` is the `main_codec`. /// * `#[main_codec(no_arbitrary)]`: will skip `derive_arbitrary` (both trait implementations and tests) #[proc_macro_attribute] #[rustfmt::skip] #[allow(unreachable_code)] -pub fn main_codec(args: TokenStream, input: TokenStream) -> TokenStream { +pub fn main_codec(args: TokenStream, input: TokenStream) -> TokenStream { #[cfg(feature = "compact")] return use_compact(args, input); diff --git a/crates/storage/codecs/src/lib.rs b/crates/storage/codecs/src/lib.rs index 993c323cb1fe..8725204fa357 100644 --- a/crates/storage/codecs/src/lib.rs +++ b/crates/storage/codecs/src/lib.rs @@ -5,14 +5,7 @@ html_favicon_url = "https://avatars0.githubusercontent.com/u/97369466?s=256", issue_tracker_base_url = "https://github.com/paradigmxyz/reth/issues/" )] -#![warn( - missing_debug_implementations, - missing_docs, - unused_crate_dependencies, - unreachable_pub, - rustdoc::all -)] -#![deny(unused_must_use, rust_2018_idioms)] +#![warn(unused_crate_dependencies)] #![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))] #![cfg_attr(not(feature = "std"), no_std)] diff --git a/crates/storage/db/Cargo.toml b/crates/storage/db/Cargo.toml index 46cc2a215808..62ef2200fe93 100644 --- a/crates/storage/db/Cargo.toml +++ b/crates/storage/db/Cargo.toml @@ -8,6 +8,9 @@ homepage.workspace = true repository.workspace = true description = "Staged syncing primitives used in reth." +[lints] +workspace = true + [dependencies] # reth reth-primitives.workspace = true diff --git a/crates/storage/db/benches/criterion.rs b/crates/storage/db/benches/criterion.rs index 223c3d1d19ab..d245c46f0347 100644 --- a/crates/storage/db/benches/criterion.rs +++ b/crates/storage/db/benches/criterion.rs @@ -1,3 +1,4 @@ +#![allow(missing_docs, elided_lifetimes_in_paths)] use criterion::{ black_box, criterion_group, criterion_main, measurement::WallTime, BenchmarkGroup, Criterion, }; diff --git a/crates/storage/db/benches/hash_keys.rs b/crates/storage/db/benches/hash_keys.rs index e2534d0b2cc4..7f432f6d54a3 100644 --- a/crates/storage/db/benches/hash_keys.rs +++ b/crates/storage/db/benches/hash_keys.rs @@ -1,3 +1,4 @@ +#![allow(missing_docs, elided_lifetimes_in_paths)] use criterion::{ black_box, criterion_group, criterion_main, measurement::WallTime, BenchmarkGroup, Criterion, }; diff --git a/crates/storage/db/benches/iai.rs b/crates/storage/db/benches/iai.rs index e4c6b7533941..cd153774361b 100644 --- a/crates/storage/db/benches/iai.rs +++ b/crates/storage/db/benches/iai.rs @@ -1,4 +1,4 @@ -#![allow(non_snake_case)] +#![allow(missing_docs, non_snake_case, unreachable_pub)] use paste::paste; use reth_db::table::{Compress, Decode, Decompress, Encode, Table}; diff --git a/crates/storage/db/build.rs b/crates/storage/db/build.rs index edec6873ad9c..1cb8e95fc40d 100644 --- a/crates/storage/db/build.rs +++ b/crates/storage/db/build.rs @@ -1,3 +1,5 @@ +#![allow(missing_docs)] + use std::error::Error; use vergen::EmitBuilder; diff --git a/crates/storage/db/src/lib.rs b/crates/storage/db/src/lib.rs index a57779d4f043..92f6b46d2ef2 100644 --- a/crates/storage/db/src/lib.rs +++ b/crates/storage/db/src/lib.rs @@ -60,8 +60,6 @@ html_favicon_url = "https://avatars0.githubusercontent.com/u/97369466?s=256", issue_tracker_base_url = "https://github.com/paradigmxyz/reth/issues/" )] -#![warn(missing_debug_implementations, missing_docs, unreachable_pub, rustdoc::all)] -#![deny(unused_must_use, rust_2018_idioms)] #![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))] /// Traits defining the database abstractions, such as cursors and transactions. diff --git a/crates/storage/libmdbx-rs/Cargo.toml b/crates/storage/libmdbx-rs/Cargo.toml index 7acda0894fc4..c6b3f4650857 100644 --- a/crates/storage/libmdbx-rs/Cargo.toml +++ b/crates/storage/libmdbx-rs/Cargo.toml @@ -8,6 +8,9 @@ description = "Idiomatic and safe MDBX wrapper with good licence" homepage.workspace = true repository.workspace = true +[lints] +workspace = true + [lib] name = "reth_libmdbx" diff --git a/crates/storage/libmdbx-rs/benches/cursor.rs b/crates/storage/libmdbx-rs/benches/cursor.rs index 89c87c6f417a..c05cef4b6ef6 100644 --- a/crates/storage/libmdbx-rs/benches/cursor.rs +++ b/crates/storage/libmdbx-rs/benches/cursor.rs @@ -1,3 +1,4 @@ +#![allow(missing_docs)] mod utils; use ::ffi::*; diff --git a/crates/storage/libmdbx-rs/benches/transaction.rs b/crates/storage/libmdbx-rs/benches/transaction.rs index 72713acd3a05..91e2c44044c7 100644 --- a/crates/storage/libmdbx-rs/benches/transaction.rs +++ b/crates/storage/libmdbx-rs/benches/transaction.rs @@ -1,3 +1,4 @@ +#![allow(missing_docs, unreachable_pub)] mod utils; use criterion::{black_box, criterion_group, criterion_main, Criterion}; diff --git a/crates/storage/libmdbx-rs/benches/utils.rs b/crates/storage/libmdbx-rs/benches/utils.rs index bf24b4866ae4..25d7b762c56f 100644 --- a/crates/storage/libmdbx-rs/benches/utils.rs +++ b/crates/storage/libmdbx-rs/benches/utils.rs @@ -1,3 +1,4 @@ +#![allow(unreachable_pub)] use reth_libmdbx::{Environment, WriteFlags}; use tempfile::{tempdir, TempDir}; diff --git a/crates/storage/libmdbx-rs/src/lib.rs b/crates/storage/libmdbx-rs/src/lib.rs index ee9e204f9510..d988932af564 100644 --- a/crates/storage/libmdbx-rs/src/lib.rs +++ b/crates/storage/libmdbx-rs/src/lib.rs @@ -4,8 +4,7 @@ html_favicon_url = "https://avatars0.githubusercontent.com/u/97369466?s=256", issue_tracker_base_url = "https://github.com/paradigmxyz/reth/issues/" )] -#![warn(missing_debug_implementations, unreachable_pub, rustdoc::all)] // TODO(danipopes): missing_docs -#![deny(unused_must_use, rust_2018_idioms)] +#![allow(missing_docs)] #![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))] pub use crate::{ diff --git a/crates/storage/nippy-jar/Cargo.toml b/crates/storage/nippy-jar/Cargo.toml index 10926c3d8c44..fb7fc4ae7d9e 100644 --- a/crates/storage/nippy-jar/Cargo.toml +++ b/crates/storage/nippy-jar/Cargo.toml @@ -8,6 +8,9 @@ homepage.workspace = true repository.workspace = true description = "Immutable data store format" +[lints] +workspace = true + [lib] name = "reth_nippy_jar" @@ -38,4 +41,4 @@ tempfile.workspace = true [features] -default = [] \ No newline at end of file +default = [] diff --git a/crates/storage/nippy-jar/src/lib.rs b/crates/storage/nippy-jar/src/lib.rs index 471e771e25d7..20d499a63603 100644 --- a/crates/storage/nippy-jar/src/lib.rs +++ b/crates/storage/nippy-jar/src/lib.rs @@ -5,8 +5,7 @@ html_favicon_url = "https://avatars0.githubusercontent.com/u/97369466?s=256", issue_tracker_base_url = "https://github.com/paradigmxyz/reth/issues/" )] -#![warn(missing_debug_implementations, unreachable_pub, rustdoc::all)] // TODO(danipopes): missing_docs -#![deny(unused_must_use, rust_2018_idioms)] +#![allow(missing_docs)] #![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))] use memmap2::Mmap; diff --git a/crates/storage/provider/Cargo.toml b/crates/storage/provider/Cargo.toml index 413b71d80c8b..64ff9742a4c9 100644 --- a/crates/storage/provider/Cargo.toml +++ b/crates/storage/provider/Cargo.toml @@ -8,6 +8,9 @@ homepage.workspace = true repository.workspace = true description = "Reth storage provider." +[lints] +workspace = true + [dependencies] # reth reth-primitives.workspace = true diff --git a/crates/storage/provider/src/lib.rs b/crates/storage/provider/src/lib.rs index 8391735e55e1..a1aa09747569 100644 --- a/crates/storage/provider/src/lib.rs +++ b/crates/storage/provider/src/lib.rs @@ -9,8 +9,6 @@ html_favicon_url = "https://avatars0.githubusercontent.com/u/97369466?s=256", issue_tracker_base_url = "https://github.com/paradigmxyz/reth/issues/" )] -#![warn(missing_debug_implementations, missing_docs, unreachable_pub, rustdoc::all)] -#![deny(unused_must_use, rust_2018_idioms)] #![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))] /// Various provider traits. diff --git a/crates/tasks/Cargo.toml b/crates/tasks/Cargo.toml index 078e61176041..ebd0df6bffef 100644 --- a/crates/tasks/Cargo.toml +++ b/crates/tasks/Cargo.toml @@ -8,6 +8,9 @@ homepage.workspace = true repository.workspace = true description = "Task management" +[lints] +workspace = true + [dependencies] # async diff --git a/crates/tasks/src/lib.rs b/crates/tasks/src/lib.rs index 7ba3fd170a26..010bc4e147d0 100644 --- a/crates/tasks/src/lib.rs +++ b/crates/tasks/src/lib.rs @@ -5,8 +5,6 @@ html_favicon_url = "https://avatars0.githubusercontent.com/u/97369466?s=256", issue_tracker_base_url = "https://github.com/paradigmxyz/reth/issues/" )] -#![warn(missing_debug_implementations, missing_docs, unreachable_pub, rustdoc::all)] -#![deny(unused_must_use, rust_2018_idioms)] #![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))] use crate::{ diff --git a/crates/tokio-util/Cargo.toml b/crates/tokio-util/Cargo.toml index 573bd78c9930..e8c21e0fa05a 100644 --- a/crates/tokio-util/Cargo.toml +++ b/crates/tokio-util/Cargo.toml @@ -8,6 +8,9 @@ homepage.workspace = true repository.workspace = true description = "Additional utilities for working with Tokio in reth." +[lints] +workspace = true + [dependencies] # async diff --git a/crates/tokio-util/src/lib.rs b/crates/tokio-util/src/lib.rs index 244b44346de0..072d55ef06af 100644 --- a/crates/tokio-util/src/lib.rs +++ b/crates/tokio-util/src/lib.rs @@ -5,8 +5,6 @@ html_favicon_url = "https://avatars0.githubusercontent.com/u/97369466?s=256", issue_tracker_base_url = "https://github.com/paradigmxyz/reth/issues/" )] -#![warn(missing_debug_implementations, missing_docs, unreachable_pub, rustdoc::all)] -#![deny(unused_must_use, rust_2018_idioms)] #![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))] mod event_listeners; diff --git a/crates/tracing/Cargo.toml b/crates/tracing/Cargo.toml index 424545b98a55..4ed18c307ad9 100644 --- a/crates/tracing/Cargo.toml +++ b/crates/tracing/Cargo.toml @@ -8,6 +8,9 @@ homepage.workspace = true repository.workspace = true description = "tracing helpers" +[lints] +workspace = true + [dependencies] tracing.workspace = true tracing-subscriber = { version = "0.3", default-features = false, features = ["env-filter", "fmt"] } diff --git a/crates/tracing/src/lib.rs b/crates/tracing/src/lib.rs index 2fc551a5f65e..561991dad9fd 100644 --- a/crates/tracing/src/lib.rs +++ b/crates/tracing/src/lib.rs @@ -13,8 +13,6 @@ html_favicon_url = "https://avatars0.githubusercontent.com/u/97369466?s=256", issue_tracker_base_url = "https://github.com/paradigmxyz/reth/issues/" )] -#![warn(missing_debug_implementations, missing_docs, unreachable_pub, rustdoc::all)] -#![deny(unused_must_use, rust_2018_idioms)] #![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))] use rolling_file::{RollingConditionBasic, RollingFileAppender}; diff --git a/crates/transaction-pool/Cargo.toml b/crates/transaction-pool/Cargo.toml index 5d00994116dc..6651437d3be2 100644 --- a/crates/transaction-pool/Cargo.toml +++ b/crates/transaction-pool/Cargo.toml @@ -8,6 +8,9 @@ homepage.workspace = true repository.workspace = true description = "Transaction pool implementation" +[lints] +workspace = true + [package.metadata.cargo-udeps.ignore] normal = [ # Used for diagrams in docs diff --git a/crates/transaction-pool/src/lib.rs b/crates/transaction-pool/src/lib.rs index 16158c0d98ab..1ab914e773c8 100644 --- a/crates/transaction-pool/src/lib.rs +++ b/crates/transaction-pool/src/lib.rs @@ -146,8 +146,6 @@ html_favicon_url = "https://avatars0.githubusercontent.com/u/97369466?s=256", issue_tracker_base_url = "https://github.com/paradigmxyz/reth/issues/" )] -#![warn(missing_debug_implementations, missing_docs, unreachable_pub, rustdoc::all)] -#![deny(unused_must_use, rust_2018_idioms)] #![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))] use crate::{identifier::TransactionId, pool::PoolInner}; diff --git a/crates/trie/Cargo.toml b/crates/trie/Cargo.toml index efdec94f86ee..8593ad8397aa 100644 --- a/crates/trie/Cargo.toml +++ b/crates/trie/Cargo.toml @@ -8,6 +8,9 @@ homepage.workspace = true repository.workspace = true description = "Merkle trie implementation" +[lints] +workspace = true + [dependencies] # reth reth-primitives.workspace = true @@ -22,7 +25,7 @@ tokio = { workspace = true, default-features = false, features = ["sync"] } # tracing tracing.workspace = true -# misc +# misc thiserror.workspace = true derive_more = "0.99" auto_impl = "1" diff --git a/crates/trie/benches/prefix_set.rs b/crates/trie/benches/prefix_set.rs index 2fd8ff28d470..343b99b021ef 100644 --- a/crates/trie/benches/prefix_set.rs +++ b/crates/trie/benches/prefix_set.rs @@ -1,3 +1,4 @@ +#![allow(missing_docs, unreachable_pub, elided_lifetimes_in_paths)] use criterion::{ black_box, criterion_group, criterion_main, measurement::WallTime, BenchmarkGroup, Criterion, }; diff --git a/crates/trie/src/lib.rs b/crates/trie/src/lib.rs index 9e842a7bfa97..866ea33b44b8 100644 --- a/crates/trie/src/lib.rs +++ b/crates/trie/src/lib.rs @@ -11,8 +11,6 @@ html_favicon_url = "https://avatars0.githubusercontent.com/u/97369466?s=256", issue_tracker_base_url = "https://github.com/paradigmxyz/reth/issues/" )] -#![warn(missing_debug_implementations, missing_docs, unreachable_pub, rustdoc::all)] -#![deny(unused_must_use, rust_2018_idioms)] #![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))] /// The implementation of a container for storing intermediate changes to a trie. diff --git a/testing/ef-tests/Cargo.toml b/testing/ef-tests/Cargo.toml index 29813d1dbeca..6f1d776d150d 100644 --- a/testing/ef-tests/Cargo.toml +++ b/testing/ef-tests/Cargo.toml @@ -8,6 +8,9 @@ license.workspace = true homepage.workspace = true repository.workspace = true +[lints] +workspace = true + [features] ef-tests = [] diff --git a/testing/ef-tests/src/lib.rs b/testing/ef-tests/src/lib.rs index 24180d5cce3e..45f296d1f5f1 100644 --- a/testing/ef-tests/src/lib.rs +++ b/testing/ef-tests/src/lib.rs @@ -5,8 +5,6 @@ html_favicon_url = "https://avatars0.githubusercontent.com/u/97369466?s=256", issue_tracker_base_url = "https://github.com/paradigmxyz/reth/issues/" )] -#![warn(missing_debug_implementations, missing_docs, unreachable_pub, rustdoc::all)] -#![deny(unused_must_use, rust_2018_idioms)] #![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))] pub mod case; From b30c42caad566b8f523849d5a36db299d28b447e Mon Sep 17 00:00:00 2001 From: Matthias Seitz Date: Thu, 4 Jan 2024 15:29:40 +0100 Subject: [PATCH 248/277] chore: use workspace lints for new crate (#5939) --- crates/consensus/beacon-core/Cargo.toml | 3 +++ crates/consensus/beacon-core/src/lib.rs | 8 -------- 2 files changed, 3 insertions(+), 8 deletions(-) diff --git a/crates/consensus/beacon-core/Cargo.toml b/crates/consensus/beacon-core/Cargo.toml index f411c2d78e9e..01623eaf9e62 100644 --- a/crates/consensus/beacon-core/Cargo.toml +++ b/crates/consensus/beacon-core/Cargo.toml @@ -7,6 +7,9 @@ license.workspace = true homepage.workspace = true repository.workspace = true +[lints] +workspace = true + [dependencies] # reth reth-consensus-common.workspace = true diff --git a/crates/consensus/beacon-core/src/lib.rs b/crates/consensus/beacon-core/src/lib.rs index d12bb4cff53c..70b113baf9b9 100644 --- a/crates/consensus/beacon-core/src/lib.rs +++ b/crates/consensus/beacon-core/src/lib.rs @@ -5,14 +5,6 @@ html_favicon_url = "https://avatars0.githubusercontent.com/u/97369466?s=256", issue_tracker_base_url = "https://github.com/paradigmxyz/reth/issues/" )] -#![warn( - missing_debug_implementations, - missing_docs, - unused_crate_dependencies, - unreachable_pub, - rustdoc::all -)] -#![deny(unused_must_use, rust_2018_idioms)] #![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))] //! Consensus for ethereum network From 3b0e7a5108f454046b35be2ee5d961b1c627a4f0 Mon Sep 17 00:00:00 2001 From: Thomas Coratger <60488569+tcoratger@users.noreply.github.com> Date: Thu, 4 Jan 2024 17:49:26 +0100 Subject: [PATCH 249/277] add documentation for `SecretKeyError` to remove `#[allow(missing_docs)]` (#5946) --- bin/reth/src/args/secret_key.rs | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/bin/reth/src/args/secret_key.rs b/bin/reth/src/args/secret_key.rs index 9ecb1792ebe0..e08f8caaf2fc 100644 --- a/bin/reth/src/args/secret_key.rs +++ b/bin/reth/src/args/secret_key.rs @@ -9,14 +9,23 @@ use thiserror::Error; /// Errors returned by loading a [`SecretKey`], including IO errors. #[derive(Error, Debug)] -#[allow(missing_docs)] pub enum SecretKeyError { + /// Error encountered during decoding of the secret key. #[error(transparent)] SecretKeyDecodeError(#[from] SecretKeyBaseError), + + /// Error related to file system path operations. #[error(transparent)] SecretKeyFsPathError(#[from] FsPathError), + + /// Represents an error when failed to access the key file. #[error("failed to access key file {secret_file:?}: {error}")] - FailedToAccessKeyFile { error: io::Error, secret_file: PathBuf }, + FailedToAccessKeyFile { + /// The encountered IO error. + error: io::Error, + /// Path to the secret key file. + secret_file: PathBuf, + }, } /// Attempts to load a [`SecretKey`] from a specified path. If no file exists there, then it From e3c3ddc14e913f1737bbc3a0e064934df9b2abf9 Mon Sep 17 00:00:00 2001 From: Matthias Seitz Date: Thu, 4 Jan 2024 17:49:50 +0100 Subject: [PATCH 250/277] chore: move OP payload builder error to OP crate (#5940) --- Cargo.lock | 1 + crates/payload/builder/src/error.rs | 35 ++++++++++------------------ crates/payload/optimism/Cargo.toml | 2 ++ crates/payload/optimism/src/error.rs | 20 ++++++++++++++++ crates/payload/optimism/src/lib.rs | 16 ++++++------- 5 files changed, 42 insertions(+), 32 deletions(-) create mode 100644 crates/payload/optimism/src/error.rs diff --git a/Cargo.lock b/Cargo.lock index 1fea112c6b90..b0ead9b67ddf 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -6252,6 +6252,7 @@ dependencies = [ "reth-revm", "reth-transaction-pool", "revm", + "thiserror", "tracing", ] diff --git a/crates/payload/builder/src/error.rs b/crates/payload/builder/src/error.rs index cd998f156a4a..8382b4b16bba 100644 --- a/crates/payload/builder/src/error.rs +++ b/crates/payload/builder/src/error.rs @@ -26,10 +26,19 @@ pub enum PayloadBuilderError { /// Thrown if the payload requests withdrawals before Shanghai activation. #[error("withdrawals set before Shanghai activation")] WithdrawalsBeforeShanghai, - /// Optimism specific payload building errors. - #[cfg(feature = "optimism")] + /// Any other payload building errors. #[error(transparent)] - Optimism(#[from] OptimismPayloadBuilderError), + Other(Box), +} + +impl PayloadBuilderError { + /// Create a new error from a boxed error. + pub fn other(error: E) -> Self + where + E: std::error::Error + Send + Sync + 'static, + { + PayloadBuilderError::Other(Box::new(error)) + } } impl From for PayloadBuilderError { @@ -38,26 +47,6 @@ impl From for PayloadBuilderError { } } -/// Optimism specific payload building errors. -#[cfg(feature = "optimism")] -#[derive(Debug, thiserror::Error)] -pub enum OptimismPayloadBuilderError { - /// Thrown when a transaction fails to convert to a - /// [reth_primitives::TransactionSignedEcRecovered]. - #[error("failed to convert deposit transaction to TransactionSignedEcRecovered")] - TransactionEcRecoverFailed, - /// Thrown when the L1 block info could not be parsed from the calldata of the - /// first transaction supplied in the payload attributes. - #[error("failed to parse L1 block info from L1 info tx calldata")] - L1BlockInfoParseFailed, - /// Thrown when a database account could not be loaded. - #[error("failed to load account {0:?}")] - AccountLoadFailed(revm_primitives::Address), - /// Thrown when force deploy of create2deployer code fails. - #[error("failed to force create2deployer account code")] - ForceCreate2DeployerFail, -} - impl From for PayloadBuilderError { fn from(_: oneshot::error::RecvError) -> Self { PayloadBuilderError::ChannelClosed diff --git a/crates/payload/optimism/Cargo.toml b/crates/payload/optimism/Cargo.toml index f5f9309804f1..7ce5c044fc74 100644 --- a/crates/payload/optimism/Cargo.toml +++ b/crates/payload/optimism/Cargo.toml @@ -25,6 +25,8 @@ revm.workspace = true # misc tracing.workspace = true +thiserror.workspace = true + [features] # This is a workaround for reth-cli crate to allow this as mandatory dependency without breaking the build even if unused. diff --git a/crates/payload/optimism/src/error.rs b/crates/payload/optimism/src/error.rs new file mode 100644 index 000000000000..7877ec48f9ce --- /dev/null +++ b/crates/payload/optimism/src/error.rs @@ -0,0 +1,20 @@ +//! Error type + +/// Optimism specific payload building errors. +#[derive(Debug, thiserror::Error)] +pub enum OptimismPayloadBuilderError { + /// Thrown when a transaction fails to convert to a + /// [reth_primitives::TransactionSignedEcRecovered]. + #[error("failed to convert deposit transaction to TransactionSignedEcRecovered")] + TransactionEcRecoverFailed, + /// Thrown when the L1 block info could not be parsed from the calldata of the + /// first transaction supplied in the payload attributes. + #[error("failed to parse L1 block info from L1 info tx calldata")] + L1BlockInfoParseFailed, + /// Thrown when a database account could not be loaded. + #[error("failed to load account {0:?}")] + AccountLoadFailed(reth_primitives::Address), + /// Thrown when force deploy of create2deployer code fails. + #[error("failed to force create2deployer account code")] + ForceCreate2DeployerFail, +} diff --git a/crates/payload/optimism/src/lib.rs b/crates/payload/optimism/src/lib.rs index 07a45324e41d..6be674e35aaf 100644 --- a/crates/payload/optimism/src/lib.rs +++ b/crates/payload/optimism/src/lib.rs @@ -10,13 +10,13 @@ #[cfg(feature = "optimism")] pub use builder::*; +pub mod error; + #[cfg(feature = "optimism")] mod builder { + use crate::error::OptimismPayloadBuilderError; use reth_basic_payload_builder::*; - use reth_payload_builder::{ - error::{OptimismPayloadBuilderError, PayloadBuilderError}, - BuiltPayload, - }; + use reth_payload_builder::{error::PayloadBuilderError, BuiltPayload}; use reth_primitives::{ constants::BEACON_NONCE, proofs, @@ -158,7 +158,7 @@ mod builder { &mut db, ) .map_err(|_| { - PayloadBuilderError::Optimism(OptimismPayloadBuilderError::ForceCreate2DeployerFail) + PayloadBuilderError::other(OptimismPayloadBuilderError::ForceCreate2DeployerFail) })?; let mut receipts = Vec::new(); @@ -173,9 +173,7 @@ mod builder { // Deposit transactions do not have signatures, so if the tx is a deposit, this // will just pull in its `from` address. let sequencer_tx = sequencer_tx.clone().try_into_ecrecovered().map_err(|_| { - PayloadBuilderError::Optimism( - OptimismPayloadBuilderError::TransactionEcRecoverFailed, - ) + PayloadBuilderError::other(OptimismPayloadBuilderError::TransactionEcRecoverFailed) })?; // Cache the depositor account prior to the state transition for the deposit nonce. @@ -190,7 +188,7 @@ mod builder { }) .transpose() .map_err(|_| { - PayloadBuilderError::Optimism(OptimismPayloadBuilderError::AccountLoadFailed( + PayloadBuilderError::other(OptimismPayloadBuilderError::AccountLoadFailed( sequencer_tx.signer(), )) })?; From 09f04a0f6cb51a5308ff04872c65300a137bdbb5 Mon Sep 17 00:00:00 2001 From: evalir Date: Thu, 4 Jan 2024 13:11:49 -0400 Subject: [PATCH 251/277] 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 b0ead9b67ddf..1f9792270e5a 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 6734ecabe269..1c75295c0c70 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 0a29633653fe..2c21371b812b 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 14a6b86908b9..7e40e7778aff 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 e9d0b862292b..6dc225ffad9d 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 c0cf0bae4391..33c580dfc7bb 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 7bcd495245a4..b9b51f00feb0 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 414dc8063aa2..7f65c5476dd6 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 89f972e235ca..62f93e7bfd0f 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 225d590ccf6f..d660bbbcf291 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 755eff7b16cb..000000000000 --- 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 91370b7cf70e..000000000000 --- 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 66d2e28cb8c0..000000000000 --- 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 f580a4dbe8da..000000000000 --- 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 5fb145ab68a3..000000000000 --- 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 b6c65bd19139..000000000000 --- 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 151352171cae..000000000000 --- 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 199af24976a8..000000000000 --- 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 a1a45d7b26df..e313abb3903d 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 66a8266fb260..000000000000 --- 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 2a12903ec09d..000000000000 --- 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 dc93393229b9..000000000000 --- 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 2ad0c8be0bd7..000000000000 --- 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 617b448502d4..000000000000 --- 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 a19da2254c23..000000000000 --- 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 bf6910cf19a2..000000000000 --- 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 927df7b76cbc..000000000000 --- 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 f6b4979da07a..000000000000 --- 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 a9db367350f6..000000000000 --- 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 04cb7c812b48..000000000000 --- 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 8cd79eb2c13f..000000000000 --- 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 2fbc36137a18..000000000000 --- 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 8345006a0789..000000000000 --- 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 f6fd892a90af..000000000000 --- 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 b6217bdb82f3..000000000000 --- 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 209fcb7a23b5..000000000000 --- 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 d3d20a9f65aa..b7a9ab73f2cb 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 c780aa2166e8..000000000000 --- 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 ebb9dbaf22da..72cc85bd8960 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 7ffe64a4d064..000000000000 --- 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 65c577680105..ce10976bfde6 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 e78e56069f6a..000000000000 --- 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 c8f54d4ec6a3..000000000000 --- 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 a7fe3a99a56c..000000000000 --- 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 84cd0992b585..f1673b45bcdb 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 c6dcfa20dac7..ecd6c8968a1e 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 560272c22a7a..db91ad806f58 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 b50598e2992c..d2fb5b0b1240 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 dd8e50780b73..ac039d148f94 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", From 0520c62bb6c7873e0dc7ea3ebabe66a0d371a9b4 Mon Sep 17 00:00:00 2001 From: Alexey Shekhirin Date: Thu, 4 Jan 2024 17:59:19 +0000 Subject: [PATCH 252/277] feat(cli): enable backtrace on panic by default (#5942) --- bin/reth/src/main.rs | 5 +++++ bin/reth/src/optimism.rs | 5 +++++ 2 files changed, 10 insertions(+) diff --git a/bin/reth/src/main.rs b/bin/reth/src/main.rs index ea057ee6b30a..758682c4defe 100644 --- a/bin/reth/src/main.rs +++ b/bin/reth/src/main.rs @@ -9,6 +9,11 @@ compile_error!("Cannot build the `reth` binary with the `optimism` feature flag #[cfg(not(feature = "optimism"))] fn main() { + // Enable backtraces unless a RUST_BACKTRACE value has already been explicitly provided. + if std::env::var("RUST_BACKTRACE").is_err() { + std::env::set_var("RUST_BACKTRACE", "1"); + } + if let Err(err) = reth::cli::run() { eprintln!("Error: {err:?}"); std::process::exit(1); diff --git a/bin/reth/src/optimism.rs b/bin/reth/src/optimism.rs index 89ba2dd840bf..dbd980b1e4d1 100644 --- a/bin/reth/src/optimism.rs +++ b/bin/reth/src/optimism.rs @@ -9,6 +9,11 @@ compile_error!("Cannot build the `op-reth` binary with the `optimism` feature fl #[cfg(feature = "optimism")] fn main() { + // Enable backtraces unless a RUST_BACKTRACE value has already been explicitly provided. + if std::env::var("RUST_BACKTRACE").is_err() { + std::env::set_var("RUST_BACKTRACE", "1"); + } + if let Err(err) = reth::cli::run() { eprintln!("Error: {err:?}"); std::process::exit(1); From b828867f8c2770e92f0562cd79ad3d735199acfb Mon Sep 17 00:00:00 2001 From: Thomas Coratger <60488569+tcoratger@users.noreply.github.com> Date: Thu, 4 Jan 2024 19:13:33 +0100 Subject: [PATCH 253/277] lints: use rust-2018-idioms to eliminate `#![allow(elided-lifetimes-in-paths)]` (#5944) --- crates/stages/benches/criterion.rs | 6 +++--- crates/storage/db/benches/criterion.rs | 8 ++++---- crates/storage/db/benches/hash_keys.rs | 4 ++-- crates/trie/benches/prefix_set.rs | 4 ++-- 4 files changed, 11 insertions(+), 11 deletions(-) diff --git a/crates/stages/benches/criterion.rs b/crates/stages/benches/criterion.rs index b8e8db7b8945..e9354503d279 100644 --- a/crates/stages/benches/criterion.rs +++ b/crates/stages/benches/criterion.rs @@ -1,4 +1,4 @@ -#![allow(missing_docs, elided_lifetimes_in_paths)] +#![allow(missing_docs)] use criterion::{ async_executor::FuturesExecutor, criterion_group, criterion_main, measurement::WallTime, BenchmarkGroup, Criterion, @@ -116,7 +116,7 @@ fn merkle(c: &mut Criterion) { fn measure_stage_with_path( path: PathBuf, - group: &mut BenchmarkGroup, + group: &mut BenchmarkGroup<'_, WallTime>, setup: F, stage: S, stage_range: StageRange, @@ -149,7 +149,7 @@ fn measure_stage_with_path( } fn measure_stage( - group: &mut BenchmarkGroup, + group: &mut BenchmarkGroup<'_, WallTime>, setup: F, stage: S, block_interval: std::ops::Range, diff --git a/crates/storage/db/benches/criterion.rs b/crates/storage/db/benches/criterion.rs index d245c46f0347..35d9b12c45c5 100644 --- a/crates/storage/db/benches/criterion.rs +++ b/crates/storage/db/benches/criterion.rs @@ -1,4 +1,4 @@ -#![allow(missing_docs, elided_lifetimes_in_paths)] +#![allow(missing_docs)] use criterion::{ black_box, criterion_group, criterion_main, measurement::WallTime, BenchmarkGroup, Criterion, }; @@ -52,7 +52,7 @@ pub fn serialization(c: &mut Criterion) { } /// Measures `Encode`, `Decode`, `Compress` and `Decompress`. -fn measure_table_serialization(group: &mut BenchmarkGroup) +fn measure_table_serialization(group: &mut BenchmarkGroup<'_, WallTime>) where T: Table + Default, T::Key: Default + Clone + for<'de> serde::Deserialize<'de>, @@ -117,7 +117,7 @@ where } /// Measures `SeqWrite`, `RandomWrite`, `SeqRead` and `RandomRead` using `cursor` and `tx.put`. -fn measure_table_db(group: &mut BenchmarkGroup) +fn measure_table_db(group: &mut BenchmarkGroup<'_, WallTime>) where T: Table + Default, T::Key: Default + Clone + for<'de> serde::Deserialize<'de>, @@ -213,7 +213,7 @@ where } /// Measures `SeqWrite`, `RandomWrite` and `SeqRead` using `cursor_dup` and `tx.put`. -fn measure_dupsort_db(group: &mut BenchmarkGroup) +fn measure_dupsort_db(group: &mut BenchmarkGroup<'_, WallTime>) where T: Table + Default + DupSort, T::Key: Default + Clone + for<'de> serde::Deserialize<'de>, diff --git a/crates/storage/db/benches/hash_keys.rs b/crates/storage/db/benches/hash_keys.rs index 7f432f6d54a3..6c5e428d855d 100644 --- a/crates/storage/db/benches/hash_keys.rs +++ b/crates/storage/db/benches/hash_keys.rs @@ -1,4 +1,4 @@ -#![allow(missing_docs, elided_lifetimes_in_paths)] +#![allow(missing_docs)] use criterion::{ black_box, criterion_group, criterion_main, measurement::WallTime, BenchmarkGroup, Criterion, }; @@ -38,7 +38,7 @@ pub fn hash_keys(c: &mut Criterion) { } } -fn measure_table_insertion(group: &mut BenchmarkGroup, size: usize) +fn measure_table_insertion(group: &mut BenchmarkGroup<'_, WallTime>, size: usize) where T: Table + Default, T::Key: Default diff --git a/crates/trie/benches/prefix_set.rs b/crates/trie/benches/prefix_set.rs index 343b99b021ef..7ff96cd84c34 100644 --- a/crates/trie/benches/prefix_set.rs +++ b/crates/trie/benches/prefix_set.rs @@ -1,4 +1,4 @@ -#![allow(missing_docs, unreachable_pub, elided_lifetimes_in_paths)] +#![allow(missing_docs, unreachable_pub)] use criterion::{ black_box, criterion_group, criterion_main, measurement::WallTime, BenchmarkGroup, Criterion, }; @@ -58,7 +58,7 @@ pub fn prefix_set_lookups(c: &mut Criterion) { } fn prefix_set_bench( - group: &mut BenchmarkGroup, + group: &mut BenchmarkGroup<'_, WallTime>, description: &str, (preload, input, expected): (Vec, Vec, Vec), ) { From d02cc4b833aeffafab885c5c6693774308c160a1 Mon Sep 17 00:00:00 2001 From: Thomas Coratger <60488569+tcoratger@users.noreply.github.com> Date: Thu, 4 Jan 2024 19:30:42 +0100 Subject: [PATCH 254/277] doc: Improve documentation for `StageEnum` to remove `#[allow(missing_docs)]` (#5945) --- bin/reth/src/args/stage_args.rs | 41 +++++++++++++++++++++++++++++++-- 1 file changed, 39 insertions(+), 2 deletions(-) diff --git a/bin/reth/src/args/stage_args.rs b/bin/reth/src/args/stage_args.rs index 997f0f687c94..46618ff2f726 100644 --- a/bin/reth/src/args/stage_args.rs +++ b/bin/reth/src/args/stage_args.rs @@ -1,19 +1,56 @@ //! Shared arguments related to stages -/// Represents a certain stage of the pipeline. +/// Represents a specific stage within the data pipeline. +/// +/// Different stages within the pipeline have dedicated functionalities and operations. #[derive(Debug, Clone, Copy, Eq, PartialEq, PartialOrd, Ord, clap::ValueEnum)] -#[allow(missing_docs)] pub enum StageEnum { + /// The headers stage within the pipeline. + /// + /// This stage handles operations related to block headers. Headers, + /// The bodies stage within the pipeline. + /// + /// This stage deals with block bodies and their associated data. Bodies, + /// The senders stage within the pipeline. + /// + /// Responsible for sender-related processes and data recovery. Senders, + /// The execution stage within the pipeline. + /// + /// Handles the execution of transactions and contracts. Execution, + /// The account hashing stage within the pipeline. + /// + /// Manages operations related to hashing account data. AccountHashing, + /// The storage hashing stage within the pipeline. + /// + /// Manages operations related to hashing storage data. StorageHashing, + /// The hashing stage within the pipeline. + /// + /// Covers general data hashing operations. Hashing, + /// The Merkle stage within the pipeline. + /// + /// Handles Merkle tree-related computations and data processing. Merkle, + /// The transaction lookup stage within the pipeline. + /// + /// Deals with the retrieval and processing of transactions. TxLookup, + /// The account history stage within the pipeline. + /// + /// Manages historical data related to accounts. AccountHistory, + /// The storage history stage within the pipeline. + /// + /// Manages historical data related to storage. StorageHistory, + /// The total difficulty stage within the pipeline. + /// + /// Handles computations and data related to total difficulty. TotalDifficulty, } From 9a25470ae280189b399ce5dab9f5ac276ceedae6 Mon Sep 17 00:00:00 2001 From: evalir Date: Thu, 4 Jan 2024 15:13:46 -0400 Subject: [PATCH 255/277] feat(`rpc-types`): use `alloy-trace-rpc-types` crate (#5949) --- Cargo.lock | 18 +++++++++++++++++- Cargo.toml | 1 + crates/rpc/rpc-types/Cargo.toml | 1 + crates/rpc/rpc-types/src/lib.rs | 4 ++++ 4 files changed, 23 insertions(+), 1 deletion(-) diff --git a/Cargo.lock b/Cargo.lock index 1f9792270e5a..f8a52c62fb50 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -216,10 +216,25 @@ dependencies = [ "syn 2.0.43", ] +[[package]] +name = "alloy-rpc-trace-types" +version = "0.1.0" +source = "git+https://github.com/alloy-rs/alloy#f5174888c523b70fa1a94fad24f3214048d3d462" +dependencies = [ + "alloy-primitives", + "alloy-rlp", + "alloy-rpc-types", + "itertools 0.12.0", + "jsonrpsee-types", + "serde", + "serde_json", + "thiserror", +] + [[package]] name = "alloy-rpc-types" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy#e64c1c2b9e4f80fe13ac4f65504a4c30fe786454" +source = "git+https://github.com/alloy-rs/alloy#f5174888c523b70fa1a94fad24f3214048d3d462" dependencies = [ "alloy-primitives", "alloy-rlp", @@ -6581,6 +6596,7 @@ version = "0.1.0-alpha.13" dependencies = [ "alloy-primitives", "alloy-rlp", + "alloy-rpc-trace-types", "alloy-rpc-types", "arbitrary", "bytes", diff --git a/Cargo.toml b/Cargo.toml index 1c75295c0c70..e88f19cb2959 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -161,6 +161,7 @@ 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"] } +alloy-rpc-trace-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/rpc/rpc-types/Cargo.toml b/crates/rpc/rpc-types/Cargo.toml index 62f93e7bfd0f..a9faa5e44dc3 100644 --- a/crates/rpc/rpc-types/Cargo.toml +++ b/crates/rpc/rpc-types/Cargo.toml @@ -16,6 +16,7 @@ workspace = true alloy-rlp = { workspace = true, features = ["arrayvec", "derive"] } alloy-primitives = { workspace = true, features = ["rand", "rlp", "serde"] } alloy-rpc-types.workspace = true +alloy-rpc-trace-types.workspace = true ethereum_ssz_derive = { version = "0.5", optional = true } ethereum_ssz = { version = "0.5", optional = true } diff --git a/crates/rpc/rpc-types/src/lib.rs b/crates/rpc/rpc-types/src/lib.rs index f1673b45bcdb..77d2367177f1 100644 --- a/crates/rpc/rpc-types/src/lib.rs +++ b/crates/rpc/rpc-types/src/lib.rs @@ -22,6 +22,10 @@ pub mod serde_helpers; // Ethereum specific rpc types coming from alloy. pub use alloy_rpc_types::*; +pub mod trace { + //! RPC types for trace endpoints and inspectors. + pub use alloy_rpc_trace_types::*; +} // Ethereum specific rpc types related to typed transaction requests and the engine API. pub use eth::{ engine, From 92f33b071c67af58471e1567eddd6406ed657a79 Mon Sep 17 00:00:00 2001 From: Thomas Coratger <60488569+tcoratger@users.noreply.github.com> Date: Fri, 5 Jan 2024 14:10:37 +0100 Subject: [PATCH 256/277] add missing documentation for various crates (#5950) --- bin/reth/src/cli/components.rs | 8 +- crates/interfaces/src/p2p/error.rs | 13 +- crates/net/discv4/src/error.rs | 11 +- crates/net/dns/src/error.rs | 25 +++- crates/net/network-api/src/error.rs | 2 +- crates/net/network/src/message.rs | 81 +++++++++--- crates/net/network/src/network.rs | 56 ++++---- crates/net/network/src/transactions.rs | 26 +++- crates/primitives/src/chain/mod.rs | 24 +++- crates/primitives/src/fs.rs | 122 ++++++++++++++---- crates/primitives/src/snapshot/compression.rs | 7 +- crates/primitives/src/stage/id.rs | 15 ++- .../revm-inspectors/src/tracing/js/mod.rs | 17 ++- .../revm/revm-inspectors/src/tracing/types.rs | 9 +- crates/rpc/ipc/src/client.rs | 5 +- crates/rpc/rpc/src/eth/error.rs | 40 ++++-- crates/rpc/rpc/src/layers/jwt_secret.rs | 19 ++- crates/snapshot/src/error.rs | 6 +- crates/stages/src/stages/merkle.rs | 8 +- crates/storage/db/src/metrics.rs | 30 ++++- crates/storage/db/src/version.rs | 19 ++- crates/storage/provider/src/traits/chain.rs | 13 +- crates/transaction-pool/src/blobstore/disk.rs | 8 +- crates/transaction-pool/src/pool/txpool.rs | 4 +- 24 files changed, 448 insertions(+), 120 deletions(-) diff --git a/bin/reth/src/cli/components.rs b/bin/reth/src/cli/components.rs index 18ef804f4a11..b7d60c08caa4 100644 --- a/bin/reth/src/cli/components.rs +++ b/bin/reth/src/cli/components.rs @@ -101,13 +101,19 @@ pub struct RethRpcComponents<'a, Reth: RethNodeComponents> { } /// A Generic implementation of the RethNodeComponents trait. +/// +/// Represents components required for the Reth node. #[derive(Clone, Debug)] -#[allow(missing_docs)] pub struct RethNodeComponentsImpl { + /// Represents the provider instance. pub provider: Provider, + /// Represents the transaction pool instance. pub pool: Pool, + /// Represents the network instance used for communication. pub network: Network, + /// Represents the task executor instance. pub task_executor: Tasks, + /// Represents the events subscription handler instance. pub events: Events, } diff --git a/crates/interfaces/src/p2p/error.rs b/crates/interfaces/src/p2p/error.rs index 29c238e5f529..667d9d5487d9 100644 --- a/crates/interfaces/src/p2p/error.rs +++ b/crates/interfaces/src/p2p/error.rs @@ -72,18 +72,29 @@ impl EthResponseValidator for RequestResult> { } /// Error variants that can happen when sending requests to a session. +/// +/// Represents errors encountered when sending requests. #[derive(Clone, Debug, Error, Eq, PartialEq)] -#[allow(missing_docs)] pub enum RequestError { + /// Closed channel to the peer. #[error("closed channel to the peer")] + /// Indicates the channel to the peer is closed. ChannelClosed, + /// Connection to a peer dropped while handling the request. #[error("connection to a peer dropped while handling the request")] + /// Represents a dropped connection while handling the request. ConnectionDropped, + /// Capability message is not supported by the remote peer. #[error("capability message is not supported by remote peer")] + /// Indicates an unsupported capability message from the remote peer. UnsupportedCapability, + /// Request timed out while awaiting response. #[error("request timed out while awaiting response")] + /// Represents a timeout while waiting for a response. Timeout, + /// Received bad response. #[error("received bad response")] + /// Indicates a bad response was received. BadResponse, } diff --git a/crates/net/discv4/src/error.rs b/crates/net/discv4/src/error.rs index 19ab703cbc7f..1fbd67d47e2b 100644 --- a/crates/net/discv4/src/error.rs +++ b/crates/net/discv4/src/error.rs @@ -4,17 +4,26 @@ use tokio::sync::{mpsc::error::SendError, oneshot::error::RecvError}; /// Error thrown when decoding a UDP packet. #[derive(Debug, thiserror::Error)] -#[allow(missing_docs)] pub enum DecodePacketError { + /// Failed to RLP decode the packet. #[error("failed to rlp decode: {0}")] + /// Indicates a failure to RLP decode the packet. Rlp(#[from] alloy_rlp::Error), + /// Received packet length is too short. #[error("received packet length is too short")] + /// Indicates the received packet length is insufficient. PacketTooShort, + /// Header/data hash mismatch. #[error("header/data hash mismatch")] + /// Indicates a mismatch between header and data hashes. HashMismatch, + /// Unsupported message ID. #[error("message ID {0} is not supported")] + /// Indicates an unsupported message ID. UnknownMessage(u8), + /// Failed to recover public key. #[error("failed to recover public key: {0}")] + /// Indicates a failure to recover the public key. Secp256k1(#[from] secp256k1::Error), } diff --git a/crates/net/dns/src/error.rs b/crates/net/dns/src/error.rs index d24c83ab9def..cd65f5699a33 100644 --- a/crates/net/dns/src/error.rs +++ b/crates/net/dns/src/error.rs @@ -3,38 +3,59 @@ use crate::tree::TreeRootEntry; /// Alias for a parse result pub(crate) type ParseEntryResult = Result; +/// Alias for lookup results pub(crate) type LookupResult = Result; /// Error while parsing a [DnsEntry](crate::tree::DnsEntry) #[derive(thiserror::Error, Debug)] -#[allow(missing_docs)] pub enum ParseDnsEntryError { + /// Unknown entry error. #[error("unknown entry: {0}")] + /// Indicates an unknown entry encountered during parsing. UnknownEntry(String), + /// Field not found error. #[error("field {0} not found")] + /// Indicates a field was not found during parsing. FieldNotFound(&'static str), + /// Base64 decoding error. #[error("base64 decoding failed: {0}")] + /// Indicates a failure during Base64 decoding. Base64DecodeError(String), + /// Base32 decoding error. #[error("base32 decoding failed: {0}")] + /// Indicates a failure during Base32 decoding. Base32DecodeError(String), + /// RLP decoding error. #[error("{0}")] + /// Indicates an error during RLP decoding. RlpDecodeError(String), + /// Invalid child hash error in a branch. #[error("invalid child hash in branch: {0}")] + /// Indicates an invalid child hash within a branch. InvalidChildHash(String), + /// Other error. #[error("{0}")] + /// Indicates other unspecified errors. Other(String), } /// Errors that can happen during lookups #[derive(thiserror::Error, Debug)] -#[allow(missing_docs)] pub(crate) enum LookupError { + /// Parse error. #[error(transparent)] + /// Represents errors during parsing. Parse(#[from] ParseDnsEntryError), + /// Invalid root error. #[error("failed to verify root {0}")] + /// Indicates failure while verifying the root entry. InvalidRoot(TreeRootEntry), + /// Request timed out error. #[error("request timed out")] + /// Indicates a timeout occurred during the request. RequestTimedOut, + /// Entry not found error. #[error("entry not found")] + /// Indicates the requested entry was not found. EntryNotFound, } diff --git a/crates/net/network-api/src/error.rs b/crates/net/network-api/src/error.rs index 66e1fd0b0766..4572145406e8 100644 --- a/crates/net/network-api/src/error.rs +++ b/crates/net/network-api/src/error.rs @@ -2,9 +2,9 @@ use thiserror::Error; use tokio::sync::{mpsc, oneshot}; /// Network Errors -#[allow(missing_docs)] #[derive(Error, Debug, Clone, PartialEq, Eq)] pub enum NetworkError { + /// Indicates that the sender has been dropped. #[error("sender has been dropped")] ChannelClosed, } diff --git a/crates/net/network/src/message.rs b/crates/net/network/src/message.rs index 8f8a5e13e337..b6861267a172 100644 --- a/crates/net/network/src/message.rs +++ b/crates/net/network/src/message.rs @@ -61,45 +61,66 @@ pub enum PeerMessage { /// Request Variants that only target block related data. #[derive(Debug, Clone, PartialEq, Eq)] -#[allow(missing_docs)] pub enum BlockRequest { + /// Requests block headers from the peer. + /// + /// The response should be sent through the channel. GetBlockHeaders(GetBlockHeaders), + + /// Requests block bodies from the peer. + /// + /// The response should be sent through the channel. GetBlockBodies(GetBlockBodies), } /// Protocol related request messages that expect a response #[derive(Debug)] -#[allow(missing_docs)] pub enum PeerRequest { - /// Request Block headers from the peer. + /// Requests block headers from the peer. /// /// The response should be sent through the channel. GetBlockHeaders { + /// The request for block headers. request: GetBlockHeaders, + /// The channel to send the response for block headers. response: oneshot::Sender>, }, - /// Request Block headers from the peer. + /// Requests block bodies from the peer. /// /// The response should be sent through the channel. GetBlockBodies { + /// The request for block bodies. request: GetBlockBodies, + /// The channel to send the response for block bodies. response: oneshot::Sender>, }, - /// Request pooled transactions from the peer. + /// Requests pooled transactions from the peer. /// /// The response should be sent through the channel. GetPooledTransactions { + /// The request for pooled transactions. request: GetPooledTransactions, + /// The channel to send the response for pooled transactions. response: oneshot::Sender>, }, - /// Request NodeData from the peer. + /// Requests NodeData from the peer. /// /// The response should be sent through the channel. - GetNodeData { request: GetNodeData, response: oneshot::Sender> }, - /// Request Receipts from the peer. + GetNodeData { + /// The request for NodeData. + request: GetNodeData, + /// The channel to send the response for NodeData. + response: oneshot::Sender>, + }, + /// Requests receipts from the peer. /// /// The response should be sent through the channel. - GetReceipts { request: GetReceipts, response: oneshot::Sender> }, + GetReceipts { + /// The request for receipts. + request: GetReceipts, + /// The channel to send the response for receipts. + response: oneshot::Sender>, + }, } // === impl PeerRequest === @@ -156,18 +177,32 @@ impl PeerRequest { /// Corresponding variant for [`PeerRequest`]. #[derive(Debug)] -#[allow(missing_docs)] pub enum PeerResponse { - /// Response to a [`GetBlockHeaders`] request. - BlockHeaders { response: oneshot::Receiver> }, - /// Response to a [`GetBlockBodies`] request. - BlockBodies { response: oneshot::Receiver> }, - /// Response to a [`GetPooledTransactions`] request. - PooledTransactions { response: oneshot::Receiver> }, - /// Response to a [`GetNodeData`] request. - NodeData { response: oneshot::Receiver> }, - /// Response to a [`GetReceipts`] request. - Receipts { response: oneshot::Receiver> }, + /// Represents a response to a request for block headers. + BlockHeaders { + /// The receiver channel for the response to a block headers request. + response: oneshot::Receiver>, + }, + /// Represents a response to a request for block bodies. + BlockBodies { + /// The receiver channel for the response to a block bodies request. + response: oneshot::Receiver>, + }, + /// Represents a response to a request for pooled transactions. + PooledTransactions { + /// The receiver channel for the response to a pooled transactions request. + response: oneshot::Receiver>, + }, + /// Represents a response to a request for NodeData. + NodeData { + /// The receiver channel for the response to a NodeData request. + response: oneshot::Receiver>, + }, + /// Represents a response to a request for receipts. + Receipts { + /// The receiver channel for the response to a receipts request. + response: oneshot::Receiver>, + }, } // === impl PeerResponse === @@ -207,12 +242,16 @@ impl PeerResponse { /// All response variants for [`PeerResponse`] #[derive(Debug)] -#[allow(missing_docs)] pub enum PeerResponseResult { + /// Represents a result containing block headers or an error. BlockHeaders(RequestResult>), + /// Represents a result containing block bodies or an error. BlockBodies(RequestResult>), + /// Represents a result containing pooled transactions or an error. PooledTransactions(RequestResult>), + /// Represents a result containing node data or an error. NodeData(RequestResult>), + /// Represents a result containing receipts or an error. Receipts(RequestResult>>), } diff --git a/crates/net/network/src/network.rs b/crates/net/network/src/network.rs index 3e2419a5b139..432529259515 100644 --- a/crates/net/network/src/network.rs +++ b/crates/net/network/src/network.rs @@ -376,52 +376,64 @@ pub trait NetworkProtocols: Send + Sync { } /// Internal messages that can be passed to the [`NetworkManager`](crate::NetworkManager). -#[allow(missing_docs)] #[derive(Debug)] pub(crate) enum NetworkHandleMessage { - /// Adds an address for a peer. + /// Adds an address for a peer, including its ID, kind, and socket address. AddPeerAddress(PeerId, PeerKind, SocketAddr), /// Removes a peer from the peerset corresponding to the given kind. RemovePeer(PeerId, PeerKind), - /// Disconnect a connection to a peer if it exists. + /// Disconnects a connection to a peer if it exists, optionally providing a disconnect reason. DisconnectPeer(PeerId, Option), - /// Add a new listener for [`NetworkEvent`]. + /// Adds a new listener for `NetworkEvent`. EventListener(UnboundedSender), - /// Broadcast event to announce a new block to all nodes. + /// Broadcasts an event to announce a new block to all nodes. AnnounceBlock(NewBlock, B256), - /// Sends the list of transactions to the given peer. - SendTransaction { peer_id: PeerId, msg: SharedTransactions }, - /// Sends the list of transactions hashes to the given peer. - SendPooledTransactionHashes { peer_id: PeerId, msg: NewPooledTransactionHashes }, - /// Send an `eth` protocol request to the peer. + /// Sends a list of transactions to the given peer. + SendTransaction { + /// The ID of the peer to which the transactions are sent. + peer_id: PeerId, + /// The shared transactions to send. + msg: SharedTransactions, + }, + /// Sends a list of transaction hashes to the given peer. + SendPooledTransactionHashes { + /// The ID of the peer to which the transaction hashes are sent. + peer_id: PeerId, + /// The new pooled transaction hashes to send. + msg: NewPooledTransactionHashes, + }, + /// Sends an `eth` protocol request to the peer. EthRequest { /// The peer to send the request to. peer_id: PeerId, /// The request to send to the peer's sessions. request: PeerRequest, }, - /// Apply a reputation change to the given peer. + /// Applies a reputation change to the given peer. ReputationChange(PeerId, ReputationChangeKind), /// Returns the client that can be used to interact with the network. FetchClient(oneshot::Sender), - /// Apply a status update. - StatusUpdate { head: Head }, - /// Get the current status + /// Applies a status update. + StatusUpdate { + /// The head status to apply. + head: Head, + }, + /// Retrieves the current status via a oneshot sender. GetStatus(oneshot::Sender), - /// Get PeerInfo for the given peerids + /// Gets `PeerInfo` for the specified peer IDs. GetPeerInfosByIds(Vec, oneshot::Sender>), - /// Get PeerInfo from all the peers + /// Gets `PeerInfo` from all the peers via a oneshot sender. GetPeerInfos(oneshot::Sender>), - /// Get PeerInfo for a specific peer + /// Gets `PeerInfo` for a specific peer via a oneshot sender. GetPeerInfoById(PeerId, oneshot::Sender>), - /// Get PeerInfo for a specific peer + /// Gets `PeerInfo` for a specific peer kind via a oneshot sender. GetPeerInfosByPeerKind(PeerKind, oneshot::Sender>), - /// Get the reputation for a specific peer + /// Gets the reputation for a specific peer via a oneshot sender. GetReputationById(PeerId, oneshot::Sender>), - /// Gracefully shutdown network + /// Initiates a graceful shutdown of the network via a oneshot sender. Shutdown(oneshot::Sender<()>), - /// Add a new listener for `DiscoveryEvent`. + /// Adds a new listener for `DiscoveryEvent`. DiscoveryListener(UnboundedSender), - /// Add an additional [RlpxSubProtocol]. + /// Adds an additional `RlpxSubProtocol`. AddRlpxSubProtocol(RlpxSubProtocol), } diff --git a/crates/net/network/src/transactions.rs b/crates/net/network/src/transactions.rs index bc7b0c958bbc..76da5e3b9dbb 100644 --- a/crates/net/network/src/transactions.rs +++ b/crates/net/network/src/transactions.rs @@ -1255,18 +1255,30 @@ enum TransactionsCommand { /// All events related to transactions emitted by the network. #[derive(Debug)] -#[allow(missing_docs)] pub enum NetworkTransactionEvent { - /// Received list of transactions from the given peer. + /// Represents the event of receiving a list of transactions from a peer. /// - /// This represents transactions that were broadcasted to use from the peer. - IncomingTransactions { peer_id: PeerId, msg: Transactions }, - /// Received list of transactions hashes to the given peer. - IncomingPooledTransactionHashes { peer_id: PeerId, msg: NewPooledTransactionHashes }, - /// Incoming `GetPooledTransactions` request from a peer. + /// This indicates transactions that were broadcasted to us from the peer. + IncomingTransactions { + /// The ID of the peer from which the transactions were received. + peer_id: PeerId, + /// The received transactions. + msg: Transactions, + }, + /// Represents the event of receiving a list of transaction hashes from a peer. + IncomingPooledTransactionHashes { + /// The ID of the peer from which the transaction hashes were received. + peer_id: PeerId, + /// The received new pooled transaction hashes. + msg: NewPooledTransactionHashes, + }, + /// Represents the event of receiving a `GetPooledTransactions` request from a peer. GetPooledTransactions { + /// The ID of the peer from which the request was received. peer_id: PeerId, + /// The received `GetPooledTransactions` request. request: GetPooledTransactions, + /// The sender for responding to the request with a result of `PooledTransactions`. response: oneshot::Sender>, }, } diff --git a/crates/primitives/src/chain/mod.rs b/crates/primitives/src/chain/mod.rs index f377674ea5a9..7c8cd34209cc 100644 --- a/crates/primitives/src/chain/mod.rs +++ b/crates/primitives/src/chain/mod.rs @@ -47,37 +47,55 @@ pub use info::ChainInfo; #[serde(rename_all = "snake_case")] #[strum(serialize_all = "snake_case")] #[repr(u64)] -#[allow(missing_docs)] pub enum NamedChain { + /// Ethereum Mainnet. Mainnet = 1, + /// Morden Testnet. Morden = 2, + /// Ropsten Testnet. Ropsten = 3, + /// Rinkeby Testnet. Rinkeby = 4, + /// Goerli Testnet. Goerli = 5, + /// Kovan Testnet. Kovan = 42, + /// Holesky Testnet. Holesky = 17000, + /// Sepolia Testnet. Sepolia = 11155111, + /// Optimism Mainnet. Optimism = 10, + /// Optimism Kovan Testnet. OptimismKovan = 69, + /// Optimism Goerli Testnet. OptimismGoerli = 420, + /// Base chain. Base = 8453, + /// Base Goerli Testnet. BaseGoerli = 84531, + /// Base Sepolia Testnet. BaseSepolia = 84532, + /// Arbitrum Mainnet. Arbitrum = 42161, + /// Arbitrum Testnet. ArbitrumTestnet = 421611, + /// Arbitrum Goerli Testnet. ArbitrumGoerli = 421613, + /// Arbitrum Nova. ArbitrumNova = 42170, + /// Binance Smart Chain Mainnet. #[serde(alias = "bsc")] - #[strum(to_string = "bsc")] BinanceSmartChain = 56, + /// Binance Smart Chain Testnet. #[serde(alias = "bsc_testnet")] - #[strum(to_string = "bsc_testnet")] BinanceSmartChainTestnet = 97, + /// Development Testnet. Dev = 1337, } diff --git a/crates/primitives/src/fs.rs b/crates/primitives/src/fs.rs index 5a23465dbcb8..f9dac8fb5c69 100644 --- a/crates/primitives/src/fs.rs +++ b/crates/primitives/src/fs.rs @@ -9,44 +9,116 @@ use std::{ /// Various error variants for `std::fs` operations that serve as an addition to the io::Error which /// does not provide any information about the path. #[derive(Debug, thiserror::Error)] -#[allow(missing_docs)] pub enum FsPathError { - /// Provides additional path context for [`std::fs::write`]. + /// Error variant for failed write operation with additional path context. #[error("failed to write to {path:?}: {source}")] - Write { source: io::Error, path: PathBuf }, - /// Provides additional path context for [`std::fs::read`]. + Write { + /// The source `io::Error`. + source: io::Error, + /// The path related to the operation. + path: PathBuf, + }, + + /// Error variant for failed read operation with additional path context. #[error("failed to read from {path:?}: {source}")] - Read { source: io::Error, path: PathBuf }, - /// Provides additional path context for [`std::fs::read_link`]. + Read { + /// The source `io::Error`. + source: io::Error, + /// The path related to the operation. + path: PathBuf, + }, + + /// Error variant for failed read link operation with additional path context. #[error("failed to read from {path:?}: {source}")] - ReadLink { source: io::Error, path: PathBuf }, - /// Provides additional path context for [`std::fs::File::create`]. + ReadLink { + /// The source `io::Error`. + source: io::Error, + /// The path related to the operation. + path: PathBuf, + }, + + /// Error variant for failed file creation operation with additional path context. #[error("failed to create file {path:?}: {source}")] - CreateFile { source: io::Error, path: PathBuf }, - /// Provides additional path context for [`std::fs::remove_file`]. + CreateFile { + /// The source `io::Error`. + source: io::Error, + /// The path related to the operation. + path: PathBuf, + }, + + /// Error variant for failed file removal operation with additional path context. #[error("failed to remove file {path:?}: {source}")] - RemoveFile { source: io::Error, path: PathBuf }, - /// Provides additional path context for [`std::fs::create_dir`]. + RemoveFile { + /// The source `io::Error`. + source: io::Error, + /// The path related to the operation. + path: PathBuf, + }, + + /// Error variant for failed directory creation operation with additional path context. #[error("failed to create dir {path:?}: {source}")] - CreateDir { source: io::Error, path: PathBuf }, - /// Provides additional path context for [`std::fs::remove_dir`]. + CreateDir { + /// The source `io::Error`. + source: io::Error, + /// The path related to the operation. + path: PathBuf, + }, + + /// Error variant for failed directory removal operation with additional path context. #[error("failed to remove dir {path:?}: {source}")] - RemoveDir { source: io::Error, path: PathBuf }, - /// Provides additional path context for [`std::fs::read_dir`]. + RemoveDir { + /// The source `io::Error`. + source: io::Error, + /// The path related to the operation. + path: PathBuf, + }, + + /// Error variant for failed directory read operation with additional path context. #[error("failed to read dir {path:?}: {source}")] - ReadDir { source: io::Error, path: PathBuf }, - /// Provides additional context for [`std::fs::rename`]. + ReadDir { + /// The source `io::Error`. + source: io::Error, + /// The path related to the operation. + path: PathBuf, + }, + + /// Error variant for failed file renaming operation with additional path context. #[error("failed to rename {from:?} to {to:?}: {source}")] - Rename { source: io::Error, from: PathBuf, to: PathBuf }, - /// Provides additional path context for [`std::fs::File::open`]. + Rename { + /// The source `io::Error`. + source: io::Error, + /// The original path. + from: PathBuf, + /// The target path. + to: PathBuf, + }, + + /// Error variant for failed file opening operation with additional path context. #[error("failed to open file {path:?}: {source}")] - Open { source: io::Error, path: PathBuf }, - /// Provides additional path context for the file whose contents should be parsed as JSON. + Open { + /// The source `io::Error`. + source: io::Error, + /// The path related to the operation. + path: PathBuf, + }, + + /// Error variant for failed file read as JSON operation with additional path context. #[error("failed to parse json file: {path:?}: {source}")] - ReadJson { source: serde_json::Error, path: PathBuf }, - /// Provides additional path context for the new JSON file. + ReadJson { + /// The source `serde_json::Error`. + source: serde_json::Error, + /// The path related to the operation. + path: PathBuf, + }, + + /// Error variant for failed JSON write to file operation with additional path context. #[error("failed to write to json file: {path:?}: {source}")] - WriteJson { source: serde_json::Error, path: PathBuf }, + WriteJson { + /// The source `serde_json::Error`. + source: serde_json::Error, + /// The path related to the operation. + path: PathBuf, + }, } impl FsPathError { diff --git a/crates/primitives/src/snapshot/compression.rs b/crates/primitives/src/snapshot/compression.rs index 69fe4b2a4328..2d5599c2cda9 100644 --- a/crates/primitives/src/snapshot/compression.rs +++ b/crates/primitives/src/snapshot/compression.rs @@ -1,16 +1,19 @@ use strum::AsRefStr; +/// Snapshot compression types. #[derive(Debug, Copy, Clone, Default, AsRefStr)] #[cfg_attr(feature = "clap", derive(clap::ValueEnum))] -#[allow(missing_docs)] -/// Snapshot compression pub enum Compression { + /// LZ4 compression algorithm. #[strum(serialize = "lz4")] Lz4, + /// Zstandard (Zstd) compression algorithm. #[strum(serialize = "zstd")] Zstd, + /// Zstandard (Zstd) compression algorithm with a dictionary. #[strum(serialize = "zstd-dict")] ZstdWithDictionary, + /// No compression, uncompressed snapshot. #[strum(serialize = "uncompressed")] #[default] Uncompressed, diff --git a/crates/primitives/src/stage/id.rs b/crates/primitives/src/stage/id.rs index 4b82298a13df..ac44a5e46d9f 100644 --- a/crates/primitives/src/stage/id.rs +++ b/crates/primitives/src/stage/id.rs @@ -2,21 +2,34 @@ /// /// For custom stages, use [`StageId::Other`] #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] -#[allow(missing_docs)] pub enum StageId { + /// Header stage in the process. Headers, + /// Total difficulty stage in the process. TotalDifficulty, + /// Bodies stage in the process. Bodies, + /// Sender recovery stage in the process. SenderRecovery, + /// Execution stage in the process. Execution, + /// Merkle unwind stage in the process. MerkleUnwind, + /// Account hashing stage in the process. AccountHashing, + /// Storage hashing stage in the process. StorageHashing, + /// Merkle execute stage in the process. MerkleExecute, + /// Transaction lookup stage in the process. TransactionLookup, + /// Index storage history stage in the process. IndexStorageHistory, + /// Index account history stage in the process. IndexAccountHistory, + /// Finish stage in the process. Finish, + /// Other custom stage with a provided string identifier. Other(&'static str), } diff --git a/crates/revm/revm-inspectors/src/tracing/js/mod.rs b/crates/revm/revm-inspectors/src/tracing/js/mod.rs index 3d82365f034f..0910d6f8b18e 100644 --- a/crates/revm/revm-inspectors/src/tracing/js/mod.rs +++ b/crates/revm/revm-inspectors/src/tracing/js/mod.rs @@ -547,23 +547,38 @@ struct CallStackItem { gas_limit: u64, } +/// Error variants that can occur during JavaScript inspection. #[derive(Debug, thiserror::Error)] -#[allow(missing_docs)] pub enum JsInspectorError { + /// Error originating from a JavaScript operation. #[error(transparent)] JsError(#[from] JsError), + + /// Failure during the evaluation of JavaScript code. #[error("failed to evaluate JS code: {0}")] EvalCode(JsError), + + /// The evaluated code is not a JavaScript object. #[error("the evaluated code is not a JS object")] ExpectedJsObject, + + /// The trace object must expose a function named `result()`. #[error("trace object must expose a function result()")] ResultFunctionMissing, + + /// The trace object must expose a function named `fault()`. #[error("trace object must expose a function fault()")] FaultFunctionMissing, + + /// The setup object must be a callable function. #[error("setup object must be a function")] SetupFunctionNotCallable, + + /// Failure during the invocation of the `setup()` function. #[error("failed to call setup(): {0}")] SetupCallFailed(JsError), + + /// Invalid JSON configuration encountered. #[error("invalid JSON config: {0}")] InvalidJsonConfig(JsError), } diff --git a/crates/revm/revm-inspectors/src/tracing/types.rs b/crates/revm/revm-inspectors/src/tracing/types.rs index beafb7f4c25f..1f3091e95bdb 100644 --- a/crates/revm/revm-inspectors/src/tracing/types.rs +++ b/crates/revm/revm-inspectors/src/tracing/types.rs @@ -369,17 +369,22 @@ impl CallTraceNode { } } -/// A unified representation of a call +/// A unified representation of a call. #[derive(Clone, Copy, Debug, Default, Eq, PartialEq, Serialize, Deserialize)] #[serde(rename_all = "UPPERCASE")] -#[allow(missing_docs)] pub enum CallKind { + /// Represents a regular call. #[default] Call, + /// Represents a static call. StaticCall, + /// Represents a call code operation. CallCode, + /// Represents a delegate call. DelegateCall, + /// Represents a contract creation operation. Create, + /// Represents a contract creation operation using the CREATE2 opcode. Create2, } diff --git a/crates/rpc/ipc/src/client.rs b/crates/rpc/ipc/src/client.rs index f440664fc141..602da3d4126b 100644 --- a/crates/rpc/ipc/src/client.rs +++ b/crates/rpc/ipc/src/client.rs @@ -116,7 +116,6 @@ impl IpcTransportClientBuilder { /// Error variants that can happen in IPC transport. #[derive(Debug, thiserror::Error)] -#[allow(missing_docs)] pub enum IpcError { /// Operation not supported #[error("operation not supported")] @@ -128,9 +127,13 @@ pub enum IpcError { #[error("failed to connect to socket {path}: {err}")] FailedToConnect { /// The path of the socket. + #[doc(hidden)] path: PathBuf, + /// The error occurred while connecting. + #[doc(hidden)] err: io::Error, }, + /// Wrapped IO Error #[error(transparent)] Io(#[from] io::Error), } diff --git a/crates/rpc/rpc/src/eth/error.rs b/crates/rpc/rpc/src/eth/error.rs index ac659aaa1284..e0370d840c4d 100644 --- a/crates/rpc/rpc/src/eth/error.rs +++ b/crates/rpc/rpc/src/eth/error.rs @@ -22,44 +22,49 @@ pub type EthResult = Result; /// Errors that can occur when interacting with the `eth_` namespace #[derive(Debug, thiserror::Error)] -#[allow(missing_docs)] pub enum EthApiError { /// When a raw transaction is empty #[error("empty transaction data")] EmptyRawTransactionData, + /// When decoding a signed transaction fails #[error("failed to decode signed transaction")] FailedToDecodeSignedTransaction, + /// When the transaction signature is invalid #[error("invalid transaction signature")] InvalidTransactionSignature, + /// Errors related to the transaction pool #[error(transparent)] PoolError(RpcPoolError), + /// When an unknown block number is encountered #[error("unknown block number")] UnknownBlockNumber, /// Thrown when querying for `finalized` or `safe` block before the merge transition is /// finalized, #[error("unknown block")] UnknownSafeOrFinalizedBlock, + /// Thrown when an unknown block or transaction index is encountered #[error("unknown block or tx index")] UnknownBlockOrTxIndex, + /// When an invalid block range is provided #[error("invalid block range")] InvalidBlockRange, /// An internal error where prevrandao is not set in the evm's environment #[error("prevrandao not in the EVM's environment after merge")] PrevrandaoNotSet, - /// Excess_blob_gas is not set for Cancun and above. - #[error("excess blob gas missing the EVM's environment after Cancun")] + /// `excess_blob_gas` is not set for Cancun and above + #[error("excess blob gas missing in the EVM's environment after Cancun")] ExcessBlobGasNotSet, /// Thrown when a call or transaction request (`eth_call`, `eth_estimateGas`, /// `eth_sendTransaction`) contains conflicting fields (legacy, EIP-1559) #[error("both gasPrice and (maxFeePerGas or maxPriorityFeePerGas) specified")] ConflictingFeeFieldsInRequest, + /// Errors related to invalid transactions #[error(transparent)] InvalidTransaction(#[from] RpcInvalidTransactionError), - /// Thrown when constructing an RPC block from a primitive block data failed. + /// Thrown when constructing an RPC block from primitive block data fails #[error(transparent)] InvalidBlockData(#[from] BlockError), - /// Thrown when a [AccountOverride](reth_rpc_types::state::AccountOverride) contains - /// conflicting `state` and `stateDiff` fields + /// Thrown when an `AccountOverride` contains conflicting `state` and `stateDiff` fields #[error("account {0:?} has both 'state' and 'stateDiff'")] BothStateAndStateDiffInOverride(Address), /// Other internal error @@ -68,7 +73,7 @@ pub enum EthApiError { /// Error related to signing #[error(transparent)] Signing(#[from] SignError), - /// Thrown when a transaction was requested but not matching transaction exists + /// Thrown when a requested transaction is not found #[error("transaction not found")] TransactionNotFound, /// Some feature is unsupported @@ -77,10 +82,10 @@ pub enum EthApiError { /// General purpose error for invalid params #[error("{0}")] InvalidParams(String), - /// When tracer config does not match the tracer + /// When the tracer config does not match the tracer #[error("invalid tracer config")] InvalidTracerConfig, - /// Percentile array is invalid + /// When the percentile array is invalid #[error("invalid reward percentiles")] InvalidRewardPercentiles, /// Error thrown when a spawned blocking task failed to deliver an anticipated response. @@ -89,16 +94,17 @@ pub enum EthApiError { /// response back to the request handler. #[error("internal blocking task error")] InternalBlockingTaskError, - /// Error thrown when a spawned blocking task failed to deliver an anticipated response. + /// Error thrown when a spawned blocking task failed to deliver an anticipated response #[error("internal eth error")] InternalEthError, - /// Error thrown when a (tracing) call exceeded the configured timeout. + /// Error thrown when a (tracing) call exceeds the configured timeout #[error("execution aborted (timeout = {0:?})")] ExecutionTimedOut(Duration), /// Internal Error thrown by the javascript tracer #[error("{0}")] InternalJsTracerError(String), #[error(transparent)] + /// Call Input error when both `data` and `input` fields are set and not equal. CallInputError(#[from] CallInputError), /// Optimism related error #[error(transparent)] @@ -563,26 +569,35 @@ impl std::error::Error for RevertError {} /// A helper error type that's mainly used to mirror `geth` Txpool's error messages #[derive(Debug, thiserror::Error)] -#[allow(missing_docs)] pub enum RpcPoolError { + /// When the transaction is already known #[error("already known")] AlreadyKnown, + /// When the sender is invalid #[error("invalid sender")] InvalidSender, + /// When the transaction is underpriced #[error("transaction underpriced")] Underpriced, + /// When the transaction pool is full #[error("txpool is full")] TxPoolOverflow, + /// When the replacement transaction is underpriced #[error("replacement transaction underpriced")] ReplaceUnderpriced, + /// When the transaction exceeds the block gas limit #[error("exceeds block gas limit")] ExceedsGasLimit, + /// When a negative value is encountered #[error("negative value")] NegativeValue, + /// When oversized data is encountered #[error("oversized data")] OversizedData, + /// When the max initcode size is exceeded #[error("max initcode size exceeded")] ExceedsMaxInitCodeSize, + /// Errors related to invalid transactions #[error(transparent)] Invalid(#[from] RpcInvalidTransactionError), /// Custom pool error @@ -597,6 +612,7 @@ pub enum RpcPoolError { /// constraint (blob vs normal tx) #[error("address already reserved")] AddressAlreadyReserved, + /// Other unspecified error #[error(transparent)] Other(Box), } diff --git a/crates/rpc/rpc/src/layers/jwt_secret.rs b/crates/rpc/rpc/src/layers/jwt_secret.rs index 6c855cbf507f..3b19e02e6cab 100644 --- a/crates/rpc/rpc/src/layers/jwt_secret.rs +++ b/crates/rpc/rpc/src/layers/jwt_secret.rs @@ -15,24 +15,41 @@ use thiserror::Error; /// Errors returned by the [`JwtSecret`] #[derive(Error, Debug)] -#[allow(missing_docs)] pub enum JwtError { + /// An error encountered while decoding the hexadecimal string for the JWT secret. #[error(transparent)] JwtSecretHexDecodeError(#[from] hex::FromHexError), + + /// The JWT key length provided is invalid, expecting a specific length. #[error("JWT key is expected to have a length of {0} digits. {1} digits key provided")] InvalidLength(usize, usize), + + /// The signature algorithm used in the JWT is not supported. Only HS256 is supported. #[error("unsupported signature algorithm. Only HS256 is supported")] UnsupportedSignatureAlgorithm, + + /// The provided signature in the JWT is invalid. #[error("provided signature is invalid")] InvalidSignature, + + /// The "iat" (issued-at) claim in the JWT is not within the allowed ±60 seconds from the + /// current time. #[error("IAT (issued-at) claim is not within ±60 seconds from the current time")] InvalidIssuanceTimestamp, + + /// The Authorization header is missing or invalid in the context of JWT validation. #[error("Authorization header is missing or invalid")] MissingOrInvalidAuthorizationHeader, + + /// An error occurred during JWT decoding. #[error("JWT decoding error: {0}")] JwtDecodingError(String), + + /// An error related to file system path handling encountered during JWT operations. #[error(transparent)] JwtFsPathError(#[from] FsPathError), + + /// An I/O error occurred during JWT operations. #[error(transparent)] IOError(#[from] std::io::Error), } diff --git a/crates/snapshot/src/error.rs b/crates/snapshot/src/error.rs index 20da642bee89..302803835cf5 100644 --- a/crates/snapshot/src/error.rs +++ b/crates/snapshot/src/error.rs @@ -5,17 +5,21 @@ use thiserror::Error; /// Error returned by [crate::Snapshotter::run] #[derive(Error, Debug)] -#[allow(missing_docs)] +/// Errors that can occur during snapshotting. pub enum SnapshotterError { + /// Inconsistent data error. #[error("inconsistent data: {0}")] InconsistentData(&'static str), + /// Error related to the interface. #[error(transparent)] Interface(#[from] RethError), + /// Error related to the database. #[error(transparent)] Database(#[from] DatabaseError), + /// Error related to the provider. #[error(transparent)] Provider(#[from] ProviderError), } diff --git a/crates/stages/src/stages/merkle.rs b/crates/stages/src/stages/merkle.rs index fadee1107d6c..7b74ca47b8fb 100644 --- a/crates/stages/src/stages/merkle.rs +++ b/crates/stages/src/stages/merkle.rs @@ -54,11 +54,13 @@ pub enum MerkleStage { }, /// The unwind portion of the merkle stage. Unwind, - /// Able to execute and unwind. Used for tests #[cfg(any(test, feature = "test-utils"))] - #[allow(missing_docs)] - Both { clean_threshold: u64 }, + Both { + /// The threshold (in number of blocks) for switching from incremental trie building + /// of changes to whole rebuild. + clean_threshold: u64, + }, } impl MerkleStage { diff --git a/crates/storage/db/src/metrics.rs b/crates/storage/db/src/metrics.rs index a81ff1623844..971ae194f207 100644 --- a/crates/storage/db/src/metrics.rs +++ b/crates/storage/db/src/metrics.rs @@ -5,14 +5,16 @@ use std::time::{Duration, Instant}; const LARGE_VALUE_THRESHOLD_BYTES: usize = 4096; +/// Transaction mode for the database, either read-only or read-write. #[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)] -#[allow(missing_docs)] pub(crate) enum TransactionMode { + /// Read-only transaction mode. ReadOnly, + /// Read-write transaction mode. ReadWrite, } - impl TransactionMode { + /// Returns the transaction mode as a string. pub(crate) const fn as_str(&self) -> &'static str { match self { TransactionMode::ReadOnly => "read-only", @@ -26,15 +28,19 @@ impl TransactionMode { } } +/// Transaction outcome after a database operation - commit, abort, or drop. #[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)] -#[allow(missing_docs)] pub(crate) enum TransactionOutcome { + /// Successful commit of the transaction. Commit, + /// Aborted transaction. Abort, + /// Dropped transaction. Drop, } impl TransactionOutcome { + /// Returns the transaction outcome as a string. pub(crate) const fn as_str(&self) -> &'static str { match self { TransactionOutcome::Commit => "commit", @@ -44,21 +50,31 @@ impl TransactionOutcome { } } +/// Types of operations conducted on the database: get, put, delete, and various cursor operations. #[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)] -#[allow(missing_docs)] pub(crate) enum Operation { + /// Database get operation. Get, + /// Database put operation. Put, + /// Database delete operation. Delete, + /// Database cursor upsert operation. CursorUpsert, + /// Database cursor insert operation. CursorInsert, + /// Database cursor append operation. CursorAppend, + /// Database cursor append duplicates operation. CursorAppendDup, + /// Database cursor delete current operation. CursorDeleteCurrent, + /// Database cursor delete current duplicates operation. CursorDeleteCurrentDuplicates, } impl Operation { + /// Returns the operation as a string. pub(crate) const fn as_str(&self) -> &'static str { match self { Operation::Get => "get", @@ -74,14 +90,20 @@ impl Operation { } } +/// Enum defining labels for various aspects used in metrics. enum Labels { + /// Label representing a table. Table, + /// Label representing a transaction mode. TransactionMode, + /// Label representing a transaction outcome. TransactionOutcome, + /// Label representing a database operation. Operation, } impl Labels { + /// Converts each label variant into its corresponding string representation. pub(crate) fn as_str(&self) -> &'static str { match self { Labels::Table => "table", diff --git a/crates/storage/db/src/version.rs b/crates/storage/db/src/version.rs index 380c170c54b7..63357b8e9fba 100644 --- a/crates/storage/db/src/version.rs +++ b/crates/storage/db/src/version.rs @@ -12,20 +12,33 @@ pub const DB_VERSION_FILE_NAME: &str = "database.version"; pub const DB_VERSION: u64 = 1; /// Error when checking a database version using [check_db_version_file] -#[allow(missing_docs)] #[derive(thiserror::Error, Debug)] pub enum DatabaseVersionError { + /// Unable to determine the version of the database; the file is missing. #[error("unable to determine the version of the database, file is missing")] MissingFile, + /// Unable to determine the version of the database; the file is malformed. #[error("unable to determine the version of the database, file is malformed")] MalformedFile, + /// Breaking database change detected. + /// + /// Your database version is incompatible with the latest database version. #[error( "breaking database change detected: your database version (v{version}) \ is incompatible with the latest database version (v{DB_VERSION})" )] - VersionMismatch { version: u64 }, + VersionMismatch { + /// The detected version in the database. + version: u64, + }, + /// IO error occurred while reading the database version file. #[error("IO error occurred while reading {path}: {err}")] - IORead { err: io::Error, path: PathBuf }, + IORead { + /// The encountered IO error. + err: io::Error, + /// The path to the database version file. + path: PathBuf, + }, } /// Checks the database version file with [DB_VERSION_FILE_NAME] name. diff --git a/crates/storage/provider/src/traits/chain.rs b/crates/storage/provider/src/traits/chain.rs index 833982d1a94c..397fea79b03c 100644 --- a/crates/storage/provider/src/traits/chain.rs +++ b/crates/storage/provider/src/traits/chain.rs @@ -63,13 +63,20 @@ impl Stream for CanonStateNotificationStream { /// and will return all [`crate::BundleStateWithReceipts`] and /// [`reth_primitives::SealedBlockWithSenders`] of both reverted and committed blocks. #[derive(Clone, Debug)] -#[allow(missing_docs)] pub enum CanonStateNotification { /// Chain got extended without reorg and only new chain is returned. - Commit { new: Arc }, + Commit { + /// The newly extended chain. + new: Arc, + }, /// Chain reorgs and both old and new chain are returned. /// Revert is just a subset of reorg where the new chain is empty. - Reorg { old: Arc, new: Arc }, + Reorg { + /// The old chain before reorganization. + old: Arc, + /// The new chain after reorganization. + new: Arc, + }, } // For one reason or another, the compiler can't derive PartialEq for CanonStateNotification. diff --git a/crates/transaction-pool/src/blobstore/disk.rs b/crates/transaction-pool/src/blobstore/disk.rs index 43caf112b793..ba0ffc605b4b 100644 --- a/crates/transaction-pool/src/blobstore/disk.rs +++ b/crates/transaction-pool/src/blobstore/disk.rs @@ -367,16 +367,22 @@ impl fmt::Debug for DiskFileBlobStoreInner { /// Errors that can occur when interacting with a disk file blob store. #[derive(Debug, thiserror::Error)] -#[allow(missing_docs)] pub enum DiskFileBlobStoreError { /// Thrown during [DiskFileBlobStore::open] if the blob store directory cannot be opened. #[error("failed to open blobstore at {0}: {1}")] + /// Indicates a failure to open the blob store directory. Open(PathBuf, io::Error), + /// Failure while reading a blob file. #[error("[{0}] failed to read blob file at {1}: {2}")] + /// Indicates a failure while reading a blob file. ReadFile(TxHash, PathBuf, io::Error), + /// Failure while writing a blob file. #[error("[{0}] failed to write blob file at {1}: {2}")] + /// Indicates a failure while writing a blob file. WriteFile(TxHash, PathBuf, io::Error), + /// Failure while deleting a blob file. #[error("[{0}] failed to delete blob file at {1}: {2}")] + /// Indicates a failure while deleting a blob file. DeleteFile(TxHash, PathBuf, io::Error), } diff --git a/crates/transaction-pool/src/pool/txpool.rs b/crates/transaction-pool/src/pool/txpool.rs index c58a9e7fb8ec..84a7c07a05a3 100644 --- a/crates/transaction-pool/src/pool/txpool.rs +++ b/crates/transaction-pool/src/pool/txpool.rs @@ -1621,8 +1621,10 @@ impl AllTransactions { } #[cfg(test)] -#[allow(missing_docs)] impl AllTransactions { + /// This function retrieves the number of transactions stored in the pool for a specific sender. + /// + /// If there are no transactions for the given sender, it returns zero by default. pub(crate) fn tx_count(&self, sender: SenderId) -> usize { self.tx_counter.get(&sender).copied().unwrap_or_default() } From 2a4d02f0c100cd6afc21517d87e116318570e1b0 Mon Sep 17 00:00:00 2001 From: yjh Date: Fri, 5 Jan 2024 21:39:07 +0800 Subject: [PATCH 257/277] chore: simplify Database impls (#5951) --- crates/revm/src/database.rs | 19 ++++--------------- 1 file changed, 4 insertions(+), 15 deletions(-) diff --git a/crates/revm/src/database.rs b/crates/revm/src/database.rs index 1eaded23b6ad..3ff3f427e555 100644 --- a/crates/revm/src/database.rs +++ b/crates/revm/src/database.rs @@ -47,26 +47,21 @@ impl Database for StateProviderDatabase { /// Returns `Ok` with `Some(AccountInfo)` if the account exists, /// `None` if it doesn't, or an error if encountered. fn basic(&mut self, address: Address) -> Result, Self::Error> { - Ok(self.0.basic_account(address)?.map(|account| AccountInfo { - balance: account.balance, - nonce: account.nonce, - code_hash: account.bytecode_hash.unwrap_or(KECCAK_EMPTY), - code: None, - })) + DatabaseRef::basic_ref(self, address) } /// Retrieves the bytecode associated with a given code hash. /// /// Returns `Ok` with the bytecode if found, or the default bytecode otherwise. fn code_by_hash(&mut self, code_hash: B256) -> Result { - Ok(self.0.bytecode_by_hash(code_hash)?.unwrap_or_default().0) + DatabaseRef::code_by_hash_ref(self, code_hash) } /// Retrieves the storage value at a specific index for a given address. /// /// Returns `Ok` with the storage value, or the default value if not found. fn storage(&mut self, address: Address, index: U256) -> Result { - Ok(self.0.storage(address, B256::new(index.to_be_bytes()))?.unwrap_or_default()) + DatabaseRef::storage_ref(self, address, index) } /// Retrieves the block hash for a given block number. @@ -74,13 +69,7 @@ impl Database for StateProviderDatabase { /// Returns `Ok` with the block hash if found, or the default hash otherwise. /// Note: It safely casts the `number` to `u64`. fn block_hash(&mut self, number: U256) -> Result { - // Attempt to convert U256 to u64 - let block_number = match number.try_into() { - Ok(value) => value, - Err(_) => return Err(Self::Error::BlockNumberOverflow(number)), - }; - - Ok(self.0.block_hash(block_number)?.unwrap_or_default()) + DatabaseRef::block_hash_ref(self, number) } } From 8b56f50f3e326c1ef861ac3899dffd7f8a533c94 Mon Sep 17 00:00:00 2001 From: evalir Date: Fri, 5 Jan 2024 11:58:32 -0400 Subject: [PATCH 258/277] chore: bump rpc types (#5953) --- Cargo.lock | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index f8a52c62fb50..6eb65eb8736b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -219,7 +219,7 @@ dependencies = [ [[package]] name = "alloy-rpc-trace-types" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy#f5174888c523b70fa1a94fad24f3214048d3d462" +source = "git+https://github.com/alloy-rs/alloy#ec44bb040721e4dd85a3f1de5aeb92d4b29563ce" dependencies = [ "alloy-primitives", "alloy-rlp", @@ -234,7 +234,7 @@ dependencies = [ [[package]] name = "alloy-rpc-types" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy#f5174888c523b70fa1a94fad24f3214048d3d462" +source = "git+https://github.com/alloy-rs/alloy#ec44bb040721e4dd85a3f1de5aeb92d4b29563ce" dependencies = [ "alloy-primitives", "alloy-rlp", From 11dfe566245450caf4ccc79276755c9ad2cbc51c Mon Sep 17 00:00:00 2001 From: Thomas Coratger <60488569+tcoratger@users.noreply.github.com> Date: Sat, 6 Jan 2024 00:54:52 +0100 Subject: [PATCH 259/277] Improve mapping of MDBX error codes to human-readable errors (#5955) Co-authored-by: Roman Krasiuk --- crates/storage/libmdbx-rs/src/error.rs | 109 ++++++++++++++++--------- 1 file changed, 70 insertions(+), 39 deletions(-) diff --git a/crates/storage/libmdbx-rs/src/error.rs b/crates/storage/libmdbx-rs/src/error.rs index 98c91222d8f9..e18ea40128ea 100644 --- a/crates/storage/libmdbx-rs/src/error.rs +++ b/crates/storage/libmdbx-rs/src/error.rs @@ -1,5 +1,5 @@ use libc::c_int; -use std::{ffi::CStr, fmt, result, str}; +use std::result; /// An MDBX result. pub type Result = result::Result; @@ -7,31 +7,47 @@ pub type Result = result::Result; /// An MDBX error kind. #[derive(Debug, thiserror::Error, Clone, PartialEq, Eq)] pub enum Error { - /// The key/value pair already exists. + /// Key/data pair already exists. + #[error("key/data pair already exists")] KeyExist, - /// The requested key/value pair was not found. + /// No matching key/data pair found. + #[error("no matching key/data pair found")] NotFound, + /// The cursor is already at the end of data. + #[error("the cursor is already at the end of data")] NoData, - /// The requested page was not found. + /// Requested page not found. + #[error("requested page not found")] PageNotFound, - /// The database is corrupted (e.g. a page was a wrong type) + /// Database is corrupted. + #[error("database is corrupted")] Corrupted, - /// The environment had a fatal error (e.g. failed to update a meta page) + /// Fatal environment error. + #[error("fatal environment error")] Panic, + /// DB version mismatch. + #[error("DB version mismatch")] VersionMismatch, - /// File is not a valid MDBX file + /// File is not an MDBX file. + #[error("file is not an MDBX file")] Invalid, - /// Environment map size reached. + /// Environment map size limit reached. + #[error("environment map size limit reached")] MapFull, - /// Environment reached the maximum number of databases. + /// Too many DBI-handles (maxdbs reached). + #[error("too many DBI-handles (maxdbs reached)")] DbsFull, - /// Environment reached the maximum number of readers. + /// Too many readers (maxreaders reached). + #[error("too many readers (maxreaders reached)")] ReadersFull, - /// The transaction has too many dirty pages (i.e. the transaction is too big). + /// Transaction has too many dirty pages (i.e., the transaction is too big). + #[error("transaction has too many dirty pages (i.e., the transaction is too big)")] TxnFull, - /// The cursor stack is too deep. + /// Cursor stack limit reached. + #[error("cursor stack limit reached")] CursorFull, - /// The page does not have enough space. + /// Page has no more space. + #[error("page has no more space")] PageFull, /// The database engine was unable to extend mapping, e.g. the address space is unavailable or /// busy. @@ -42,29 +58,67 @@ pub enum Error { /// environment should be re-opened to continue. /// - The engine was unable to extend the mapping during a write transaction or an explicit /// call to change the geometry of the environment. + #[error("database engine was unable to extend mapping")] UnableExtendMapSize, + /// Environment or database is not compatible with the requested operation or flags. + #[error("environment or database is not compatible with the requested operation or flags")] Incompatible, + /// Invalid reuse of reader locktable slot. + #[error("invalid reuse of reader locktable slot")] BadRslot, + /// Transaction is not valid for requested operation. + #[error("transaction is not valid for requested operation")] BadTxn, + /// Invalid size or alignment of key or data for the target database. + #[error("invalid size or alignment of key or data for the target database")] BadValSize, + /// The specified DBI-handle is invalid. + #[error("the specified DBI-handle is invalid")] BadDbi, + /// Unexpected internal error. + #[error("unexpected internal error")] Problem, + /// Another write transaction is running. + #[error("another write transaction is running")] Busy, + /// The specified key has more than one associated value. + #[error("the specified key has more than one associated value")] Multival, + /// Wrong signature of a runtime object(s). + #[error("wrong signature of a runtime object(s)")] BadSignature, + /// Database should be recovered, but cannot be done automatically since it's in read-only + /// mode. + #[error("database should be recovered, but cannot be done automatically since it's in read-only mode")] WannaRecovery, + /// The given key value is mismatched to the current cursor position. + #[error("the given key value is mismatched to the current cursor position")] KeyMismatch, + /// Decode error: An invalid parameter was specified. + #[error("invalid parameter specified")] DecodeError, + /// The environment opened in read-only. + #[error("the environment opened in read-only")] Access, + /// Database is too large for the current system. + #[error("database is too large for the current system")] TooLarge, + /// Decode error length difference: + /// + /// An invalid parameter was specified, or the environment has an active write transaction. + #[error("invalid parameter specified or active write transaction")] DecodeErrorLenDiff, /// If the [Environment](crate::Environment) was opened with /// [EnvironmentKind::WriteMap](crate::EnvironmentKind::WriteMap) flag, nested transactions are /// not supported. + #[error("nested transactions are not supported with WriteMap")] NestedTransactionsUnsupportedWithWriteMap, /// If the [Environment](crate::Environment) was opened with in read-only mode - /// [Mode::ReadOnly](crate::flags::Mode::ReadOnly), write transactions can't be opened.. + /// [Mode::ReadOnly](crate::flags::Mode::ReadOnly), write transactions can't be opened. + #[error("write transactions are not supported in read-only mode")] WriteTransactionUnsupportedInReadOnlyMode, + /// Unknown error code. + #[error("unknown error code")] Other(i32), } @@ -142,23 +196,6 @@ impl Error { Error::Other(err_code) => *err_code, } } - - /// Returns the message for this error - pub fn as_str(&self) -> &str { - match self { - Self::DecodeErrorLenDiff => "mismatched data length", - Self::NestedTransactionsUnsupportedWithWriteMap => { - "nested transactions are not supported on an environment with writemap" - } - Self::WriteTransactionUnsupportedInReadOnlyMode => { - "write transactions are not supported on an environment opened in read-only mode" - } - _ => unsafe { - let err = ffi::mdbx_strerror(self.to_err_code()); - str::from_utf8_unchecked(CStr::from_ptr(err).to_bytes()) - }, - } - } } impl From for i32 { @@ -167,12 +204,6 @@ impl From for i32 { } } -impl fmt::Display for Error { - fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(fmt, "{}", self.as_str()) - } -} - #[inline] pub(crate) fn mdbx_result(err_code: c_int) -> Result { match err_code { @@ -199,9 +230,9 @@ mod test { #[test] fn test_description() { - assert_eq!("Permission denied", Error::from_err_code(13).to_string()); + assert_eq!("the environment opened in read-only", Error::from_err_code(13).to_string()); - assert_eq!("MDBX_INVALID: File is not an MDBX file", Error::Invalid.to_string()); + assert_eq!("file is not an MDBX file", Error::Invalid.to_string()); } #[test] From 8910f76ab4d093aa8cd5458e4c971a0f3178fd51 Mon Sep 17 00:00:00 2001 From: Matthias Seitz Date: Sat, 6 Jan 2024 13:26:11 +0100 Subject: [PATCH 260/277] chore: use available created_address fn (#5957) --- .../revm/revm-inspectors/src/tracing/js/mod.rs | 3 +-- crates/revm/revm-inspectors/src/tracing/mod.rs | 16 +++++++--------- crates/revm/revm-inspectors/src/tracing/utils.rs | 16 ++-------------- 3 files changed, 10 insertions(+), 25 deletions(-) diff --git a/crates/revm/revm-inspectors/src/tracing/js/mod.rs b/crates/revm/revm-inspectors/src/tracing/js/mod.rs index 0910d6f8b18e..f6982b6f6d1d 100644 --- a/crates/revm/revm-inspectors/src/tracing/js/mod.rs +++ b/crates/revm/revm-inspectors/src/tracing/js/mod.rs @@ -8,7 +8,6 @@ use crate::tracing::{ builtins::{register_builtins, PrecompileList}, }, types::CallKind, - utils::get_create_address, }; use alloy_primitives::{Address, Bytes, B256, U256}; use boa_engine::{Context, JsError, JsObject, JsResult, JsValue, Source}; @@ -449,7 +448,7 @@ where let _ = data.journaled_state.load_account(inputs.caller, data.db); let nonce = data.journaled_state.account(inputs.caller).info.nonce; - let address = get_create_address(inputs, nonce); + let address = inputs.created_address(nonce); self.push_call( address, inputs.init_code.clone(), diff --git a/crates/revm/revm-inspectors/src/tracing/mod.rs b/crates/revm/revm-inspectors/src/tracing/mod.rs index 6b4b93636364..e9b6674b3067 100644 --- a/crates/revm/revm-inspectors/src/tracing/mod.rs +++ b/crates/revm/revm-inspectors/src/tracing/mod.rs @@ -1,6 +1,10 @@ +use self::parity::stack_push_count; use crate::tracing::{ - types::{CallKind, LogCallOrder}, - utils::get_create_address, + arena::PushTraceKind, + types::{ + CallKind, CallTraceNode, LogCallOrder, RecordedMemory, StorageChange, StorageChangeReason, + }, + utils::gas_used, }; use alloy_primitives::{Address, Bytes, Log, B256, U256}; pub use arena::CallTraceArena; @@ -22,12 +26,6 @@ mod fourbyte; mod opcount; pub mod types; mod utils; -use self::parity::stack_push_count; -use crate::tracing::{ - arena::PushTraceKind, - types::{CallTraceNode, RecordedMemory, StorageChange, StorageChangeReason}, - utils::gas_used, -}; pub use builder::{ geth::{self, GethTraceBuilder}, parity::{self, ParityTraceBuilder}, @@ -502,7 +500,7 @@ where let nonce = data.journaled_state.account(inputs.caller).info.nonce; self.start_trace_on_call( data, - get_create_address(inputs, nonce), + inputs.created_address(nonce), inputs.init_code.clone(), inputs.value, inputs.scheme.into(), diff --git a/crates/revm/revm-inspectors/src/tracing/utils.rs b/crates/revm/revm-inspectors/src/tracing/utils.rs index ac3172eb7bcd..d13c8aa1c890 100644 --- a/crates/revm/revm-inspectors/src/tracing/utils.rs +++ b/crates/revm/revm-inspectors/src/tracing/utils.rs @@ -1,10 +1,9 @@ //! Util functions for revm related ops -use alloy_primitives::{hex, Address, Bytes, B256}; +use alloy_primitives::{hex, Bytes}; use alloy_sol_types::{ContractError, GenericRevertReason}; use revm::{ - interpreter::CreateInputs, - primitives::{CreateScheme, SpecId, KECCAK_EMPTY}, + primitives::{SpecId, KECCAK_EMPTY}, DatabaseRef, }; @@ -27,17 +26,6 @@ pub(crate) fn gas_used(spec: SpecId, spent: u64, refunded: u64) -> u64 { spent - (refunded).min(spent / refund_quotient) } -/// Get the address of a contract creation -#[inline] -pub(crate) fn get_create_address(call: &CreateInputs, nonce: u64) -> Address { - match call.scheme { - CreateScheme::Create => call.caller.create(nonce), - CreateScheme::Create2 { salt } => { - call.caller.create2_from_code(B256::from(salt), call.init_code.clone()) - } - } -} - /// Loads the code for the given account from the account itself or the database /// /// Returns None if the code hash is the KECCAK_EMPTY hash From bc192d936148cb172e57ca22e07c8d0b4e66c2fe Mon Sep 17 00:00:00 2001 From: Matthias Seitz Date: Sat, 6 Jan 2024 13:30:59 +0100 Subject: [PATCH 261/277] chore: make clippy happy (#5959) --- crates/net/discv4/src/lib.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/crates/net/discv4/src/lib.rs b/crates/net/discv4/src/lib.rs index 8fdfd16a9811..dcaafec1b53c 100644 --- a/crates/net/discv4/src/lib.rs +++ b/crates/net/discv4/src/lib.rs @@ -1605,7 +1605,9 @@ impl Discv4Service { // process all incoming datagrams while let Poll::Ready(Some(event)) = self.ingress.poll_recv(cx) { match event { - IngressEvent::RecvError(_) => {} + IngressEvent::RecvError(err) => { + debug!(target: "discv4", %err, "failed to read datagram"); + } IngressEvent::BadPacket(from, err, data) => { debug!(target: "discv4", ?from, ?err, packet=?hex::encode(&data), "bad packet"); } From a5a0fff511e7f89cafe0d08f693de3a53743618c Mon Sep 17 00:00:00 2001 From: Matthias Seitz Date: Sat, 6 Jan 2024 13:40:32 +0100 Subject: [PATCH 262/277] test: enable ignored tests (#5958) --- crates/net/network/src/transactions.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/crates/net/network/src/transactions.rs b/crates/net/network/src/transactions.rs index 76da5e3b9dbb..d3382bacd7c8 100644 --- a/crates/net/network/src/transactions.rs +++ b/crates/net/network/src/transactions.rs @@ -1297,7 +1297,6 @@ mod tests { use std::future::poll_fn; #[tokio::test(flavor = "multi_thread")] - #[cfg_attr(not(feature = "geth-tests"), ignore)] async fn test_ignored_tx_broadcasts_while_initially_syncing() { reth_tracing::init_test_tracing(); let net = Testnet::create(3).await; @@ -1380,7 +1379,6 @@ mod tests { } #[tokio::test(flavor = "multi_thread")] - #[cfg_attr(not(feature = "geth-tests"), ignore)] async fn test_tx_broadcasts_through_two_syncs() { reth_tracing::init_test_tracing(); let net = Testnet::create(3).await; From c6de11730c5e6828a527e2e221c403927b72b624 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Sun, 7 Jan 2024 11:27:34 +0000 Subject: [PATCH 263/277] chore(deps): weekly `cargo update` (#5967) Co-authored-by: github-merge-queue --- Cargo.lock | 202 ++++++++++++++++++++++++++--------------------------- 1 file changed, 101 insertions(+), 101 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 6eb65eb8736b..e458e96ba1a5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -213,7 +213,7 @@ checksum = "1a047897373be4bbb0224c1afdabca92648dc57a9c9ef6e7b0be3aff7a859c83" dependencies = [ "proc-macro2", "quote", - "syn 2.0.43", + "syn 2.0.48", ] [[package]] @@ -263,7 +263,7 @@ dependencies = [ "proc-macro-error", "proc-macro2", "quote", - "syn 2.0.43", + "syn 2.0.48", "syn-solidity", "tiny-keccak", ] @@ -379,9 +379,9 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.78" +version = "1.0.79" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca87830a3e3fb156dc96cfbd31cb620265dd053be734723f22b760d6cc3c3051" +checksum = "080e9890a082662b09c1ad45f567faeeb47f22b5fb23895fbe1e651e718e25ca" [[package]] name = "aquamarine" @@ -394,7 +394,7 @@ dependencies = [ "proc-macro-error", "proc-macro2", "quote", - "syn 2.0.43", + "syn 2.0.48", ] [[package]] @@ -611,18 +611,18 @@ checksum = "16e62a023e7c117e27523144c5d2459f4397fcc3cab0085af8e2224f643a0193" dependencies = [ "proc-macro2", "quote", - "syn 2.0.43", + "syn 2.0.48", ] [[package]] name = "async-trait" -version = "0.1.76" +version = "0.1.77" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "531b97fb4cd3dfdce92c35dedbfdc1f0b9d8091c8ca943d6dae340ef5012d514" +checksum = "c980ee35e870bd1a4d2c8294d4c04d0499e67bca1e4b5cefcc693c2fa00caea9" dependencies = [ "proc-macro2", "quote", - "syn 2.0.43", + "syn 2.0.48", ] [[package]] @@ -738,9 +738,9 @@ checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b" [[package]] name = "basic-toml" -version = "0.1.7" +version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f2139706359229bfa8f19142ac1155b4b80beafb7a60471ac5dd109d4a19778" +checksum = "2db21524cad41c5591204d22d75e1970a2d1f71060214ca931dc7d5afe2c14e5" dependencies = [ "serde", ] @@ -803,7 +803,7 @@ dependencies = [ "regex", "rustc-hash", "shlex", - "syn 2.0.43", + "syn 2.0.48", "which", ] @@ -824,7 +824,7 @@ dependencies = [ "regex", "rustc-hash", "shlex", - "syn 2.0.43", + "syn 2.0.48", ] [[package]] @@ -1028,7 +1028,7 @@ checksum = "005fa0c5bd20805466dda55eb34cd709bb31a2592bb26927b47714eeed6914d8" dependencies = [ "proc-macro2", "quote", - "syn 2.0.43", + "syn 2.0.48", "synstructure", ] @@ -1188,7 +1188,7 @@ checksum = "2d886547e41f740c616ae73108f6eb70afe6d940c7bc697cb30f13daec073037" dependencies = [ "camino", "cargo-platform", - "semver 1.0.20", + "semver 1.0.21", "serde", "serde_json", "thiserror", @@ -1294,9 +1294,9 @@ dependencies = [ [[package]] name = "clang-sys" -version = "1.6.1" +version = "1.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c688fc74432808e3eb684cae8830a86be1d66a2bd58e1f248ed0960a590baf6f" +checksum = "67523a3b4be3ce1989d607a828d036249522dd9c1c8de7f4dd2dae43a37369d1" dependencies = [ "glob", "libc", @@ -1305,9 +1305,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.4.12" +version = "4.4.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dcfab8ba68f3668e89f6ff60f5b205cea56aa7b769451a59f34b8682f51c056d" +checksum = "52bdc885e4cacc7f7c9eedc1ef6da641603180c783c41a15c264944deeaab642" dependencies = [ "clap_builder", "clap_derive", @@ -1334,7 +1334,7 @@ dependencies = [ "heck", "proc-macro2", "quote", - "syn 2.0.43", + "syn 2.0.48", ] [[package]] @@ -1368,7 +1368,7 @@ dependencies = [ "proc-macro2", "quote", "serde", - "syn 2.0.43", + "syn 2.0.48", ] [[package]] @@ -1541,9 +1541,9 @@ dependencies = [ [[package]] name = "cpufeatures" -version = "0.2.11" +version = "0.2.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce420fe07aecd3e67c5f910618fe65e94158f6dcc0adf44e00d69ce2bdfe0fd0" +checksum = "53fe5e26ff1b7aef8bca9c6080520cfb8d9333c7568e1829cef191a9723e5504" dependencies = [ "libc", ] @@ -1792,7 +1792,7 @@ checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3" dependencies = [ "proc-macro2", "quote", - "syn 2.0.43", + "syn 2.0.48", ] [[package]] @@ -1864,7 +1864,7 @@ dependencies = [ "proc-macro2", "quote", "strsim 0.10.0", - "syn 2.0.43", + "syn 2.0.48", ] [[package]] @@ -1897,7 +1897,7 @@ checksum = "836a9bbc7ad63342d6d6e7b815ccab164bc77a2d95d84bc3117a8c0d5c98e2d5" dependencies = [ "darling_core 0.20.3", "quote", - "syn 2.0.43", + "syn 2.0.48", ] [[package]] @@ -1983,7 +1983,7 @@ checksum = "67e77553c4162a157adbf834ebae5b415acbecbeafc7a74b0e886657506a7611" dependencies = [ "proc-macro2", "quote", - "syn 2.0.43", + "syn 2.0.48", ] [[package]] @@ -2157,7 +2157,7 @@ checksum = "487585f4d0c6655fe74905e2504d8ad6908e4db67f744eb140876906c2f3175d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.43", + "syn 2.0.48", ] [[package]] @@ -2358,7 +2358,7 @@ dependencies = [ "heck", "proc-macro2", "quote", - "syn 2.0.43", + "syn 2.0.48", ] [[package]] @@ -2371,18 +2371,18 @@ dependencies = [ "num-traits", "proc-macro2", "quote", - "syn 2.0.43", + "syn 2.0.48", ] [[package]] name = "enumn" -version = "0.1.12" +version = "0.1.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c2ad8cef1d801a4686bfd8919f0b30eac4c8e48968c437a6405ded4fb5272d2b" +checksum = "6fd000fd6988e73bbe993ea3db9b1aa64906ab88766d654973924340c8cddb42" dependencies = [ "proc-macro2", "quote", - "syn 2.0.43", + "syn 2.0.48", ] [[package]] @@ -2530,7 +2530,7 @@ dependencies = [ "regex", "serde", "serde_json", - "syn 2.0.43", + "syn 2.0.48", "toml 0.8.2", "walkdir", ] @@ -2548,7 +2548,7 @@ dependencies = [ "proc-macro2", "quote", "serde_json", - "syn 2.0.43", + "syn 2.0.48", ] [[package]] @@ -2574,7 +2574,7 @@ dependencies = [ "serde", "serde_json", "strum", - "syn 2.0.43", + "syn 2.0.48", "tempfile", "thiserror", "tiny-keccak", @@ -2913,7 +2913,7 @@ checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" dependencies = [ "proc-macro2", "quote", - "syn 2.0.43", + "syn 2.0.48", ] [[package]] @@ -4071,12 +4071,12 @@ checksum = "302d7ab3130588088d277783b1e2d2e10c9e9e4a16dd9050e6ec93fb3e7048f4" [[package]] name = "libloading" -version = "0.7.4" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b67380fd3b2fbe7527a606e18729d21c6f3951633d0500574c4dc22d2d638b9f" +checksum = "c571b676ddfc9a8c12f1f3d3085a7b163966a8fd8098a90640953ce5f6170161" dependencies = [ "cfg-if", - "winapi", + "windows-sys 0.48.0", ] [[package]] @@ -4300,14 +4300,14 @@ checksum = "38b4faf00617defe497754acde3024865bc143d44a86799b24e191ecff91354f" dependencies = [ "proc-macro2", "quote", - "syn 2.0.43", + "syn 2.0.48", ] [[package]] name = "metrics-process" -version = "1.0.15" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "397ca18f9d62947522bd73ea09f437168f50da0ae22fb577d7f39bb2eee64907" +checksum = "97ab55aa892047d9fa19d390afc5318492f956de3ec88a098adb4c0e663b4914" dependencies = [ "libproc", "mach2", @@ -4625,7 +4625,7 @@ dependencies = [ "proc-macro-crate 1.3.1", "proc-macro2", "quote", - "syn 2.0.43", + "syn 2.0.48", ] [[package]] @@ -4637,7 +4637,7 @@ dependencies = [ "proc-macro-crate 2.0.1", "proc-macro2", "quote", - "syn 2.0.43", + "syn 2.0.48", ] [[package]] @@ -4899,9 +4899,9 @@ checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" [[package]] name = "pest" -version = "2.7.5" +version = "2.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae9cee2a55a544be8b89dc6848072af97a20f2422603c10865be2a42b580fff5" +checksum = "1f200d8d83c44a45b21764d1916299752ca035d15ecd46faca3e9a2a2bf6ad06" dependencies = [ "memchr", "thiserror", @@ -4961,7 +4961,7 @@ dependencies = [ "phf_shared", "proc-macro2", "quote", - "syn 2.0.43", + "syn 2.0.48", ] [[package]] @@ -4990,7 +4990,7 @@ checksum = "4359fd9c9171ec6e8c62926d6faaf553a8dc3f64e1507e76da7911b4f6a04405" dependencies = [ "proc-macro2", "quote", - "syn 2.0.43", + "syn 2.0.48", ] [[package]] @@ -5176,12 +5176,12 @@ dependencies = [ [[package]] name = "prettyplease" -version = "0.2.15" +version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae005bd773ab59b4725093fd7df83fd7892f7d8eafb48dbd7de6e024e4215f9d" +checksum = "a41cf62165e97c7f814d2221421dbb9afcbcdb0a88068e5ea206e19951c2cbb5" dependencies = [ "proc-macro2", - "syn 2.0.43", + "syn 2.0.48", ] [[package]] @@ -5244,9 +5244,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.71" +version = "1.0.76" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75cb1540fadbd5b8fbccc4dddad2734eba435053f725621c070711a14bb5f4b8" +checksum = "95fc56cda0b5c3325f5fbbd7ff9fda9e02bb00bb3dac51252d2f1bfa1cb8cc8c" dependencies = [ "unicode-ident", ] @@ -5362,9 +5362,9 @@ dependencies = [ [[package]] name = "quote" -version = "1.0.33" +version = "1.0.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae" +checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef" dependencies = [ "proc-macro2", ] @@ -6162,7 +6162,7 @@ dependencies = [ "quote", "regex", "serial_test", - "syn 2.0.43", + "syn 2.0.48", "trybuild", ] @@ -6805,7 +6805,7 @@ dependencies = [ "once_cell", "revm-primitives", "ripemd", - "secp256k1 0.28.0", + "secp256k1 0.28.1", "sha2", "substrate-bn", ] @@ -7008,7 +7008,7 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" dependencies = [ - "semver 1.0.20", + "semver 1.0.21", ] [[package]] @@ -7214,11 +7214,11 @@ dependencies = [ [[package]] name = "secp256k1" -version = "0.28.0" +version = "0.28.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2acea373acb8c21ecb5a23741452acd2593ed44ee3d343e72baaa143bc89d0d5" +checksum = "3f622567e3b4b38154fb8190bcf6b160d7a4301d70595a49195b48c116007a27" dependencies = [ - "secp256k1-sys 0.9.1", + "secp256k1-sys 0.9.2", ] [[package]] @@ -7232,9 +7232,9 @@ dependencies = [ [[package]] name = "secp256k1-sys" -version = "0.9.1" +version = "0.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4dd97a086ec737e30053fd5c46f097465d25bb81dd3608825f65298c4c98be83" +checksum = "e5d1746aae42c19d583c3c1a8c646bfad910498e2051c551a7f2e3c0c9fbb7eb" dependencies = [ "cc", ] @@ -7273,9 +7273,9 @@ dependencies = [ [[package]] name = "semver" -version = "1.0.20" +version = "1.0.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "836fa6a3e1e547f9a2c4040802ec865b5d85f4014efe00555d7090a3dcaa1090" +checksum = "b97ed7a9823b74f99c7742f5336af7be5ecd3eeafcb1507d1fa93347b1d589b0" dependencies = [ "serde", ] @@ -7303,38 +7303,38 @@ checksum = "cd0b0ec5f1c1ca621c432a25813d8d60c88abe6d3e08a3eb9cf37d97a0fe3d73" [[package]] name = "serde" -version = "1.0.193" +version = "1.0.195" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "25dd9975e68d0cb5aa1120c288333fc98731bd1dd12f561e468ea4728c042b89" +checksum = "63261df402c67811e9ac6def069e4786148c4563f4b50fd4bf30aa370d626b02" dependencies = [ "serde_derive", ] [[package]] name = "serde_bytes" -version = "0.11.13" +version = "0.11.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8bb1879ea93538b78549031e2d54da3e901fd7e75f2e4dc758d760937b123d10" +checksum = "8b8497c313fd43ab992087548117643f6fcd935cbf36f176ffda0aacf9591734" dependencies = [ "serde", ] [[package]] name = "serde_derive" -version = "1.0.193" +version = "1.0.195" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43576ca501357b9b071ac53cdc7da8ef0cbd9493d8df094cd821777ea6e894d3" +checksum = "46fe8f8603d81ba86327b23a2e9cdf49e1255fb94a4c5f297f6ee0547178ea2c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.43", + "syn 2.0.48", ] [[package]] name = "serde_json" -version = "1.0.108" +version = "1.0.111" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d1c7e3eac408d115102c4c24ad393e0821bb3a5df4d506a80f85f7a742a526b" +checksum = "176e46fa42316f18edd598015a5166857fc835ec732f5215eac6b7bdbf0a84f4" dependencies = [ "itoa", "ryu", @@ -7399,7 +7399,7 @@ dependencies = [ "darling 0.20.3", "proc-macro2", "quote", - "syn 2.0.43", + "syn 2.0.48", ] [[package]] @@ -7424,7 +7424,7 @@ checksum = "91d129178576168c589c9ec973feedf7d3126c01ac2bf08795109aa35b69fb8f" dependencies = [ "proc-macro2", "quote", - "syn 2.0.43", + "syn 2.0.48", ] [[package]] @@ -7742,7 +7742,7 @@ dependencies = [ "proc-macro2", "quote", "rustversion", - "syn 2.0.43", + "syn 2.0.48", ] [[package]] @@ -7819,9 +7819,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.43" +version = "2.0.48" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee659fb5f3d355364e1f3e5bc10fb82068efbf824a1e9d1c9504244a6469ad53" +checksum = "0f3531638e407dfc0814761abb7c00a5b54992b849452a0646b7f65c9f770f3f" dependencies = [ "proc-macro2", "quote", @@ -7837,7 +7837,7 @@ dependencies = [ "paste", "proc-macro2", "quote", - "syn 2.0.43", + "syn 2.0.48", ] [[package]] @@ -7848,7 +7848,7 @@ checksum = "285ba80e733fac80aa4270fbcdf83772a79b80aa35c97075320abfee4a915b06" dependencies = [ "proc-macro2", "quote", - "syn 2.0.43", + "syn 2.0.48", "unicode-xid", ] @@ -7943,7 +7943,7 @@ dependencies = [ "prettyplease", "proc-macro2", "quote", - "syn 2.0.43", + "syn 2.0.48", ] [[package]] @@ -7967,22 +7967,22 @@ checksum = "a38c90d48152c236a3ab59271da4f4ae63d678c5d7ad6b7714d7cb9760be5e4b" [[package]] name = "thiserror" -version = "1.0.53" +version = "1.0.56" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2cd5904763bad08ad5513ddbb12cf2ae273ca53fa9f68e843e236ec6dfccc09" +checksum = "d54378c645627613241d077a3a79db965db602882668f9136ac42af9ecb730ad" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.53" +version = "1.0.56" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3dcf4a824cce0aeacd6f38ae6f24234c8e80d68632338ebaa1443b5df9e29e19" +checksum = "fa0faa943b50f3db30a20aa7e265dbc66076993efed8463e8de414e5d06d3471" dependencies = [ "proc-macro2", "quote", - "syn 2.0.43", + "syn 2.0.48", ] [[package]] @@ -8107,7 +8107,7 @@ checksum = "5b8a1e28f2deaa14e508979454cb3a223b10b938b45af148bc0986de36f1923b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.43", + "syn 2.0.48", ] [[package]] @@ -8321,7 +8321,7 @@ checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.43", + "syn 2.0.48", ] [[package]] @@ -8495,9 +8495,9 @@ checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" [[package]] name = "trybuild" -version = "1.0.86" +version = "1.0.88" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8419ecd263363827c5730386f418715766f584e2f874d32c23c5b00bd9727e7e" +checksum = "76de4f783e610194f6c98bfd53f9fc52bb2e0d02c947621e8a0f4ecc799b2880" dependencies = [ "basic-toml", "glob", @@ -8786,7 +8786,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn 2.0.43", + "syn 2.0.48", "wasm-bindgen-shared", ] @@ -8820,7 +8820,7 @@ checksum = "f0eb82fcb7930ae6219a7ecfd55b217f5f0893484b7a13022ebb2b2bf20b5283" dependencies = [ "proc-macro2", "quote", - "syn 2.0.43", + "syn 2.0.48", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -9134,9 +9134,9 @@ checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04" [[package]] name = "winnow" -version = "0.5.31" +version = "0.5.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97a4882e6b134d6c28953a387571f1acdd3496830d5e36c5e3a1075580ea641c" +checksum = "b7520bbdec7211caa7c4e682eb1fbe07abe20cee6756b6e00f537c82c11816aa" dependencies = [ "memchr", ] @@ -9241,7 +9241,7 @@ checksum = "9e6936f0cce458098a201c245a11bef556c6a0181129c7034d10d76d1ec3a2b8" dependencies = [ "proc-macro2", "quote", - "syn 2.0.43", + "syn 2.0.48", "synstructure", ] @@ -9262,7 +9262,7 @@ checksum = "9ce1b18ccd8e73a9321186f97e46f9f04b778851177567b1975109d26a08d2a6" dependencies = [ "proc-macro2", "quote", - "syn 2.0.43", + "syn 2.0.48", ] [[package]] @@ -9282,7 +9282,7 @@ checksum = "e6a647510471d372f2e6c2e6b7219e44d8c574d24fdc11c610a61455782f18c3" dependencies = [ "proc-macro2", "quote", - "syn 2.0.43", + "syn 2.0.48", "synstructure", ] @@ -9303,7 +9303,7 @@ checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" dependencies = [ "proc-macro2", "quote", - "syn 2.0.43", + "syn 2.0.48", ] [[package]] @@ -9326,7 +9326,7 @@ checksum = "7a4a1638a1934450809c2266a70362bfc96cd90550c073f5b8a55014d1010157" dependencies = [ "proc-macro2", "quote", - "syn 2.0.43", + "syn 2.0.48", ] [[package]] From 3038c3c7ba35b1262b3ee55be136514e2e4c44c4 Mon Sep 17 00:00:00 2001 From: Thomas Coratger <60488569+tcoratger@users.noreply.github.com> Date: Sun, 7 Jan 2024 12:29:00 +0100 Subject: [PATCH 264/277] feat(header): add `is_zero_difficulty` util function for POS (#5966) Co-authored-by: Matthias Seitz --- crates/consensus/beacon-core/src/lib.rs | 4 +--- crates/consensus/beacon/src/engine/mod.rs | 10 ++++++---- crates/primitives/src/header.rs | 13 +++++++++++++ 3 files changed, 20 insertions(+), 7 deletions(-) diff --git a/crates/consensus/beacon-core/src/lib.rs b/crates/consensus/beacon-core/src/lib.rs index 70b113baf9b9..a825ac62f27e 100644 --- a/crates/consensus/beacon-core/src/lib.rs +++ b/crates/consensus/beacon-core/src/lib.rs @@ -55,9 +55,7 @@ impl Consensus for BeaconConsensus { ) -> Result<(), ConsensusError> { if self.chain_spec.fork(Hardfork::Paris).active_at_ttd(total_difficulty, header.difficulty) { - // EIP-3675: Upgrade consensus to Proof-of-Stake: - // https://eips.ethereum.org/EIPS/eip-3675#replacing-difficulty-with-0 - if header.difficulty != U256::ZERO { + if !header.is_zero_difficulty() { return Err(ConsensusError::TheMergeDifficultyIsNotZero) } diff --git a/crates/consensus/beacon/src/engine/mod.rs b/crates/consensus/beacon/src/engine/mod.rs index ccbdd113167f..78f20380f817 100644 --- a/crates/consensus/beacon/src/engine/mod.rs +++ b/crates/consensus/beacon/src/engine/mod.rs @@ -23,7 +23,7 @@ use reth_interfaces::{ use reth_payload_builder::{PayloadBuilderAttributes, PayloadBuilderHandle}; use reth_primitives::{ constants::EPOCH_SLOTS, stage::StageId, BlockNumHash, BlockNumber, Head, Header, SealedBlock, - SealedHeader, B256, U256, + SealedHeader, B256, }; use reth_provider::{ BlockIdReader, BlockReader, BlockSource, CanonChainTracker, ChainSpecProvider, ProviderError, @@ -490,7 +490,7 @@ where // we need to check if the parent block is the last POW block, if so then the payload is // the first POS. The engine API spec mandates a zero hash to be returned: - if parent_header.difficulty != U256::ZERO { + if !parent_header.is_zero_difficulty() { return Some(B256::ZERO); } @@ -506,7 +506,7 @@ where // Edge case: the `latestValid` field is the zero hash if the parent block is the terminal // PoW block, which we need to identify by looking at the parent's block difficulty if let Ok(Some(parent)) = self.blockchain.header_by_hash_or_number(parent_hash.into()) { - if parent.difficulty != U256::ZERO { + if !parent.is_zero_difficulty() { parent_hash = B256::ZERO; } } @@ -1881,7 +1881,9 @@ mod tests { }; use assert_matches::assert_matches; use reth_interfaces::test_utils::generators::{self, Rng}; - use reth_primitives::{stage::StageCheckpoint, ChainSpec, ChainSpecBuilder, B256, MAINNET}; + use reth_primitives::{ + stage::StageCheckpoint, ChainSpec, ChainSpecBuilder, B256, MAINNET, U256, + }; use reth_provider::{BlockWriter, ProviderFactory}; use reth_rpc_types::engine::{ForkchoiceState, ForkchoiceUpdated, PayloadStatus}; use reth_rpc_types_compat::engine::payload::try_block_to_payload_v1; diff --git a/crates/primitives/src/header.rs b/crates/primitives/src/header.rs index 6864b86d914f..b5c192b77c31 100644 --- a/crates/primitives/src/header.rs +++ b/crates/primitives/src/header.rs @@ -126,6 +126,19 @@ impl Default for Header { } impl Header { + /// Checks if the block's difficulty is set to zero, indicating a Proof-of-Stake header. + /// + /// This function is linked to EIP-3675, proposing the consensus upgrade to Proof-of-Stake: + /// [EIP-3675](https://eips.ethereum.org/EIPS/eip-3675#replacing-difficulty-with-0) + /// + /// Verifies whether, as per the EIP, the block's difficulty is updated to zero, + /// signifying the transition to a Proof-of-Stake mechanism. + /// + /// Returns `true` if the block's difficulty matches the constant zero set by the EIP. + pub fn is_zero_difficulty(&self) -> bool { + self.difficulty.is_zero() + } + /// Performs a sanity check on the extradata field of the header. /// /// # Errors From 434b43af80b6caa83cc6e5333ffb6092108dc76b Mon Sep 17 00:00:00 2001 From: Matthias Seitz Date: Sun, 7 Jan 2024 23:05:10 +0100 Subject: [PATCH 265/277] chore: use existing fn for loading network secret key (#5961) --- bin/reth/src/builder/mod.rs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/bin/reth/src/builder/mod.rs b/bin/reth/src/builder/mod.rs index 40fbe5203e25..2e589d67c769 100644 --- a/bin/reth/src/builder/mod.rs +++ b/bin/reth/src/builder/mod.rs @@ -483,10 +483,7 @@ impl NodeConfig { DB: Database + Unpin + Clone + 'static, { info!(target: "reth::cli", "Connecting to P2P network"); - let network_secret_path = - self.network.p2p_secret_key.clone().unwrap_or_else(|| data_dir.p2p_secret_path()); - debug!(target: "reth::cli", ?network_secret_path, "Loading p2p key file"); - let secret_key = get_secret_key(&network_secret_path)?; + let secret_key = self.network_secret(data_dir)?; let default_peers_path = data_dir.known_peers_path(); let network_config = self.load_network_config( config, From 313e5b7827aca2b4e9b3a26886869aabfdbaf1dd Mon Sep 17 00:00:00 2001 From: Matthias Seitz Date: Sun, 7 Jan 2024 23:05:17 +0100 Subject: [PATCH 266/277] chore: cleanup max block fetching (#5960) --- bin/reth/src/builder/mod.rs | 38 +++++++++++++++++-------------------- 1 file changed, 17 insertions(+), 21 deletions(-) diff --git a/bin/reth/src/builder/mod.rs b/bin/reth/src/builder/mod.rs index 2e589d67c769..1c54a291141c 100644 --- a/bin/reth/src/builder/mod.rs +++ b/bin/reth/src/builder/mod.rs @@ -438,19 +438,19 @@ impl NodeConfig { /// Returns the max block that the node should run to, looking it up from the network if /// necessary - pub async fn max_block( + async fn max_block( &self, network_client: &Client, - provider_factory: ProviderFactory, + provider: Provider, ) -> eyre::Result> where - DB: Database, + Provider: HeaderProvider, Client: HeadersClient, { let max_block = if let Some(block) = self.debug.max_block { Some(block) } else if let Some(tip) = self.debug.tip { - Some(self.lookup_or_fetch_tip(provider_factory, network_client, tip).await?) + Some(self.lookup_or_fetch_tip(provider, network_client, tip).await?) } else { None }; @@ -751,42 +751,38 @@ impl NodeConfig { /// If it doesn't exist, download the header and return the block number. /// /// NOTE: The download is attempted with infinite retries. - async fn lookup_or_fetch_tip( + async fn lookup_or_fetch_tip( &self, - provider_factory: ProviderFactory, + provider: Provider, client: Client, tip: B256, ) -> RethResult where - DB: Database, + Provider: HeaderProvider, Client: HeadersClient, { - Ok(self.fetch_tip(provider_factory, client, BlockHashOrNumber::Hash(tip)).await?.number) + let header = provider.header_by_hash_or_number(tip.into())?; + + // try to look up the header in the database + if let Some(header) = header { + info!(target: "reth::cli", ?tip, "Successfully looked up tip block in the database"); + return Ok(header.number) + } + + Ok(self.fetch_tip_from_network(client, tip.into()).await?.number) } /// Attempt to look up the block with the given number and return the header. /// /// NOTE: The download is attempted with infinite retries. - async fn fetch_tip( + async fn fetch_tip_from_network( &self, - factory: ProviderFactory, client: Client, tip: BlockHashOrNumber, ) -> RethResult where - DB: Database, Client: HeadersClient, { - let provider = factory.provider()?; - - let header = provider.header_by_hash_or_number(tip)?; - - // try to look up the header in the database - if let Some(header) = header { - info!(target: "reth::cli", ?tip, "Successfully looked up tip block in the database"); - return Ok(header.seal_slow()) - } - info!(target: "reth::cli", ?tip, "Fetching tip block from the network."); loop { match get_single_header(&client, tip).await { From 8d6880c1d285fc7a69d101db1aa4cf656348ac67 Mon Sep 17 00:00:00 2001 From: Matthias Seitz Date: Mon, 8 Jan 2024 12:08:14 +0100 Subject: [PATCH 267/277] chore: add non critical spawn with graceful shutdown signal fn (#5962) --- crates/tasks/src/lib.rs | 55 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 55 insertions(+) diff --git a/crates/tasks/src/lib.rs b/crates/tasks/src/lib.rs index 010bc4e147d0..a4a1d28a4b25 100644 --- a/crates/tasks/src/lib.rs +++ b/crates/tasks/src/lib.rs @@ -507,6 +507,41 @@ impl TaskExecutor { self.handle.spawn(task) } + + /// This spawns a regular task onto the runtime. + /// + /// The [TaskManager] will wait until the given future has completed before shutting down. + /// + /// # Example + /// + /// ```no_run + /// # async fn t(executor: reth_tasks::TaskExecutor) { + /// + /// executor.spawn_with_graceful_shutdown_signal(|shutdown| async move { + /// // await the shutdown signal + /// let guard = shutdown.await; + /// // do work before exiting the program + /// tokio::time::sleep(std::time::Duration::from_secs(1)).await; + /// // allow graceful shutdown + /// drop(guard); + /// }); + /// # } + /// ``` + pub fn spawn_with_graceful_shutdown_signal( + &self, + f: impl FnOnce(GracefulShutdown) -> F, + ) -> JoinHandle<()> + where + F: Future + Send + 'static, + { + let on_shutdown = GracefulShutdown::new( + self.on_shutdown.clone(), + GracefulShutdownGuard::new(Arc::clone(&self.graceful_tasks)), + ); + let fut = f(on_shutdown); + + self.handle.spawn(fut) + } } impl TaskSpawner for TaskExecutor { @@ -546,6 +581,16 @@ pub trait TaskSpawnerExt: Send + Sync + Unpin + std::fmt::Debug + DynClone { ) -> JoinHandle<()> where F: Future + Send + 'static; + + /// This spawns a regular task onto the runtime. + /// + /// The [TaskManager] will wait until the given future has completed before shutting down. + fn spawn_with_graceful_shutdown_signal( + &self, + f: impl FnOnce(GracefulShutdown) -> F, + ) -> JoinHandle<()> + where + F: Future + Send + 'static; } impl TaskSpawnerExt for TaskExecutor { @@ -559,6 +604,16 @@ impl TaskSpawnerExt for TaskExecutor { { TaskExecutor::spawn_critical_with_graceful_shutdown_signal(self, name, f) } + + fn spawn_with_graceful_shutdown_signal( + &self, + f: impl FnOnce(GracefulShutdown) -> F, + ) -> JoinHandle<()> + where + F: Future + Send + 'static, + { + TaskExecutor::spawn_with_graceful_shutdown_signal(self, f) + } } /// Determines how a task is spawned From d51375c4f3b1221b4a9429aea239d792d67ecbcc Mon Sep 17 00:00:00 2001 From: "Supernovahs.eth" <91280922+supernovahs@users.noreply.github.com> Date: Mon, 8 Jan 2024 18:43:48 +0530 Subject: [PATCH 268/277] refactor : Simplify is_some in Header (#5969) --- crates/primitives/src/header.rs | 64 +++++++++++++++++++++++---------- 1 file changed, 46 insertions(+), 18 deletions(-) diff --git a/crates/primitives/src/header.rs b/crates/primitives/src/header.rs index b5c192b77c31..00099dff9e83 100644 --- a/crates/primitives/src/header.rs +++ b/crates/primitives/src/header.rs @@ -282,6 +282,34 @@ impl Header { self.extra_data.len() // extra data } + /// Checks if `blob_gas_used` is present in the header. + /// + /// Returns `true` if `blob_gas_used` is `Some`, otherwise `false`. + fn has_blob_gas_used(&self) -> bool { + self.blob_gas_used.is_some() + } + + /// Checks if `excess_blob_gas` is present in the header. + /// + /// Returns `true` if `excess_blob_gas` is `Some`, otherwise `false`. + fn has_excess_blob_gas(&self) -> bool { + self.excess_blob_gas.is_some() + } + + // Checks if `withdrawals_root` is present in the header. + /// + /// Returns `true` if `withdrawals_root` is `Some`, otherwise `false`. + fn has_withdrawals_root(&self) -> bool { + self.withdrawals_root.is_some() + } + + /// Checks if `parent_beacon_block_root` is present in the header. + /// + /// Returns `true` if `parent_beacon_block_root` is `Some`, otherwise `false`. + fn has_parent_beacon_block_root(&self) -> bool { + self.parent_beacon_block_root.is_some() + } + fn header_payload_length(&self) -> usize { let mut length = 0; length += self.parent_hash.length(); // Hash of the previous block. @@ -303,10 +331,10 @@ impl Header { if let Some(base_fee) = self.base_fee_per_gas { // Adding base fee length if it exists. length += U256::from(base_fee).length(); - } else if self.withdrawals_root.is_some() || - self.blob_gas_used.is_some() || - self.excess_blob_gas.is_some() || - self.parent_beacon_block_root.is_some() + } else if self.has_withdrawals_root() || + self.has_blob_gas_used() || + self.has_excess_blob_gas() || + self.has_parent_beacon_block_root() { // Placeholder code for empty lists. length += 1; @@ -315,9 +343,9 @@ impl Header { if let Some(root) = self.withdrawals_root { // Adding withdrawals_root length if it exists. length += root.length(); - } else if self.blob_gas_used.is_some() || - self.excess_blob_gas.is_some() || - self.parent_beacon_block_root.is_some() + } else if self.has_blob_gas_used() || + self.has_excess_blob_gas() || + self.has_parent_beacon_block_root() { // Placeholder code for a missing string value. length += 1; @@ -326,7 +354,7 @@ impl Header { if let Some(blob_gas_used) = self.blob_gas_used { // Adding blob_gas_used length if it exists. length += U256::from(blob_gas_used).length(); - } else if self.excess_blob_gas.is_some() || self.parent_beacon_block_root.is_some() { + } else if self.has_excess_blob_gas() || self.has_parent_beacon_block_root() { // Placeholder code for empty lists. length += 1; } @@ -334,7 +362,7 @@ impl Header { if let Some(excess_blob_gas) = self.excess_blob_gas { // Adding excess_blob_gas length if it exists. length += U256::from(excess_blob_gas).length(); - } else if self.parent_beacon_block_root.is_some() { + } else if self.has_parent_beacon_block_root() { // Placeholder code for empty lists. length += 1; } @@ -383,10 +411,10 @@ impl Encodable for Header { // but withdrawals root is present. if let Some(ref base_fee) = self.base_fee_per_gas { U256::from(*base_fee).encode(out); - } else if self.withdrawals_root.is_some() || - self.blob_gas_used.is_some() || - self.excess_blob_gas.is_some() || - self.parent_beacon_block_root.is_some() + } else if self.has_withdrawals_root() || + self.has_blob_gas_used() || + self.has_excess_blob_gas() || + self.has_parent_beacon_block_root() { out.put_u8(EMPTY_LIST_CODE); } @@ -395,9 +423,9 @@ impl Encodable for Header { // but blob gas used is present. if let Some(ref root) = self.withdrawals_root { root.encode(out); - } else if self.blob_gas_used.is_some() || - self.excess_blob_gas.is_some() || - self.parent_beacon_block_root.is_some() + } else if self.has_blob_gas_used() || + self.has_excess_blob_gas() || + self.has_parent_beacon_block_root() { out.put_u8(EMPTY_STRING_CODE); } @@ -406,7 +434,7 @@ impl Encodable for Header { // but excess blob gas is present. if let Some(ref blob_gas_used) = self.blob_gas_used { U256::from(*blob_gas_used).encode(out); - } else if self.excess_blob_gas.is_some() || self.parent_beacon_block_root.is_some() { + } else if self.has_excess_blob_gas() || self.has_parent_beacon_block_root() { out.put_u8(EMPTY_LIST_CODE); } @@ -414,7 +442,7 @@ impl Encodable for Header { // but parent beacon block root is present. if let Some(ref excess_blob_gas) = self.excess_blob_gas { U256::from(*excess_blob_gas).encode(out); - } else if self.parent_beacon_block_root.is_some() { + } else if self.has_parent_beacon_block_root() { out.put_u8(EMPTY_LIST_CODE); } From 1208f6b8745baa3b91ee256e37f912216d7f6bd6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Urbanek?= Date: Mon, 8 Jan 2024 15:37:27 +0100 Subject: [PATCH 269/277] Update full node prune config docs (#5933) --- book/run/pruning.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/book/run/pruning.md b/book/run/pruning.md index a96a891477d3..acfb93c9cb09 100644 --- a/book/run/pruning.md +++ b/book/run/pruning.md @@ -78,7 +78,7 @@ Essentially, the full node is the same as following configuration for the pruned block_interval = 5 [prune.parts] -sender_recovery = { distance = 10_064 } +sender_recovery = "full" # transaction_lookup is not pruned receipts = { before = 11052984 } # Beacon Deposit Contract deployment block: https://etherscan.io/tx/0xe75fb554e433e03763a1560646ee22dcb74e5274b34c5ad644e7c0f619a7e1d0 account_history = { distance = 10_064 } @@ -92,7 +92,7 @@ storage_history = { distance = 10_064 } Meaning, it prunes: - Account History and Storage History up to the last 10064 blocks -- Sender Recovery up to the last 10064 blocks. The caveat is that it's pruned gradually after the initial sync +- All of Sender Recovery data. The caveat is that it's pruned gradually after the initial sync is completed, so the disk space is reclaimed slowly. - Receipts up to the last 10064 blocks, preserving all receipts with the logs from Beacon Deposit Contract From 5353ac3c05f3a09df272ea3abcef21f50cdbaaff Mon Sep 17 00:00:00 2001 From: Dan Cline <6798349+Rjected@users.noreply.github.com> Date: Mon, 8 Jan 2024 11:19:19 -0500 Subject: [PATCH 270/277] chore: update MSRV to 1.75 (#5974) --- .github/workflows/lint.yml | 2 +- Cargo.toml | 2 +- clippy.toml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index 293831dcc2d5..b23830912c1f 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -47,7 +47,7 @@ jobs: - uses: actions/checkout@v4 - uses: dtolnay/rust-toolchain@master with: - toolchain: "1.70" # MSRV + toolchain: "1.75" # MSRV - uses: Swatinem/rust-cache@v2 with: cache-on-failure: true diff --git a/Cargo.toml b/Cargo.toml index e88f19cb2959..080fc48bf87e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -77,7 +77,7 @@ rust.rust_2018_idioms = "deny" [workspace.package] version = "0.1.0-alpha.13" edition = "2021" -rust-version = "1.70" +rust-version = "1.75" license = "MIT OR Apache-2.0" homepage = "https://paradigmxyz.github.io/reth" repository = "https://github.com/paradigmxyz/reth" diff --git a/clippy.toml b/clippy.toml index e7b6d30ab98e..3d11f32f43d9 100644 --- a/clippy.toml +++ b/clippy.toml @@ -1,3 +1,3 @@ -msrv = "1.70" +msrv = "1.75" ignore-interior-mutability = ["bytes::Bytes", "reth_primitives::trie::nibbles::Nibbles"] too-large-for-stack = 128 From 228739240507cc3e5c3472ac62cb7b8161ccaa90 Mon Sep 17 00:00:00 2001 From: "Supernovahs.eth" <91280922+supernovahs@users.noreply.github.com> Date: Mon, 8 Jan 2024 23:22:24 +0530 Subject: [PATCH 271/277] feat: Add random lookup for discv4 (#5975) --- crates/net/discv4/Cargo.toml | 1 - crates/net/discv4/src/lib.rs | 6 ++++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/crates/net/discv4/Cargo.toml b/crates/net/discv4/Cargo.toml index 74b6d4786d05..fa0e284ff2c1 100644 --- a/crates/net/discv4/Cargo.toml +++ b/crates/net/discv4/Cargo.toml @@ -23,7 +23,6 @@ discv5.workspace = true secp256k1 = { workspace = true, features = ["global-context", "rand-std", "recovery", "serde"] } enr = { workspace = true, default-features = false, features = ["rust-secp256k1"] } rlp = "0.5" # needed for enr - # async/futures tokio = { workspace = true, features = ["io-util", "net", "time"] } tokio-stream.workspace = true diff --git a/crates/net/discv4/src/lib.rs b/crates/net/discv4/src/lib.rs index dcaafec1b53c..378c15987230 100644 --- a/crates/net/discv4/src/lib.rs +++ b/crates/net/discv4/src/lib.rs @@ -283,6 +283,12 @@ impl Discv4 { self.lookup_node(Some(node_id)).await } + /// Performs a random lookup for node records. + pub async fn lookup_random(&self) -> Result, Discv4Error> { + let target = PeerId::random(); + self.lookup_node(Some(target)).await + } + /// Sends a message to the service to lookup the closest nodes pub fn send_lookup(&self, node_id: PeerId) { let cmd = Discv4Command::Lookup { node_id: Some(node_id), tx: None }; From 408d1c7e9ec20df331f2229b1eeb3d1bf7204cf4 Mon Sep 17 00:00:00 2001 From: Alexey Shekhirin Date: Mon, 8 Jan 2024 17:54:47 +0000 Subject: [PATCH 272/277] feat(storage): handle-slow-readers callback for MDBX (#5941) --- Cargo.lock | 21 ++ crates/storage/db/Cargo.toml | 1 + .../storage/db/src/implementation/mdbx/mod.rs | 38 +++- .../storage/db/src/implementation/mdbx/tx.rs | 10 +- crates/storage/libmdbx-rs/Cargo.toml | 1 + crates/storage/libmdbx-rs/src/environment.rs | 213 ++++++++++++++++++ crates/storage/libmdbx-rs/src/lib.rs | 3 +- 7 files changed, 278 insertions(+), 9 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index e458e96ba1a5..c1932d1789b9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4069,6 +4069,25 @@ version = "0.2.151" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "302d7ab3130588088d277783b1e2d2e10c9e9e4a16dd9050e6ec93fb3e7048f4" +[[package]] +name = "libffi" +version = "3.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce826c243048e3d5cec441799724de52e2d42f820468431fc3fceee2341871e2" +dependencies = [ + "libc", + "libffi-sys", +] + +[[package]] +name = "libffi-sys" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f36115160c57e8529781b4183c2bb51fdc1f6d6d1ed345591d84be7703befb3c" +dependencies = [ + "cc", +] + [[package]] name = "libloading" version = "0.8.1" @@ -5876,6 +5895,7 @@ dependencies = [ "itertools 0.12.0", "metrics 0.21.1", "modular-bitfield", + "once_cell", "page_size", "parity-scale-codec", "parking_lot 0.12.1", @@ -6123,6 +6143,7 @@ dependencies = [ "derive_more", "indexmap 2.1.0", "libc", + "libffi", "parking_lot 0.12.1", "pprof", "rand 0.8.5", diff --git a/crates/storage/db/Cargo.toml b/crates/storage/db/Cargo.toml index 62ef2200fe93..d214405ded10 100644 --- a/crates/storage/db/Cargo.toml +++ b/crates/storage/db/Cargo.toml @@ -55,6 +55,7 @@ itertools.workspace = true arbitrary = { workspace = true, features = ["derive"], optional = true } proptest = { workspace = true, optional = true } proptest-derive = { workspace = true, optional = true } +once_cell = "1.19.0" [dev-dependencies] # reth libs with arbitrary diff --git a/crates/storage/db/src/implementation/mdbx/mod.rs b/crates/storage/db/src/implementation/mdbx/mod.rs index 71554291502f..b2792170bc75 100644 --- a/crates/storage/db/src/implementation/mdbx/mod.rs +++ b/crates/storage/db/src/implementation/mdbx/mod.rs @@ -9,11 +9,13 @@ use crate::{ }; use eyre::Context; use metrics::{gauge, Label}; +use once_cell::sync::Lazy; use reth_interfaces::db::LogLevel; use reth_libmdbx::{ - DatabaseFlags, Environment, EnvironmentFlags, Geometry, Mode, PageSize, SyncMode, RO, RW, + DatabaseFlags, Environment, EnvironmentFlags, Geometry, HandleSlowReadersReturnCode, Mode, + PageSize, SyncMode, RO, RW, }; -use reth_tracing::tracing::error; +use reth_tracing::tracing::{error, warn}; use std::{ops::Deref, path::Path}; use tx::Tx; @@ -26,6 +28,13 @@ const TERABYTE: usize = GIGABYTE * 1024; /// MDBX allows up to 32767 readers (`MDBX_READERS_LIMIT`), but we limit it to slightly below that const DEFAULT_MAX_READERS: u64 = 32_000; +/// Space that a read-only transaction can occupy until the warning is emitted. +/// See [reth_libmdbx::EnvironmentBuilder::set_handle_slow_readers] for more information. +const MAX_SAFE_READER_SPACE: usize = 10 * GIGABYTE; + +static PROCESS_ID: Lazy = + Lazy::new(|| if cfg!(unix) { std::os::unix::process::parent_id() } else { std::process::id() }); + /// Environment used when opening a MDBX environment. RO/RW. #[derive(Debug)] pub enum DatabaseEnvKind { @@ -168,6 +177,29 @@ impl DatabaseEnv { shrink_threshold: None, page_size: Some(PageSize::Set(default_page_size())), }); + let _ = *PROCESS_ID; // Initialize the process ID at the time of environment opening + inner_env.set_handle_slow_readers( + |process_id: u32, thread_id: u32, read_txn_id: u64, gap: usize, space: usize, retry: isize| { + if space > MAX_SAFE_READER_SPACE { + let message = if process_id == *PROCESS_ID { + "Current process has a long-lived database transaction that grows the database file." + } else { + "External process has a long-lived database transaction that grows the database file. Use shorter-lived read transactions or shut down the node." + }; + warn!( + target: "storage::db::mdbx", + ?process_id, + ?thread_id, + ?read_txn_id, + ?gap, + ?space, + ?retry, + message + ) + } + + HandleSlowReadersReturnCode::ProceedWithoutKillingReader + }); inner_env.set_flags(EnvironmentFlags { mode, // We disable readahead because it improves performance for linear scans, but @@ -176,7 +208,7 @@ impl DatabaseEnv { coalesce: true, ..Default::default() }); - // configure more readers + // Configure more readers inner_env.set_max_readers(DEFAULT_MAX_READERS); // This parameter sets the maximum size of the "reclaimed list", and the unit of measurement // is "pages". Reclaimed list is the list of freed pages that's populated during the diff --git a/crates/storage/db/src/implementation/mdbx/tx.rs b/crates/storage/db/src/implementation/mdbx/tx.rs index ffa2a0fe2467..e67bc4222138 100644 --- a/crates/storage/db/src/implementation/mdbx/tx.rs +++ b/crates/storage/db/src/implementation/mdbx/tx.rs @@ -103,7 +103,7 @@ impl Tx { ) -> R { if let Some(mut metrics_handler) = self.metrics_handler.take() { metrics_handler.close_recorded = true; - metrics_handler.log_backtrace_on_long_transaction(); + metrics_handler.log_backtrace_on_long_read_transaction(); let start = Instant::now(); let (result, commit_latency) = f(self); @@ -135,7 +135,7 @@ impl Tx { f: impl FnOnce(&Transaction) -> R, ) -> R { if let Some(metrics_handler) = &self.metrics_handler { - metrics_handler.log_backtrace_on_long_transaction(); + metrics_handler.log_backtrace_on_long_read_transaction(); OperationMetrics::record(T::NAME, operation, value_size, || f(&self.inner)) } else { f(&self.inner) @@ -153,7 +153,7 @@ struct MetricsHandler { /// to do anything on [Drop::drop]. close_recorded: bool, /// If `true`, the backtrace of transaction has already been recorded and logged. - /// See [MetricsHandler::log_backtrace_on_long_transaction]. + /// See [MetricsHandler::log_backtrace_on_long_read_transaction]. backtrace_recorded: AtomicBool, _marker: PhantomData, } @@ -183,7 +183,7 @@ impl MetricsHandler { /// /// NOTE: Backtrace is recorded using [Backtrace::force_capture], so `RUST_BACKTRACE` env var is /// not needed. - fn log_backtrace_on_long_transaction(&self) { + fn log_backtrace_on_long_read_transaction(&self) { if !self.backtrace_recorded.load(Ordering::Relaxed) && self.transaction_mode().is_read_only() { @@ -206,7 +206,7 @@ impl MetricsHandler { impl Drop for MetricsHandler { fn drop(&mut self) { if !self.close_recorded { - self.log_backtrace_on_long_transaction(); + self.log_backtrace_on_long_read_transaction(); TransactionMetrics::record_close( self.transaction_mode(), diff --git a/crates/storage/libmdbx-rs/Cargo.toml b/crates/storage/libmdbx-rs/Cargo.toml index c6b3f4650857..9bc7a4bf71c5 100644 --- a/crates/storage/libmdbx-rs/Cargo.toml +++ b/crates/storage/libmdbx-rs/Cargo.toml @@ -20,6 +20,7 @@ byteorder = "1" derive_more = "0.99" indexmap = "2" libc = "0.2" +libffi = "3.2.0" parking_lot.workspace = true thiserror.workspace = true diff --git a/crates/storage/libmdbx-rs/src/environment.rs b/crates/storage/libmdbx-rs/src/environment.rs index 4c63e3a4aad2..b83591a583bd 100644 --- a/crates/storage/libmdbx-rs/src/environment.rs +++ b/crates/storage/libmdbx-rs/src/environment.rs @@ -6,6 +6,7 @@ use crate::{ Mode, Transaction, TransactionKind, }; use byteorder::{ByteOrder, NativeEndian}; +use ffi::{mdbx_pid_t, mdbx_tid_t, MDBX_env, MDBX_hsr_func, MDBX_txn}; use mem::size_of; use std::{ ffi::CString, @@ -48,6 +49,7 @@ impl Environment { geometry: None, log_level: None, kind: Default::default(), + handle_slow_readers: None, } } @@ -494,6 +496,90 @@ impl Default for Geometry { } } +/// Handle-Slow-Readers callback function to resolve database full/overflow issue due to a reader(s) +/// which prevents the old data from being recycled. +/// +/// Read transactions prevent reuse of pages freed by newer write transactions, thus the database +/// can grow quickly. This callback will be called when there is not enough space in the database +/// (i.e. before increasing the database size or before `MDBX_MAP_FULL` error) and thus can be +/// used to resolve issues with a "long-lived" read transacttions. +/// +/// Depending on the arguments and needs, your implementation may wait, +/// terminate a process or thread that is performing a long read, or perform +/// some other action. In doing so it is important that the returned code always +/// corresponds to the performed action. +/// +/// # Arguments +/// +/// * `process_id` – A proceess id of the reader process. +/// * `thread_id` – A thread id of the reader thread. +/// * `read_txn_id` – An oldest read transaction number on which stalled. +/// * `gap` – A lag from the last committed txn. +/// * `space` – A space that actually become available for reuse after this reader finished. The +/// callback function can take this value into account to evaluate the impact that a long-running +/// transaction has. +/// * `retry` – A retry number starting from 0. If callback has returned 0 at least once, then at +/// end of current handling loop the callback function will be called additionally with negative +/// `retry` value to notify about the end of loop. The callback function can use this fact to +/// implement timeout reset logic while waiting for a readers. +/// +/// # Returns +/// A return code that determines the further actions for MDBX and must match the action which +/// was executed by the callback: +/// * `-2` or less – An error condition and the reader was not killed. +/// * `-1` – The callback was unable to solve the problem and agreed on `MDBX_MAP_FULL` error; MDBX +/// should increase the database size or return `MDBX_MAP_FULL` error. +/// * `0` – The callback solved the problem or just waited for a while, libmdbx should rescan the +/// reader lock table and retry. This also includes a situation when corresponding transaction +/// terminated in normal way by `mdbx_txn_abort()` or `mdbx_txn_reset()`, and may be restarted. +/// I.e. reader slot isn't needed to be cleaned from transaction. +/// * `1` – Transaction aborted asynchronous and reader slot should be cleared immediately, i.e. +/// read transaction will not continue but `mdbx_txn_abort()` nor `mdbx_txn_reset()` will be +/// called later. +/// * `2` or greater – The reader process was terminated or killed, and MDBX should entirely reset +/// reader registration. +pub type HandleSlowReadersCallback = fn( + process_id: u32, + thread_id: u32, + read_txn_id: u64, + gap: usize, + space: usize, + retry: isize, +) -> HandleSlowReadersReturnCode; + +#[derive(Debug)] +pub enum HandleSlowReadersReturnCode { + /// An error condition and the reader was not killed. + Error, + /// The callback was unable to solve the problem and agreed on `MDBX_MAP_FULL` error; + /// MDBX should increase the database size or return `MDBX_MAP_FULL` error. + ProceedWithoutKillingReader, + /// The callback solved the problem or just waited for a while, libmdbx should rescan the + /// reader lock table and retry. This also includes a situation when corresponding transaction + /// terminated in normal way by `mdbx_txn_abort()` or `mdbx_txn_reset()`, and may be restarted. + /// I.e. reader slot isn't needed to be cleaned from transaction. + Success, + /// Transaction aborted asynchronous and reader slot should be cleared immediately, i.e. read + /// transaction will not continue but `mdbx_txn_abort()` nor `mdbx_txn_reset()` will be called + /// later. + ClearReaderSlot, + /// The reader process was terminated or killed, and MDBX should entirely reset reader + /// registration. + ReaderProcessTerminated, +} + +impl From for i32 { + fn from(value: HandleSlowReadersReturnCode) -> Self { + match value { + HandleSlowReadersReturnCode::Error => -2, + HandleSlowReadersReturnCode::ProceedWithoutKillingReader => -1, + HandleSlowReadersReturnCode::Success => 0, + HandleSlowReadersReturnCode::ClearReaderSlot => 1, + HandleSlowReadersReturnCode::ReaderProcessTerminated => 2, + } + } +} + /// Options for opening or creating an environment. #[derive(Debug, Clone)] pub struct EnvironmentBuilder { @@ -509,6 +595,7 @@ pub struct EnvironmentBuilder { geometry: Option, Option)>>, log_level: Option, kind: EnvironmentKind, + handle_slow_readers: Option, } impl EnvironmentBuilder { @@ -589,6 +676,13 @@ impl EnvironmentBuilder { ))?; } + if let Some(handle_slow_readers) = self.handle_slow_readers { + mdbx_result(ffi::mdbx_env_set_hsr( + env, + handle_slow_readers_callback(handle_slow_readers), + ))?; + } + #[cfg(unix)] fn path_to_bytes>(path: P) -> Vec { use std::os::unix::ffi::OsStrExt; @@ -765,8 +859,127 @@ impl EnvironmentBuilder { self } + /// Set the Handle-Slow-Readers callback. See [HandleSlowReadersCallback] for more information. + pub fn set_handle_slow_readers(&mut self, hsr: HandleSlowReadersCallback) -> &mut Self { + self.handle_slow_readers = Some(hsr); + self + } + pub fn set_log_level(&mut self, log_level: ffi::MDBX_log_level_t) -> &mut Self { self.log_level = Some(log_level); self } } + +/// Creates an instance of `MDBX_hsr_func`. +/// +/// Caution: this leaks the memory for callbacks, so they're alive throughout the program. It's +/// fine, because we also expect the database environment to be alive during this whole time. +unsafe fn handle_slow_readers_callback(callback: HandleSlowReadersCallback) -> MDBX_hsr_func { + // Move the callback function to heap and intentionally leak it, so it's not dropped and the + // MDBX env can use it throughout the whole program. + let callback = Box::leak(Box::new(callback)); + + // Wrap the callback into an ffi binding. The callback is needed for a nicer UX with Rust types, + // and without `env` and `txn` arguments that we don't want to expose to the user. Again, + // move the closure to heap and leak. + let hsr = Box::leak(Box::new( + |_env: *const MDBX_env, + _txn: *const MDBX_txn, + pid: mdbx_pid_t, + tid: mdbx_tid_t, + laggard: u64, + gap: ::libc::c_uint, + space: usize, + retry: ::libc::c_int| + -> i32 { + callback(pid as u32, tid as u32, laggard, gap as usize, space, retry as isize).into() + }, + )); + + // Create a pointer to the C function from the Rust closure, and forcefully forget the original + // closure. + let closure = libffi::high::Closure8::new(hsr); + let closure_ptr = *closure.code_ptr(); + std::mem::forget(closure); + + // Cast the closure to FFI `extern fn` type. + Some(std::mem::transmute(closure_ptr)) +} + +#[cfg(test)] +mod tests { + use crate::{Environment, Error, Geometry, HandleSlowReadersReturnCode, PageSize, WriteFlags}; + use std::{ + ops::RangeInclusive, + sync::atomic::{AtomicBool, Ordering}, + }; + + #[test] + fn test_handle_slow_readers_callback() { + static CALLED: AtomicBool = AtomicBool::new(false); + + let tempdir = tempfile::tempdir().unwrap(); + let env = Environment::builder() + .set_geometry(Geometry::> { + size: Some(0..=1024 * 1024), // Max 1MB, so we can hit the limit + page_size: Some(PageSize::MinimalAcceptable), // To create as many pages as possible + ..Default::default() + }) + .set_handle_slow_readers( + |_process_id: u32, + _thread_id: u32, + _read_txn_id: u64, + _gap: usize, + _space: usize, + _retry: isize| { + CALLED.store(true, Ordering::Relaxed); + + HandleSlowReadersReturnCode::ProceedWithoutKillingReader + }, + ) + .open(tempdir.path()) + .unwrap(); + + // Insert some data in the database, so the read transaction can lock on the snapshot of it + { + let tx = env.begin_rw_txn().unwrap(); + let db = tx.open_db(None).unwrap(); + for i in 0usize..1_000 { + tx.put(db.dbi(), i.to_le_bytes(), b"0", WriteFlags::empty()).unwrap() + } + tx.commit().unwrap(); + } + + // Create a read transaction + let _tx_ro = env.begin_ro_txn().unwrap(); + + // Change previously inserted data, so the read transaction would use the previous snapshot + { + let tx = env.begin_rw_txn().unwrap(); + let db = tx.open_db(None).unwrap(); + for i in 0usize..1_000 { + tx.put(db.dbi(), i.to_le_bytes(), b"1", WriteFlags::empty()).unwrap(); + } + tx.commit().unwrap(); + } + + // Insert more data in the database, so we hit the DB size limit error, and MDBX tries to + // kick long-lived readers and delete their snapshots + { + let tx = env.begin_rw_txn().unwrap(); + let db = tx.open_db(None).unwrap(); + for i in 1_000usize..1_000_000 { + match tx.put(db.dbi(), i.to_le_bytes(), b"0", WriteFlags::empty()) { + Ok(_) => continue, + Err(Error::MapFull) => break, + result @ Err(_) => result.unwrap(), + } + } + tx.commit().unwrap(); + } + + // Expect the HSR to be called + assert!(CALLED.load(Ordering::Relaxed)); + } +} diff --git a/crates/storage/libmdbx-rs/src/lib.rs b/crates/storage/libmdbx-rs/src/lib.rs index d988932af564..08f8164ad491 100644 --- a/crates/storage/libmdbx-rs/src/lib.rs +++ b/crates/storage/libmdbx-rs/src/lib.rs @@ -12,7 +12,8 @@ pub use crate::{ cursor::{Cursor, Iter, IterDup}, database::Database, environment::{ - Environment, EnvironmentBuilder, EnvironmentKind, Geometry, Info, PageSize, Stat, + Environment, EnvironmentBuilder, EnvironmentKind, Geometry, HandleSlowReadersCallback, + HandleSlowReadersReturnCode, Info, PageSize, Stat, }, error::{Error, Result}, flags::*, From bb7dcfaf12bc0647d7572b909b3876e5f093a5c0 Mon Sep 17 00:00:00 2001 From: Roman Krasiuk Date: Mon, 8 Jan 2024 19:39:42 +0100 Subject: [PATCH 273/277] perf(tree): cache canonical trie updates (#5871) --- Cargo.lock | 1 + crates/blockchain-tree/Cargo.toml | 1 + crates/blockchain-tree/src/blockchain_tree.rs | 62 +++++++++------- crates/blockchain-tree/src/chain.rs | 29 ++++---- crates/blockchain-tree/src/metrics.rs | 4 ++ crates/consensus/auto-seal/src/task.rs | 7 +- crates/storage/provider/src/chain.rs | 71 +++++++++++++++---- .../src/providers/database/provider.rs | 2 +- crates/trie/src/updates.rs | 2 +- 9 files changed, 127 insertions(+), 52 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index c1932d1789b9..c3fae470f2bd 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5831,6 +5831,7 @@ dependencies = [ "reth-provider", "reth-revm", "reth-stages", + "reth-trie", "tokio", "tracing", ] diff --git a/crates/blockchain-tree/Cargo.toml b/crates/blockchain-tree/Cargo.toml index fd4b97e0eda4..7565fe06d702 100644 --- a/crates/blockchain-tree/Cargo.toml +++ b/crates/blockchain-tree/Cargo.toml @@ -23,6 +23,7 @@ reth-interfaces.workspace = true reth-db.workspace = true reth-provider.workspace = true reth-stages.workspace = true +reth-trie.workspace = true # common parking_lot.workspace = true diff --git a/crates/blockchain-tree/src/blockchain_tree.rs b/crates/blockchain-tree/src/blockchain_tree.rs index c02e15d8290a..8f460a04be96 100644 --- a/crates/blockchain-tree/src/blockchain_tree.rs +++ b/crates/blockchain-tree/src/blockchain_tree.rs @@ -859,12 +859,12 @@ impl BlockchainTree { &mut self, hashes: impl IntoIterator>, ) -> RethResult<()> { - // check unconnected block buffer for childs of the canonical hashes + // check unconnected block buffer for children of the canonical hashes for added_block in hashes.into_iter() { self.try_connect_buffered_blocks(added_block.into()) } - // check unconnected block buffer for childs of the chains + // check unconnected block buffer for children of the chains let mut all_chain_blocks = Vec::new(); for (_, chain) in self.state.chains.iter() { for (&number, blocks) in chain.blocks().iter() { @@ -969,7 +969,7 @@ impl BlockchainTree { /// # Note /// /// This unwinds the database if necessary, i.e. if parts of the canonical chain have been - /// re-orged. + /// reorged. /// /// # Returns /// @@ -1160,29 +1160,43 @@ impl BlockchainTree { chain: Chain, recorder: &mut MakeCanonicalDurationsRecorder, ) -> RethResult<()> { - // Compute state root before opening write transaction. - let hashed_state = chain.state().hash_state_slow(); - let (state_root, trie_updates) = chain - .state() - .state_root_calculator( - self.externals.provider_factory.provider()?.tx_ref(), - &hashed_state, - ) - .root_with_updates() - .map_err(Into::::into)?; - let tip = chain.tip(); - if state_root != tip.state_root { - return Err(RethError::Provider(ProviderError::StateRootMismatch(Box::new( - RootMismatch { - root: GotExpected { got: state_root, expected: tip.state_root }, - block_number: tip.number, - block_hash: tip.hash, - }, - )))) - } + let (blocks, state, chain_trie_updates) = chain.into_inner(); + let hashed_state = state.hash_state_slow(); + + // Compute state root or retrieve cached trie updates before opening write transaction. + let block_hash_numbers = + blocks.iter().map(|(number, b)| (number, b.hash)).collect::>(); + let trie_updates = match chain_trie_updates { + Some(updates) => { + debug!(target: "blockchain_tree", blocks = ?block_hash_numbers, "Using cached trie updates"); + self.metrics.trie_updates_insert_cached.increment(1); + updates + } + None => { + debug!(target: "blockchain_tree", blocks = ?block_hash_numbers, "Recomputing state root for insert"); + let (state_root, trie_updates) = state + .state_root_calculator( + self.externals.provider_factory.provider()?.tx_ref(), + &hashed_state, + ) + .root_with_updates() + .map_err(Into::::into)?; + let tip = blocks.tip(); + if state_root != tip.state_root { + return Err(RethError::Provider(ProviderError::StateRootMismatch(Box::new( + RootMismatch { + root: GotExpected { got: state_root, expected: tip.state_root }, + block_number: tip.number, + block_hash: tip.hash, + }, + )))); + } + self.metrics.trie_updates_insert_recomputed.increment(1); + trie_updates + } + }; recorder.record_relative(MakeCanonicalAction::RetrieveStateTrieUpdates); - let (blocks, state) = chain.into_inner(); let provider_rw = self.externals.provider_factory.provider_rw()?; provider_rw .append_blocks_with_state( diff --git a/crates/blockchain-tree/src/chain.rs b/crates/blockchain-tree/src/chain.rs index 1af8f73cf2a5..8e192a4b25c2 100644 --- a/crates/blockchain-tree/src/chain.rs +++ b/crates/blockchain-tree/src/chain.rs @@ -21,6 +21,7 @@ use reth_provider::{ providers::BundleStateProvider, BundleStateDataProvider, BundleStateWithReceipts, Chain, ExecutorFactory, StateRootProvider, }; +use reth_trie::updates::TrieUpdates; use std::{ collections::BTreeMap, ops::{Deref, DerefMut}, @@ -84,7 +85,7 @@ impl AppendableChain { canonical_fork, }; - let bundle_state = Self::validate_and_execute( + let (bundle_state, trie_updates) = Self::validate_and_execute( block.clone(), parent_header, state_provider, @@ -94,7 +95,7 @@ impl AppendableChain { ) .map_err(|err| InsertBlockError::new(block.block.clone(), err.into()))?; - Ok(Self { chain: Chain::new(vec![block], bundle_state) }) + Ok(Self { chain: Chain::new(vec![block], bundle_state, trie_updates) }) } /// Create a new chain that forks off of the canonical chain. @@ -127,7 +128,7 @@ impl AppendableChain { ) .map_err(|err| InsertBlockError::new(block.block.clone(), err.into()))?; - Ok(Self { chain: Chain::new(vec![block], bundle_state) }) + Ok(Self { chain: Chain::new(vec![block], bundle_state, None) }) } /// Create a new chain that forks off of an existing sidechain. @@ -183,7 +184,7 @@ impl AppendableChain { state.state_mut().take_n_reverts(size - 1); // If all is okay, return new chain back. Present chain is not modified. - Ok(Self { chain: Chain::from_block(block, state) }) + Ok(Self { chain: Chain::from_block(block, state, None) }) } /// Validate and execute the given block that _extends the canonical chain_, validating its @@ -202,7 +203,7 @@ impl AppendableChain { externals: &TreeExternals, block_kind: BlockKind, block_validation_kind: BlockValidationKind, - ) -> RethResult + ) -> RethResult<(BundleStateWithReceipts, Option)> where BSDP: BundleStateDataProvider, DB: Database, @@ -227,16 +228,18 @@ impl AppendableChain { // validation was requested. if block_kind.extends_canonical_head() && block_validation_kind.is_exhaustive() { // check state root - let state_root = provider.state_root(&bundle_state)?; + let (state_root, trie_updates) = provider.state_root_with_updates(&bundle_state)?; if block.state_root != state_root { return Err(ConsensusError::BodyStateRootDiff( GotExpected { got: state_root, expected: block.state_root }.into(), ) .into()) } - } - Ok(bundle_state) + Ok((bundle_state, Some(trie_updates))) + } else { + Ok((bundle_state, None)) + } } /// Validate and execute the given sidechain block, skipping state root validation. @@ -251,14 +254,15 @@ impl AppendableChain { DB: Database, EF: ExecutorFactory, { - Self::validate_and_execute( + let (state, _) = Self::validate_and_execute( block, parent_block, bundle_state_data_provider, externals, BlockKind::ForksHistoricalBlock, BlockValidationKind::SkipStateRootValidation, - ) + )?; + Ok(state) } /// Validate and execute the given block, and append it to this chain. @@ -297,7 +301,7 @@ impl AppendableChain { canonical_fork, }; - let block_state = Self::validate_and_execute( + let (block_state, trie_updates) = Self::validate_and_execute( block.clone(), parent_block, bundle_state_data, @@ -307,7 +311,8 @@ impl AppendableChain { ) .map_err(|err| InsertBlockError::new(block.block.clone(), err.into()))?; // extend the state. - self.chain.append_block(block, block_state); + self.chain.append_block(block, block_state, trie_updates); + Ok(()) } } diff --git a/crates/blockchain-tree/src/metrics.rs b/crates/blockchain-tree/src/metrics.rs index 5db2b3cbed8b..056544354eb3 100644 --- a/crates/blockchain-tree/src/metrics.rs +++ b/crates/blockchain-tree/src/metrics.rs @@ -19,6 +19,10 @@ pub struct TreeMetrics { pub latest_reorg_depth: Gauge, /// Longest sidechain height pub longest_sidechain_height: Gauge, + /// The number of times cached trie updates were used for insert. + pub trie_updates_insert_cached: Counter, + /// The number of times trie updates were recomputed for insert. + pub trie_updates_insert_recomputed: Counter, } /// Metrics for the blockchain tree block buffer diff --git a/crates/consensus/auto-seal/src/task.rs b/crates/consensus/auto-seal/src/task.rs index 047e82be9e22..a67f1cbebd57 100644 --- a/crates/consensus/auto-seal/src/task.rs +++ b/crates/consensus/auto-seal/src/task.rs @@ -192,8 +192,11 @@ where debug!(target: "consensus::auto", header=?sealed_block_with_senders.hash(), "sending block notification"); - let chain = - Arc::new(Chain::new(vec![sealed_block_with_senders], bundle_state)); + let chain = Arc::new(Chain::new( + vec![sealed_block_with_senders], + bundle_state, + None, + )); // send block notification let _ = canon_state_notification diff --git a/crates/storage/provider/src/chain.rs b/crates/storage/provider/src/chain.rs index fd055c97bb7e..26fe84d22050 100644 --- a/crates/storage/provider/src/chain.rs +++ b/crates/storage/provider/src/chain.rs @@ -6,6 +6,7 @@ use reth_primitives::{ Address, BlockHash, BlockNumHash, BlockNumber, ForkBlock, Receipt, SealedBlock, SealedBlockWithSenders, SealedHeader, TransactionSigned, TransactionSignedEcRecovered, TxHash, }; +use reth_trie::updates::TrieUpdates; use revm::db::BundleState; use std::{borrow::Cow, collections::BTreeMap, fmt}; @@ -24,6 +25,9 @@ pub struct Chain { /// /// This state also contains the individual changes that lead to the current state. state: BundleStateWithReceipts, + /// State trie updates after block is added to the chain. + /// NOTE: Currently, trie updates are present only if the block extends canonical chain. + trie_updates: Option, } impl Chain { @@ -31,13 +35,22 @@ impl Chain { pub fn new( blocks: impl IntoIterator, state: BundleStateWithReceipts, + trie_updates: Option, ) -> Self { - Self { blocks: BTreeMap::from_iter(blocks.into_iter().map(|b| (b.number, b))), state } + Self { + blocks: BTreeMap::from_iter(blocks.into_iter().map(|b| (b.number, b))), + state, + trie_updates, + } } /// Create new Chain from a single block and its state. - pub fn from_block(block: SealedBlockWithSenders, state: BundleStateWithReceipts) -> Self { - Self::new([block], state) + pub fn from_block( + block: SealedBlockWithSenders, + state: BundleStateWithReceipts, + trie_updates: Option, + ) -> Self { + Self::new([block], state, trie_updates) } /// Get the blocks in this chain. @@ -101,8 +114,10 @@ impl Chain { /// Destructure the chain into its inner components, the blocks and the state at the tip of the /// chain. - pub fn into_inner(self) -> (ChainBlocks<'static>, BundleStateWithReceipts) { - (ChainBlocks { blocks: Cow::Owned(self.blocks) }, self.state) + pub fn into_inner( + self, + ) -> (ChainBlocks<'static>, BundleStateWithReceipts, Option) { + (ChainBlocks { blocks: Cow::Owned(self.blocks) }, self.state, self.trie_updates) } /// Destructure the chain into its inner components, the blocks and the state at the tip of the @@ -184,9 +199,15 @@ impl Chain { /// Append a single block with state to the chain. /// This method assumes that blocks attachment to the chain has already been validated. - pub fn append_block(&mut self, block: SealedBlockWithSenders, state: BundleStateWithReceipts) { + pub fn append_block( + &mut self, + block: SealedBlockWithSenders, + state: BundleStateWithReceipts, + trie_updates: Option, + ) { self.blocks.insert(block.number, block); self.state.extend(state); + self.append_trie_updates(trie_updates); } /// Merge two chains by appending the given chain into the current one. @@ -206,10 +227,23 @@ impl Chain { // Insert blocks from other chain self.blocks.extend(other.blocks); self.state.extend(other.state); + self.append_trie_updates(other.trie_updates); Ok(()) } + /// Append trie updates. + /// If existing or incoming trie updates are not set, reset as neither is valid anymore. + fn append_trie_updates(&mut self, other_trie_updates: Option) { + if let Some((trie_updates, other)) = self.trie_updates.as_mut().zip(other_trie_updates) { + // Extend trie updates. + trie_updates.extend(other.into_iter()); + } else { + // Reset trie updates as they are no longer valid. + self.trie_updates.take(); + } + } + /// Split this chain at the given block. /// /// The given block will be the last block in the first returned chain. @@ -257,12 +291,19 @@ impl Chain { let state = std::mem::take(&mut self.state); let (canonical_state, pending_state) = state.split_at(split_at); + // TODO: Currently, trie updates are reset on chain split. + // Add tests ensuring that it is valid to leave updates in the pending chain. ChainSplit::Split { canonical: Chain { state: canonical_state.expect("split in range"), blocks: self.blocks, + trie_updates: None, + }, + pending: Chain { + state: pending_state, + blocks: higher_number_blocks, + trie_updates: None, }, - pending: Chain { state: pending_state, blocks: higher_number_blocks }, } } } @@ -509,15 +550,21 @@ mod tests { let mut block_state_extended = block_state1.clone(); block_state_extended.extend(block_state2.clone()); - let chain = Chain::new(vec![block1.clone(), block2.clone()], block_state_extended); + let chain = Chain::new(vec![block1.clone(), block2.clone()], block_state_extended, None); let (split1_state, split2_state) = chain.state.clone().split_at(2); - let chain_split1 = - Chain { state: split1_state.unwrap(), blocks: BTreeMap::from([(1, block1.clone())]) }; + let chain_split1 = Chain { + state: split1_state.unwrap(), + blocks: BTreeMap::from([(1, block1.clone())]), + trie_updates: None, + }; - let chain_split2 = - Chain { state: split2_state, blocks: BTreeMap::from([(2, block2.clone())]) }; + let chain_split2 = Chain { + state: split2_state, + blocks: BTreeMap::from([(2, block2.clone())]), + trie_updates: None, + }; // return tip state assert_eq!(chain.state_at_block(block2.number), Some(chain.state.clone())); diff --git a/crates/storage/provider/src/providers/database/provider.rs b/crates/storage/provider/src/providers/database/provider.rs index 52f675873239..6532ea6fd2b4 100644 --- a/crates/storage/provider/src/providers/database/provider.rs +++ b/crates/storage/provider/src/providers/database/provider.rs @@ -2317,7 +2317,7 @@ impl BlockExecutionWriter for DatabaseProvider { } } - Ok(Chain::new(blocks, execution_state)) + Ok(Chain::new(blocks, execution_state, None)) } } diff --git a/crates/trie/src/updates.rs b/crates/trie/src/updates.rs index 1836e222df65..35fa443484c6 100644 --- a/crates/trie/src/updates.rs +++ b/crates/trie/src/updates.rs @@ -41,7 +41,7 @@ impl TrieOp { } /// The aggregation of trie updates. -#[derive(Debug, Default, Clone, Deref)] +#[derive(Debug, Default, Clone, PartialEq, Eq, Deref)] pub struct TrieUpdates { trie_operations: HashMap, } From 189cf490ebf51dc3e1bccf92000dfaa98028e53b Mon Sep 17 00:00:00 2001 From: Alexey Shekhirin Date: Mon, 8 Jan 2024 19:01:17 +0000 Subject: [PATCH 274/277] feat(storage): log tx opening location (#5973) --- bin/reth/src/cli/db_type.rs | 2 +- crates/storage/db/src/abstraction/database.rs | 3 +++ .../storage/db/src/implementation/mdbx/tx.rs | 25 ++++++++++++++++--- .../provider/src/providers/database/mod.rs | 3 +++ 4 files changed, 28 insertions(+), 5 deletions(-) diff --git a/bin/reth/src/cli/db_type.rs b/bin/reth/src/cli/db_type.rs index be0048174666..8af7eb7a5f3e 100644 --- a/bin/reth/src/cli/db_type.rs +++ b/bin/reth/src/cli/db_type.rs @@ -54,7 +54,7 @@ impl DatabaseBuilder { let db_path = data_dir.db_path(); tracing::info!(target: "reth::cli", path = ?db_path, "Opening database"); - let db = Arc::new(init_db(db_path.clone(), log_level)?); + let db = Arc::new(init_db(db_path.clone(), log_level)?.with_metrics()); Ok(DatabaseInstance::Real { db, data_dir }) } } diff --git a/crates/storage/db/src/abstraction/database.rs b/crates/storage/db/src/abstraction/database.rs index e185b4438263..51ec6f5dff21 100644 --- a/crates/storage/db/src/abstraction/database.rs +++ b/crates/storage/db/src/abstraction/database.rs @@ -16,9 +16,11 @@ pub trait Database: Send + Sync + Sealed { type TXMut: DbTxMut + DbTx + TableImporter + Send + Sync + Debug + 'static; /// Create read only transaction. + #[track_caller] fn tx(&self) -> Result; /// Create read write transaction only possible if database is open with write access. + #[track_caller] fn tx_mut(&self) -> Result; /// Takes a function and passes a read-only transaction into it, making sure it's closed in the @@ -66,6 +68,7 @@ impl Database for Arc { impl Database for &DB { type TX = ::TX; type TXMut = ::TXMut; + fn tx(&self) -> Result { ::tx(self) } diff --git a/crates/storage/db/src/implementation/mdbx/tx.rs b/crates/storage/db/src/implementation/mdbx/tx.rs index e67bc4222138..506504b795ad 100644 --- a/crates/storage/db/src/implementation/mdbx/tx.rs +++ b/crates/storage/db/src/implementation/mdbx/tx.rs @@ -13,7 +13,7 @@ use crate::{ use parking_lot::RwLock; use reth_interfaces::db::{DatabaseWriteError, DatabaseWriteOperation}; use reth_libmdbx::{ffi::DBI, CommitLatency, Transaction, TransactionKind, WriteFlags, RW}; -use reth_tracing::tracing::warn; +use reth_tracing::tracing::{debug, warn}; use std::{ backtrace::Backtrace, marker::PhantomData, @@ -49,12 +49,16 @@ impl Tx { } /// Creates new `Tx` object with a `RO` or `RW` transaction and optionally enables metrics. + #[track_caller] pub fn new_with_metrics(inner: Transaction, with_metrics: bool) -> Self { - let metrics_handler = with_metrics.then(|| { + let metrics_handler = if with_metrics { let handler = MetricsHandler::::new(inner.id()); TransactionMetrics::record_open(handler.transaction_mode()); - handler - }); + handler.log_transaction_opened(); + Some(handler) + } else { + None + }; Self { inner, db_handles: Default::default(), metrics_handler } } @@ -177,6 +181,18 @@ impl MetricsHandler { } } + /// Logs the caller location and ID of the transaction that was opened. + #[track_caller] + fn log_transaction_opened(&self) { + debug!( + target: "storage::db::mdbx", + caller = %core::panic::Location::caller(), + id = %self.txn_id, + mode = %self.transaction_mode().as_str(), + "Transaction opened", + ); + } + /// Logs the backtrace of current call if the duration that the read transaction has been open /// is more than [LONG_TRANSACTION_DURATION]. /// The backtrace is recorded and logged just once, guaranteed by `backtrace_recorded` atomic. @@ -196,6 +212,7 @@ impl MetricsHandler { target: "storage::db::mdbx", ?open_duration, ?backtrace, + %self.txn_id, "The database read transaction has been open for too long" ); } diff --git a/crates/storage/provider/src/providers/database/mod.rs b/crates/storage/provider/src/providers/database/mod.rs index 307d9cba8604..30c6e508170d 100644 --- a/crates/storage/provider/src/providers/database/mod.rs +++ b/crates/storage/provider/src/providers/database/mod.rs @@ -101,6 +101,7 @@ impl ProviderFactory { /// Returns a provider with a created `DbTx` inside, which allows fetching data from the /// database using different types of providers. Example: [`HeaderProvider`] /// [`BlockHashReader`]. This may fail if the inner read database transaction fails to open. + #[track_caller] pub fn provider(&self) -> ProviderResult> { let mut provider = DatabaseProvider::new(self.db.tx()?, self.chain_spec.clone()); @@ -115,6 +116,7 @@ impl ProviderFactory { /// data from the database using different types of providers. Example: [`HeaderProvider`] /// [`BlockHashReader`]. This may fail if the inner read/write database transaction fails to /// open. + #[track_caller] pub fn provider_rw(&self) -> ProviderResult> { let mut provider = DatabaseProvider::new_rw(self.db.tx_mut()?, self.chain_spec.clone()); @@ -126,6 +128,7 @@ impl ProviderFactory { } /// Storage provider for latest block + #[track_caller] pub fn latest(&self) -> ProviderResult { trace!(target: "providers::db", "Returning latest state provider"); Ok(Box::new(LatestStateProvider::new(self.db.tx()?))) From ae329773bceac769d845bea7196043e19b18a394 Mon Sep 17 00:00:00 2001 From: Dan Cline <6798349+Rjected@users.noreply.github.com> Date: Mon, 8 Jan 2024 17:42:49 -0500 Subject: [PATCH 275/277] chore: improve BLS constant documentation (#5976) --- crates/rpc/rpc-types/src/beacon/constants.rs | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/crates/rpc/rpc-types/src/beacon/constants.rs b/crates/rpc/rpc-types/src/beacon/constants.rs index 917a307247dc..945a4ba20450 100644 --- a/crates/rpc/rpc-types/src/beacon/constants.rs +++ b/crates/rpc/rpc-types/src/beacon/constants.rs @@ -1,4 +1,17 @@ -pub const BLS_DST: &[u8] = b"BLS_SIG_BLS12381G2_XMD:SHA-256_SSWU_RO_POP_"; +/// The Domain Separation Tag for hash_to_point in Ethereum beacon chain BLS12-381 signatures. +/// +/// This is also the name of the ciphersuite that defines beacon chain BLS signatures. +/// +/// See: +/// +/// +pub const BLS_DST_SIG: &[u8] = b"BLS_SIG_BLS12381G2_XMD:SHA-256_SSWU_RO_POP_"; + +/// The number of bytes in a BLS12-381 public key. pub const BLS_PUBLIC_KEY_BYTES_LEN: usize = 48; + +/// The number of bytes in a BLS12-381 secret key. pub const BLS_SECRET_KEY_BYTES_LEN: usize = 32; + +/// The number of bytes in a BLS12-381 signature. pub const BLS_SIGNATURE_BYTES_LEN: usize = 96; From e12277303f8ed4c40fe0833a52ab6ad94da5152c Mon Sep 17 00:00:00 2001 From: Alexey Shekhirin Date: Tue, 9 Jan 2024 10:23:11 +0000 Subject: [PATCH 276/277] docs: update MSRV to 1.75.0 (#5981) --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 8057134de465..d74df398401c 100644 --- a/README.md +++ b/README.md @@ -80,7 +80,7 @@ When updating this, also update: - .github/workflows/lint.yml --> -The Minimum Supported Rust Version (MSRV) of this project is [1.70.0](https://blog.rust-lang.org/2023/06/01/Rust-1.70.0.html). +The Minimum Supported Rust Version (MSRV) of this project is [1.75.0](https://blog.rust-lang.org/2023/12/28/Rust-1.75.0.html). See the book for detailed instructions on how to [build from source](https://paradigmxyz.github.io/reth/installation/source.html). From 8078c515c77fa6c4ce03efe397d58fe2e5c697a8 Mon Sep 17 00:00:00 2001 From: Luca Provini Date: Tue, 9 Jan 2024 12:19:41 +0100 Subject: [PATCH 277/277] Json structured logs (#5784) Co-authored-by: Alexey Shekhirin --- Cargo.lock | 28 ++++ bin/reth/src/args/log_args.rs | 157 ++++++++++------- bin/reth/src/cli/mod.rs | 58 +------ crates/tracing/Cargo.toml | 5 +- crates/tracing/src/formatter.rs | 86 ++++++++++ crates/tracing/src/layers.rs | 180 ++++++++++++++++++++ crates/tracing/src/lib.rs | 287 +++++++++++++++++++++----------- 7 files changed, 591 insertions(+), 210 deletions(-) create mode 100644 crates/tracing/src/formatter.rs create mode 100644 crates/tracing/src/layers.rs diff --git a/Cargo.lock b/Cargo.lock index c3fae470f2bd..75812e226f1d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -6733,10 +6733,13 @@ dependencies = [ name = "reth-tracing" version = "0.1.0-alpha.13" dependencies = [ + "clap", + "eyre", "rolling-file", "tracing", "tracing-appender", "tracing-journald", + "tracing-logfmt", "tracing-subscriber", ] @@ -8390,6 +8393,28 @@ dependencies = [ "tracing-core", ] +[[package]] +name = "tracing-logfmt" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "84bab42e40ace4e4ff19c92023ee1dbc1510db60976828fbbdc6994852c7d065" +dependencies = [ + "time", + "tracing", + "tracing-core", + "tracing-subscriber", +] + +[[package]] +name = "tracing-serde" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc6b213177105856957181934e4920de57730fc69bf42c37ee5bb664d406d9e1" +dependencies = [ + "serde", + "tracing-core", +] + [[package]] name = "tracing-subscriber" version = "0.3.18" @@ -8400,12 +8425,15 @@ dependencies = [ "nu-ansi-term", "once_cell", "regex", + "serde", + "serde_json", "sharded-slab", "smallvec", "thread_local", "tracing", "tracing-core", "tracing-log", + "tracing-serde", ] [[package]] diff --git a/bin/reth/src/args/log_args.rs b/bin/reth/src/args/log_args.rs index 6c4b736b44e6..6584acc071fe 100644 --- a/bin/reth/src/args/log_args.rs +++ b/bin/reth/src/args/log_args.rs @@ -1,19 +1,13 @@ //! clap [Args](clap::Args) for logging configuration. use crate::dirs::{LogsDir, PlatformPath}; -use clap::{Args, ValueEnum}; +use clap::{ArgAction, Args, ValueEnum}; use reth_tracing::{ - tracing_subscriber::{registry::LookupSpan, EnvFilter}, - BoxedLayer, FileWorkerGuard, + tracing_subscriber::filter::Directive, FileInfo, FileWorkerGuard, LayerInfo, LogFormat, + RethTracer, Tracer, }; use std::{fmt, fmt::Display}; -use tracing::Subscriber; - -/// Default [directives](reth_tracing::tracing_subscriber::filter::Directive) for [EnvFilter] which -/// disables high-frequency debug logs from `hyper` and `trust-dns` -const DEFAULT_ENV_FILTER_DIRECTIVES: [&str; 3] = - ["hyper::proto::h1=off", "trust_dns_proto=off", "atrust_dns_resolver=off"]; - +use tracing::{level_filters::LevelFilter, Level}; /// Constant to convert megabytes to bytes const MB_TO_BYTES: u64 = 1024 * 1024; @@ -21,6 +15,27 @@ const MB_TO_BYTES: u64 = 1024 * 1024; #[derive(Debug, Args)] #[command(next_help_heading = "Logging")] pub struct LogArgs { + /// The format to use for logs written to stdout. + #[arg(long = "log.stdout.format", value_name = "FORMAT", global = true, default_value_t = LogFormat::Terminal)] + pub log_stdout_format: LogFormat, + + /// The filter to use for logs written to stdout. + #[arg( + long = "log.stdout.filter", + value_name = "FILTER", + global = true, + default_value = "info" + )] + pub log_stdout_filter: String, + + /// The format to use for logs written to the log file. + #[arg(long = "log.file.format", value_name = "FORMAT", global = true, default_value_t = LogFormat::Terminal)] + pub log_file_format: LogFormat, + + /// The filter to use for logs written to the log file. + #[arg(long = "log.file.filter", value_name = "FILTER", global = true, default_value = "debug")] + pub log_file_filter: String, + /// The path to put log files in. #[arg(long = "log.file.directory", value_name = "PATH", global = true, default_value_t)] pub log_file_directory: PlatformPath, @@ -34,10 +49,6 @@ pub struct LogArgs { #[arg(long = "log.file.max-files", value_name = "COUNT", global = true, default_value_t = 5)] pub log_file_max_files: usize, - /// The filter to use for logs written to the log file. - #[arg(long = "log.file.filter", value_name = "FILTER", global = true, default_value = "debug")] - pub log_file_filter: String, - /// Write logs to journald. #[arg(long = "log.journald", global = true)] pub journald: bool, @@ -60,55 +71,50 @@ pub struct LogArgs { default_value_t = ColorMode::Always )] pub color: ColorMode, + /// The verbosity settings for the tracer. + #[clap(flatten)] + pub verbosity: Verbosity, } impl LogArgs { - /// Builds tracing layers from the current log options. - pub fn layers(&self) -> eyre::Result<(Vec>, Option)> - where - S: Subscriber, - for<'a> S: LookupSpan<'a>, - { - let mut layers = Vec::new(); - - // Function to create a new EnvFilter with environment (from `RUST_LOG` env var), default - // (from `DEFAULT_DIRECTIVES`) and additional directives. - let create_env_filter = |additional_directives: &str| -> eyre::Result { - let env_filter = EnvFilter::builder().from_env_lossy(); - - DEFAULT_ENV_FILTER_DIRECTIVES - .into_iter() - .chain(additional_directives.split(',')) - .try_fold(env_filter, |env_filter, directive| { - Ok(env_filter.add_directive(directive.parse()?)) - }) - }; - - // Create and add the journald layer if enabled + /// Creates a [LayerInfo] instance. + fn layer(&self, format: LogFormat, filter: String, use_color: bool) -> LayerInfo { + LayerInfo::new( + format, + filter, + self.verbosity.directive(), + if use_color { Some(self.color.to_string()) } else { None }, + ) + } + + /// File info from the current log options. + fn file_info(&self) -> FileInfo { + FileInfo::new( + self.log_file_directory.clone().into(), + self.log_file_max_size * MB_TO_BYTES, + self.log_file_max_files, + ) + } + + /// Initializes tracing with the configured options from cli args. + pub fn init_tracing(&self) -> eyre::Result> { + let mut tracer = RethTracer::new(); + + let stdout = self.layer(self.log_stdout_format, self.log_stdout_filter.clone(), true); + tracer = tracer.with_stdout(stdout); + if self.journald { - let journald_filter = create_env_filter(&self.journald_filter)?; - layers.push( - reth_tracing::journald(journald_filter).expect("Could not connect to journald"), - ); + tracer = tracer.with_journald(self.journald_filter.clone()); } - // Create and add the file logging layer if enabled - let file_guard = if self.log_file_max_files > 0 { - let file_filter = create_env_filter(&self.log_file_filter)?; - let (layer, guard) = reth_tracing::file( - file_filter, - &self.log_file_directory, - "reth.log", - self.log_file_max_size * MB_TO_BYTES, - self.log_file_max_files, - ); - layers.push(layer); - Some(guard) - } else { - None - }; + if self.log_file_max_files > 0 { + let info = self.file_info(); + let file = self.layer(self.log_file_format, self.log_file_filter.clone(), false); + tracer = tracer.with_file(file, info); + } - Ok((layers, file_guard)) + let guard = tracer.init()?; + Ok(guard) } } @@ -132,3 +138,42 @@ impl Display for ColorMode { } } } + +/// The verbosity settings for the cli. +#[derive(Debug, Copy, Clone, Args)] +#[command(next_help_heading = "Display")] +pub struct Verbosity { + /// Set the minimum log level. + /// + /// -v Errors + /// -vv Warnings + /// -vvv Info + /// -vvvv Debug + /// -vvvvv Traces (warning: very verbose!) + #[clap(short, long, action = ArgAction::Count, global = true, default_value_t = 3, verbatim_doc_comment, help_heading = "Display")] + verbosity: u8, + + /// Silence all log output. + #[clap(long, alias = "silent", short = 'q', global = true, help_heading = "Display")] + quiet: bool, +} + +impl Verbosity { + /// Get the corresponding [Directive] for the given verbosity, or none if the verbosity + /// corresponds to silent. + pub fn directive(&self) -> Directive { + if self.quiet { + LevelFilter::OFF.into() + } else { + let level = match self.verbosity - 1 { + 0 => Level::ERROR, + 1 => Level::WARN, + 2 => Level::INFO, + 3 => Level::DEBUG, + _ => Level::TRACE, + }; + + level.into() + } + } +} diff --git a/bin/reth/src/cli/mod.rs b/bin/reth/src/cli/mod.rs index cb631394bc4b..72cdef47afab 100644 --- a/bin/reth/src/cli/mod.rs +++ b/bin/reth/src/cli/mod.rs @@ -12,13 +12,9 @@ use crate::{ runner::CliRunner, version::{LONG_VERSION, SHORT_VERSION}, }; -use clap::{value_parser, ArgAction, Args, Parser, Subcommand}; +use clap::{value_parser, Parser, Subcommand}; use reth_primitives::ChainSpec; -use reth_tracing::{ - tracing::{metadata::LevelFilter, Level}, - tracing_subscriber::filter::Directive, - FileWorkerGuard, -}; +use reth_tracing::FileWorkerGuard; use std::sync::Arc; pub mod components; @@ -67,9 +63,6 @@ pub struct Cli { #[clap(flatten)] logs: LogArgs, - - #[clap(flatten)] - verbosity: Verbosity, } impl Cli { @@ -101,13 +94,7 @@ impl Cli { /// If file logging is enabled, this function returns a guard that must be kept alive to ensure /// that all logs are flushed to disk. pub fn init_tracing(&self) -> eyre::Result> { - let mut layers = - vec![reth_tracing::stdout(self.verbosity.directive(), &self.logs.color.to_string())]; - - let (additional_layers, guard) = self.logs.layers()?; - layers.extend(additional_layers); - - reth_tracing::init(layers); + let guard = self.logs.init_tracing()?; Ok(guard) } @@ -173,45 +160,6 @@ impl Commands { } } -/// The verbosity settings for the cli. -#[derive(Debug, Copy, Clone, Args)] -#[command(next_help_heading = "Display")] -pub struct Verbosity { - /// Set the minimum log level. - /// - /// -v Errors - /// -vv Warnings - /// -vvv Info - /// -vvvv Debug - /// -vvvvv Traces (warning: very verbose!) - #[clap(short, long, action = ArgAction::Count, global = true, default_value_t = 3, verbatim_doc_comment, help_heading = "Display")] - verbosity: u8, - - /// Silence all log output. - #[clap(long, alias = "silent", short = 'q', global = true, help_heading = "Display")] - quiet: bool, -} - -impl Verbosity { - /// Get the corresponding [Directive] for the given verbosity, or none if the verbosity - /// corresponds to silent. - pub fn directive(&self) -> Directive { - if self.quiet { - LevelFilter::OFF.into() - } else { - let level = match self.verbosity - 1 { - 0 => Level::ERROR, - 1 => Level::WARN, - 2 => Level::INFO, - 3 => Level::DEBUG, - _ => Level::TRACE, - }; - - format!("{level}").parse().unwrap() - } - } -} - #[cfg(test)] mod tests { use clap::CommandFactory; diff --git a/crates/tracing/Cargo.toml b/crates/tracing/Cargo.toml index 4ed18c307ad9..59631365d603 100644 --- a/crates/tracing/Cargo.toml +++ b/crates/tracing/Cargo.toml @@ -13,7 +13,10 @@ workspace = true [dependencies] tracing.workspace = true -tracing-subscriber = { version = "0.3", default-features = false, features = ["env-filter", "fmt"] } +tracing-subscriber = { version = "0.3", default-features = false, features = ["env-filter", "fmt", "json"] } tracing-appender.workspace = true tracing-journald = "0.3" +tracing-logfmt = "0.3.3" rolling-file = "0.2.0" +eyre.workspace = true +clap = { workspace = true, features = ["derive"] } \ No newline at end of file diff --git a/crates/tracing/src/formatter.rs b/crates/tracing/src/formatter.rs new file mode 100644 index 000000000000..ffd89903d1f3 --- /dev/null +++ b/crates/tracing/src/formatter.rs @@ -0,0 +1,86 @@ +use crate::layers::BoxedLayer; +use clap::ValueEnum; +use std::{fmt, fmt::Display}; +use tracing_appender::non_blocking::NonBlocking; +use tracing_subscriber::{EnvFilter, Layer, Registry}; + +/// Represents the logging format. +/// +/// This enum defines the supported formats for logging output. +/// It is used to configure the format layer of a tracing subscriber. +#[derive(Debug, Copy, Clone, ValueEnum, Eq, PartialEq)] +pub enum LogFormat { + /// Represents JSON formatting for logs. + /// This format outputs log records as JSON objects, + /// making it suitable for structured logging. + Json, + + /// Represents logfmt (key=value) formatting for logs. + /// This format is concise and human-readable, + /// typically used in command-line applications. + LogFmt, + + /// Represents terminal-friendly formatting for logs. + Terminal, +} + +impl LogFormat { + /// Applies the specified logging format to create a new layer. + /// + /// This method constructs a tracing layer with the selected format, + /// along with additional configurations for filtering and output. + /// + /// # Arguments + /// * `filter` - An `EnvFilter` used to determine which log records to output. + /// * `color` - An optional string that enables or disables ANSI color codes in the logs. + /// * `file_writer` - An optional `NonBlocking` writer for directing logs to a file. + /// + /// # Returns + /// A `BoxedLayer` that can be added to a tracing subscriber. + pub fn apply( + &self, + filter: EnvFilter, + color: Option, + file_writer: Option, + ) -> BoxedLayer { + let ansi = if let Some(color) = color { + std::env::var("RUST_LOG_STYLE").map(|val| val != "never").unwrap_or(color != "never") + } else { + false + }; + let target = std::env::var("RUST_LOG_TARGET").map(|val| val != "0").unwrap_or(true); + + match self { + LogFormat::Json => { + let layer = + tracing_subscriber::fmt::layer().json().with_ansi(ansi).with_target(target); + + if let Some(writer) = file_writer { + layer.with_writer(writer).with_filter(filter).boxed() + } else { + layer.with_filter(filter).boxed() + } + } + LogFormat::LogFmt => tracing_logfmt::layer().with_filter(filter).boxed(), + LogFormat::Terminal => { + let layer = tracing_subscriber::fmt::layer().with_ansi(ansi).with_target(target); + + if let Some(writer) = file_writer { + layer.with_writer(writer).with_filter(filter).boxed() + } else { + layer.with_filter(filter).boxed() + } + } + } + } +} + +impl Display for LogFormat { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + LogFormat::Json => write!(f, "json"), + LogFormat::LogFmt => write!(f, "logfmt"), + LogFormat::Terminal => write!(f, "terminal"), + } + } +} diff --git a/crates/tracing/src/layers.rs b/crates/tracing/src/layers.rs new file mode 100644 index 000000000000..bcaef640866e --- /dev/null +++ b/crates/tracing/src/layers.rs @@ -0,0 +1,180 @@ +use std::path::{Path, PathBuf}; + +use rolling_file::{RollingConditionBasic, RollingFileAppender}; +use tracing_appender::non_blocking::WorkerGuard; +use tracing_subscriber::{filter::Directive, EnvFilter, Layer, Registry}; + +use crate::formatter::LogFormat; + +/// A worker guard returned by the file layer. +/// +/// When a guard is dropped, all events currently in-memory are flushed to the log file this guard +/// belongs to. +pub type FileWorkerGuard = tracing_appender::non_blocking::WorkerGuard; + +/// A boxed tracing [Layer]. +pub(crate) type BoxedLayer = Box + Send + Sync>; + +const RETH_LOG_FILE_NAME: &str = "reth.log"; + +/// Default [directives](Directive) for [EnvFilter] which disables high-frequency debug logs from +/// `hyper` and `trust-dns` +const DEFAULT_ENV_FILTER_DIRECTIVES: [&str; 3] = + ["hyper::proto::h1=off", "trust_dns_proto=off", "trust_dns_resolver=off"]; + +/// Manages the collection of layers for a tracing subscriber. +/// +/// `Layers` acts as a container for different logging layers such as stdout, file, or journald. +/// Each layer can be configured separately and then combined into a tracing subscriber. +pub(crate) struct Layers { + inner: Vec>, +} + +impl Layers { + /// Creates a new `Layers` instance. + pub(crate) fn new() -> Self { + Self { inner: vec![] } + } + + /// Consumes the `Layers` instance, returning the inner vector of layers. + pub(crate) fn into_inner(self) -> Vec> { + self.inner + } + + /// Adds a journald layer to the layers collection. + /// + /// # Arguments + /// * `filter` - A string containing additional filter directives for this layer. + /// + /// # Returns + /// An `eyre::Result<()>` indicating the success or failure of the operation. + pub(crate) fn journald(&mut self, filter: &str) -> eyre::Result<()> { + let journald_filter = build_env_filter(None, filter)?; + let layer = tracing_journald::layer()?.with_filter(journald_filter).boxed(); + self.inner.push(layer); + Ok(()) + } + + /// Adds a stdout layer with specified formatting and filtering. + /// + /// # Type Parameters + /// * `S` - The type of subscriber that will use these layers. + /// + /// # Arguments + /// * `format` - The log message format. + /// * `directive` - Directive for the default logging level. + /// * `filter` - Additional filter directives as a string. + /// * `color` - Optional color configuration for the log messages. + /// + /// # Returns + /// An `eyre::Result<()>` indicating the success or failure of the operation. + pub(crate) fn stdout( + &mut self, + format: LogFormat, + directive: Directive, + filter: &str, + color: Option, + ) -> eyre::Result<()> { + let filter = build_env_filter(Some(directive), filter)?; + let layer = format.apply(filter, color, None); + self.inner.push(layer.boxed()); + Ok(()) + } + + /// Adds a file logging layer to the layers collection. + /// + /// # Arguments + /// * `format` - The format for log messages. + /// * `filter` - Additional filter directives as a string. + /// * `file_info` - Information about the log file including path and rotation strategy. + /// + /// # Returns + /// An `eyre::Result` representing the file logging worker. + pub(crate) fn file( + &mut self, + format: LogFormat, + filter: &str, + file_info: FileInfo, + ) -> eyre::Result { + let (writer, guard) = file_info.create_log_writer(); + let file_filter = build_env_filter(None, filter)?; + let layer = format.apply(file_filter, None, Some(writer)); + self.inner.push(layer); + Ok(guard) + } +} + +/// Holds configuration information for file logging. +/// +/// Contains details about the log file's path, name, size, and rotation strategy. +#[derive(Debug, Clone)] +pub struct FileInfo { + dir: PathBuf, + file_name: String, + max_size_bytes: u64, + max_files: usize, +} + +impl FileInfo { + /// Creates a new `FileInfo` instance. + pub fn new(dir: PathBuf, max_size_bytes: u64, max_files: usize) -> Self { + Self { dir, file_name: RETH_LOG_FILE_NAME.to_string(), max_size_bytes, max_files } + } + + /// Creates the log directory if it doesn't exist. + /// + /// # Returns + /// A reference to the path of the log directory. + fn create_log_dir(&self) -> &Path { + let log_dir: &Path = self.dir.as_ref(); + if !log_dir.exists() { + std::fs::create_dir_all(log_dir).expect("Could not create log directory"); + } + log_dir + } + + /// Creates a non-blocking writer for the log file. + /// + /// # Returns + /// A tuple containing the non-blocking writer and its associated worker guard. + fn create_log_writer(&self) -> (tracing_appender::non_blocking::NonBlocking, WorkerGuard) { + let log_dir = self.create_log_dir(); + let (writer, guard) = tracing_appender::non_blocking( + RollingFileAppender::new( + log_dir.join(&self.file_name), + RollingConditionBasic::new().max_size(self.max_size_bytes), + self.max_files, + ) + .expect("Could not initialize file logging"), + ); + (writer, guard) + } +} + +/// Builds an environment filter for logging. +/// +/// The events are filtered by `default_directive`, unless overridden by `RUST_LOG`. +/// +/// # Arguments +/// * `default_directive` - An optional `Directive` that sets the default directive. +/// * `directives` - Additional directives as a comma-separated string. +/// +/// # Returns +/// An `eyre::Result` that can be used to configure a tracing subscriber. +fn build_env_filter( + default_directive: Option, + directives: &str, +) -> eyre::Result { + let env_filter = if let Some(default_directive) = default_directive { + EnvFilter::builder().with_default_directive(default_directive).from_env_lossy() + } else { + EnvFilter::builder().from_env_lossy() + }; + + DEFAULT_ENV_FILTER_DIRECTIVES + .into_iter() + .chain(directives.split(',')) + .try_fold(env_filter, |env_filter, directive| { + Ok(env_filter.add_directive(directive.parse()?)) + }) +} diff --git a/crates/tracing/src/lib.rs b/crates/tracing/src/lib.rs index 561991dad9fd..b49680f9a243 100644 --- a/crates/tracing/src/lib.rs +++ b/crates/tracing/src/lib.rs @@ -1,12 +1,39 @@ -//! Reth tracing subscribers and utilities. +//! The `tracing` module provides functionalities for setting up and configuring logging. //! -//! Contains a standardized set of layers: +//! It includes structures and functions to create and manage various logging layers: stdout, +//! file, or journald. The module's primary entry point is the `Tracer` struct, which can be +//! configured to use different logging formats and destinations. If no layer is specified, it will +//! default to stdout. //! -//! - [`stdout()`] -//! - [`file()`] -//! - [`journald()`] +//! # Examples //! -//! As well as a simple way to initialize a subscriber: [`init`]. +//! Basic usage: +//! +//! ``` +//! use reth_tracing::{ +//! LayerInfo, RethTracer, Tracer, +//! tracing::level_filters::LevelFilter, +//! LogFormat, +//! }; +//! +//! fn main() -> eyre::Result<()> { +//! let tracer = RethTracer::new().with_stdout(LayerInfo::new( +//! LogFormat::Json, +//! "debug".to_string(), +//! LevelFilter::INFO.into(), +//! None, +//! )); +//! +//! tracer.init()?; +//! +//! // Your application logic here +//! +//! Ok(()) +//! } +//! ``` +//! +//! This example sets up a tracer with JSON format logging for journald and terminal-friendly +//! format for file logging. #![doc( html_logo_url = "https://raw.githubusercontent.com/paradigmxyz/reth/main/assets/reth-docs.png", @@ -15,120 +42,184 @@ )] #![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))] -use rolling_file::{RollingConditionBasic, RollingFileAppender}; -use std::path::Path; -use tracing::Subscriber; -use tracing_subscriber::{ - filter::Directive, prelude::*, registry::LookupSpan, EnvFilter, Layer, Registry, -}; +use tracing_subscriber::{filter::Directive, EnvFilter}; // Re-export tracing crates pub use tracing; pub use tracing_subscriber; -/// A boxed tracing [Layer]. -pub type BoxedLayer = Box + Send + Sync>; +// Re-export LogFormat +pub use formatter::LogFormat; +pub use layers::{FileInfo, FileWorkerGuard}; -/// Initializes a new [Subscriber] based on the given layers. -pub fn init(layers: Vec>) { - // To avoid panicking in tests, we silently fail if we cannot initialize the subscriber. - let _ = tracing_subscriber::registry().with(layers).try_init(); -} +mod formatter; +mod layers; -/// Builds a new tracing layer that writes to stdout. -/// -/// The events are filtered by `default_directive`, unless overridden by `RUST_LOG`. +use crate::layers::Layers; +use tracing::level_filters::LevelFilter; +use tracing_appender::non_blocking::WorkerGuard; +use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt}; + +/// Tracer for application logging. /// -/// Colors can be disabled with `RUST_LOG_STYLE=never`, and event targets can be displayed with -/// `RUST_LOG_TARGET=1`. -pub fn stdout(default_directive: impl Into, color: &str) -> BoxedLayer -where - S: Subscriber, - for<'a> S: LookupSpan<'a>, -{ - // TODO: Auto-detect - let with_ansi = - std::env::var("RUST_LOG_STYLE").map(|val| val != "never").unwrap_or(color != "never"); - let with_target = std::env::var("RUST_LOG_TARGET").map(|val| val != "0").unwrap_or(true); - - let filter = - EnvFilter::builder().with_default_directive(default_directive.into()).from_env_lossy(); - - tracing_subscriber::fmt::layer() - .with_ansi(with_ansi) - .with_target(with_target) - .with_filter(filter) - .boxed() +/// Manages the configuration and initialization of logging layers, +/// including standard output, optional journald, and optional file logging. +#[derive(Debug, Clone)] +pub struct RethTracer { + stdout: LayerInfo, + journald: Option, + file: Option<(LayerInfo, FileInfo)>, } -/// Builds a new tracing layer that appends to a log file. -/// -/// The events are filtered by `filter`. -/// -/// The boxed layer and a guard is returned. When the guard is dropped the buffer for the log -/// file is immediately flushed to disk. Any events after the guard is dropped may be missed. -#[must_use = "tracing guard must be kept alive to flush events to disk"] -pub fn file( - filter: EnvFilter, - dir: impl AsRef, - file_name: impl AsRef, - max_size_bytes: u64, - max_files: usize, -) -> (BoxedLayer, tracing_appender::non_blocking::WorkerGuard) -where - S: Subscriber, - for<'a> S: LookupSpan<'a>, -{ - // Create log dir if it doesn't exist (RFA doesn't do this for us) - let log_dir = dir.as_ref(); - if !log_dir.exists() { - std::fs::create_dir_all(log_dir).expect("Could not create log directory"); +impl RethTracer { + /// Constructs a new `Tracer` with default settings. + /// + /// Initializes with default stdout layer configuration. + /// Journald and file layers are not set by default. + pub fn new() -> Self { + Self { stdout: LayerInfo::default(), journald: None, file: None } + } + + /// Sets a custom configuration for the stdout layer. + /// + /// # Arguments + /// * `config` - The `LayerInfo` to use for the stdout layer. + pub fn with_stdout(mut self, config: LayerInfo) -> Self { + self.stdout = config; + self } - // Create layer - let (writer, guard) = tracing_appender::non_blocking( - RollingFileAppender::new( - log_dir.join(file_name.as_ref()), - RollingConditionBasic::new().max_size(max_size_bytes), - max_files, - ) - .expect("Could not initialize file logging"), - ); - let layer = tracing_subscriber::fmt::layer() - .with_ansi(false) - .with_writer(writer) - .with_filter(filter) - .boxed(); - - (layer, guard) + /// Sets the journald layer filter. + /// + /// # Arguments + /// * `filter` - The `filter` to use for the journald layer. + pub fn with_journald(mut self, filter: String) -> Self { + self.journald = Some(filter); + self + } + + /// Sets the file layer configuration and associated file info. + /// + /// # Arguments + /// * `config` - The `LayerInfo` to use for the file layer. + /// * `file_info` - The `FileInfo` containing details about the log file. + pub fn with_file(mut self, config: LayerInfo, file_info: FileInfo) -> Self { + self.file = Some((config, file_info)); + self + } } -/// A worker guard returned by [`file()`]. -/// -/// When a guard is dropped, all events currently in-memory are flushed to the log file this guard -/// belongs to. -pub type FileWorkerGuard = tracing_appender::non_blocking::WorkerGuard; +impl Default for RethTracer { + fn default() -> Self { + Self::new() + } +} -/// Builds a new tracing layer that writes events to journald. +/// Configuration for a logging layer. /// -/// The events are filtered by `filter`. +/// This struct holds configuration parameters for a tracing layer, including +/// the format, filtering directives, optional coloring, and directive. +#[derive(Debug, Clone)] +pub struct LayerInfo { + format: LogFormat, + filters: String, + directive: Directive, + color: Option, +} + +impl LayerInfo { + /// Constructs a new `LayerInfo`. + /// + /// # Arguments + /// * `format` - Specifies the format for log messages. Possible values are: + /// - `LogFormat::Json` for JSON formatting. + /// - `LogFormat::LogFmt` for logfmt (key=value) formatting. + /// - `LogFormat::Terminal` for human-readable, terminal-friendly formatting. + /// * `filters` - Additional filtering parameters as a string. + /// * `directive` - Directive for filtering log messages. + /// * `color` - Optional color configuration for the log messages. + pub fn new( + format: LogFormat, + filters: String, + directive: Directive, + color: Option, + ) -> Self { + Self { format, directive, filters, color } + } +} + +impl Default for LayerInfo { + /// Provides default values for `LayerInfo`. + /// + /// By default, it uses terminal format, INFO level filter, + /// no additional filters, and no color configuration. + fn default() -> Self { + Self { + format: LogFormat::Terminal, + directive: LevelFilter::INFO.into(), + filters: "debug".to_string(), + color: Some("always".to_string()), + } + } +} + +/// Trait defining a general interface for logging configuration. /// -/// If the layer cannot connect to journald for any reason this function will return an error. -pub fn journald(filter: EnvFilter) -> std::io::Result> -where - S: Subscriber, - for<'a> S: LookupSpan<'a>, -{ - Ok(tracing_journald::layer()?.with_filter(filter).boxed()) +/// The `Tracer` trait provides a standardized way to initialize logging configurations +/// in an application. Implementations of this trait can specify different logging setups, +/// such as standard output logging, file logging, journald logging, or custom logging +/// configurations tailored for specific environments (like testing). +pub trait Tracer { + /// Initialize the logging configuration. + /// # Returns + /// An `eyre::Result` which is `Ok` with an optional `WorkerGuard` if a file layer is used, + /// or an `Err` in case of an error during initialization. + fn init(self) -> eyre::Result>; +} + +impl Tracer for RethTracer { + /// Initializes the logging system based on the configured layers. + /// + /// This method sets up the global tracing subscriber with the specified + /// stdout, journald, and file layers. + /// + /// The default layer is stdout. + /// + /// # Returns + /// An `eyre::Result` which is `Ok` with an optional `WorkerGuard` if a file layer is used, + /// or an `Err` in case of an error during initialization. + fn init(self) -> eyre::Result> { + let mut layers = Layers::new(); + + layers.stdout( + self.stdout.format, + self.stdout.directive, + &self.stdout.filters, + self.stdout.color, + )?; + + if let Some(config) = self.journald { + layers.journald(&config)?; + } + + let file_guard = if let Some((config, file_info)) = self.file { + Some(layers.file(config.format, &config.filters, file_info)?) + } else { + None + }; + + tracing_subscriber::registry().with(layers.into_inner()).init(); + Ok(file_guard) + } } -/// Initializes a tracing subscriber for tests. +/// Initializes a tracing subscriber for tests. /// -/// The filter is configurable via `RUST_LOG`. +/// The filter is configurable via `RUST_LOG`. /// -/// # Note +/// # Note /// -/// The subscriber will silently fail if it could not be installed. +/// The subscriber will silently fail if it could not be installed. pub fn init_test_tracing() { let _ = tracing_subscriber::fmt() .with_env_filter(EnvFilter::from_default_env())