Compare commits

..

29 Commits

Author SHA1 Message Date
yongkangc
5433e6dd8e refactor(storage): introduce RocksDBWriteMode for flexible write strategies
- Replaced RocksTx with RocksDBWriteMode in EitherWriter to support both transaction and batch writes.
- Updated constructors and methods to utilize RocksDBWriteMode, enhancing write flexibility.
- Added batch handling capabilities in RocksDBProvider, allowing manual commit of batch operations.
- Improved documentation for clarity on usage and commit behavior.
2025-12-15 05:10:02 +00:00
github-actions[bot]
679234f105 chore(deps): weekly cargo update (#20359)
Co-authored-by: github-merge-queue <118344674+github-merge-queue@users.noreply.github.com>
2025-12-14 20:54:42 +00:00
phrwlk
419c7b489b fix(rpc): remove dead flashbots module config (#20364) 2025-12-14 20:54:15 +00:00
Rej Ect
06dac07b5f ci(hive): bump actions/cache to v5 (#20349) 2025-12-13 09:04:07 +00:00
YK
5621132b8b feat: add RocksDB variant to EitherReader and EitherWriter (#20288) 2025-12-13 04:06:44 +00:00
Matthias Seitz
3380eb69c8 fix: only collect already tracked accounts (#20341) 2025-12-12 22:09:21 +00:00
Arsenii Kulikov
0366497ada perf: skip redundant recovery (#20343) 2025-12-12 22:01:05 +00:00
Alexey Shekhirin
cd71f3d5a4 feat(engine): record total latencies on instrumented state provider drop (#20337) 2025-12-12 21:14:44 +00:00
Alexey Shekhirin
64909d33e6 feat(engine): cli argument to disable state cache (#20143) 2025-12-12 17:51:22 +00:00
Alexey Shekhirin
3c9ad31344 chore(engine): make InstrumentedStateProvider public (#20335) 2025-12-12 16:41:42 +00:00
gustavo
f3e14fd061 feat(rpc): handle dedicated eth_simulate errors (#20099) 2025-12-12 16:40:13 +00:00
Alexey Shekhirin
daf6b88dc6 feat(node): engine args defaults (#20203) 2025-12-12 15:54:05 +00:00
emmmm
d2d58f9a0e docs: add missing RPC namespaces to JSON-RPC intro (#20321) 2025-12-12 15:40:38 +00:00
Matthias Seitz
ace4e515b5 chore: bump inspectors 0.33.2 (#20334) 2025-12-12 15:39:04 +00:00
Hesham Shabanah
134164954b feat: add --max-peers CLI flag (#20139)
Co-authored-by: Matthias Seitz <matthias.seitz@outlook.de>
2025-12-12 13:26:44 +00:00
Lorsmirq Benton
2775dd1f23 docs: correct comments in custom-inspector (#20304) 2025-12-12 13:21:03 +00:00
Alexey Shekhirin
ac0f9687bd chore(engine): move noisy multiproof debug logs to trace level (#20331) 2025-12-12 13:01:01 +00:00
Arsenii Kulikov
a9c21a395d perf: spawn rpc handlers as blocking (#20330) 2025-12-12 12:15:02 +00:00
Federico Magnani
df7ad9ae45 chore(ethapi): increase visibility tx_batch_sender (#20315)
Co-authored-by: Matthias Seitz <matthias.seitz@outlook.de>
2025-12-12 12:14:43 +00:00
sashass1315
5903e42a98 docs: refresh repo layout crate lists (#20319) 2025-12-12 10:59:57 +00:00
Matthias Seitz
3c41b99599 chore: lower block buffer size (#20324) 2025-12-12 08:15:54 +00:00
pepes
d70d80fff1 fix(docs): document discv5 discovery port 9200 (#20322) 2025-12-12 08:12:08 +00:00
gustavo
ed3a8a03d5 feat(node-core): make rpc server args customizable (#20312) 2025-12-11 23:24:31 +00:00
YK
bfcd46d01d feat: add account_history_in_rocksdb field to StorageSettings (#20282) 2025-12-11 19:37:36 +00:00
Brian Picciano
194d545fae feat(engine): Add BAL stub methods to ExecutionPayload and BlockOrPayload (#20311)
Co-authored-by: Matthias Seitz <matthias.seitz@outlook.de>
2025-12-11 19:07:43 +00:00
sashass1315
97243ec1f4 docs: fix misleading links (#20300) 2025-12-11 18:49:18 +00:00
DaniPopes
93c1b0f52f ci: add more sccache (#20316) 2025-12-11 18:46:11 +00:00
Arsenii Kulikov
474c09095f feat: bump alloy-evm (#20314) 2025-12-11 19:46:34 +01:00
Matthias Seitz
24c298133f feat: allow larger ws frames on client side (#20307) 2025-12-11 16:43:10 +00:00
64 changed files with 2326 additions and 334 deletions

View File

@@ -11,6 +11,7 @@ env:
CARGO_TERM_COLOR: always
BASELINE: base
SEED: reth
RUSTC_WRAPPER: "sccache"
name: bench
jobs:
@@ -22,6 +23,7 @@ jobs:
submodules: true
- uses: rui314/setup-mold@v1
- uses: dtolnay/rust-toolchain@stable
- uses: mozilla-actions/sccache-action@v0.0.9
- uses: Swatinem/rust-cache@v2
with:
cache-on-failure: true

View File

@@ -10,6 +10,9 @@ on:
types: [opened, reopened, synchronize, closed]
merge_group:
env:
RUSTC_WRAPPER: "sccache"
jobs:
build:
runs-on: depot-ubuntu-latest-8
@@ -33,6 +36,8 @@ jobs:
- name: Install Rust nightly
uses: dtolnay/rust-toolchain@nightly
- uses: mozilla-actions/sccache-action@v0.0.9
- name: Build docs
run: cd docs/vocs && bash scripts/build-cargo-docs.sh

View File

@@ -13,6 +13,7 @@ on:
env:
CARGO_TERM_COLOR: always
RUSTC_WRAPPER: "sccache"
name: compact-codec
jobs:
@@ -26,6 +27,7 @@ jobs:
steps:
- uses: rui314/setup-mold@v1
- uses: dtolnay/rust-toolchain@stable
- uses: mozilla-actions/sccache-action@v0.0.9
- uses: Swatinem/rust-cache@v2
with:
cache-on-failure: true

View File

@@ -11,6 +11,7 @@ on:
env:
CARGO_TERM_COLOR: always
SEED: rustethereumethereumrust
RUSTC_WRAPPER: "sccache"
concurrency:
group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }}
@@ -26,6 +27,7 @@ jobs:
steps:
- uses: actions/checkout@v6
- uses: dtolnay/rust-toolchain@stable
- uses: mozilla-actions/sccache-action@v0.0.9
- uses: taiki-e/install-action@nextest
- uses: Swatinem/rust-cache@v2
with:

View File

@@ -44,7 +44,7 @@ jobs:
- name: Restore hive assets cache
id: cache-hive
uses: actions/cache@v4
uses: actions/cache@v5
with:
path: ./hive_assets
key: hive-assets-${{ steps.hive-commit.outputs.hash }}-${{ hashFiles('.github/assets/hive/build_simulators.sh') }}

View File

@@ -22,6 +22,7 @@ env:
CARGO_TERM_COLOR: always
DOCKER_IMAGE_NAME_URL: https://ghcr.io/${{ github.repository_owner }}/reth
DOCKER_OP_IMAGE_NAME_URL: https://ghcr.io/${{ github.repository_owner }}/op-reth
RUSTC_WRAPPER: "sccache"
jobs:
dry-run:
@@ -51,6 +52,7 @@ jobs:
steps:
- uses: actions/checkout@v6
- uses: dtolnay/rust-toolchain@stable
- uses: mozilla-actions/sccache-action@v0.0.9
- name: Verify crate version matches tag
# Check that the Cargo version starts with the tag,
# so that Cargo version 1.4.8 can be matched against both v1.4.8 and v1.4.8-rc.1
@@ -104,6 +106,7 @@ jobs:
- uses: dtolnay/rust-toolchain@stable
with:
target: ${{ matrix.configs.target }}
- uses: mozilla-actions/sccache-action@v0.0.9
- name: Install cross main
id: cross_main
run: |

View File

@@ -9,6 +9,7 @@ on:
env:
CARGO_TERM_COLOR: always
RUSTC_WRAPPER: "sccache"
concurrency:
group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }}
@@ -41,6 +42,7 @@ jobs:
- uses: actions/checkout@v6
- uses: rui314/setup-mold@v1
- uses: dtolnay/rust-toolchain@stable
- uses: mozilla-actions/sccache-action@v0.0.9
- uses: Swatinem/rust-cache@v2
with:
cache-on-failure: true

View File

@@ -9,6 +9,7 @@ on:
env:
CARGO_TERM_COLOR: always
RUSTC_WRAPPER: "sccache"
concurrency:
group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }}
@@ -41,6 +42,7 @@ jobs:
- uses: actions/checkout@v6
- uses: rui314/setup-mold@v1
- uses: dtolnay/rust-toolchain@stable
- uses: mozilla-actions/sccache-action@v0.0.9
- uses: Swatinem/rust-cache@v2
with:
cache-on-failure: true

69
Cargo.lock generated
View File

@@ -97,9 +97,9 @@ checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923"
[[package]]
name = "alloy-chains"
version = "0.2.21"
version = "0.2.23"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1b9ebac8ff9c2f07667e1803dc777304337e160ce5153335beb45e8ec0751808"
checksum = "35d744058a9daa51a8cf22a3009607498fcf82d3cf4c5444dd8056cdf651f471"
dependencies = [
"alloy-primitives",
"alloy-rlp",
@@ -238,6 +238,18 @@ dependencies = [
"thiserror 2.0.17",
]
[[package]]
name = "alloy-eip7928"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "926b2c0d34e641cf8b17bf54ce50fda16715b9f68ad878fa6128bae410c6f890"
dependencies = [
"alloy-primitives",
"alloy-rlp",
"borsh",
"serde",
]
[[package]]
name = "alloy-eips"
version = "1.1.3"
@@ -266,9 +278,9 @@ dependencies = [
[[package]]
name = "alloy-evm"
version = "0.25.0"
version = "0.25.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "70cd39002a40b8d528f4a3f8ecc7e59dc2204d9bfae249296d7d379f291f9cba"
checksum = "e6ccc4c702c840148af1ce784cc5c6ed9274a020ef32417c5b1dbeab8c317673"
dependencies = [
"alloy-consensus",
"alloy-eips",
@@ -383,9 +395,9 @@ dependencies = [
[[package]]
name = "alloy-op-evm"
version = "0.25.0"
version = "0.25.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6bec5d35135e72ab8096f9b7713840725dc0cd0f9e20ed283156808874da76f4"
checksum = "0f640da852f93ddaa3b9a602b7ca41d80e0023f77a67b68aaaf511c32f1fe0ce"
dependencies = [
"alloy-consensus",
"alloy-eips",
@@ -1369,9 +1381,9 @@ dependencies = [
[[package]]
name = "async-compression"
version = "0.4.34"
version = "0.4.36"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0e86f6d3dc9dc4352edeea6b8e499e13e3f5dc3b964d7ca5fd411415a3498473"
checksum = "98ec5f6c2f8bc326c994cb9e241cc257ddaba9afa8555a43cffbb5dd86efaa37"
dependencies = [
"compression-codecs",
"compression-core",
@@ -1529,9 +1541,9 @@ checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6"
[[package]]
name = "base64ct"
version = "1.8.0"
version = "1.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "55248b47b0caf0546f7988906588779981c43bb1bc9d0c44087278f80cdb44ba"
checksum = "0e050f626429857a27ddccb31e0aca21356bfa709c04041aefddac081a8f068a"
[[package]]
name = "bech32"
@@ -2377,9 +2389,9 @@ dependencies = [
[[package]]
name = "compression-codecs"
version = "0.4.33"
version = "0.4.35"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "302266479cb963552d11bd042013a58ef1adc56768016c8b82b4199488f2d4ad"
checksum = "b0f7ac3e5b97fdce45e8922fb05cae2c37f7bbd63d30dd94821dacfd8f3f2bf2"
dependencies = [
"brotli",
"compression-core",
@@ -3993,9 +4005,9 @@ checksum = "1d674e81391d1e1ab681a28d99df07927c6d4aa5b027d7da16ba32d1d21ecd99"
[[package]]
name = "flate2"
version = "1.1.7"
version = "1.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a2152dbcb980c05735e2a651d96011320a949eb31a0c8b38b72645ce97dec676"
checksum = "bfe33edd8e85a12a67454e37f8c75e730830d83e313556ab9ebf9ee7fbeb3bfb"
dependencies = [
"crc32fast",
"miniz_oxide",
@@ -5462,9 +5474,9 @@ checksum = "f9fbbcab51052fe104eb5e5d351cf728d30a5be1fe14d9be8a3b097481fb97de"
[[package]]
name = "libp2p-identity"
version = "0.2.12"
version = "0.2.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3104e13b51e4711ff5738caa1fb54467c8604c2e94d607e27745bcf709068774"
checksum = "f0c7892c221730ba55f7196e98b0b8ba5e04b4155651736036628e9f73ed6fc3"
dependencies = [
"asn1_der",
"bs58",
@@ -6199,9 +6211,9 @@ checksum = "d6790f58c7ff633d8771f42965289203411a5e5c68388703c06e14f24770b41e"
[[package]]
name = "op-alloy"
version = "0.23.0"
version = "0.23.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5f8cef53b364f406ed2be3a447b2d8f2f18b07a6ff1255c287debb4cda68095b"
checksum = "e9b8fee21003dd4f076563de9b9d26f8c97840157ef78593cd7f262c5ca99848"
dependencies = [
"op-alloy-consensus",
"op-alloy-network",
@@ -6254,9 +6266,9 @@ dependencies = [
[[package]]
name = "op-alloy-provider"
version = "0.23.0"
version = "0.23.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9f1c952895ad45087d35d323e3fb73c0b5de7c6852494d81ebe997030366196a"
checksum = "6753d90efbaa8ea8bcb89c1737408ca85fa60d7adb875049d3f382c063666f86"
dependencies = [
"alloy-network",
"alloy-primitives",
@@ -7351,9 +7363,9 @@ checksum = "ba39f3699c378cd8970968dcbff9c43159ea4cfbd88d43c00b22f2ef10a435d2"
[[package]]
name = "reqwest"
version = "0.12.24"
version = "0.12.25"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9d0946410b9f7b082a427e4ef5c8ff541a88b357bc6c637c40db3a68ac70a36f"
checksum = "b6eff9328d40131d43bd911d42d79eb6a47312002a4daefc9e37f17e74a7701a"
dependencies = [
"base64 0.22.1",
"bytes",
@@ -8208,6 +8220,7 @@ name = "reth-engine-tree"
version = "1.9.3"
dependencies = [
"alloy-consensus",
"alloy-eip7928",
"alloy-eips",
"alloy-evm",
"alloy-primitives",
@@ -11183,9 +11196,9 @@ dependencies = [
[[package]]
name = "revm-inspectors"
version = "0.33.1"
version = "0.33.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6c93974333e7acc4b2dc024b10def99707f7375a4d53db7a7f8351722d25673f"
checksum = "01def7351cd9af844150b8e88980bcd11304f33ce23c3d7c25f2a8dab87c1345"
dependencies = [
"alloy-primitives",
"alloy-rpc-types-eth",
@@ -12067,9 +12080,9 @@ dependencies = [
[[package]]
name = "simd-adler32"
version = "0.3.7"
version = "0.3.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d66dc143e6b11c1eddc06d5c423cfc97062865baf299914ab64caa38182078fe"
checksum = "e320a6c5ad31d271ad523dcf3ad13e2767ad8b1cb8f047f75a8aeaf8da139da2"
[[package]]
name = "similar"
@@ -12891,9 +12904,9 @@ dependencies = [
[[package]]
name = "tower-http"
version = "0.6.7"
version = "0.6.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9cf146f99d442e8e68e585f5d798ccd3cad9a7835b917e09728880a862706456"
checksum = "d4e6559d53cc268e5031cd8429d05415bc4cb4aefc4aa5d6cc35fbf5b924a1f8"
dependencies = [
"async-compression",
"base64 0.22.1",

View File

@@ -481,13 +481,14 @@ revm-primitives = { version = "21.0.2", default-features = false }
revm-interpreter = { version = "31.1.0", default-features = false }
revm-database-interface = { version = "8.0.5", default-features = false }
op-revm = { version = "14.1.0", default-features = false }
revm-inspectors = "0.33.1"
revm-inspectors = "0.33.2"
# eth
alloy-chains = { version = "0.2.5", default-features = false }
alloy-dyn-abi = "1.4.1"
alloy-eip2124 = { version = "0.2.0", default-features = false }
alloy-evm = { version = "0.25.0", default-features = false }
alloy-eip7928 = { version = "0.1.0" }
alloy-evm = { version = "0.25.1", default-features = false }
alloy-primitives = { version = "1.4.1", default-features = false, features = ["map-foldhash"] }
alloy-rlp = { version = "0.3.10", default-features = false, features = ["core-net"] }
alloy-sol-macro = "1.4.1"

View File

@@ -93,6 +93,7 @@ impl Command {
transaction_senders_in_static_files: _,
storages_history_in_rocksdb: _,
transaction_hash_numbers_in_rocksdb: _,
account_history_in_rocksdb: _,
} = settings.unwrap_or_else(StorageSettings::legacy);
// Update the setting based on the key

View File

@@ -29,9 +29,12 @@ impl<N: Network, PrimitiveBlock> RpcBlockProvider<N, PrimitiveBlock> {
ProviderBuilder::default()
.connect_with_config(
rpc_url,
ConnectionConfig::default()
.with_max_retries(u32::MAX)
.with_ws_config(WebSocketConfig::default().max_message_size(None)),
ConnectionConfig::default().with_max_retries(u32::MAX).with_ws_config(
WebSocketConfig::default()
// allow larger messages/frames for big blocks
.max_frame_size(Some(128 * 1024 * 1024))
.max_message_size(Some(128 * 1024 * 1024)),
),
)
.await?,
),

View File

@@ -1,5 +1,7 @@
//! Engine tree configuration.
use alloy_eips::merge::EPOCH_SLOTS;
/// Triggers persistence when the number of canonical blocks in memory exceeds this threshold.
pub const DEFAULT_PERSISTENCE_THRESHOLD: u64 = 2;
@@ -40,7 +42,7 @@ pub const DEFAULT_RESERVED_CPU_CORES: usize = 1;
/// Default maximum concurrency for prewarm task.
pub const DEFAULT_PREWARM_MAX_CONCURRENCY: usize = 16;
const DEFAULT_BLOCK_BUFFER_LIMIT: u32 = 256;
const DEFAULT_BLOCK_BUFFER_LIMIT: u32 = EPOCH_SLOTS as u32 * 2;
const DEFAULT_MAX_INVALID_HEADER_CACHE_LENGTH: u32 = 256;
const DEFAULT_MAX_EXECUTE_BLOCK_BATCH_SIZE: usize = 4;
const DEFAULT_CROSS_BLOCK_CACHE_SIZE: u64 = 4 * 1024 * 1024 * 1024;
@@ -89,6 +91,8 @@ pub struct TreeConfig {
/// Whether to always compare trie updates from the state root task to the trie updates from
/// the regular state root calculation.
always_compare_trie_updates: bool,
/// Whether to disable state cache.
disable_state_cache: bool,
/// Whether to disable parallel prewarming.
disable_prewarming: bool,
/// Whether to disable the parallel sparse trie state root algorithm.
@@ -143,6 +147,7 @@ impl Default for TreeConfig {
max_execute_block_batch_size: DEFAULT_MAX_EXECUTE_BLOCK_BATCH_SIZE,
legacy_state_root: false,
always_compare_trie_updates: false,
disable_state_cache: false,
disable_prewarming: false,
disable_parallel_sparse_trie: false,
state_provider_metrics: false,
@@ -173,6 +178,7 @@ impl TreeConfig {
max_execute_block_batch_size: usize,
legacy_state_root: bool,
always_compare_trie_updates: bool,
disable_state_cache: bool,
disable_prewarming: bool,
disable_parallel_sparse_trie: bool,
state_provider_metrics: bool,
@@ -197,6 +203,7 @@ impl TreeConfig {
max_execute_block_batch_size,
legacy_state_root,
always_compare_trie_updates,
disable_state_cache,
disable_prewarming,
disable_parallel_sparse_trie,
state_provider_metrics,
@@ -271,7 +278,12 @@ impl TreeConfig {
self.disable_parallel_sparse_trie
}
/// Returns whether or not parallel prewarming should be used.
/// Returns whether or not state cache is disabled.
pub const fn disable_state_cache(&self) -> bool {
self.disable_state_cache
}
/// Returns whether or not parallel prewarming is disabled.
pub const fn disable_prewarming(&self) -> bool {
self.disable_prewarming
}
@@ -363,6 +375,12 @@ impl TreeConfig {
self
}
/// Setter for whether to disable state cache.
pub const fn without_state_cache(mut self, disable_state_cache: bool) -> Self {
self.disable_state_cache = disable_state_cache;
self
}
/// Setter for whether to disable parallel prewarming.
pub const fn without_prewarming(mut self, disable_prewarming: bool) -> Self {
self.disable_prewarming = disable_prewarming;

View File

@@ -39,6 +39,7 @@ reth-trie.workspace = true
alloy-evm.workspace = true
alloy-consensus.workspace = true
alloy-eips.workspace = true
alloy-eip7928.workspace = true
alloy-primitives.workspace = true
alloy-rlp.workspace = true
alloy-rpc-types-engine.workspace = true

View File

@@ -22,7 +22,7 @@ const NANOS_PER_SEC: u32 = 1_000_000_000;
/// An atomic version of [`Duration`], using an [`AtomicU64`] to store the total nanoseconds in the
/// duration.
#[derive(Default)]
#[derive(Debug, Default)]
pub(crate) struct AtomicDuration {
/// The nanoseconds part of the duration
///
@@ -59,7 +59,8 @@ impl AtomicDuration {
}
/// A wrapper of a state provider and latency metrics.
pub(crate) struct InstrumentedStateProvider<S> {
#[derive(Debug)]
pub struct InstrumentedStateProvider<S> {
/// The state provider
state_provider: S,
@@ -80,11 +81,12 @@ impl<S> InstrumentedStateProvider<S>
where
S: StateProvider,
{
/// Creates a new [`InstrumentedStateProvider`] from a state provider
pub(crate) fn from_state_provider(state_provider: S) -> Self {
/// Creates a new [`InstrumentedStateProvider`] from a state provider with the provided label
/// for metrics.
pub fn from_state_provider(state_provider: S, source: &'static str) -> Self {
Self {
state_provider,
metrics: StateProviderMetrics::default(),
metrics: StateProviderMetrics::new_with_labels(&[("source", source)]),
total_storage_fetch_latency: AtomicDuration::zero(),
total_code_fetch_latency: AtomicDuration::zero(),
total_account_fetch_latency: AtomicDuration::zero(),
@@ -134,6 +136,12 @@ impl<S> InstrumentedStateProvider<S> {
}
}
impl<S> Drop for InstrumentedStateProvider<S> {
fn drop(&mut self) {
self.record_total_latency();
}
}
/// Metrics for the instrumented state provider
#[derive(Metrics, Clone)]
#[metrics(scope = "sync.state_provider")]

View File

@@ -54,7 +54,7 @@ use tracing::*;
mod block_buffer;
mod cached_state;
pub mod error;
mod instrumented_state;
pub mod instrumented_state;
mod invalid_headers;
mod metrics;
mod payload_processor;

View File

@@ -106,6 +106,8 @@ where
cross_block_cache_size: u64,
/// Whether transactions should not be executed on prewarming task.
disable_transaction_prewarming: bool,
/// Whether state cache should be disable
disable_state_cache: bool,
/// Determines how to configure the evm for execution.
evm_config: Evm,
/// Whether precompile cache should be disabled.
@@ -149,6 +151,7 @@ where
cross_block_cache_size: config.cross_block_cache_size(),
disable_transaction_prewarming: config.disable_prewarming(),
evm_config,
disable_state_cache: config.disable_state_cache(),
precompile_cache_disabled: config.precompile_cache_disabled(),
precompile_cache_map,
sparse_state_trie: Arc::default(),
@@ -382,9 +385,15 @@ where
transactions = mpsc::channel().1;
}
let saved_cache = self.cache_for(env.parent_hash);
let cache = saved_cache.cache().clone();
let cache_metrics = saved_cache.metrics().clone();
let (saved_cache, cache, cache_metrics) = if self.disable_state_cache {
(None, None, None)
} else {
let saved_cache = self.cache_for(env.parent_hash);
let cache = saved_cache.cache().clone();
let cache_metrics = saved_cache.metrics().clone();
(Some(saved_cache), Some(cache), Some(cache_metrics))
};
// configure prewarming
let prewarm_ctx = PrewarmContext {
env,
@@ -596,12 +605,12 @@ impl<Tx, Err> PayloadHandle<Tx, Err> {
}
/// Returns a clone of the caches used by prewarming
pub(super) fn caches(&self) -> StateExecutionCache {
pub(super) fn caches(&self) -> Option<StateExecutionCache> {
self.prewarm_handle.cache.clone()
}
/// Returns a clone of the cache metrics used by prewarming
pub(super) fn cache_metrics(&self) -> CachedStateMetrics {
pub(super) fn cache_metrics(&self) -> Option<CachedStateMetrics> {
self.prewarm_handle.cache_metrics.clone()
}
@@ -631,9 +640,9 @@ impl<Tx, Err> PayloadHandle<Tx, Err> {
#[derive(Debug)]
pub(crate) struct CacheTaskHandle {
/// The shared cache the task operates with.
cache: StateExecutionCache,
cache: Option<StateExecutionCache>,
/// Metrics for the caches
cache_metrics: CachedStateMetrics,
cache_metrics: Option<CachedStateMetrics>,
/// Channel to the spawned prewarm task if any
to_prewarm_task: Option<std::sync::mpsc::Sender<PrewarmTaskEvent>>,
}

View File

@@ -354,7 +354,7 @@ impl MultiproofManager {
fn dispatch(&self, input: PendingMultiproofTask) {
// If there are no proof targets, we can just send an empty multiproof back immediately
if input.proof_targets_is_empty() {
debug!(
trace!(
sequence_number = input.proof_sequence_number(),
"No proof targets, sending empty multiproof back immediately"
);
@@ -1045,7 +1045,7 @@ impl MultiProofTask {
let storage_targets =
merged_targets.values().map(|slots| slots.len()).sum::<usize>();
batch_metrics.prefetch_proofs_requested += self.on_prefetch_proof(merged_targets);
debug!(
trace!(
target: "engine::tree::payload_processor::multiproof",
account_targets,
storage_targets,
@@ -1135,7 +1135,7 @@ impl MultiProofTask {
let batch_len = merged_update.len();
batch_metrics.state_update_proofs_requested +=
self.on_state_update(batch_source, merged_update);
debug!(
trace!(
target: "engine::tree::payload_processor::multiproof",
?batch_source,
len = batch_len,
@@ -1271,7 +1271,7 @@ impl MultiProofTask {
// Convert ProofResultMessage to SparseTrieUpdate
match proof_result.result {
Ok(proof_result_data) => {
debug!(
trace!(
target: "engine::tree::payload_processor::multiproof",
sequence = proof_result.sequence_number,
total_proofs = batch_metrics.proofs_processed,

View File

@@ -29,7 +29,7 @@ use metrics::{Counter, Gauge, Histogram};
use reth_evm::{execute::ExecutableTxFor, ConfigureEvm, Evm, EvmFor, SpecFor};
use reth_metrics::Metrics;
use reth_primitives_traits::NodePrimitives;
use reth_provider::{BlockReader, StateProviderFactory, StateReader};
use reth_provider::{BlockReader, StateProviderBox, StateProviderFactory, StateReader};
use reth_revm::{database::StateProviderDatabase, db::BundleState, state::EvmState};
use reth_trie::MultiProofTargets;
use std::{
@@ -255,31 +255,35 @@ where
self;
let hash = env.hash;
debug!(target: "engine::caching", parent_hash=?hash, "Updating execution cache");
// Perform all cache operations atomically under the lock
execution_cache.update_with_guard(|cached| {
// consumes the `SavedCache` held by the prewarming task, which releases its usage guard
let (caches, cache_metrics) = saved_cache.split();
let new_cache = SavedCache::new(hash, caches, cache_metrics);
if let Some(saved_cache) = saved_cache {
debug!(target: "engine::caching", parent_hash=?hash, "Updating execution cache");
// Perform all cache operations atomically under the lock
execution_cache.update_with_guard(|cached| {
// consumes the `SavedCache` held by the prewarming task, which releases its usage
// guard
let (caches, cache_metrics) = saved_cache.split();
let new_cache = SavedCache::new(hash, caches, cache_metrics);
// Insert state into cache while holding the lock
if new_cache.cache().insert_state(&state).is_err() {
// Clear the cache on error to prevent having a polluted cache
*cached = None;
debug!(target: "engine::caching", "cleared execution cache on update error");
return;
}
// Insert state into cache while holding the lock
if new_cache.cache().insert_state(&state).is_err() {
// Clear the cache on error to prevent having a polluted cache
*cached = None;
debug!(target: "engine::caching", "cleared execution cache on update error");
return;
}
new_cache.update_metrics();
new_cache.update_metrics();
// Replace the shared cache with the new one; the previous cache (if any) is dropped.
*cached = Some(new_cache);
});
// Replace the shared cache with the new one; the previous cache (if any) is
// dropped.
*cached = Some(new_cache);
});
let elapsed = start.elapsed();
debug!(target: "engine::caching", parent_hash=?hash, elapsed=?elapsed, "Updated execution cache");
let elapsed = start.elapsed();
debug!(target: "engine::caching", parent_hash=?hash, elapsed=?elapsed, "Updated execution cache");
metrics.cache_saving_duration.set(elapsed.as_secs_f64());
metrics.cache_saving_duration.set(elapsed.as_secs_f64());
}
}
/// Executes the task.
@@ -356,7 +360,7 @@ where
{
pub(super) env: ExecutionEnv<Evm>,
pub(super) evm_config: Evm,
pub(super) saved_cache: SavedCache,
pub(super) saved_cache: Option<SavedCache>,
/// Provider to obtain the state
pub(super) provider: StateProviderBuilder<N, P>,
pub(super) metrics: PrewarmMetrics,
@@ -400,10 +404,13 @@ where
};
// Use the caches to create a new provider with caching
let caches = saved_cache.cache().clone();
let cache_metrics = saved_cache.metrics().clone();
let state_provider =
CachedStateProvider::new_with_caches(state_provider, caches, cache_metrics);
let state_provider: StateProviderBox = if let Some(saved_cache) = saved_cache {
let caches = saved_cache.cache().clone();
let cache_metrics = saved_cache.metrics().clone();
Box::new(CachedStateProvider::new_with_caches(state_provider, caches, cache_metrics))
} else {
state_provider
};
let state_provider = StateProviderDatabase::new(state_provider);

View File

@@ -11,6 +11,7 @@ use crate::tree::{
StateProviderDatabase, TreeConfig,
};
use alloy_consensus::transaction::Either;
use alloy_eip7928::BlockAccessList;
use alloy_eips::{eip1898::BlockWithParent, NumHash};
use alloy_evm::Evm;
use alloy_primitives::B256;
@@ -368,7 +369,7 @@ where
)
.into())
};
let state_provider = ensure_ok!(provider_builder.build());
let mut state_provider = ensure_ok!(provider_builder.build());
drop(_enter);
// fetch parent block
@@ -411,18 +412,19 @@ where
// Use cached state provider before executing, used in execution after prewarming threads
// complete
let state_provider = CachedStateProvider::new_with_caches(
state_provider,
handle.caches(),
handle.cache_metrics(),
);
if let Some((caches, cache_metrics)) = handle.caches().zip(handle.cache_metrics()) {
state_provider = Box::new(CachedStateProvider::new_with_caches(
state_provider,
caches,
cache_metrics,
));
};
// Execute the block and handle any execution errors
let (output, senders) = match if self.config.state_provider_metrics() {
let state_provider = InstrumentedStateProvider::from_state_provider(&state_provider);
let result = self.execute_block(&state_provider, env, &input, &mut handle);
state_provider.record_total_latency();
result
let state_provider =
InstrumentedStateProvider::from_state_provider(&state_provider, "engine");
self.execute_block(&state_provider, env, &input, &mut handle)
} else {
self.execute_block(&state_provider, env, &input, &mut handle)
} {
@@ -1243,4 +1245,10 @@ impl<T: PayloadTypes> BlockOrPayload<T> {
Self::Block(_) => "block",
}
}
/// Returns the block access list if available.
pub const fn block_access_list(&self) -> Option<Result<BlockAccessList, alloy_rlp::Error>> {
// TODO decode and return `BlockAccessList`
None
}
}

View File

@@ -5,7 +5,6 @@ use alloy_consensus::{
};
use alloy_eips::merge::BEACON_NONCE;
use alloy_evm::{block::BlockExecutorFactory, eth::EthBlockExecutionCtx};
use alloy_primitives::Bytes;
use reth_chainspec::{EthChainSpec, EthereumHardforks};
use reth_evm::execute::{BlockAssembler, BlockAssemblerInput, BlockExecutionError};
use reth_execution_types::BlockExecutionResult;
@@ -17,14 +16,12 @@ use revm::context::Block as _;
pub struct EthBlockAssembler<ChainSpec = reth_chainspec::ChainSpec> {
/// The chainspec.
pub chain_spec: Arc<ChainSpec>,
/// Extra data to use for the blocks.
pub extra_data: Bytes,
}
impl<ChainSpec> EthBlockAssembler<ChainSpec> {
/// Creates a new [`EthBlockAssembler`].
pub fn new(chain_spec: Arc<ChainSpec>) -> Self {
Self { chain_spec, extra_data: Default::default() }
pub const fn new(chain_spec: Arc<ChainSpec>) -> Self {
Self { chain_spec }
}
}
@@ -110,7 +107,7 @@ where
gas_limit: evm_env.block_env.gas_limit(),
difficulty: evm_env.block_env.difficulty(),
gas_used: *gas_used,
extra_data: self.extra_data.clone(),
extra_data: ctx.extra_data,
parent_beacon_block_root: ctx.parent_beacon_block_root,
blob_gas_used: block_blob_gas_used,
excess_blob_gas,

View File

@@ -116,12 +116,6 @@ impl<ChainSpec, EvmFactory> EthEvmConfig<ChainSpec, EvmFactory> {
pub const fn chain_spec(&self) -> &Arc<ChainSpec> {
self.executor_factory.spec()
}
/// Sets the extra data for the block assembler.
pub fn with_extra_data(mut self, extra_data: Bytes) -> Self {
self.block_assembler.extra_data = extra_data;
self
}
}
impl<ChainSpec, EvmF> ConfigureEvm for EthEvmConfig<ChainSpec, EvmF>
@@ -193,6 +187,7 @@ where
parent_beacon_block_root: block.header().parent_beacon_block_root,
ommers: &block.body().ommers,
withdrawals: block.body().withdrawals.as_ref().map(Cow::Borrowed),
extra_data: block.header().extra_data.clone(),
})
}
@@ -206,6 +201,7 @@ where
parent_beacon_block_root: attributes.parent_beacon_block_root,
ommers: &[],
withdrawals: attributes.withdrawals.map(Cow::Owned),
extra_data: attributes.extra_data,
})
}
}
@@ -282,6 +278,7 @@ where
parent_beacon_block_root: payload.sidecar.parent_beacon_block_root(),
ommers: &[],
withdrawals: payload.payload.withdrawals().map(|w| Cow::Owned(w.clone().into())),
extra_data: payload.payload.as_v1().extra_data.clone(),
})
}

View File

@@ -32,7 +32,7 @@ use reth_node_builder::{
EngineValidatorBuilder, EthApiBuilder, EthApiCtx, Identity, PayloadValidatorBuilder,
RethRpcAddOns, RpcAddOns, RpcHandle,
},
BuilderContext, DebugNode, Node, NodeAdapter, PayloadBuilderConfig,
BuilderContext, DebugNode, Node, NodeAdapter,
};
use reth_payload_primitives::PayloadTypes;
use reth_provider::{providers::ProviderFactoryBuilder, EthStorage};
@@ -437,9 +437,7 @@ where
type EVM = EthEvmConfig<Types::ChainSpec>;
async fn build_evm(self, ctx: &BuilderContext<Node>) -> eyre::Result<Self::EVM> {
let evm_config = EthEvmConfig::new(ctx.chain_spec())
.with_extra_data(ctx.payload_builder_config().extra_data_bytes());
Ok(evm_config)
Ok(EthEvmConfig::new(ctx.chain_spec()))
}
}

View File

@@ -54,7 +54,8 @@ where
evm_config,
EthereumBuilderConfig::new()
.with_gas_limit(gas_limit)
.with_max_blobs_per_block(conf.max_blobs_per_block()),
.with_max_blobs_per_block(conf.max_blobs_per_block())
.with_extra_data(conf.extra_data_bytes()),
))
}
}

View File

@@ -1,4 +1,5 @@
use alloy_eips::eip1559::ETHEREUM_BLOCK_GAS_LIMIT_30M;
use alloy_primitives::Bytes;
use reth_primitives_traits::constants::GAS_LIMIT_BOUND_DIVISOR;
/// Settings for the Ethereum builder.
@@ -13,6 +14,8 @@ pub struct EthereumBuilderConfig {
///
/// If `None`, defaults to the protocol maximum.
pub max_blobs_per_block: Option<u64>,
/// Extra data for built blocks.
pub extra_data: Bytes,
}
impl Default for EthereumBuilderConfig {
@@ -28,6 +31,7 @@ impl EthereumBuilderConfig {
desired_gas_limit: ETHEREUM_BLOCK_GAS_LIMIT_30M,
await_payload_on_missing: true,
max_blobs_per_block: None,
extra_data: Bytes::new(),
}
}
@@ -49,6 +53,12 @@ impl EthereumBuilderConfig {
self.max_blobs_per_block = max_blobs_per_block;
self
}
/// Set the extra data for built blocks.
pub fn with_extra_data(mut self, extra_data: Bytes) -> Self {
self.extra_data = extra_data;
self
}
}
impl EthereumBuilderConfig {

View File

@@ -168,7 +168,7 @@ where
gas_limit: builder_config.gas_limit(parent_header.gas_limit),
parent_beacon_block_root: attributes.parent_beacon_block_root(),
withdrawals: Some(attributes.withdrawals().clone()),
extra_data: None,
extra_data: builder_config.extra_data,
},
)
.map_err(PayloadBuilderError::other)?;

View File

@@ -502,7 +502,7 @@ pub struct NextBlockEnvAttributes {
/// Withdrawals
pub withdrawals: Option<Withdrawals>,
/// Optional extra data.
pub extra_data: Option<Bytes>,
pub extra_data: Bytes,
}
/// Abstraction over transaction environment.

View File

@@ -1372,21 +1372,6 @@ where
// reallocations
let mut new_txs = Vec::with_capacity(transactions.len());
for tx in transactions {
// recover transaction
let tx = match tx.try_into_recovered() {
Ok(tx) => tx,
Err(badtx) => {
trace!(target: "net::tx",
peer_id=format!("{peer_id:#}"),
hash=%badtx.tx_hash(),
client_version=%peer.client_version,
"failed ecrecovery for transaction"
);
has_bad_transactions = true;
continue
}
};
match self.transactions_by_peers.entry(*tx.tx_hash()) {
Entry::Occupied(mut entry) => {
// transaction was already inserted
@@ -1404,6 +1389,21 @@ where
} else {
// this is a new transaction that should be imported into the pool
// recover transaction
let tx = match tx.try_into_recovered() {
Ok(tx) => tx,
Err(badtx) => {
trace!(target: "net::tx",
peer_id=format!("{peer_id:#}"),
hash=%badtx.tx_hash(),
client_version=%peer.client_version,
"failed ecrecovery for transaction"
);
has_bad_transactions = true;
continue
}
};
let pool_transaction = Pool::Transaction::from_pooled(tx);
new_txs.push(pool_transaction);

View File

@@ -859,8 +859,8 @@ impl<Node: FullNodeTypes> BuilderContext<Node> {
.request_handler(self.provider().clone())
.split_with_handle();
self.executor.spawn_critical("p2p txpool", Box::pin(txpool));
self.executor.spawn_critical("p2p eth request handler", Box::pin(eth));
self.executor.spawn_critical_blocking("p2p txpool", Box::pin(txpool));
self.executor.spawn_critical_blocking("p2p eth request handler", Box::pin(eth));
let default_peers_path = self.config().datadir().known_peers();
let known_peers_file = self.config().network.persistent_peers_file(default_peers_path);

View File

@@ -1,13 +1,198 @@
//! clap [Args](clap::Args) for engine purposes
use clap::Args;
use clap::{builder::Resettable, Args};
use reth_engine_primitives::{TreeConfig, DEFAULT_MULTIPROOF_TASK_CHUNK_SIZE};
use std::sync::OnceLock;
use crate::node_config::{
DEFAULT_CROSS_BLOCK_CACHE_SIZE_MB, DEFAULT_MEMORY_BLOCK_BUFFER_TARGET,
DEFAULT_PERSISTENCE_THRESHOLD, DEFAULT_RESERVED_CPU_CORES,
};
/// Global static engine defaults
static ENGINE_DEFAULTS: OnceLock<DefaultEngineValues> = OnceLock::new();
/// Default values for engine that can be customized
///
/// Global defaults can be set via [`DefaultEngineValues::try_init`].
#[derive(Debug, Clone)]
pub struct DefaultEngineValues {
persistence_threshold: u64,
memory_block_buffer_target: u64,
legacy_state_root_task_enabled: bool,
state_cache_disabled: bool,
prewarming_disabled: bool,
parallel_sparse_trie_disabled: bool,
state_provider_metrics: bool,
cross_block_cache_size: u64,
state_root_task_compare_updates: bool,
accept_execution_requests_hash: bool,
multiproof_chunking_enabled: bool,
multiproof_chunk_size: usize,
reserved_cpu_cores: usize,
precompile_cache_disabled: bool,
state_root_fallback: bool,
always_process_payload_attributes_on_canonical_head: bool,
allow_unwind_canonical_header: bool,
storage_worker_count: Option<usize>,
account_worker_count: Option<usize>,
}
impl DefaultEngineValues {
/// Initialize the global engine defaults with this configuration
pub fn try_init(self) -> Result<(), Self> {
ENGINE_DEFAULTS.set(self)
}
/// Get a reference to the global engine defaults
pub fn get_global() -> &'static Self {
ENGINE_DEFAULTS.get_or_init(Self::default)
}
/// Set the default persistence threshold
pub const fn with_persistence_threshold(mut self, v: u64) -> Self {
self.persistence_threshold = v;
self
}
/// Set the default memory block buffer target
pub const fn with_memory_block_buffer_target(mut self, v: u64) -> Self {
self.memory_block_buffer_target = v;
self
}
/// Set whether to enable legacy state root task by default
pub const fn with_legacy_state_root_task_enabled(mut self, v: bool) -> Self {
self.legacy_state_root_task_enabled = v;
self
}
/// Set whether to disable state cache by default
pub const fn with_state_cache_disabled(mut self, v: bool) -> Self {
self.state_cache_disabled = v;
self
}
/// Set whether to disable prewarming by default
pub const fn with_prewarming_disabled(mut self, v: bool) -> Self {
self.prewarming_disabled = v;
self
}
/// Set whether to disable parallel sparse trie by default
pub const fn with_parallel_sparse_trie_disabled(mut self, v: bool) -> Self {
self.parallel_sparse_trie_disabled = v;
self
}
/// Set whether to enable state provider metrics by default
pub const fn with_state_provider_metrics(mut self, v: bool) -> Self {
self.state_provider_metrics = v;
self
}
/// Set the default cross-block cache size in MB
pub const fn with_cross_block_cache_size(mut self, v: u64) -> Self {
self.cross_block_cache_size = v;
self
}
/// Set whether to compare state root task updates by default
pub const fn with_state_root_task_compare_updates(mut self, v: bool) -> Self {
self.state_root_task_compare_updates = v;
self
}
/// Set whether to accept execution requests hash by default
pub const fn with_accept_execution_requests_hash(mut self, v: bool) -> Self {
self.accept_execution_requests_hash = v;
self
}
/// Set whether to enable multiproof chunking by default
pub const fn with_multiproof_chunking_enabled(mut self, v: bool) -> Self {
self.multiproof_chunking_enabled = v;
self
}
/// Set the default multiproof chunk size
pub const fn with_multiproof_chunk_size(mut self, v: usize) -> Self {
self.multiproof_chunk_size = v;
self
}
/// Set the default number of reserved CPU cores
pub const fn with_reserved_cpu_cores(mut self, v: usize) -> Self {
self.reserved_cpu_cores = v;
self
}
/// Set whether to disable precompile cache by default
pub const fn with_precompile_cache_disabled(mut self, v: bool) -> Self {
self.precompile_cache_disabled = v;
self
}
/// Set whether to enable state root fallback by default
pub const fn with_state_root_fallback(mut self, v: bool) -> Self {
self.state_root_fallback = v;
self
}
/// Set whether to always process payload attributes on canonical head by default
pub const fn with_always_process_payload_attributes_on_canonical_head(
mut self,
v: bool,
) -> Self {
self.always_process_payload_attributes_on_canonical_head = v;
self
}
/// Set whether to allow unwinding canonical header by default
pub const fn with_allow_unwind_canonical_header(mut self, v: bool) -> Self {
self.allow_unwind_canonical_header = v;
self
}
/// Set the default storage worker count
pub const fn with_storage_worker_count(mut self, v: Option<usize>) -> Self {
self.storage_worker_count = v;
self
}
/// Set the default account worker count
pub const fn with_account_worker_count(mut self, v: Option<usize>) -> Self {
self.account_worker_count = v;
self
}
}
impl Default for DefaultEngineValues {
fn default() -> Self {
Self {
persistence_threshold: DEFAULT_PERSISTENCE_THRESHOLD,
memory_block_buffer_target: DEFAULT_MEMORY_BLOCK_BUFFER_TARGET,
legacy_state_root_task_enabled: false,
state_cache_disabled: false,
prewarming_disabled: false,
parallel_sparse_trie_disabled: false,
state_provider_metrics: false,
cross_block_cache_size: DEFAULT_CROSS_BLOCK_CACHE_SIZE_MB,
state_root_task_compare_updates: false,
accept_execution_requests_hash: false,
multiproof_chunking_enabled: true,
multiproof_chunk_size: DEFAULT_MULTIPROOF_TASK_CHUNK_SIZE,
reserved_cpu_cores: DEFAULT_RESERVED_CPU_CORES,
precompile_cache_disabled: false,
state_root_fallback: false,
always_process_payload_attributes_on_canonical_head: false,
allow_unwind_canonical_header: false,
storage_worker_count: None,
account_worker_count: None,
}
}
}
/// Parameters for configuring the engine driver.
#[derive(Debug, Clone, Args, PartialEq, Eq)]
#[command(next_help_heading = "Engine")]
@@ -18,15 +203,15 @@ pub struct EngineArgs {
///
/// To persist blocks as fast as the node receives them, set this value to zero. This will
/// cause more frequent DB writes.
#[arg(long = "engine.persistence-threshold", default_value_t = DEFAULT_PERSISTENCE_THRESHOLD)]
#[arg(long = "engine.persistence-threshold", default_value_t = DefaultEngineValues::get_global().persistence_threshold)]
pub persistence_threshold: u64,
/// Configure the target number of blocks to keep in memory.
#[arg(long = "engine.memory-block-buffer-target", default_value_t = DEFAULT_MEMORY_BLOCK_BUFFER_TARGET)]
#[arg(long = "engine.memory-block-buffer-target", default_value_t = DefaultEngineValues::get_global().memory_block_buffer_target)]
pub memory_block_buffer_target: u64,
/// Enable legacy state root
#[arg(long = "engine.legacy-state-root", default_value = "false")]
#[arg(long = "engine.legacy-state-root", default_value_t = DefaultEngineValues::get_global().legacy_state_root_task_enabled)]
pub legacy_state_root_task_enabled: bool,
/// CAUTION: This CLI flag has no effect anymore, use --engine.disable-caching-and-prewarming
@@ -35,8 +220,12 @@ pub struct EngineArgs {
#[deprecated]
pub caching_and_prewarming_enabled: bool,
/// Disable state cache
#[arg(long = "engine.disable-state-cache", default_value_t = DefaultEngineValues::get_global().state_cache_disabled)]
pub state_cache_disabled: bool,
/// Disable parallel prewarming
#[arg(long = "engine.disable-prewarming", alias = "engine.disable-caching-and-prewarming")]
#[arg(long = "engine.disable-prewarming", alias = "engine.disable-caching-and-prewarming", default_value_t = DefaultEngineValues::get_global().prewarming_disabled)]
pub prewarming_disabled: bool,
/// CAUTION: This CLI flag has no effect anymore, use --engine.disable-parallel-sparse-trie
@@ -46,38 +235,38 @@ pub struct EngineArgs {
pub parallel_sparse_trie_enabled: bool,
/// Disable the parallel sparse trie in the engine.
#[arg(long = "engine.disable-parallel-sparse-trie", default_value = "false")]
#[arg(long = "engine.disable-parallel-sparse-trie", default_value_t = DefaultEngineValues::get_global().parallel_sparse_trie_disabled)]
pub parallel_sparse_trie_disabled: bool,
/// Enable state provider latency metrics. This allows the engine to collect and report stats
/// about how long state provider calls took during execution, but this does introduce slight
/// overhead to state provider calls.
#[arg(long = "engine.state-provider-metrics", default_value = "false")]
#[arg(long = "engine.state-provider-metrics", default_value_t = DefaultEngineValues::get_global().state_provider_metrics)]
pub state_provider_metrics: bool,
/// Configure the size of cross-block cache in megabytes
#[arg(long = "engine.cross-block-cache-size", default_value_t = DEFAULT_CROSS_BLOCK_CACHE_SIZE_MB)]
#[arg(long = "engine.cross-block-cache-size", default_value_t = DefaultEngineValues::get_global().cross_block_cache_size)]
pub cross_block_cache_size: u64,
/// Enable comparing trie updates from the state root task to the trie updates from the regular
/// state root calculation.
#[arg(long = "engine.state-root-task-compare-updates")]
#[arg(long = "engine.state-root-task-compare-updates", default_value_t = DefaultEngineValues::get_global().state_root_task_compare_updates)]
pub state_root_task_compare_updates: bool,
/// Enables accepting requests hash instead of an array of requests in `engine_newPayloadV4`.
#[arg(long = "engine.accept-execution-requests-hash")]
#[arg(long = "engine.accept-execution-requests-hash", default_value_t = DefaultEngineValues::get_global().accept_execution_requests_hash)]
pub accept_execution_requests_hash: bool,
/// Whether multiproof task should chunk proof targets.
#[arg(long = "engine.multiproof-chunking", default_value = "true")]
#[arg(long = "engine.multiproof-chunking", default_value_t = DefaultEngineValues::get_global().multiproof_chunking_enabled)]
pub multiproof_chunking_enabled: bool,
/// Multiproof task chunk size for proof targets.
#[arg(long = "engine.multiproof-chunk-size", default_value_t = DEFAULT_MULTIPROOF_TASK_CHUNK_SIZE)]
#[arg(long = "engine.multiproof-chunk-size", default_value_t = DefaultEngineValues::get_global().multiproof_chunk_size)]
pub multiproof_chunk_size: usize,
/// Configure the number of reserved CPU cores for non-reth processes
#[arg(long = "engine.reserved-cpu-cores", default_value_t = DEFAULT_RESERVED_CPU_CORES)]
#[arg(long = "engine.reserved-cpu-cores", default_value_t = DefaultEngineValues::get_global().reserved_cpu_cores)]
pub reserved_cpu_cores: usize,
/// CAUTION: This CLI flag has no effect anymore, use --engine.disable-precompile-cache
@@ -87,11 +276,11 @@ pub struct EngineArgs {
pub precompile_cache_enabled: bool,
/// Disable precompile cache
#[arg(long = "engine.disable-precompile-cache", default_value = "false")]
#[arg(long = "engine.disable-precompile-cache", default_value_t = DefaultEngineValues::get_global().precompile_cache_disabled)]
pub precompile_cache_disabled: bool,
/// Enable state root fallback, useful for testing
#[arg(long = "engine.state-root-fallback", default_value = "false")]
#[arg(long = "engine.state-root-fallback", default_value_t = DefaultEngineValues::get_global().state_root_fallback)]
pub state_root_fallback: bool,
/// Always process payload attributes and begin a payload build process even if
@@ -101,51 +290,73 @@ pub struct EngineArgs {
/// Note: This is a no-op on OP Stack.
#[arg(
long = "engine.always-process-payload-attributes-on-canonical-head",
default_value = "false"
default_value_t = DefaultEngineValues::get_global().always_process_payload_attributes_on_canonical_head
)]
pub always_process_payload_attributes_on_canonical_head: bool,
/// Allow unwinding canonical header to ancestor during forkchoice updates.
/// See `TreeConfig::unwind_canonical_header` for more details.
#[arg(long = "engine.allow-unwind-canonical-header", default_value = "false")]
#[arg(long = "engine.allow-unwind-canonical-header", default_value_t = DefaultEngineValues::get_global().allow_unwind_canonical_header)]
pub allow_unwind_canonical_header: bool,
/// Configure the number of storage proof workers in the Tokio blocking pool.
/// If not specified, defaults to 2x available parallelism, clamped between 2 and 64.
#[arg(long = "engine.storage-worker-count")]
#[arg(long = "engine.storage-worker-count", default_value = Resettable::from(DefaultEngineValues::get_global().storage_worker_count.map(|v| v.to_string().into())))]
pub storage_worker_count: Option<usize>,
/// Configure the number of account proof workers in the Tokio blocking pool.
/// If not specified, defaults to the same count as storage workers.
#[arg(long = "engine.account-worker-count")]
#[arg(long = "engine.account-worker-count", default_value = Resettable::from(DefaultEngineValues::get_global().account_worker_count.map(|v| v.to_string().into())))]
pub account_worker_count: Option<usize>,
}
#[allow(deprecated)]
impl Default for EngineArgs {
fn default() -> Self {
let DefaultEngineValues {
persistence_threshold,
memory_block_buffer_target,
legacy_state_root_task_enabled,
state_cache_disabled,
prewarming_disabled,
parallel_sparse_trie_disabled,
state_provider_metrics,
cross_block_cache_size,
state_root_task_compare_updates,
accept_execution_requests_hash,
multiproof_chunking_enabled,
multiproof_chunk_size,
reserved_cpu_cores,
precompile_cache_disabled,
state_root_fallback,
always_process_payload_attributes_on_canonical_head,
allow_unwind_canonical_header,
storage_worker_count,
account_worker_count,
} = DefaultEngineValues::get_global().clone();
Self {
persistence_threshold: DEFAULT_PERSISTENCE_THRESHOLD,
memory_block_buffer_target: DEFAULT_MEMORY_BLOCK_BUFFER_TARGET,
legacy_state_root_task_enabled: false,
state_root_task_compare_updates: false,
persistence_threshold,
memory_block_buffer_target,
legacy_state_root_task_enabled,
state_root_task_compare_updates,
caching_and_prewarming_enabled: true,
prewarming_disabled: false,
state_cache_disabled,
prewarming_disabled,
parallel_sparse_trie_enabled: true,
parallel_sparse_trie_disabled: false,
state_provider_metrics: false,
cross_block_cache_size: DEFAULT_CROSS_BLOCK_CACHE_SIZE_MB,
accept_execution_requests_hash: false,
multiproof_chunking_enabled: true,
multiproof_chunk_size: DEFAULT_MULTIPROOF_TASK_CHUNK_SIZE,
reserved_cpu_cores: DEFAULT_RESERVED_CPU_CORES,
parallel_sparse_trie_disabled,
state_provider_metrics,
cross_block_cache_size,
accept_execution_requests_hash,
multiproof_chunking_enabled,
multiproof_chunk_size,
reserved_cpu_cores,
precompile_cache_enabled: true,
precompile_cache_disabled: false,
state_root_fallback: false,
always_process_payload_attributes_on_canonical_head: false,
allow_unwind_canonical_header: false,
storage_worker_count: None,
account_worker_count: None,
precompile_cache_disabled,
state_root_fallback,
always_process_payload_attributes_on_canonical_head,
allow_unwind_canonical_header,
storage_worker_count,
account_worker_count,
}
}
}
@@ -157,6 +368,7 @@ impl EngineArgs {
.with_persistence_threshold(self.persistence_threshold)
.with_memory_block_buffer_target(self.memory_block_buffer_target)
.with_legacy_state_root(self.legacy_state_root_task_enabled)
.without_state_cache(self.state_cache_disabled)
.without_prewarming(self.prewarming_disabled)
.with_disable_parallel_sparse_trie(self.parallel_sparse_trie_disabled)
.with_state_provider_metrics(self.state_provider_metrics)
@@ -202,4 +414,66 @@ mod tests {
let args = CommandParser::<EngineArgs>::parse_from(["reth"]).args;
assert_eq!(args, default_args);
}
#[test]
#[allow(deprecated)]
fn engine_args() {
let args = EngineArgs {
persistence_threshold: 100,
memory_block_buffer_target: 50,
legacy_state_root_task_enabled: true,
caching_and_prewarming_enabled: true,
state_cache_disabled: true,
prewarming_disabled: true,
parallel_sparse_trie_enabled: true,
parallel_sparse_trie_disabled: true,
state_provider_metrics: true,
cross_block_cache_size: 256,
state_root_task_compare_updates: true,
accept_execution_requests_hash: true,
multiproof_chunking_enabled: true,
multiproof_chunk_size: 512,
reserved_cpu_cores: 4,
precompile_cache_enabled: true,
precompile_cache_disabled: true,
state_root_fallback: true,
always_process_payload_attributes_on_canonical_head: true,
allow_unwind_canonical_header: true,
storage_worker_count: Some(16),
account_worker_count: Some(8),
};
let parsed_args = CommandParser::<EngineArgs>::parse_from([
"reth",
"--engine.persistence-threshold",
"100",
"--engine.memory-block-buffer-target",
"50",
"--engine.legacy-state-root",
"--engine.disable-state-cache",
"--engine.disable-prewarming",
"--engine.disable-parallel-sparse-trie",
"--engine.state-provider-metrics",
"--engine.cross-block-cache-size",
"256",
"--engine.state-root-task-compare-updates",
"--engine.accept-execution-requests-hash",
"--engine.multiproof-chunking",
"--engine.multiproof-chunk-size",
"512",
"--engine.reserved-cpu-cores",
"4",
"--engine.disable-precompile-cache",
"--engine.state-root-fallback",
"--engine.always-process-payload-attributes-on-canonical-head",
"--engine.allow-unwind-canonical-header",
"--engine.storage-worker-count",
"16",
"--engine.account-worker-count",
"8",
])
.args;
assert_eq!(parsed_args, args);
}
}

View File

@@ -6,7 +6,7 @@ pub use network::{DiscoveryArgs, NetworkArgs};
/// RpcServerArg struct for configuring the RPC
mod rpc_server;
pub use rpc_server::RpcServerArgs;
pub use rpc_server::{DefaultRpcServerArgs, RpcServerArgs};
/// `RpcStateCacheArgs` struct for configuring RPC state cache
mod rpc_state_cache;
@@ -66,7 +66,7 @@ pub use benchmark_args::BenchmarkArgs;
/// EngineArgs for configuring the engine
mod engine;
pub use engine::EngineArgs;
pub use engine::{DefaultEngineValues, EngineArgs};
/// `RessArgs` for configuring ress subprotocol.
mod ress_args;

View File

@@ -119,6 +119,18 @@ pub struct NetworkArgs {
#[arg(long)]
pub max_inbound_peers: Option<usize>,
/// Maximum number of total peers (inbound + outbound).
///
/// Splits peers using approximately 2:1 inbound:outbound ratio. Cannot be used together with
/// `--max-outbound-peers` or `--max-inbound-peers`.
#[arg(
long,
value_name = "COUNT",
conflicts_with = "max_outbound_peers",
conflicts_with = "max_inbound_peers"
)]
pub max_peers: Option<usize>,
/// Max concurrent `GetPooledTransactions` requests.
#[arg(long = "max-tx-reqs", value_name = "COUNT", default_value_t = DEFAULT_MAX_COUNT_CONCURRENT_REQUESTS, verbatim_doc_comment)]
pub max_concurrent_tx_requests: u32,
@@ -245,6 +257,34 @@ impl NetworkArgs {
bootnodes.into_iter().filter_map(|node| node.resolve_blocking().ok()).collect()
})
}
/// Returns the max inbound peers (2:1 ratio).
pub fn resolved_max_inbound_peers(&self) -> Option<usize> {
if let Some(max_peers) = self.max_peers {
if max_peers == 0 {
Some(0)
} else {
let outbound = (max_peers / 3).max(1);
Some(max_peers.saturating_sub(outbound))
}
} else {
self.max_inbound_peers
}
}
/// Returns the max outbound peers (1:2 ratio).
pub fn resolved_max_outbound_peers(&self) -> Option<usize> {
if let Some(max_peers) = self.max_peers {
if max_peers == 0 {
Some(0)
} else {
Some((max_peers / 3).max(1))
}
} else {
self.max_outbound_peers
}
}
/// Configures and returns a `TransactionsManagerConfig` based on the current settings.
pub const fn transactions_manager_config(&self) -> TransactionsManagerConfig {
TransactionsManagerConfig {
@@ -291,8 +331,8 @@ impl NetworkArgs {
.peers_config_with_basic_nodes_from_file(
self.persistent_peers_file(peers_file).as_deref(),
)
.with_max_inbound_opt(self.max_inbound_peers)
.with_max_outbound_opt(self.max_outbound_peers)
.with_max_inbound_opt(self.resolved_max_inbound_peers())
.with_max_outbound_opt(self.resolved_max_outbound_peers())
.with_ip_filter(ip_filter);
// Configure basic network stack
@@ -434,6 +474,7 @@ impl Default for NetworkArgs {
port: DEFAULT_DISCOVERY_PORT,
max_outbound_peers: None,
max_inbound_peers: None,
max_peers: None,
max_concurrent_tx_requests: DEFAULT_MAX_COUNT_CONCURRENT_REQUESTS,
max_concurrent_tx_requests_per_peer: DEFAULT_MAX_COUNT_CONCURRENT_REQUESTS_PER_PEER,
soft_limit_byte_size_pooled_transactions_response:
@@ -758,6 +799,96 @@ mod tests {
assert!(args.disable_tx_gossip);
}
#[test]
fn parse_max_peers_flag() {
let args = CommandParser::<NetworkArgs>::parse_from(["reth", "--max-peers", "90"]).args;
assert_eq!(args.max_peers, Some(90));
assert_eq!(args.max_outbound_peers, None);
assert_eq!(args.max_inbound_peers, None);
assert_eq!(args.resolved_max_outbound_peers(), Some(30));
assert_eq!(args.resolved_max_inbound_peers(), Some(60));
}
#[test]
fn max_peers_conflicts_with_outbound() {
let result = CommandParser::<NetworkArgs>::try_parse_from([
"reth",
"--max-peers",
"90",
"--max-outbound-peers",
"50",
]);
assert!(
result.is_err(),
"Should fail when both --max-peers and --max-outbound-peers are used"
);
}
#[test]
fn max_peers_conflicts_with_inbound() {
let result = CommandParser::<NetworkArgs>::try_parse_from([
"reth",
"--max-peers",
"90",
"--max-inbound-peers",
"30",
]);
assert!(
result.is_err(),
"Should fail when both --max-peers and --max-inbound-peers are used"
);
}
#[test]
fn max_peers_split_calculation() {
let args = CommandParser::<NetworkArgs>::parse_from(["reth", "--max-peers", "90"]).args;
assert_eq!(args.max_peers, Some(90));
assert_eq!(args.resolved_max_outbound_peers(), Some(30));
assert_eq!(args.resolved_max_inbound_peers(), Some(60));
}
#[test]
fn max_peers_small_values() {
let args1 = CommandParser::<NetworkArgs>::parse_from(["reth", "--max-peers", "1"]).args;
assert_eq!(args1.resolved_max_outbound_peers(), Some(1));
assert_eq!(args1.resolved_max_inbound_peers(), Some(0));
let args2 = CommandParser::<NetworkArgs>::parse_from(["reth", "--max-peers", "2"]).args;
assert_eq!(args2.resolved_max_outbound_peers(), Some(1));
assert_eq!(args2.resolved_max_inbound_peers(), Some(1));
let args3 = CommandParser::<NetworkArgs>::parse_from(["reth", "--max-peers", "3"]).args;
assert_eq!(args3.resolved_max_outbound_peers(), Some(1));
assert_eq!(args3.resolved_max_inbound_peers(), Some(2));
}
#[test]
fn resolved_peers_without_max_peers() {
let args = CommandParser::<NetworkArgs>::parse_from([
"reth",
"--max-outbound-peers",
"75",
"--max-inbound-peers",
"15",
])
.args;
assert_eq!(args.max_peers, None);
assert_eq!(args.resolved_max_outbound_peers(), Some(75));
assert_eq!(args.resolved_max_inbound_peers(), Some(15));
}
#[test]
fn resolved_peers_with_defaults() {
let args = CommandParser::<NetworkArgs>::parse_from(["reth"]).args;
assert_eq!(args.max_peers, None);
assert_eq!(args.resolved_max_outbound_peers(), None);
assert_eq!(args.resolved_max_inbound_peers(), None);
}
#[test]
fn network_args_default_sanity_test() {
let default_args = NetworkArgs::default();

View File

@@ -7,7 +7,7 @@ use crate::args::{
use alloy_primitives::Address;
use alloy_rpc_types_engine::JwtSecret;
use clap::{
builder::{PossibleValue, RangedU64ValueParser, TypedValueParser},
builder::{PossibleValue, RangedU64ValueParser, Resettable, TypedValueParser},
Arg, Args, Command,
};
use rand::Rng;
@@ -19,12 +19,16 @@ use std::{
ffi::OsStr,
net::{IpAddr, Ipv4Addr},
path::PathBuf,
sync::OnceLock,
time::Duration,
};
use url::Url;
use super::types::MaxOr;
/// Global static RPC server defaults
static RPC_SERVER_DEFAULTS: OnceLock<DefaultRpcServerArgs> = OnceLock::new();
/// Default max number of subscriptions per connection.
pub(crate) const RPC_DEFAULT_MAX_SUBS_PER_CONN: u32 = 1024;
@@ -42,74 +46,437 @@ pub(crate) const RPC_DEFAULT_MAX_RESPONSE_SIZE_MB: u32 = 160;
/// Once exceeded, the server can reject new connections.
pub(crate) const RPC_DEFAULT_MAX_CONNECTIONS: u32 = 500;
/// Default values for RPC server that can be customized
///
/// Global defaults can be set via [`DefaultRpcServerArgs::try_init`].
#[derive(Debug, Clone)]
pub struct DefaultRpcServerArgs {
http: bool,
http_addr: IpAddr,
http_port: u16,
http_disable_compression: bool,
http_api: Option<RpcModuleSelection>,
http_corsdomain: Option<String>,
ws: bool,
ws_addr: IpAddr,
ws_port: u16,
ws_allowed_origins: Option<String>,
ws_api: Option<RpcModuleSelection>,
ipcdisable: bool,
ipcpath: String,
ipc_socket_permissions: Option<String>,
auth_addr: IpAddr,
auth_port: u16,
auth_jwtsecret: Option<PathBuf>,
auth_ipc: bool,
auth_ipc_path: String,
disable_auth_server: bool,
rpc_jwtsecret: Option<JwtSecret>,
rpc_max_request_size: MaxU32,
rpc_max_response_size: MaxU32,
rpc_max_subscriptions_per_connection: MaxU32,
rpc_max_connections: MaxU32,
rpc_max_tracing_requests: usize,
rpc_max_blocking_io_requests: usize,
rpc_max_trace_filter_blocks: u64,
rpc_max_blocks_per_filter: ZeroAsNoneU64,
rpc_max_logs_per_response: ZeroAsNoneU64,
rpc_gas_cap: u64,
rpc_evm_memory_limit: u64,
rpc_tx_fee_cap: u128,
rpc_max_simulate_blocks: u64,
rpc_eth_proof_window: u64,
rpc_proof_permits: usize,
rpc_pending_block: PendingBlockKind,
rpc_forwarder: Option<Url>,
builder_disallow: Option<HashSet<Address>>,
rpc_state_cache: RpcStateCacheArgs,
gas_price_oracle: GasPriceOracleArgs,
rpc_send_raw_transaction_sync_timeout: Duration,
}
impl DefaultRpcServerArgs {
/// Initialize the global RPC server defaults with this configuration
pub fn try_init(self) -> Result<(), Self> {
RPC_SERVER_DEFAULTS.set(self)
}
/// Get a reference to the global RPC server defaults
pub fn get_global() -> &'static Self {
RPC_SERVER_DEFAULTS.get_or_init(Self::default)
}
/// Set the default HTTP enabled state
pub const fn with_http(mut self, v: bool) -> Self {
self.http = v;
self
}
/// Set the default HTTP address
pub const fn with_http_addr(mut self, v: IpAddr) -> Self {
self.http_addr = v;
self
}
/// Set the default HTTP port
pub const fn with_http_port(mut self, v: u16) -> Self {
self.http_port = v;
self
}
/// Set whether to disable HTTP compression by default
pub const fn with_http_disable_compression(mut self, v: bool) -> Self {
self.http_disable_compression = v;
self
}
/// Set the default HTTP API modules
pub fn with_http_api(mut self, v: Option<RpcModuleSelection>) -> Self {
self.http_api = v;
self
}
/// Set the default HTTP CORS domain
pub fn with_http_corsdomain(mut self, v: Option<String>) -> Self {
self.http_corsdomain = v;
self
}
/// Set the default WS enabled state
pub const fn with_ws(mut self, v: bool) -> Self {
self.ws = v;
self
}
/// Set the default WS address
pub const fn with_ws_addr(mut self, v: IpAddr) -> Self {
self.ws_addr = v;
self
}
/// Set the default WS port
pub const fn with_ws_port(mut self, v: u16) -> Self {
self.ws_port = v;
self
}
/// Set the default WS allowed origins
pub fn with_ws_allowed_origins(mut self, v: Option<String>) -> Self {
self.ws_allowed_origins = v;
self
}
/// Set the default WS API modules
pub fn with_ws_api(mut self, v: Option<RpcModuleSelection>) -> Self {
self.ws_api = v;
self
}
/// Set whether to disable IPC by default
pub const fn with_ipcdisable(mut self, v: bool) -> Self {
self.ipcdisable = v;
self
}
/// Set the default IPC path
pub fn with_ipcpath(mut self, v: String) -> Self {
self.ipcpath = v;
self
}
/// Set the default IPC socket permissions
pub fn with_ipc_socket_permissions(mut self, v: Option<String>) -> Self {
self.ipc_socket_permissions = v;
self
}
/// Set the default auth server address
pub const fn with_auth_addr(mut self, v: IpAddr) -> Self {
self.auth_addr = v;
self
}
/// Set the default auth server port
pub const fn with_auth_port(mut self, v: u16) -> Self {
self.auth_port = v;
self
}
/// Set the default auth JWT secret path
pub fn with_auth_jwtsecret(mut self, v: Option<PathBuf>) -> Self {
self.auth_jwtsecret = v;
self
}
/// Set the default auth IPC enabled state
pub const fn with_auth_ipc(mut self, v: bool) -> Self {
self.auth_ipc = v;
self
}
/// Set the default auth IPC path
pub fn with_auth_ipc_path(mut self, v: String) -> Self {
self.auth_ipc_path = v;
self
}
/// Set whether to disable the auth server by default
pub const fn with_disable_auth_server(mut self, v: bool) -> Self {
self.disable_auth_server = v;
self
}
/// Set the default RPC JWT secret
pub const fn with_rpc_jwtsecret(mut self, v: Option<JwtSecret>) -> Self {
self.rpc_jwtsecret = v;
self
}
/// Set the default max request size
pub const fn with_rpc_max_request_size(mut self, v: MaxU32) -> Self {
self.rpc_max_request_size = v;
self
}
/// Set the default max response size
pub const fn with_rpc_max_response_size(mut self, v: MaxU32) -> Self {
self.rpc_max_response_size = v;
self
}
/// Set the default max subscriptions per connection
pub const fn with_rpc_max_subscriptions_per_connection(mut self, v: MaxU32) -> Self {
self.rpc_max_subscriptions_per_connection = v;
self
}
/// Set the default max connections
pub const fn with_rpc_max_connections(mut self, v: MaxU32) -> Self {
self.rpc_max_connections = v;
self
}
/// Set the default max tracing requests
pub const fn with_rpc_max_tracing_requests(mut self, v: usize) -> Self {
self.rpc_max_tracing_requests = v;
self
}
/// Set the default max blocking IO requests
pub const fn with_rpc_max_blocking_io_requests(mut self, v: usize) -> Self {
self.rpc_max_blocking_io_requests = v;
self
}
/// Set the default max trace filter blocks
pub const fn with_rpc_max_trace_filter_blocks(mut self, v: u64) -> Self {
self.rpc_max_trace_filter_blocks = v;
self
}
/// Set the default max blocks per filter
pub const fn with_rpc_max_blocks_per_filter(mut self, v: ZeroAsNoneU64) -> Self {
self.rpc_max_blocks_per_filter = v;
self
}
/// Set the default max logs per response
pub const fn with_rpc_max_logs_per_response(mut self, v: ZeroAsNoneU64) -> Self {
self.rpc_max_logs_per_response = v;
self
}
/// Set the default gas cap
pub const fn with_rpc_gas_cap(mut self, v: u64) -> Self {
self.rpc_gas_cap = v;
self
}
/// Set the default EVM memory limit
pub const fn with_rpc_evm_memory_limit(mut self, v: u64) -> Self {
self.rpc_evm_memory_limit = v;
self
}
/// Set the default tx fee cap
pub const fn with_rpc_tx_fee_cap(mut self, v: u128) -> Self {
self.rpc_tx_fee_cap = v;
self
}
/// Set the default max simulate blocks
pub const fn with_rpc_max_simulate_blocks(mut self, v: u64) -> Self {
self.rpc_max_simulate_blocks = v;
self
}
/// Set the default eth proof window
pub const fn with_rpc_eth_proof_window(mut self, v: u64) -> Self {
self.rpc_eth_proof_window = v;
self
}
/// Set the default proof permits
pub const fn with_rpc_proof_permits(mut self, v: usize) -> Self {
self.rpc_proof_permits = v;
self
}
/// Set the default pending block kind
pub const fn with_rpc_pending_block(mut self, v: PendingBlockKind) -> Self {
self.rpc_pending_block = v;
self
}
/// Set the default RPC forwarder
pub fn with_rpc_forwarder(mut self, v: Option<Url>) -> Self {
self.rpc_forwarder = v;
self
}
/// Set the default builder disallow addresses
pub fn with_builder_disallow(mut self, v: Option<HashSet<Address>>) -> Self {
self.builder_disallow = v;
self
}
/// Set the default RPC state cache args
pub const fn with_rpc_state_cache(mut self, v: RpcStateCacheArgs) -> Self {
self.rpc_state_cache = v;
self
}
/// Set the default gas price oracle args
pub const fn with_gas_price_oracle(mut self, v: GasPriceOracleArgs) -> Self {
self.gas_price_oracle = v;
self
}
/// Set the default send raw transaction sync timeout
pub const fn with_rpc_send_raw_transaction_sync_timeout(mut self, v: Duration) -> Self {
self.rpc_send_raw_transaction_sync_timeout = v;
self
}
}
impl Default for DefaultRpcServerArgs {
fn default() -> Self {
Self {
http: false,
http_addr: Ipv4Addr::LOCALHOST.into(),
http_port: constants::DEFAULT_HTTP_RPC_PORT,
http_disable_compression: false,
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(),
ipc_socket_permissions: None,
auth_addr: Ipv4Addr::LOCALHOST.into(),
auth_port: constants::DEFAULT_AUTH_PORT,
auth_jwtsecret: None,
auth_ipc: false,
auth_ipc_path: constants::DEFAULT_ENGINE_API_IPC_ENDPOINT.to_string(),
disable_auth_server: false,
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_blocking_io_requests: constants::DEFAULT_MAX_BLOCKING_IO_REQUEST,
rpc_max_trace_filter_blocks: constants::DEFAULT_MAX_TRACE_FILTER_BLOCKS,
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: constants::gas_oracle::RPC_DEFAULT_GAS_CAP,
rpc_evm_memory_limit: (1 << 32) - 1,
rpc_tx_fee_cap: constants::DEFAULT_TX_FEE_CAP_WEI,
rpc_max_simulate_blocks: constants::DEFAULT_MAX_SIMULATE_BLOCKS,
rpc_eth_proof_window: constants::DEFAULT_ETH_PROOF_WINDOW,
rpc_proof_permits: constants::DEFAULT_PROOF_PERMITS,
rpc_pending_block: PendingBlockKind::Full,
rpc_forwarder: None,
builder_disallow: None,
rpc_state_cache: RpcStateCacheArgs::default(),
gas_price_oracle: GasPriceOracleArgs::default(),
rpc_send_raw_transaction_sync_timeout:
constants::RPC_DEFAULT_SEND_RAW_TX_SYNC_TIMEOUT_SECS,
}
}
}
/// Parameters for configuring the rpc more granularity via CLI
#[derive(Debug, Clone, Args, PartialEq, Eq)]
#[command(next_help_heading = "RPC")]
pub struct RpcServerArgs {
/// Enable the HTTP-RPC server
#[arg(long, default_value_if("dev", "true", "true"))]
#[arg(long, default_value_if("dev", "true", "true"), default_value_t = DefaultRpcServerArgs::get_global().http)]
pub http: bool,
/// Http server address to listen on
#[arg(long = "http.addr", default_value_t = IpAddr::V4(Ipv4Addr::LOCALHOST))]
#[arg(long = "http.addr", default_value_t = DefaultRpcServerArgs::get_global().http_addr)]
pub http_addr: IpAddr,
/// Http server port to listen on
#[arg(long = "http.port", default_value_t = constants::DEFAULT_HTTP_RPC_PORT)]
#[arg(long = "http.port", default_value_t = DefaultRpcServerArgs::get_global().http_port)]
pub http_port: u16,
/// Disable compression for HTTP responses
#[arg(long = "http.disable-compression", default_value_t = false)]
#[arg(long = "http.disable-compression", default_value_t = DefaultRpcServerArgs::get_global().http_disable_compression)]
pub http_disable_compression: bool,
/// Rpc Modules to be configured for the HTTP server
#[arg(long = "http.api", value_parser = RpcModuleSelectionValueParser::default())]
#[arg(long = "http.api", value_parser = RpcModuleSelectionValueParser::default(), default_value = Resettable::from(DefaultRpcServerArgs::get_global().http_api.as_ref().map(|v| v.to_string().into())))]
pub http_api: Option<RpcModuleSelection>,
/// Http Corsdomain to allow request from
#[arg(long = "http.corsdomain")]
#[arg(long = "http.corsdomain", default_value = Resettable::from(DefaultRpcServerArgs::get_global().http_corsdomain.as_ref().map(|v| v.to_string().into())))]
pub http_corsdomain: Option<String>,
/// Enable the WS-RPC server
#[arg(long)]
#[arg(long, default_value_t = DefaultRpcServerArgs::get_global().ws)]
pub ws: bool,
/// Ws server address to listen on
#[arg(long = "ws.addr", default_value_t = IpAddr::V4(Ipv4Addr::LOCALHOST))]
#[arg(long = "ws.addr", default_value_t = DefaultRpcServerArgs::get_global().ws_addr)]
pub ws_addr: IpAddr,
/// Ws server port to listen on
#[arg(long = "ws.port", default_value_t = constants::DEFAULT_WS_RPC_PORT)]
#[arg(long = "ws.port", default_value_t = DefaultRpcServerArgs::get_global().ws_port)]
pub ws_port: u16,
/// Origins from which to accept `WebSocket` requests
#[arg(id = "ws.origins", long = "ws.origins", alias = "ws.corsdomain")]
#[arg(id = "ws.origins", long = "ws.origins", alias = "ws.corsdomain", default_value = Resettable::from(DefaultRpcServerArgs::get_global().ws_allowed_origins.as_ref().map(|v| v.to_string().into())))]
pub ws_allowed_origins: Option<String>,
/// Rpc Modules to be configured for the WS server
#[arg(long = "ws.api", value_parser = RpcModuleSelectionValueParser::default())]
#[arg(long = "ws.api", value_parser = RpcModuleSelectionValueParser::default(), default_value = Resettable::from(DefaultRpcServerArgs::get_global().ws_api.as_ref().map(|v| v.to_string().into())))]
pub ws_api: Option<RpcModuleSelection>,
/// Disable the IPC-RPC server
#[arg(long)]
#[arg(long, default_value_t = DefaultRpcServerArgs::get_global().ipcdisable)]
pub ipcdisable: bool,
/// Filename for IPC socket/pipe within the datadir
#[arg(long, default_value_t = constants::DEFAULT_IPC_ENDPOINT.to_string())]
#[arg(long, default_value_t = DefaultRpcServerArgs::get_global().ipcpath.clone())]
pub ipcpath: String,
/// Set the permissions for the IPC socket file, in octal format.
///
/// If not specified, the permissions will be set by the system's umask.
#[arg(long = "ipc.permissions")]
#[arg(long = "ipc.permissions", default_value = Resettable::from(DefaultRpcServerArgs::get_global().ipc_socket_permissions.as_ref().map(|v| v.to_string().into())))]
pub ipc_socket_permissions: Option<String>,
/// Auth server address to listen on
#[arg(long = "authrpc.addr", default_value_t = IpAddr::V4(Ipv4Addr::LOCALHOST))]
#[arg(long = "authrpc.addr", default_value_t = DefaultRpcServerArgs::get_global().auth_addr)]
pub auth_addr: IpAddr,
/// Auth server port to listen on
#[arg(long = "authrpc.port", default_value_t = constants::DEFAULT_AUTH_PORT)]
#[arg(long = "authrpc.port", default_value_t = DefaultRpcServerArgs::get_global().auth_port)]
pub auth_port: u16,
/// Path to a JWT secret to use for the authenticated engine-API RPC server.
@@ -118,22 +485,22 @@ pub struct RpcServerArgs {
///
/// If no path is provided, a secret will be generated and stored in the datadir under
/// `<DIR>/<CHAIN_ID>/jwt.hex`. For mainnet this would be `~/.reth/mainnet/jwt.hex` by default.
#[arg(long = "authrpc.jwtsecret", value_name = "PATH", global = true, required = false)]
#[arg(long = "authrpc.jwtsecret", value_name = "PATH", global = true, required = false, default_value = Resettable::from(DefaultRpcServerArgs::get_global().auth_jwtsecret.as_ref().map(|v| v.to_string_lossy().into())))]
pub auth_jwtsecret: Option<PathBuf>,
/// Enable auth engine API over IPC
#[arg(long)]
#[arg(long, default_value_t = DefaultRpcServerArgs::get_global().auth_ipc)]
pub auth_ipc: bool,
/// Filename for auth IPC socket/pipe within the datadir
#[arg(long = "auth-ipc.path", default_value_t = constants::DEFAULT_ENGINE_API_IPC_ENDPOINT.to_string())]
#[arg(long = "auth-ipc.path", default_value_t = DefaultRpcServerArgs::get_global().auth_ipc_path.clone())]
pub auth_ipc_path: String,
/// Disable the auth/engine API server.
///
/// This will prevent the authenticated engine-API server from starting. Use this if you're
/// running a node that doesn't need to serve engine API requests.
#[arg(long = "disable-auth-server", alias = "disable-engine-api")]
#[arg(long = "disable-auth-server", alias = "disable-engine-api", default_value_t = DefaultRpcServerArgs::get_global().disable_auth_server)]
pub disable_auth_server: bool,
/// Hex encoded JWT secret to authenticate the regular RPC server(s), see `--http.api` and
@@ -141,23 +508,23 @@ pub struct RpcServerArgs {
///
/// This is __not__ used for the authenticated engine-API RPC server, see
/// `--authrpc.jwtsecret`.
#[arg(long = "rpc.jwtsecret", value_name = "HEX", global = true, required = false)]
#[arg(long = "rpc.jwtsecret", value_name = "HEX", global = true, required = false, default_value = Resettable::from(DefaultRpcServerArgs::get_global().rpc_jwtsecret.as_ref().map(|v| format!("{:?}", v).into())))]
pub rpc_jwtsecret: Option<JwtSecret>,
/// Set the maximum RPC request payload size for both HTTP and WS in megabytes.
#[arg(long = "rpc.max-request-size", alias = "rpc-max-request-size", default_value_t = RPC_DEFAULT_MAX_REQUEST_SIZE_MB.into())]
#[arg(long = "rpc.max-request-size", alias = "rpc-max-request-size", default_value_t = DefaultRpcServerArgs::get_global().rpc_max_request_size)]
pub rpc_max_request_size: MaxU32,
/// Set the maximum RPC response payload size for both HTTP and WS in megabytes.
#[arg(long = "rpc.max-response-size", alias = "rpc-max-response-size", visible_alias = "rpc.returndata.limit", default_value_t = RPC_DEFAULT_MAX_RESPONSE_SIZE_MB.into())]
#[arg(long = "rpc.max-response-size", alias = "rpc-max-response-size", visible_alias = "rpc.returndata.limit", default_value_t = DefaultRpcServerArgs::get_global().rpc_max_response_size)]
pub rpc_max_response_size: MaxU32,
/// Set the maximum concurrent subscriptions per connection.
#[arg(long = "rpc.max-subscriptions-per-connection", alias = "rpc-max-subscriptions-per-connection", default_value_t = RPC_DEFAULT_MAX_SUBS_PER_CONN.into())]
#[arg(long = "rpc.max-subscriptions-per-connection", alias = "rpc-max-subscriptions-per-connection", default_value_t = DefaultRpcServerArgs::get_global().rpc_max_subscriptions_per_connection)]
pub rpc_max_subscriptions_per_connection: MaxU32,
/// Maximum number of RPC server connections.
#[arg(long = "rpc.max-connections", alias = "rpc-max-connections", value_name = "COUNT", default_value_t = RPC_DEFAULT_MAX_CONNECTIONS.into())]
#[arg(long = "rpc.max-connections", alias = "rpc-max-connections", value_name = "COUNT", default_value_t = DefaultRpcServerArgs::get_global().rpc_max_connections)]
pub rpc_max_connections: MaxU32,
/// Maximum number of concurrent tracing requests.
@@ -166,7 +533,7 @@ pub struct RpcServerArgs {
/// Tracing requests are generally CPU bound.
/// Choosing a value that is higher than the available CPU cores can have a negative impact on
/// the performance of the node and affect the node's ability to maintain sync.
#[arg(long = "rpc.max-tracing-requests", alias = "rpc-max-tracing-requests", value_name = "COUNT", default_value_t = constants::default_max_tracing_requests())]
#[arg(long = "rpc.max-tracing-requests", alias = "rpc-max-tracing-requests", value_name = "COUNT", default_value_t = DefaultRpcServerArgs::get_global().rpc_max_tracing_requests)]
pub rpc_max_tracing_requests: usize,
/// Maximum number of concurrent blocking IO requests.
@@ -174,19 +541,19 @@ pub struct RpcServerArgs {
/// Blocking IO requests include `eth_call`, `eth_estimateGas`, and similar methods that
/// require EVM execution. These are spawned as blocking tasks to avoid blocking the async
/// runtime.
#[arg(long = "rpc.max-blocking-io-requests", alias = "rpc-max-blocking-io-requests", value_name = "COUNT", default_value_t = constants::DEFAULT_MAX_BLOCKING_IO_REQUEST)]
#[arg(long = "rpc.max-blocking-io-requests", alias = "rpc-max-blocking-io-requests", value_name = "COUNT", default_value_t = DefaultRpcServerArgs::get_global().rpc_max_blocking_io_requests)]
pub rpc_max_blocking_io_requests: usize,
/// Maximum number of blocks for `trace_filter` requests.
#[arg(long = "rpc.max-trace-filter-blocks", alias = "rpc-max-trace-filter-blocks", value_name = "COUNT", default_value_t = constants::DEFAULT_MAX_TRACE_FILTER_BLOCKS)]
#[arg(long = "rpc.max-trace-filter-blocks", alias = "rpc-max-trace-filter-blocks", value_name = "COUNT", default_value_t = DefaultRpcServerArgs::get_global().rpc_max_trace_filter_blocks)]
pub rpc_max_trace_filter_blocks: u64,
/// Maximum number of blocks that could be scanned per filter request. (0 = entire chain)
#[arg(long = "rpc.max-blocks-per-filter", alias = "rpc-max-blocks-per-filter", value_name = "COUNT", default_value_t = ZeroAsNoneU64::new(constants::DEFAULT_MAX_BLOCKS_PER_FILTER))]
#[arg(long = "rpc.max-blocks-per-filter", alias = "rpc-max-blocks-per-filter", value_name = "COUNT", default_value_t = DefaultRpcServerArgs::get_global().rpc_max_blocks_per_filter)]
pub rpc_max_blocks_per_filter: ZeroAsNoneU64,
/// Maximum number of logs that can be returned in a single response. (0 = no limit)
#[arg(long = "rpc.max-logs-per-response", alias = "rpc-max-logs-per-response", value_name = "COUNT", default_value_t = ZeroAsNoneU64::new(constants::DEFAULT_MAX_LOGS_PER_RESPONSE as u64))]
#[arg(long = "rpc.max-logs-per-response", alias = "rpc-max-logs-per-response", value_name = "COUNT", default_value_t = DefaultRpcServerArgs::get_global().rpc_max_logs_per_response)]
pub rpc_max_logs_per_response: ZeroAsNoneU64,
/// Maximum gas limit for `eth_call` and call tracing RPC methods.
@@ -195,7 +562,7 @@ pub struct RpcServerArgs {
alias = "rpc-gascap",
value_name = "GAS_CAP",
value_parser = MaxOr::new(RangedU64ValueParser::<u64>::new().range(1..)),
default_value_t = constants::gas_oracle::RPC_DEFAULT_GAS_CAP
default_value_t = DefaultRpcServerArgs::get_global().rpc_gas_cap
)]
pub rpc_gas_cap: u64,
@@ -205,7 +572,7 @@ pub struct RpcServerArgs {
alias = "rpc-evm-memory-limit",
value_name = "MEMORY_LIMIT",
value_parser = MaxOr::new(RangedU64ValueParser::<u64>::new().range(1..)),
default_value_t = (1 << 32) - 1
default_value_t = DefaultRpcServerArgs::get_global().rpc_evm_memory_limit
)]
pub rpc_evm_memory_limit: u64,
@@ -223,7 +590,7 @@ pub struct RpcServerArgs {
#[arg(
long = "rpc.max-simulate-blocks",
value_name = "BLOCKS_COUNT",
default_value_t = constants::DEFAULT_MAX_SIMULATE_BLOCKS
default_value_t = DefaultRpcServerArgs::get_global().rpc_max_simulate_blocks
)]
pub rpc_max_simulate_blocks: u64,
@@ -232,7 +599,7 @@ pub struct RpcServerArgs {
/// configured number of blocks from current tip (up to `tip - window`).
#[arg(
long = "rpc.eth-proof-window",
default_value_t = constants::DEFAULT_ETH_PROOF_WINDOW,
default_value_t = DefaultRpcServerArgs::get_global().rpc_eth_proof_window,
value_parser = RangedU64ValueParser::<u64>::new().range(..=constants::MAX_ETH_PROOF_WINDOW)
)]
pub rpc_eth_proof_window: u64,
@@ -254,7 +621,7 @@ pub struct RpcServerArgs {
/// Path to file containing disallowed addresses, json-encoded list of strings. Block
/// validation API will reject blocks containing transactions from these addresses.
#[arg(long = "builder.disallow", value_name = "PATH", value_parser = reth_cli_util::parsers::read_json_from_file::<HashSet<Address>>)]
#[arg(long = "builder.disallow", value_name = "PATH", value_parser = reth_cli_util::parsers::read_json_from_file::<HashSet<Address>>, default_value = Resettable::from(DefaultRpcServerArgs::get_global().builder_disallow.as_ref().map(|v| format!("{:?}", v).into())))]
pub builder_disallow: Option<HashSet<Address>>,
/// State cache configuration.
@@ -398,50 +765,93 @@ impl RpcServerArgs {
impl Default for RpcServerArgs {
fn default() -> Self {
let DefaultRpcServerArgs {
http,
http_addr,
http_port,
http_disable_compression,
http_api,
http_corsdomain,
ws,
ws_addr,
ws_port,
ws_allowed_origins,
ws_api,
ipcdisable,
ipcpath,
ipc_socket_permissions,
auth_addr,
auth_port,
auth_jwtsecret,
auth_ipc,
auth_ipc_path,
disable_auth_server,
rpc_jwtsecret,
rpc_max_request_size,
rpc_max_response_size,
rpc_max_subscriptions_per_connection,
rpc_max_connections,
rpc_max_tracing_requests,
rpc_max_blocking_io_requests,
rpc_max_trace_filter_blocks,
rpc_max_blocks_per_filter,
rpc_max_logs_per_response,
rpc_gas_cap,
rpc_evm_memory_limit,
rpc_tx_fee_cap,
rpc_max_simulate_blocks,
rpc_eth_proof_window,
rpc_proof_permits,
rpc_pending_block,
rpc_forwarder,
builder_disallow,
rpc_state_cache,
gas_price_oracle,
rpc_send_raw_transaction_sync_timeout,
} = DefaultRpcServerArgs::get_global().clone();
Self {
http: false,
http_addr: Ipv4Addr::LOCALHOST.into(),
http_port: constants::DEFAULT_HTTP_RPC_PORT,
http_disable_compression: false,
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(),
ipc_socket_permissions: None,
auth_addr: Ipv4Addr::LOCALHOST.into(),
auth_port: constants::DEFAULT_AUTH_PORT,
auth_jwtsecret: None,
auth_ipc: false,
auth_ipc_path: constants::DEFAULT_ENGINE_API_IPC_ENDPOINT.to_string(),
disable_auth_server: false,
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_blocking_io_requests: constants::DEFAULT_MAX_BLOCKING_IO_REQUEST,
rpc_max_trace_filter_blocks: constants::DEFAULT_MAX_TRACE_FILTER_BLOCKS,
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: constants::gas_oracle::RPC_DEFAULT_GAS_CAP,
rpc_evm_memory_limit: (1 << 32) - 1,
rpc_tx_fee_cap: constants::DEFAULT_TX_FEE_CAP_WEI,
rpc_max_simulate_blocks: constants::DEFAULT_MAX_SIMULATE_BLOCKS,
rpc_eth_proof_window: constants::DEFAULT_ETH_PROOF_WINDOW,
rpc_pending_block: PendingBlockKind::Full,
gas_price_oracle: GasPriceOracleArgs::default(),
rpc_state_cache: RpcStateCacheArgs::default(),
rpc_proof_permits: constants::DEFAULT_PROOF_PERMITS,
rpc_forwarder: None,
builder_disallow: Default::default(),
rpc_send_raw_transaction_sync_timeout:
constants::RPC_DEFAULT_SEND_RAW_TX_SYNC_TIMEOUT_SECS,
http,
http_addr,
http_port,
http_disable_compression,
http_api,
http_corsdomain,
ws,
ws_addr,
ws_port,
ws_allowed_origins,
ws_api,
ipcdisable,
ipcpath,
ipc_socket_permissions,
auth_addr,
auth_port,
auth_jwtsecret,
auth_ipc,
auth_ipc_path,
disable_auth_server,
rpc_jwtsecret,
rpc_max_request_size,
rpc_max_response_size,
rpc_max_subscriptions_per_connection,
rpc_max_connections,
rpc_max_tracing_requests,
rpc_max_blocking_io_requests,
rpc_max_trace_filter_blocks,
rpc_max_blocks_per_filter,
rpc_max_logs_per_response,
rpc_gas_cap,
rpc_evm_memory_limit,
rpc_tx_fee_cap,
rpc_max_simulate_blocks,
rpc_eth_proof_window,
rpc_proof_permits,
rpc_pending_block,
rpc_forwarder,
builder_disallow,
rpc_state_cache,
gas_price_oracle,
rpc_send_raw_transaction_sync_timeout,
}
}
}
@@ -554,4 +964,159 @@ mod tests {
let expected = 1_000_000_000_000_000_000u128;
assert_eq!(args.rpc_tx_fee_cap, expected); // 1 ETH default cap
}
#[test]
fn test_rpc_server_args() {
let args = RpcServerArgs {
http: true,
http_addr: "127.0.0.1".parse().unwrap(),
http_port: 8545,
http_disable_compression: false,
http_api: Some(RpcModuleSelection::try_from_selection(["eth", "admin"]).unwrap()),
http_corsdomain: Some("*".to_string()),
ws: true,
ws_addr: "127.0.0.1".parse().unwrap(),
ws_port: 8546,
ws_allowed_origins: Some("*".to_string()),
ws_api: Some(RpcModuleSelection::try_from_selection(["eth", "admin"]).unwrap()),
ipcdisable: false,
ipcpath: "reth.ipc".to_string(),
ipc_socket_permissions: Some("0o666".to_string()),
auth_addr: "127.0.0.1".parse().unwrap(),
auth_port: 8551,
auth_jwtsecret: Some(std::path::PathBuf::from("/tmp/jwt.hex")),
auth_ipc: false,
auth_ipc_path: "engine.ipc".to_string(),
disable_auth_server: false,
rpc_jwtsecret: Some(
JwtSecret::from_hex(
"0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef",
)
.unwrap(),
),
rpc_max_request_size: 15u32.into(),
rpc_max_response_size: 160u32.into(),
rpc_max_subscriptions_per_connection: 1024u32.into(),
rpc_max_connections: 500u32.into(),
rpc_max_tracing_requests: 16,
rpc_max_blocking_io_requests: 256,
rpc_max_trace_filter_blocks: 4000,
rpc_max_blocks_per_filter: 1000u64.into(),
rpc_max_logs_per_response: 10000u64.into(),
rpc_gas_cap: 50_000_000,
rpc_evm_memory_limit: 256,
rpc_tx_fee_cap: 2_000_000_000_000_000_000u128,
rpc_max_simulate_blocks: 256,
rpc_eth_proof_window: 100_000,
rpc_proof_permits: 16,
rpc_pending_block: PendingBlockKind::Full,
rpc_forwarder: Some("http://localhost:8545".parse().unwrap()),
builder_disallow: None,
rpc_state_cache: RpcStateCacheArgs {
max_blocks: 5000,
max_receipts: 2000,
max_headers: 1000,
max_concurrent_db_requests: 512,
},
gas_price_oracle: GasPriceOracleArgs {
blocks: 20,
ignore_price: 2,
max_price: 500_000_000_000,
percentile: 60,
default_suggested_fee: None,
},
rpc_send_raw_transaction_sync_timeout: std::time::Duration::from_secs(30),
};
let parsed_args = CommandParser::<RpcServerArgs>::parse_from([
"reth",
"--http",
"--http.addr",
"127.0.0.1",
"--http.port",
"8545",
"--http.api",
"eth,admin",
"--http.corsdomain",
"*",
"--ws",
"--ws.addr",
"127.0.0.1",
"--ws.port",
"8546",
"--ws.origins",
"*",
"--ws.api",
"eth,admin",
"--ipcpath",
"reth.ipc",
"--ipc.permissions",
"0o666",
"--authrpc.addr",
"127.0.0.1",
"--authrpc.port",
"8551",
"--authrpc.jwtsecret",
"/tmp/jwt.hex",
"--auth-ipc.path",
"engine.ipc",
"--rpc.jwtsecret",
"0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef",
"--rpc.max-request-size",
"15",
"--rpc.max-response-size",
"160",
"--rpc.max-subscriptions-per-connection",
"1024",
"--rpc.max-connections",
"500",
"--rpc.max-tracing-requests",
"16",
"--rpc.max-blocking-io-requests",
"256",
"--rpc.max-trace-filter-blocks",
"4000",
"--rpc.max-blocks-per-filter",
"1000",
"--rpc.max-logs-per-response",
"10000",
"--rpc.gascap",
"50000000",
"--rpc.evm-memory-limit",
"256",
"--rpc.txfeecap",
"2.0",
"--rpc.max-simulate-blocks",
"256",
"--rpc.eth-proof-window",
"100000",
"--rpc.proof-permits",
"16",
"--rpc.pending-block",
"full",
"--rpc.forwarder",
"http://localhost:8545",
"--rpc-cache.max-blocks",
"5000",
"--rpc-cache.max-receipts",
"2000",
"--rpc-cache.max-headers",
"1000",
"--rpc-cache.max-concurrent-db-requests",
"512",
"--gpo.blocks",
"20",
"--gpo.ignoreprice",
"2",
"--gpo.maxprice",
"500000000000",
"--gpo.percentile",
"60",
"--rpc.send-raw-transaction-sync-timeout",
"30s",
])
.args;
assert_eq!(parsed_args, args);
}
}

View File

@@ -3,7 +3,7 @@
use crate::{MessageValidationKind, PayloadAttributes};
use alloc::vec::Vec;
use alloy_eips::{eip1898::BlockWithParent, eip4895::Withdrawal, eip7685::Requests, BlockNumHash};
use alloy_primitives::B256;
use alloy_primitives::{Bytes, B256};
use alloy_rpc_types_engine::ExecutionData;
use core::fmt::Debug;
use serde::{de::DeserializeOwned, Serialize};
@@ -40,6 +40,11 @@ pub trait ExecutionPayload:
/// Returns `None` for pre-Shanghai blocks.
fn withdrawals(&self) -> Option<&Vec<Withdrawal>>;
/// Returns the access list included in this payload.
///
/// Returns `None` for pre-Amsterdam blocks.
fn block_access_list(&self) -> Option<&Bytes>;
/// Returns the beacon block root associated with this payload.
///
/// Returns `None` for pre-merge payloads.
@@ -69,6 +74,10 @@ impl ExecutionPayload for ExecutionData {
self.payload.withdrawals()
}
fn block_access_list(&self) -> Option<&Bytes> {
None
}
fn parent_beacon_block_root(&self) -> Option<B256> {
self.sidecar.parent_beacon_block_root()
}
@@ -172,6 +181,10 @@ impl ExecutionPayload for op_alloy_rpc_types_engine::OpExecutionData {
self.payload.as_v2().map(|p| &p.withdrawals)
}
fn block_access_list(&self) -> Option<&Bytes> {
None
}
fn parent_beacon_block_root(&self) -> Option<B256> {
self.sidecar.parent_beacon_block_root()
}

View File

@@ -139,7 +139,7 @@ impl RethRpcServerConfig for RpcServerArgs {
fn transport_rpc_module_config(&self) -> TransportRpcModuleConfig {
let mut config = TransportRpcModuleConfig::default()
.with_config(RpcModuleConfig::new(self.eth_config(), self.flashbots_config()));
.with_config(RpcModuleConfig::new(self.eth_config()));
if self.http {
config = config.with_http(

View File

@@ -38,7 +38,7 @@ use reth_network_api::{noop::NoopNetwork, NetworkInfo, Peers};
use reth_primitives_traits::{NodePrimitives, TxTy};
use reth_rpc::{
AdminApi, DebugApi, EngineEthApi, EthApi, EthApiBuilder, EthBundle, MinerApi, NetApi,
OtterscanApi, RPCApi, RethApi, TraceApi, TxPoolApi, ValidationApiConfig, Web3Api,
OtterscanApi, RPCApi, RethApi, TraceApi, TxPoolApi, Web3Api,
};
use reth_rpc_api::servers::*;
use reth_rpc_eth_api::{
@@ -413,8 +413,6 @@ impl<N: NodePrimitives> Default for RpcModuleBuilder<N, (), (), (), (), ()> {
pub struct RpcModuleConfig {
/// `eth` namespace settings
eth: EthConfig,
/// `flashbots` namespace settings
flashbots: ValidationApiConfig,
}
// === impl RpcModuleConfig ===
@@ -426,8 +424,8 @@ impl RpcModuleConfig {
}
/// Returns a new RPC module config given the eth namespace config
pub const fn new(eth: EthConfig, flashbots: ValidationApiConfig) -> Self {
Self { eth, flashbots }
pub const fn new(eth: EthConfig) -> Self {
Self { eth }
}
/// Get a reference to the eth namespace config
@@ -445,7 +443,6 @@ impl RpcModuleConfig {
#[derive(Clone, Debug, Default)]
pub struct RpcModuleConfigBuilder {
eth: Option<EthConfig>,
flashbots: Option<ValidationApiConfig>,
}
// === impl RpcModuleConfigBuilder ===
@@ -457,16 +454,10 @@ impl RpcModuleConfigBuilder {
self
}
/// Configures a custom flashbots namespace config
pub fn flashbots(mut self, flashbots: ValidationApiConfig) -> Self {
self.flashbots = Some(flashbots);
self
}
/// Consumes the type and creates the [`RpcModuleConfig`]
pub fn build(self) -> RpcModuleConfig {
let Self { eth, flashbots } = self;
RpcModuleConfig { eth: eth.unwrap_or_default(), flashbots: flashbots.unwrap_or_default() }
let Self { eth } = self;
RpcModuleConfig { eth: eth.unwrap_or_default() }
}
/// Get a reference to the eth namespace config, if any

View File

@@ -29,7 +29,7 @@ use reth_revm::{cancelled::CancelOnDrop, database::StateProviderDatabase, db::St
use reth_rpc_convert::{RpcConvert, RpcTxReq};
use reth_rpc_eth_types::{
cache::db::StateProviderTraitObjWrapper,
error::FromEthApiError,
error::{AsEthApiError, FromEthApiError},
simulate::{self, EthSimulateError},
EthApiError, StateCacheDb,
};
@@ -159,6 +159,13 @@ pub trait EthCall: EstimateCall + Call + LoadPendingBlock + LoadBlock + FullEthA
.context_for_next_block(&parent, this.next_env_attributes(&parent)?)
.map_err(RethError::other)
.map_err(Self::Error::from_eth_err)?;
let map_err = |e: EthApiError| -> Self::Error {
match e.as_simulate_error() {
Some(sim_err) => Self::Error::from_eth_err(EthApiError::other(sim_err)),
None => Self::Error::from_eth_err(e),
}
};
let (result, results) = if trace_transfers {
// prepare inspector to capture transfer inside the evm so they are recorded
// and included in logs
@@ -173,7 +180,8 @@ pub trait EthCall: EstimateCall + Call + LoadPendingBlock + LoadBlock + FullEthA
default_gas_limit,
chain_id,
this.converter(),
)?
)
.map_err(map_err)?
} else {
let evm = this.evm_config().evm_with_env(&mut db, evm_env);
let builder = this.evm_config().create_block_builder(evm, &parent, ctx);
@@ -183,7 +191,8 @@ pub trait EthCall: EstimateCall + Call + LoadPendingBlock + LoadBlock + FullEthA
default_gas_limit,
chain_id,
this.converter(),
)?
)
.map_err(map_err)?
};
parent = result.block.clone_sealed_header();

View File

@@ -420,7 +420,7 @@ impl<H: BlockHeader> BuildPendingEnv<H> for NextBlockEnvAttributes {
gas_limit: parent.gas_limit(),
parent_beacon_block_root: parent.parent_beacon_block_root(),
withdrawals: parent.withdrawals_root().map(|_| Default::default()),
extra_data: None,
extra_data: parent.extra_data().clone(),
}
}
}

View File

@@ -1,7 +1,7 @@
//! Helper traits to wrap generic l1 errors, in network specific error type configured in
//! `reth_rpc_eth_api::EthApiTypes`.
use crate::{EthApiError, RevertError};
use crate::{simulate::EthSimulateError, EthApiError, RevertError};
use alloy_primitives::Bytes;
use reth_errors::ProviderError;
use reth_evm::{ConfigureEvm, EvmErrorFor, HaltReasonFor};
@@ -74,6 +74,32 @@ pub trait AsEthApiError {
false
}
/// Returns [`EthSimulateError`] if this error maps to a simulate-specific error code.
fn as_simulate_error(&self) -> Option<EthSimulateError> {
let err = self.as_err()?;
match err {
EthApiError::InvalidTransaction(tx_err) => match tx_err {
RpcInvalidTransactionError::NonceTooLow { tx, state } => {
Some(EthSimulateError::NonceTooLow { tx: *tx, state: *state })
}
RpcInvalidTransactionError::NonceTooHigh => Some(EthSimulateError::NonceTooHigh),
RpcInvalidTransactionError::FeeCapTooLow => {
Some(EthSimulateError::BaseFeePerGasTooLow)
}
RpcInvalidTransactionError::GasTooLow => Some(EthSimulateError::IntrinsicGasTooLow),
RpcInvalidTransactionError::InsufficientFunds { cost, balance } => {
Some(EthSimulateError::InsufficientFunds { cost: *cost, balance: *balance })
}
RpcInvalidTransactionError::SenderNoEOA => Some(EthSimulateError::SenderNotEOA),
RpcInvalidTransactionError::MaxInitCodeSizeExceeded => {
Some(EthSimulateError::MaxInitCodeSizeExceeded)
}
_ => None,
},
_ => None,
}
}
}
impl AsEthApiError for EthApiError {

View File

@@ -23,7 +23,7 @@ use reth_storage_api::noop::NoopProvider;
use revm::{
context::Block,
context_interface::result::ExecutionResult,
primitives::{Address, Bytes, TxKind},
primitives::{Address, Bytes, TxKind, U256},
Database,
};
@@ -36,12 +36,67 @@ pub enum EthSimulateError {
/// Max gas limit for entire operation exceeded.
#[error("Client adjustable limit reached")]
GasLimitReached,
/// Block number in sequence did not increase.
#[error("Block number in sequence did not increase")]
BlockNumberInvalid,
/// Block timestamp in sequence did not increase or stay the same.
#[error("Block timestamp in sequence did not increase")]
BlockTimestampInvalid,
/// Transaction nonce is too low.
#[error("nonce too low: next nonce {state}, tx nonce {tx}")]
NonceTooLow {
/// Transaction nonce.
tx: u64,
/// Current state nonce.
state: u64,
},
/// Transaction nonce is too high.
#[error("nonce too high")]
NonceTooHigh,
/// Transaction's baseFeePerGas is too low.
#[error("max fee per gas less than block base fee")]
BaseFeePerGasTooLow,
/// Not enough gas provided to pay for intrinsic gas.
#[error("intrinsic gas too low")]
IntrinsicGasTooLow,
/// Insufficient funds to pay for gas fees and value.
#[error("insufficient funds for gas * price + value: have {balance} want {cost}")]
InsufficientFunds {
/// Transaction cost.
cost: U256,
/// Sender balance.
balance: U256,
},
/// Sender is not an EOA.
#[error("sender is not an EOA")]
SenderNotEOA,
/// Max init code size exceeded.
#[error("max initcode size exceeded")]
MaxInitCodeSizeExceeded,
/// `MovePrecompileToAddress` referenced itself in replacement.
#[error("MovePrecompileToAddress referenced itself")]
PrecompileSelfReference,
/// Multiple `MovePrecompileToAddress` referencing the same address.
#[error("Multiple MovePrecompileToAddress referencing the same address")]
PrecompileDuplicateAddress,
}
impl EthSimulateError {
const fn error_code(&self) -> i32 {
/// Returns the JSON-RPC error code for a `eth_simulateV1` error.
pub const fn error_code(&self) -> i32 {
match self {
Self::NonceTooLow { .. } => -38010,
Self::NonceTooHigh => -38011,
Self::BaseFeePerGasTooLow => -38012,
Self::IntrinsicGasTooLow => -38013,
Self::InsufficientFunds { .. } => -38014,
Self::BlockGasLimitExceeded => -38015,
Self::BlockNumberInvalid => -38020,
Self::BlockTimestampInvalid => -38021,
Self::PrecompileSelfReference => -38022,
Self::PrecompileDuplicateAddress => -38023,
Self::SenderNotEOA => -38024,
Self::MaxInitCodeSizeExceeded => -38025,
Self::GasLimitReached => -38026,
}
}

View File

@@ -539,7 +539,7 @@ where
/// Returns the transaction batch sender
#[inline]
const fn tx_batch_sender(
pub const fn tx_batch_sender(
&self,
) -> &mpsc::UnboundedSender<BatchTxRequest<<N::Pool as TransactionPool>::Transaction>> {
&self.tx_batch_sender

View File

@@ -67,7 +67,7 @@ where
gas_limit: parent.gas_limit(),
parent_beacon_block_root: request.payload_attributes.parent_beacon_block_root,
withdrawals: request.payload_attributes.withdrawals.map(Into::into),
extra_data: request.extra_data,
extra_data: request.extra_data.unwrap_or_default(),
};
let mut builder = evm_config

View File

@@ -25,6 +25,9 @@ pub struct StorageSettings {
/// Whether `TransactionHashNumbers` is stored in `RocksDB`.
#[serde(default)]
pub transaction_hash_numbers_in_rocksdb: bool,
/// Whether `AccountsHistory` is stored in `RocksDB`.
#[serde(default)]
pub account_history_in_rocksdb: bool,
}
impl StorageSettings {
@@ -39,6 +42,7 @@ impl StorageSettings {
transaction_senders_in_static_files: false,
storages_history_in_rocksdb: false,
transaction_hash_numbers_in_rocksdb: false,
account_history_in_rocksdb: false,
}
}
@@ -65,4 +69,10 @@ impl StorageSettings {
self.transaction_hash_numbers_in_rocksdb = value;
self
}
/// Sets the `account_history_in_rocksdb` flag to the provided value.
pub const fn with_account_history_in_rocksdb(mut self, value: bool) -> Self {
self.account_history_in_rocksdb = value;
self
}
}

View File

@@ -1,8 +1,10 @@
//! Generic reader and writer abstractions for interacting with either database tables or static
//! files.
use std::ops::Range;
use std::{marker::PhantomData, ops::Range};
#[cfg(all(unix, feature = "rocksdb"))]
use crate::providers::rocksdb::RocksDBWriteMode;
use crate::{
providers::{StaticFileProvider, StaticFileProviderRWRefMut},
StaticFileProviderFactory,
@@ -24,8 +26,8 @@ use reth_storage_errors::provider::ProviderResult;
use strum::{Display, EnumIs};
/// Type alias for [`EitherReader`] constructors.
type EitherReaderTy<P, T> =
EitherReader<CursorTy<<P as DBProvider>::Tx, T>, <P as NodePrimitivesProvider>::Primitives>;
type EitherReaderTy<'a, P, T> =
EitherReader<'a, CursorTy<<P as DBProvider>::Tx, T>, <P as NodePrimitivesProvider>::Primitives>;
/// Type alias for [`EitherWriter`] constructors.
type EitherWriterTy<'a, P, T> = EitherWriter<
@@ -34,13 +36,31 @@ type EitherWriterTy<'a, P, T> = EitherWriter<
<P as NodePrimitivesProvider>::Primitives,
>;
/// Represents a destination for writing data, either to database or static files.
// Helper types so constructors stay exported even when RocksDB feature is off.
// RocksDBWriteMode encapsulates the choice between Transaction and Batch writes.
#[cfg(all(unix, feature = "rocksdb"))]
type RocksWriteModeArg<'a> = crate::providers::rocksdb::RocksDBWriteMode<'a>;
#[cfg(not(all(unix, feature = "rocksdb")))]
type RocksWriteModeArg<'a> = ();
#[cfg(all(unix, feature = "rocksdb"))]
type RocksTxRefArg<'a> = &'a crate::providers::rocksdb::RocksTx<'a>;
#[cfg(not(all(unix, feature = "rocksdb")))]
type RocksTxRefArg<'a> = ();
/// Represents a destination for writing data, either to database, static files, or `RocksDB`.
#[derive(Debug, Display)]
pub enum EitherWriter<'a, CURSOR, N> {
/// Write to database table via cursor
Database(CURSOR),
/// Write to static file
StaticFile(StaticFileProviderRWRefMut<'a, N>),
/// Write to `RocksDB` (transaction or batch - internal detail).
///
/// Uses [`RocksDBWriteMode`] to encapsulate the choice between full transaction
/// semantics and high-throughput batch writes.
#[cfg(all(unix, feature = "rocksdb"))]
RocksDB(RocksDBWriteMode<'a>),
}
impl<'a> EitherWriter<'a, (), ()> {
@@ -109,6 +129,65 @@ impl<'a> EitherWriter<'a, (), ()> {
))
}
}
/// Creates a new [`EitherWriter`] for storages history based on storage settings.
///
/// Accepts either Transaction or Batch mode via [`RocksDBWriteMode`].
pub fn new_storages_history<P>(
provider: &P,
_rocksdb_mode: RocksWriteModeArg<'a>,
) -> ProviderResult<EitherWriterTy<'a, P, tables::StoragesHistory>>
where
P: DBProvider + NodePrimitivesProvider + StorageSettingsCache,
P::Tx: DbTxMut,
{
#[cfg(all(unix, feature = "rocksdb"))]
if provider.cached_storage_settings().storages_history_in_rocksdb {
return Ok(EitherWriter::RocksDB(_rocksdb_mode));
}
Ok(EitherWriter::Database(provider.tx_ref().cursor_write::<tables::StoragesHistory>()?))
}
/// Creates a new [`EitherWriter`] for accounts history based on storage settings.
///
/// Accepts either Transaction or Batch mode via [`RocksDBWriteMode`].
pub fn new_accounts_history<P>(
provider: &P,
_rocksdb_mode: RocksWriteModeArg<'a>,
) -> ProviderResult<EitherWriterTy<'a, P, tables::AccountsHistory>>
where
P: DBProvider + NodePrimitivesProvider + StorageSettingsCache,
P::Tx: DbTxMut,
{
#[cfg(all(unix, feature = "rocksdb"))]
if provider.cached_storage_settings().account_history_in_rocksdb {
return Ok(EitherWriter::RocksDB(_rocksdb_mode));
}
Ok(EitherWriter::Database(provider.tx_ref().cursor_write::<tables::AccountsHistory>()?))
}
/// Creates a new [`EitherWriter`] for transaction hash numbers based on storage settings.
///
/// Accepts either Transaction or Batch mode via [`RocksDBWriteMode`].
pub fn new_transaction_hash_numbers<P>(
provider: &P,
_rocksdb_mode: RocksWriteModeArg<'a>,
) -> ProviderResult<EitherWriterTy<'a, P, tables::TransactionHashNumbers>>
where
P: DBProvider + NodePrimitivesProvider + StorageSettingsCache,
P::Tx: DbTxMut,
{
#[cfg(all(unix, feature = "rocksdb"))]
if provider.cached_storage_settings().transaction_hash_numbers_in_rocksdb {
return Ok(EitherWriter::RocksDB(_rocksdb_mode));
}
Ok(EitherWriter::Database(
provider.tx_ref().cursor_write::<tables::TransactionHashNumbers>()?,
))
}
}
impl<'a, CURSOR, N: NodePrimitives> EitherWriter<'a, CURSOR, N> {
@@ -119,6 +198,8 @@ impl<'a, CURSOR, N: NodePrimitives> EitherWriter<'a, CURSOR, N> {
match self {
Self::Database(_) => Ok(()),
Self::StaticFile(writer) => writer.increment_block(expected_block_number),
#[cfg(all(unix, feature = "rocksdb"))]
Self::RocksDB(_) => Err(ProviderError::UnsupportedProvider),
}
}
@@ -132,6 +213,26 @@ impl<'a, CURSOR, N: NodePrimitives> EitherWriter<'a, CURSOR, N> {
match self {
Self::Database(_) => Ok(()),
Self::StaticFile(writer) => writer.ensure_at_block(block_number),
#[cfg(all(unix, feature = "rocksdb"))]
Self::RocksDB(_) => Err(ProviderError::UnsupportedProvider),
}
}
/// Commits `RocksDB` writes if this is a `RocksDB` writer.
///
/// For [`Self::Database`] and [`Self::StaticFile`], this is a no-op as they use
/// different commit patterns (MDBX transaction commit, static file sync).
///
/// # Commit Order
///
/// Call this AFTER the outer MDBX transaction commits successfully. This ensures
/// that if `RocksDB` commit fails, the primary data (MDBX) is still intact and
/// the `RocksDB` data (which is derived) can be rebuilt.
#[cfg(all(unix, feature = "rocksdb"))]
pub fn commit(self) -> ProviderResult<()> {
match self {
Self::Database(_) | Self::StaticFile(_) => Ok(()),
Self::RocksDB(mode) => mode.commit(),
}
}
}
@@ -146,6 +247,8 @@ where
match self {
Self::Database(cursor) => Ok(cursor.append(tx_num, receipt)?),
Self::StaticFile(writer) => writer.append_receipt(tx_num, receipt),
#[cfg(all(unix, feature = "rocksdb"))]
Self::RocksDB(_) => Err(ProviderError::UnsupportedProvider),
}
}
}
@@ -159,6 +262,8 @@ where
match self {
Self::Database(cursor) => Ok(cursor.append(tx_num, sender)?),
Self::StaticFile(writer) => writer.append_transaction_sender(tx_num, sender),
#[cfg(all(unix, feature = "rocksdb"))]
Self::RocksDB(_) => Err(ProviderError::UnsupportedProvider),
}
}
@@ -175,6 +280,8 @@ where
Ok(())
}
Self::StaticFile(writer) => writer.append_transaction_senders(senders),
#[cfg(all(unix, feature = "rocksdb"))]
Self::RocksDB(_) => Err(ProviderError::UnsupportedProvider),
}
}
@@ -206,41 +313,87 @@ where
writer.prune_transaction_senders(to_delete, block)?;
}
#[cfg(all(unix, feature = "rocksdb"))]
Self::RocksDB(_) => return Err(ProviderError::UnsupportedProvider),
}
Ok(())
}
}
/// Represents a source for reading data, either from database or static files.
/// Represents a source for reading data, either from database, static files, or `RocksDB`.
#[derive(Debug, Display)]
pub enum EitherReader<CURSOR, N> {
pub enum EitherReader<'a, CURSOR, N> {
/// Read from database table via cursor
Database(CURSOR),
Database(CURSOR, PhantomData<&'a ()>),
/// Read from static file
StaticFile(StaticFileProvider<N>),
StaticFile(StaticFileProvider<N>, PhantomData<&'a ()>),
/// Read from `RocksDB` transaction
#[cfg(all(unix, feature = "rocksdb"))]
RocksDB(&'a crate::providers::rocksdb::RocksTx<'a>),
}
impl EitherReader<(), ()> {
impl<'a> EitherReader<'a, (), ()> {
/// Creates a new [`EitherReader`] for senders based on storage settings.
pub fn new_senders<P>(
provider: &P,
) -> ProviderResult<EitherReaderTy<P, tables::TransactionSenders>>
) -> ProviderResult<EitherReaderTy<'a, P, tables::TransactionSenders>>
where
P: DBProvider + NodePrimitivesProvider + StorageSettingsCache + StaticFileProviderFactory,
P::Tx: DbTx,
{
if EitherWriterDestination::senders(provider).is_static_file() {
Ok(EitherReader::StaticFile(provider.static_file_provider()))
Ok(EitherReader::StaticFile(provider.static_file_provider(), PhantomData))
} else {
Ok(EitherReader::Database(
provider.tx_ref().cursor_read::<tables::TransactionSenders>()?,
PhantomData,
))
}
}
/// Creates a new [`EitherReader`] for storages history based on storage settings.
pub fn new_storages_history<P>(
provider: &P,
_rocksdb_tx: RocksTxRefArg<'a>,
) -> ProviderResult<EitherReaderTy<'a, P, tables::StoragesHistory>>
where
P: DBProvider + NodePrimitivesProvider + StorageSettingsCache,
P::Tx: DbTx,
{
#[cfg(all(unix, feature = "rocksdb"))]
if provider.cached_storage_settings().storages_history_in_rocksdb {
return Ok(EitherReader::RocksDB(_rocksdb_tx));
}
Ok(EitherReader::Database(
provider.tx_ref().cursor_read::<tables::StoragesHistory>()?,
PhantomData,
))
}
/// Creates a new [`EitherReader`] for transaction hash numbers based on storage settings.
pub fn new_transaction_hash_numbers<P>(
provider: &P,
_rocksdb_tx: RocksTxRefArg<'a>,
) -> ProviderResult<EitherReaderTy<'a, P, tables::TransactionHashNumbers>>
where
P: DBProvider + NodePrimitivesProvider + StorageSettingsCache,
P::Tx: DbTx,
{
#[cfg(all(unix, feature = "rocksdb"))]
if provider.cached_storage_settings().transaction_hash_numbers_in_rocksdb {
return Ok(EitherReader::RocksDB(_rocksdb_tx));
}
Ok(EitherReader::Database(
provider.tx_ref().cursor_read::<tables::TransactionHashNumbers>()?,
PhantomData,
))
}
}
impl<CURSOR, N: NodePrimitives> EitherReader<CURSOR, N>
impl<CURSOR, N: NodePrimitives> EitherReader<'_, CURSOR, N>
where
CURSOR: DbCursorRO<tables::TransactionSenders>,
{
@@ -250,11 +403,11 @@ where
range: Range<TxNumber>,
) -> ProviderResult<HashMap<TxNumber, Address>> {
match self {
Self::Database(cursor) => cursor
Self::Database(cursor, _) => cursor
.walk_range(range)?
.map(|result| result.map_err(ProviderError::from))
.collect::<ProviderResult<HashMap<_, _>>>(),
Self::StaticFile(provider) => range
Self::StaticFile(provider, _) => range
.clone()
.zip(provider.fetch_range_iter(
StaticFileSegment::TransactionSenders,
@@ -266,6 +419,8 @@ where
Some(result.map(|sender| (tx_num, sender)))
})
.collect::<ProviderResult<HashMap<_, _>>>(),
#[cfg(all(unix, feature = "rocksdb"))]
Self::RocksDB(_) => Err(ProviderError::UnsupportedProvider),
}
}
}
@@ -277,6 +432,8 @@ pub enum EitherWriterDestination {
Database,
/// Write to static file
StaticFile,
/// Write to `RocksDB`
RocksDB,
}
impl EitherWriterDestination {
@@ -336,9 +493,9 @@ mod tests {
let provider = factory.database_provider_ro().unwrap();
let mut reader = EitherReader::new_senders(&provider).unwrap();
if transaction_senders_in_static_files {
assert!(matches!(reader, EitherReader::StaticFile(_)));
assert!(matches!(reader, EitherReader::StaticFile(_, _)));
} else {
assert!(matches!(reader, EitherReader::Database(_)));
assert!(matches!(reader, EitherReader::Database(_, _)));
}
assert_eq!(

View File

@@ -32,9 +32,9 @@ pub use consistent::ConsistentProvider;
// RocksDB currently only supported on Unix platforms
// Windows support is planned for future releases
#[cfg(all(unix, feature = "rocksdb"))]
mod rocksdb;
pub(crate) mod rocksdb;
#[cfg(all(unix, feature = "rocksdb"))]
pub use rocksdb::{RocksDBBuilder, RocksDBProvider};
pub use rocksdb::{RocksDBBuilder, RocksDBProvider, RocksDBWriteMode, RocksTx};
/// Helper trait to bound [`NodeTypes`] so that combined with database they satisfy
/// [`ProviderNodeTypes`].

View File

@@ -2,4 +2,7 @@
mod metrics;
mod provider;
pub use provider::{RocksDBBuilder, RocksDBProvider};
pub use provider::{RocksDBBuilder, RocksDBProvider, RocksDBWriteMode, RocksTx};
#[cfg(test)]
pub(crate) use provider::RocksDBBatch;

View File

@@ -8,8 +8,9 @@ use reth_storage_errors::{
provider::{ProviderError, ProviderResult},
};
use rocksdb::{
BlockBasedOptions, Cache, ColumnFamilyDescriptor, CompactionPri, DBCompressionType, Options,
WriteBatch, DB,
BlockBasedOptions, Cache, ColumnFamilyDescriptor, CompactionPri, DBCompressionType,
IteratorMode, Options, Transaction, TransactionDB, TransactionDBOptions, TransactionOptions,
WriteBatchWithTransaction, WriteOptions,
};
use std::{
fmt,
@@ -17,6 +18,7 @@ use std::{
sync::Arc,
time::Instant,
};
use tracing::warn;
/// Default cache size for `RocksDB` block cache (128 MB).
const DEFAULT_CACHE_SIZE: usize = 128 << 20;
@@ -177,7 +179,15 @@ impl RocksDBBuilder {
})
.collect();
let db = DB::open_cf_descriptors(&options, &self.path, cf_descriptors).map_err(|e| {
// Use TransactionDB for MDBX-like transaction semantics (read-your-writes, rollback)
let txn_db_options = TransactionDBOptions::default();
let db = TransactionDB::open_cf_descriptors(
&options,
&txn_db_options,
&self.path,
cf_descriptors,
)
.map_err(|e| {
ProviderError::Database(DatabaseError::Open(DatabaseErrorInfo {
message: e.to_string().into(),
code: -1,
@@ -209,14 +219,22 @@ macro_rules! compress_to_buf_or_ref {
pub struct RocksDBProvider(Arc<RocksDBProviderInner>);
/// Inner state for `RocksDB` provider.
#[derive(Debug)]
struct RocksDBProviderInner {
/// `RocksDB` database instance.
db: DB,
/// `RocksDB` database instance with transaction support.
db: TransactionDB,
/// Metrics latency & operations.
metrics: Option<RocksDBMetrics>,
}
impl fmt::Debug for RocksDBProviderInner {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("RocksDBProviderInner")
.field("db", &"<TransactionDB>")
.field("metrics", &self.metrics)
.finish()
}
}
impl Clone for RocksDBProvider {
fn clone(&self) -> Self {
Self(self.0.clone())
@@ -234,6 +252,34 @@ impl RocksDBProvider {
RocksDBBuilder::new(path)
}
/// Creates a new transaction with MDBX-like semantics (read-your-writes, rollback).
pub fn tx(&self) -> RocksTx<'_> {
let write_options = WriteOptions::default();
let txn_options = TransactionOptions::default();
let inner = self.0.db.transaction_opt(&write_options, &txn_options);
RocksTx { inner, provider: self }
}
/// Creates a new batch for manual commit.
///
/// Use [`Self::write_batch`] for closure-based atomic writes.
/// Use this method when the batch needs to be held by [`EitherWriter`](crate::EitherWriter).
///
/// # Example
///
/// ```ignore
/// let batch = provider.batch();
/// batch.put::<SomeTable>(key, &value)?;
/// batch.commit()?;
/// ```
pub fn batch(&self) -> RocksDBBatch<'_> {
RocksDBBatch {
provider: self,
inner: WriteBatchWithTransaction::<true>::default(),
buf: Vec::new(),
}
}
/// Gets the column family handle for a table.
fn get_cf_handle<T: Table>(&self) -> Result<&rocksdb::ColumnFamily, DatabaseError> {
self.0
@@ -331,12 +377,17 @@ impl RocksDBProvider {
{
// Note: Using "Batch" as table name for batch operations across multiple tables
self.execute_with_operation_metric(RocksDBOperation::BatchWrite, "Batch", |this| {
let mut batch_handle =
RocksDBBatch { provider: this, inner: WriteBatch::default(), buf: Vec::new() };
let mut batch_handle = RocksDBBatch {
provider: this,
inner: WriteBatchWithTransaction::<true>::default(),
buf: Vec::new(),
};
f(&mut batch_handle)?;
this.0.db.write(batch_handle.inner).map_err(|e| {
// Take ownership of inner to prevent Drop from logging a warning
let batch = std::mem::take(&mut batch_handle.inner);
this.0.db.write(batch).map_err(|e| {
ProviderError::Database(DatabaseError::Commit(DatabaseErrorInfo {
message: e.to_string().into(),
code: -1,
@@ -347,17 +398,19 @@ impl RocksDBProvider {
}
/// Handle for building a batch of operations atomically.
///
/// Uses `WriteBatchWithTransaction<true>` for compatibility with `TransactionDB`.
pub struct RocksDBBatch<'a> {
provider: &'a RocksDBProvider,
inner: WriteBatch,
inner: WriteBatchWithTransaction<true>,
buf: Vec<u8>,
}
impl<'a> fmt::Debug for RocksDBBatch<'a> {
impl fmt::Debug for RocksDBBatch<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("RocksDBBatch")
.field("provider", &self.provider)
.field("batch", &"<WriteBatch>")
.field("batch", &"<WriteBatchWithTransaction>")
// Number of operations in this batch
.field("length", &self.inner.len())
// Total serialized size (encoded key + compressed value + metadata) of this batch
@@ -390,6 +443,250 @@ impl<'a> RocksDBBatch<'a> {
self.inner.delete_cf(self.provider.get_cf_handle::<T>()?, key.encode().as_ref());
Ok(())
}
/// Commits the batch to the database.
///
/// This consumes the batch and writes all operations atomically to `RocksDB`.
///
/// # Errors
///
/// Returns an error if the write fails.
pub fn commit(mut self) -> ProviderResult<()> {
// Take ownership of inner to prevent Drop from logging a warning
let batch = std::mem::take(&mut self.inner);
self.provider.0.db.write(batch).map_err(|e| {
ProviderError::Database(DatabaseError::Commit(DatabaseErrorInfo {
message: e.to_string().into(),
code: -1,
}))
})
}
/// Returns the number of operations in this batch.
pub fn len(&self) -> usize {
self.inner.len()
}
/// Returns `true` if the batch contains no operations.
pub fn is_empty(&self) -> bool {
self.inner.is_empty()
}
}
impl Drop for RocksDBBatch<'_> {
fn drop(&mut self) {
if !self.inner.is_empty() {
warn!(
target: "reth::storage",
batch_len = %self.inner.len(),
"RocksDBBatch dropped without commit - data discarded"
);
}
}
}
/// `RocksDB` transaction wrapper providing MDBX-like semantics.
///
/// Supports:
/// - Read-your-writes: reads see uncommitted writes within the same transaction
/// - Atomic commit/rollback
/// - Iteration over uncommitted data
///
/// Note: `Transaction` is `Send` but NOT `Sync`. This wrapper does not implement
/// `DbTx`/`DbTxMut` traits directly; use RocksDB-specific methods instead.
pub struct RocksTx<'db> {
inner: Transaction<'db, TransactionDB>,
provider: &'db RocksDBProvider,
}
impl fmt::Debug for RocksTx<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("RocksTx").field("provider", &self.provider).finish_non_exhaustive()
}
}
impl<'db> RocksTx<'db> {
/// Gets a value from the specified table. Sees uncommitted writes in this transaction.
pub fn get<T: Table>(&self, key: T::Key) -> ProviderResult<Option<T::Value>> {
let encoded_key = key.encode();
self.get_encoded::<T>(&encoded_key)
}
/// Gets a value using pre-encoded key. Sees uncommitted writes in this transaction.
pub fn get_encoded<T: Table>(
&self,
key: &<T::Key as Encode>::Encoded,
) -> ProviderResult<Option<T::Value>> {
let cf = self.provider.get_cf_handle::<T>()?;
let result = self.inner.get_cf(cf, key.as_ref()).map_err(|e| {
ProviderError::Database(DatabaseError::Read(DatabaseErrorInfo {
message: e.to_string().into(),
code: -1,
}))
})?;
Ok(result.and_then(|value| T::Value::decompress(&value).ok()))
}
/// Puts a value into the specified table.
pub fn put<T: Table>(&self, key: T::Key, value: &T::Value) -> ProviderResult<()> {
let encoded_key = key.encode();
self.put_encoded::<T>(&encoded_key, value)
}
/// Puts a value using pre-encoded key.
pub fn put_encoded<T: Table>(
&self,
key: &<T::Key as Encode>::Encoded,
value: &T::Value,
) -> ProviderResult<()> {
let cf = self.provider.get_cf_handle::<T>()?;
let mut buf = Vec::new();
let value_bytes = compress_to_buf_or_ref!(buf, value).unwrap_or(&buf);
self.inner.put_cf(cf, key.as_ref(), value_bytes).map_err(|e| {
ProviderError::Database(DatabaseError::Write(Box::new(DatabaseWriteError {
info: DatabaseErrorInfo { message: e.to_string().into(), code: -1 },
operation: DatabaseWriteOperation::PutUpsert,
table_name: T::NAME,
key: key.as_ref().to_vec(),
})))
})
}
/// Deletes a value from the specified table.
pub fn delete<T: Table>(&self, key: T::Key) -> ProviderResult<()> {
let cf = self.provider.get_cf_handle::<T>()?;
self.inner.delete_cf(cf, key.encode().as_ref()).map_err(|e| {
ProviderError::Database(DatabaseError::Delete(DatabaseErrorInfo {
message: e.to_string().into(),
code: -1,
}))
})
}
/// Creates an iterator for the specified table. Sees uncommitted writes in this transaction.
///
/// Returns an iterator that yields `(encoded_key, compressed_value)` pairs.
pub fn iter<T: Table>(&self) -> ProviderResult<RocksTxIter<'_, T>> {
let cf = self.provider.get_cf_handle::<T>()?;
let iter = self.inner.iterator_cf(cf, IteratorMode::Start);
Ok(RocksTxIter { inner: iter, _marker: std::marker::PhantomData })
}
/// Creates an iterator starting from the given key (inclusive).
pub fn iter_from<T: Table>(&self, key: T::Key) -> ProviderResult<RocksTxIter<'_, T>> {
let cf = self.provider.get_cf_handle::<T>()?;
let encoded_key = key.encode();
let iter = self
.inner
.iterator_cf(cf, IteratorMode::From(encoded_key.as_ref(), rocksdb::Direction::Forward));
Ok(RocksTxIter { inner: iter, _marker: std::marker::PhantomData })
}
/// Commits the transaction, persisting all changes.
pub fn commit(self) -> ProviderResult<()> {
self.inner.commit().map_err(|e| {
ProviderError::Database(DatabaseError::Commit(DatabaseErrorInfo {
message: e.to_string().into(),
code: -1,
}))
})
}
/// Rolls back the transaction, discarding all changes.
pub fn rollback(self) -> ProviderResult<()> {
self.inner.rollback().map_err(|e| {
ProviderError::Database(DatabaseError::Other(format!("rollback failed: {e}")))
})
}
}
/// Iterator over a `RocksDB` table within a transaction.
///
/// Yields decoded `(Key, Value)` pairs. Sees uncommitted writes.
pub struct RocksTxIter<'tx, T: Table> {
inner: rocksdb::DBIteratorWithThreadMode<'tx, Transaction<'tx, TransactionDB>>,
_marker: std::marker::PhantomData<T>,
}
impl<T: Table> fmt::Debug for RocksTxIter<'_, T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("RocksTxIter").field("table", &T::NAME).finish_non_exhaustive()
}
}
impl<T: Table> Iterator for RocksTxIter<'_, T> {
type Item = ProviderResult<(T::Key, T::Value)>;
fn next(&mut self) -> Option<Self::Item> {
let (key_bytes, value_bytes) = match self.inner.next()? {
Ok(kv) => kv,
Err(e) => {
return Some(Err(ProviderError::Database(DatabaseError::Read(DatabaseErrorInfo {
message: e.to_string().into(),
code: -1,
}))))
}
};
// Decode key
let key = match <T::Key as reth_db_api::table::Decode>::decode(&key_bytes) {
Ok(k) => k,
Err(_) => return Some(Err(ProviderError::Database(DatabaseError::Decode))),
};
// Decompress value
let value = match T::Value::decompress(&value_bytes) {
Ok(v) => v,
Err(_) => return Some(Err(ProviderError::Database(DatabaseError::Decode))),
};
Some(Ok((key, value)))
}
}
/// `RocksDB` write strategy - internal implementation detail.
///
/// This enum encapsulates the choice between full transaction semantics
/// and high-throughput batch writes. Use [`RocksDBWriteMode::Transaction`] for
/// read-modify-write operations that need read-your-writes semantics.
/// Use [`RocksDBWriteMode::Batch`] for bulk sync operations where
/// read-your-writes is not needed.
pub enum RocksDBWriteMode<'a> {
/// Full transaction with read-your-writes, rollback support.
/// Use for read-modify-write operations.
Transaction(RocksTx<'a>),
/// Write-only batch for maximum throughput.
/// Use for bulk sync operations where read-your-writes is not needed.
Batch(RocksDBBatch<'a>),
}
impl fmt::Debug for RocksDBWriteMode<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Transaction(tx) => f.debug_tuple("Transaction").field(tx).finish(),
Self::Batch(batch) => f.debug_tuple("Batch").field(batch).finish(),
}
}
}
impl<'a> RocksDBWriteMode<'a> {
/// Puts a value into the specified table.
pub fn put<T: Table>(&mut self, key: T::Key, value: &T::Value) -> ProviderResult<()> {
match self {
Self::Transaction(tx) => tx.put::<T>(key, value),
Self::Batch(batch) => batch.put::<T>(key, value),
}
}
/// Commits the transaction or batch.
pub fn commit(self) -> ProviderResult<()> {
match self {
Self::Transaction(tx) => tx.commit(),
Self::Batch(batch) => batch.commit(),
}
}
}
/// Converts Reth's [`LogLevel`] to `RocksDB`'s [`rocksdb::LogLevel`].
@@ -524,44 +821,321 @@ mod tests {
#[test]
fn test_statistics_enabled() {
let temp_dir = TempDir::new().unwrap();
// Just verify that building with statistics doesn't panic
let provider = RocksDBBuilder::new(temp_dir.path())
.with_table::<TestTable>()
.with_statistics()
.build()
.unwrap();
// Do operations
// Do operations - data should be immediately readable with TransactionDB
for i in 0..10 {
let value = vec![i as u8];
provider.put::<TestTable>(i, &value).unwrap();
// Verify write is visible
assert_eq!(provider.get::<TestTable>(i).unwrap(), Some(value));
}
// Verify statistics enabled
let stats = provider.0.db.property_value("rocksdb.stats").unwrap();
assert!(stats.is_some(), "Statistics should be enabled");
let stats_str = stats.unwrap();
assert!(stats_str.contains("DB Stats"));
}
#[test]
fn test_compression_after_flush() {
fn test_data_persistence() {
let temp_dir = TempDir::new().unwrap();
let provider =
RocksDBBuilder::new(temp_dir.path()).with_table::<TestTable>().build().unwrap();
// Insert compressible data
// Insert data - TransactionDB writes are immediately visible
let value = vec![42u8; 1000];
for i in 0..100 {
provider.put::<TestTable>(i, &value).unwrap();
}
// Get CF handle and flush it
let cf = provider.0.db.cf_handle("TestTable").expect("CF should exist");
provider.0.db.flush_cf(cf).unwrap();
// Verify data is persisted by reading it back
// Verify data is readable
for i in 0..100 {
assert!(provider.get::<TestTable>(i).unwrap().is_some(), "Data should be persisted");
assert!(provider.get::<TestTable>(i).unwrap().is_some(), "Data should be readable");
}
}
#[test]
fn test_transaction_read_your_writes() {
let temp_dir = TempDir::new().unwrap();
let provider =
RocksDBBuilder::new(temp_dir.path()).with_table::<TestTable>().build().unwrap();
// Create a transaction
let tx = provider.tx();
// Write data within the transaction
let key = 42u64;
let value = b"test_value".to_vec();
tx.put::<TestTable>(key, &value).unwrap();
// Read-your-writes: should see uncommitted data in same transaction
let result = tx.get::<TestTable>(key).unwrap();
assert_eq!(
result,
Some(value.clone()),
"Transaction should see its own uncommitted writes"
);
// Data should NOT be visible via provider (outside transaction)
let provider_result = provider.get::<TestTable>(key).unwrap();
assert_eq!(provider_result, None, "Uncommitted data should not be visible outside tx");
// Commit the transaction
tx.commit().unwrap();
// Now data should be visible via provider
let committed_result = provider.get::<TestTable>(key).unwrap();
assert_eq!(committed_result, Some(value), "Committed data should be visible");
}
#[test]
fn test_transaction_rollback() {
let temp_dir = TempDir::new().unwrap();
let provider =
RocksDBBuilder::new(temp_dir.path()).with_table::<TestTable>().build().unwrap();
// First, put some initial data
let key = 100u64;
let initial_value = b"initial".to_vec();
provider.put::<TestTable>(key, &initial_value).unwrap();
// Create a transaction and modify data
let tx = provider.tx();
let new_value = b"modified".to_vec();
tx.put::<TestTable>(key, &new_value).unwrap();
// Verify modification is visible within transaction
assert_eq!(tx.get::<TestTable>(key).unwrap(), Some(new_value));
// Rollback instead of commit
tx.rollback().unwrap();
// Data should be unchanged (initial value)
let result = provider.get::<TestTable>(key).unwrap();
assert_eq!(result, Some(initial_value), "Rollback should preserve original data");
}
#[test]
fn test_transaction_iterator() {
let temp_dir = TempDir::new().unwrap();
let provider =
RocksDBBuilder::new(temp_dir.path()).with_table::<TestTable>().build().unwrap();
// Create a transaction
let tx = provider.tx();
// Write multiple entries
for i in 0..5u64 {
let value = format!("value_{i}").into_bytes();
tx.put::<TestTable>(i, &value).unwrap();
}
// Iterate - should see uncommitted writes
let mut count = 0;
for result in tx.iter::<TestTable>().unwrap() {
let (key, value) = result.unwrap();
assert_eq!(value, format!("value_{key}").into_bytes());
count += 1;
}
assert_eq!(count, 5, "Iterator should see all uncommitted writes");
// Commit
tx.commit().unwrap();
}
#[test]
fn test_batch_manual_commit() {
let temp_dir = TempDir::new().unwrap();
let provider =
RocksDBBuilder::new(temp_dir.path()).with_table::<TestTable>().build().unwrap();
// Create a batch via provider.batch()
let mut batch = provider.batch();
// Add entries
for i in 0..10u64 {
let value = format!("batch_value_{i}").into_bytes();
batch.put::<TestTable>(i, &value).unwrap();
}
// Verify len/is_empty
assert_eq!(batch.len(), 10);
assert!(!batch.is_empty());
// Data should NOT be visible before commit
assert_eq!(provider.get::<TestTable>(0).unwrap(), None);
// Commit the batch
batch.commit().unwrap();
// Now data should be visible
for i in 0..10u64 {
let value = format!("batch_value_{i}").into_bytes();
assert_eq!(provider.get::<TestTable>(i).unwrap(), Some(value));
}
}
#[test]
fn test_write_mode_batch() {
let temp_dir = TempDir::new().unwrap();
let provider =
RocksDBBuilder::new(temp_dir.path()).with_table::<TestTable>().build().unwrap();
// Create write mode using Batch
let batch = provider.batch();
let mut mode = RocksDBWriteMode::Batch(batch);
// Write via RocksDBWriteMode
let key = 42u64;
let value = b"test_via_mode".to_vec();
mode.put::<TestTable>(key, &value).unwrap();
// Commit via RocksDBWriteMode
mode.commit().unwrap();
// Verify data is visible
assert_eq!(provider.get::<TestTable>(key).unwrap(), Some(value));
}
#[test]
fn test_write_mode_transaction() {
let temp_dir = TempDir::new().unwrap();
let provider =
RocksDBBuilder::new(temp_dir.path()).with_table::<TestTable>().build().unwrap();
// Create write mode using Transaction
let tx = provider.tx();
let mut mode = RocksDBWriteMode::Transaction(tx);
// Write via RocksDBWriteMode
let key = 100u64;
let value = b"test_via_tx_mode".to_vec();
mode.put::<TestTable>(key, &value).unwrap();
// Commit via RocksDBWriteMode
mode.commit().unwrap();
// Verify data is visible
assert_eq!(provider.get::<TestTable>(key).unwrap(), Some(value));
}
#[test]
fn test_batch_empty_commit() {
let temp_dir = TempDir::new().unwrap();
let provider =
RocksDBBuilder::new(temp_dir.path()).with_table::<TestTable>().build().unwrap();
// Create an empty batch
let batch = provider.batch();
assert!(batch.is_empty());
assert_eq!(batch.len(), 0);
// Commit should succeed (no-op)
batch.commit().unwrap();
}
#[test]
fn test_batch_drop_without_commit_does_not_persist() {
let temp_dir = TempDir::new().unwrap();
let provider =
RocksDBBuilder::new(temp_dir.path()).with_table::<TestTable>().build().unwrap();
// Create a batch and add entries but DON'T commit
{
let mut batch = provider.batch();
for i in 0..5u64 {
let value = format!("dropped_value_{i}").into_bytes();
batch.put::<TestTable>(i, &value).unwrap();
}
// batch dropped here without commit - should log warning
}
// Data should NOT be persisted
for i in 0..5u64 {
assert_eq!(
provider.get::<TestTable>(i).unwrap(),
None,
"Dropped batch should not persist data"
);
}
}
#[test]
fn test_write_mode_debug_formatting() {
let temp_dir = TempDir::new().unwrap();
let provider =
RocksDBBuilder::new(temp_dir.path()).with_table::<TestTable>().build().unwrap();
// Test Batch variant debug output
let batch = provider.batch();
let mode_batch = RocksDBWriteMode::Batch(batch);
let debug_str = format!("{:?}", mode_batch);
assert!(debug_str.contains("Batch"), "Debug should contain 'Batch': {debug_str}");
// Test Transaction variant debug output
let tx = provider.tx();
let mode_tx = RocksDBWriteMode::Transaction(tx);
let debug_str = format!("{:?}", mode_tx);
assert!(
debug_str.contains("Transaction"),
"Debug should contain 'Transaction': {debug_str}"
);
}
#[test]
fn test_batch_delete_operation() {
let temp_dir = TempDir::new().unwrap();
let provider =
RocksDBBuilder::new(temp_dir.path()).with_table::<TestTable>().build().unwrap();
// First, insert some data
let mut batch = provider.batch();
for i in 0..5u64 {
batch.put::<TestTable>(i, &vec![i as u8]).unwrap();
}
batch.commit().unwrap();
// Verify data exists
for i in 0..5u64 {
assert!(provider.get::<TestTable>(i).unwrap().is_some());
}
// Now delete via batch
let mut delete_batch = provider.batch();
for i in 0..5u64 {
delete_batch.delete::<TestTable>(i).unwrap();
}
delete_batch.commit().unwrap();
// Verify data is deleted
for i in 0..5u64 {
assert_eq!(provider.get::<TestTable>(i).unwrap(), None);
}
}
#[test]
fn test_batch_overwrite_existing_key() {
let temp_dir = TempDir::new().unwrap();
let provider =
RocksDBBuilder::new(temp_dir.path()).with_table::<TestTable>().build().unwrap();
let key = 42u64;
let initial_value = b"initial".to_vec();
let updated_value = b"updated".to_vec();
// Insert initial value
let mut batch = provider.batch();
batch.put::<TestTable>(key, &initial_value).unwrap();
batch.commit().unwrap();
assert_eq!(provider.get::<TestTable>(key).unwrap(), Some(initial_value));
// Overwrite with new value
let mut batch = provider.batch();
batch.put::<TestTable>(key, &updated_value).unwrap();
batch.commit().unwrap();
assert_eq!(provider.get::<TestTable>(key).unwrap(), Some(updated_value));
}
}

View File

@@ -213,17 +213,17 @@ where
}
/// Converts the changed accounts to a map of sender ids to sender info (internal identifier
/// used for accounts)
/// used for __tracked__ accounts)
fn changed_senders(
&self,
accs: impl Iterator<Item = ChangedAccount>,
) -> FxHashMap<SenderId, SenderInfo> {
let mut identifiers = self.identifiers.write();
let identifiers = self.identifiers.read();
accs.into_iter()
.map(|acc| {
.filter_map(|acc| {
let ChangedAccount { address, nonce, balance } = acc;
let sender_id = identifiers.sender_id_or_create(address);
(sender_id, SenderInfo { state_nonce: nonce, balance })
let sender_id = identifiers.sender_id(&address)?;
Some((sender_id, SenderInfo { state_nonce: nonce, balance }))
})
.collect()
}

View File

@@ -2,8 +2,8 @@
## Abstractions
- We created a [Database trait abstraction](https://github.com/paradigmxyz/reth/blob/main/crates/cli/commands/src/db/mod.rs) using Rust Stable GATs which frees us from being bound to a single database implementation. We currently use MDBX, but are exploring [redb](https://github.com/cberner/redb) as an alternative.
- We then iterated on [`Transaction`](https://github.com/paradigmxyz/reth/blob/main/crates/storage/errors/src/db.rs) as a non-leaky abstraction with helpers for strictly-typed and unit-tested higher-level database abstractions.
- We created a [Database trait abstraction](https://github.com/paradigmxyz/reth/blob/main/crates/storage/db-api/src/database.rs) using Rust Stable GATs which frees us from being bound to a single database implementation. We currently use MDBX, but are exploring [redb](https://github.com/cberner/redb) as an alternative.
- We then iterated on [`Transaction`](https://github.com/paradigmxyz/reth/blob/main/crates/storage/db-api/src/transaction.rs) as a non-leaky abstraction with helpers for strictly-typed and unit-tested higher-level database abstractions.
## Codecs

View File

@@ -40,10 +40,19 @@ All binaries are stored in [`bin`](../../bin).
These crates are related to the database.
- [`storage/codecs`](../../crates/storage/codecs): Different storage codecs.
- [`storage/codecs/derive`](../../crates/storage/codecs/derive): Derive macros for storage codecs.
- [`storage/libmdbx-rs`](../../crates/storage/libmdbx-rs): Rust bindings for [libmdbx](https://github.com/erthink/libmdbx). A fork of an earlier Apache-licensed version of [libmdbx-rs][libmdbx-rs].
- [`storage/db`](../../crates/storage/db): Strongly typed Database abstractions (transactions, cursors, tables) over lower level database backends.
- Implemented backends: mdbx
- [`storage/db-api`](../../crates/storage/db-api): High-level database access traits used across storage crates.
- [`storage/db-common`](../../crates/storage/db-common): Shared database helpers and utilities.
- [`storage/db-models`](../../crates/storage/db-models): Typed database models for on-disk tables.
- [`storage/storage-api`](../../crates/storage/storage-api): Storage-facing APIs used by higher-level components.
- [`storage/provider`](../../crates/storage/provider): Traits which provide a higher level api over the database to access the Ethereum state and historical data (transactions, blocks etc.)
- [`storage/rpc-provider`](../../crates/storage/rpc-provider): Storage provider implementations tailored for RPC access patterns.
- [`storage/errors`](../../crates/storage/errors): Common error types used by storage crates.
- [`storage/nippy-jar`](../../crates/storage/nippy-jar): Compressed columnar storage for historical data.
- [`storage/zstd-compressors`](../../crates/storage/zstd-compressors): Zstandard-based compressors used by storage components.
### Networking
@@ -62,16 +71,21 @@ The networking component mainly lives in [`net/network`](../../crates/net/networ
- Contains: Peer banlist.
- [`net/network-api`](../../crates/net/network-api): Contains traits that define the networking component as a whole. Other components that interface with the network stack only need to depend on this crate for the relevant types.
- [`net/nat`](../../crates/net/nat): A small helper crate that resolves the external IP of the running node using various methods (such as a manually provided IP, using UPnP etc.)
- [`net/network-types`](../../crates/net/network-types): Common networking types (peer identifiers, capabilities, messages, etc.).
- [`net/p2p`](../../crates/net/p2p): Higher-level P2P networking helpers and utilities.
- [`net/peers`](../../crates/net/peers): Peer set management, scoring and reputation support.
#### Discovery
- [`net/discv4`](../../crates/net/discv4): An implementation of the [discv4][discv4] protocol
- [`net/discv5`](../../crates/net/discv5): An implementation of the discv5 node discovery protocol.
- [`net/dns`](../../crates/net/dns): An implementation of node discovery via DNS ([EIP-1459][eip-1459])
#### Protocol
- [`net/eth-wire`](../../crates/net/eth-wire): Implements the `eth` wire protocol and the ``RLPx`` networking stack.
- [`net/ecies`](../../crates/net/ecies): Implementation of the Elliptic Curve Integrated Encryption Scheme used in the ``RLPx`` handshake.
- [`net/eth-wire-types`](../../crates/net/eth-wire-types): Common types used by the `eth` wire protocol and RLPx networking stack.
#### Downloaders
@@ -81,7 +95,9 @@ The networking component mainly lives in [`net/network`](../../crates/net/networ
Different consensus mechanisms.
- [`consensus/common`](../../crates/consensus/common): Common consensus functions and traits (e.g. fee calculation)
- [`consensus/common`](../../crates/consensus/common): Common consensus functions and traits (e.g. fee calculation).
- [`consensus/consensus`](../../crates/consensus/consensus): Core consensus engine interfaces and implementations.
- [`consensus/debug-client`](../../crates/consensus/debug-client): Utilities for interacting with the consensus engine in debugging and testing scenarios.
### Execution
@@ -96,7 +112,9 @@ Crates related to transaction execution.
These crates implement the main syncing drivers of reth.
- [`stages`](../../crates/stages): A pipelined sync, including implementation of various stages. This is used during initial sync and is faster than the tree-like structure for longer sync ranges.
- [`stages/api`](../../crates/stages/api): Public API for the staged sync pipeline.
- [`stages/stages`](../../crates/stages/stages): Implementations of the individual sync stages and the pipeline driver. This is used during initial sync and is faster than the tree-like structure for longer sync ranges.
- [`stages/types`](../../crates/stages/types): Shared types used by the staged sync pipeline.
### RPC
@@ -146,6 +164,10 @@ Crates related to building and validating payloads (blocks).
- [`transaction-pool`](../../crates/transaction-pool): An in-memory pending transactions pool.
- [`payload/builder`](../../crates/payload/builder): Abstractions for payload building and a payload builder service that works with multiple kinds of payload resolvers.
- [`payload/basic`](../../crates/payload/basic): A basic payload generator.
- [`payload/builder-primitives`](../../crates/payload/builder-primitives): Common primitives used by payload builders.
- [`payload/primitives`](../../crates/payload/primitives): Shared types used when building and validating payloads.
- [`payload/util`](../../crates/payload/util): Utility helpers used by payload building and validation logic.
- [`payload/validator`](../../crates/payload/validator): Payload validation helpers and utilities.
### Primitives
@@ -169,6 +191,12 @@ Small utility crates.
- [`metrics/metrics-derive`](https://github.com/rkrasiuk/metrics-derive): A derive-style API for creating metrics
- [`metrics/reth-node-metrics`](../../crates/node/metrics/): The implementation of metrics server, recorder, hooks.
- [`tracing`](../../crates/tracing): A small utility crate to install a uniform [`tracing`][tracing] subscriber
- [`fs-util`](../../crates/fs-util): Small filesystem utilities shared across the node.
- [`tokio-util`](../../crates/tokio-util): Tokio-related utilities used by reth.
- [`static-file`](../../crates/static-file): Utilities for bundling and serving static files.
- [`tracing-otlp`](../../crates/tracing-otlp): Exporter for sending [`tracing`][tracing] spans to OTLP/OTel backends.
- [`errors`](../../crates/errors): Common error types shared across multiple crates.
- [`e2e-test-utils`](../../crates/e2e-test-utils): Helpers for end-to-end tests of the node.
[libmdbx-rs]: https://crates.io/crates/libmdbx
[discv4]: https://github.com/ethereum/devp2p/blob/master/discv4.md

View File

@@ -189,6 +189,11 @@ Networking:
--max-inbound-peers <MAX_INBOUND_PEERS>
Maximum number of inbound peers. default: 30
--max-peers <COUNT>
Maximum number of total peers (inbound + outbound).
Splits peers using approximately 2:1 inbound:outbound ratio. Cannot be used together with `--max-outbound-peers` or `--max-inbound-peers`.
--max-tx-reqs <COUNT>
Max concurrent `GetPooledTransactions` requests.
@@ -892,6 +897,9 @@ Engine:
--engine.legacy-state-root
Enable legacy state root
--engine.disable-state-cache
Disable state cache
--engine.disable-prewarming
Disable parallel prewarming

View File

@@ -135,6 +135,11 @@ Networking:
--max-inbound-peers <MAX_INBOUND_PEERS>
Maximum number of inbound peers. default: 30
--max-peers <COUNT>
Maximum number of total peers (inbound + outbound).
Splits peers using approximately 2:1 inbound:outbound ratio. Cannot be used together with `--max-outbound-peers` or `--max-inbound-peers`.
--max-tx-reqs <COUNT>
Max concurrent `GetPooledTransactions` requests.

View File

@@ -135,6 +135,11 @@ Networking:
--max-inbound-peers <MAX_INBOUND_PEERS>
Maximum number of inbound peers. default: 30
--max-peers <COUNT>
Maximum number of total peers (inbound + outbound).
Splits peers using approximately 2:1 inbound:outbound ratio. Cannot be used together with `--max-outbound-peers` or `--max-inbound-peers`.
--max-tx-reqs <COUNT>
Max concurrent `GetPooledTransactions` requests.

View File

@@ -277,6 +277,11 @@ Networking:
--max-inbound-peers <MAX_INBOUND_PEERS>
Maximum number of inbound peers. default: 30
--max-peers <COUNT>
Maximum number of total peers (inbound + outbound).
Splits peers using approximately 2:1 inbound:outbound ratio. Cannot be used together with `--max-outbound-peers` or `--max-inbound-peers`.
--max-tx-reqs <COUNT>
Max concurrent `GetPooledTransactions` requests.

View File

@@ -189,6 +189,11 @@ Networking:
--max-inbound-peers <MAX_INBOUND_PEERS>
Maximum number of inbound peers. default: 30
--max-peers <COUNT>
Maximum number of total peers (inbound + outbound).
Splits peers using approximately 2:1 inbound:outbound ratio. Cannot be used together with `--max-outbound-peers` or `--max-inbound-peers`.
--max-tx-reqs <COUNT>
Max concurrent `GetPooledTransactions` requests.
@@ -892,6 +897,9 @@ Engine:
--engine.legacy-state-root
Enable legacy state root
--engine.disable-state-cache
Disable state cache
--engine.disable-prewarming
Disable parallel prewarming

View File

@@ -135,6 +135,11 @@ Networking:
--max-inbound-peers <MAX_INBOUND_PEERS>
Maximum number of inbound peers. default: 30
--max-peers <COUNT>
Maximum number of total peers (inbound + outbound).
Splits peers using approximately 2:1 inbound:outbound ratio. Cannot be used together with `--max-outbound-peers` or `--max-inbound-peers`.
--max-tx-reqs <COUNT>
Max concurrent `GetPooledTransactions` requests.

View File

@@ -135,6 +135,11 @@ Networking:
--max-inbound-peers <MAX_INBOUND_PEERS>
Maximum number of inbound peers. default: 30
--max-peers <COUNT>
Maximum number of total peers (inbound + outbound).
Splits peers using approximately 2:1 inbound:outbound ratio. Cannot be used together with `--max-outbound-peers` or `--max-inbound-peers`.
--max-tx-reqs <COUNT>
Max concurrent `GetPooledTransactions` requests.

View File

@@ -277,6 +277,11 @@ Networking:
--max-inbound-peers <MAX_INBOUND_PEERS>
Maximum number of inbound peers. default: 30
--max-peers <COUNT>
Maximum number of total peers (inbound + outbound).
Splits peers using approximately 2:1 inbound:outbound ratio. Cannot be used together with `--max-outbound-peers` or `--max-inbound-peers`.
--max-tx-reqs <COUNT>
Max concurrent `GetPooledTransactions` requests.

View File

@@ -26,8 +26,14 @@ The methods are grouped into namespaces, which are listed below:
| [`trace`](/jsonrpc/trace) | The `trace` API provides several methods to inspect the Ethereum state, including Parity-style traces. | No |
| [`admin`](/jsonrpc/admin) | The `admin` API allows you to configure your node. | **Yes** |
| [`rpc`](/jsonrpc/rpc) | The `rpc` API provides information about the RPC server and its modules. | No |
| `reth` | The `reth` API provides reth-specific methods like balance changes and chain notifications. | No |
| `ots` | The `ots` API provides Otterscan-compatible methods for block exploration. | No |
| `flashbots` | The `flashbots` API provides block submission validation methods for builders. | No |
| `miner` | The `miner` API allows you to configure miner/builder settings like extra data and gas limits. | **Yes** |
| `mev` | The `mev` API provides MEV bundle submission and simulation methods. | No |
| `testing` | The `testing` API provides methods for building blocks in a single call (testing only). | **Yes** |
Note that some APIs are sensitive, since they can be used to configure your node (`admin`), or access accounts stored on the node (`eth`).
Note that some APIs are sensitive, since they can be used to configure your node (`admin`, `miner`), access accounts stored on the node (`eth`), or perform testing operations (`testing`).
Generally, it is advisable to not expose any JSONRPC namespace publicly, unless you know what you are doing.
@@ -61,7 +67,7 @@ To enable JSON-RPC namespaces on the HTTP server, pass each namespace separated
reth node --http --http.api eth,net,trace
```
You can pass the `all` option, which is a convenient wrapper for all the JSON-RPC namespaces `admin,debug,eth,net,trace,txpool,web3,rpc` on the HTTP server:
You can pass the `all` option, which is a convenient wrapper for all the JSON-RPC namespaces `admin,debug,eth,net,trace,txpool,web3,rpc,reth,ots,flashbots,miner,mev,testing` on the HTTP server:
```bash
reth node --http --http.api all
@@ -117,7 +123,7 @@ You can use `curl`, a programming language with a low-level library, or a tool l
As a reminder, you need to run the command below to enable all of these APIs using an HTTP transport:
```bash
reth node --http --http.api "admin,debug,eth,net,trace,txpool,web3,rpc"
reth node --http --http.api "admin,debug,eth,net,trace,txpool,web3,rpc,reth,ots,flashbots,miner,mev,testing"
```
This allows you to then call:

View File

@@ -13,6 +13,13 @@ This section provides essential information about the ports used by the system,
- **Purpose:** Peering with other nodes for synchronization of blockchain data. Nodes communicate through this port to maintain network consensus and share updated information.
- **Exposure Recommendation:** This port should be exposed to enable seamless interaction and synchronization with other nodes in the network.
## Discovery v5 Port
- **Port:** `9200`
- **Protocol:** UDP
- **Purpose:** Used for discv5 peer discovery protocol. This is a newer discovery protocol that can be enabled with `--enable-discv5-discovery`. It operates independently from the legacy discv4 discovery on port 30303.
- **Exposure Recommendation:** This port should be exposed if discv5 discovery is enabled to allow peer discovery.
## Metrics Port
- **Port:** `9001`

View File

@@ -41,7 +41,7 @@ use reth_ethereum::{
builder::{
components::{BasicPayloadServiceBuilder, ComponentsBuilder, PayloadBuilderBuilder},
rpc::{PayloadValidatorBuilder, RpcAddOns},
BuilderContext, Node, NodeAdapter, NodeBuilder,
BuilderContext, Node, NodeAdapter, NodeBuilder, PayloadBuilderConfig,
},
core::{args::RpcServerArgs, node_config::NodeConfig},
node::{
@@ -337,7 +337,8 @@ where
ctx.provider().clone(),
pool,
evm_config,
EthereumBuilderConfig::new(),
EthereumBuilderConfig::new()
.with_extra_data(ctx.payload_builder_config().extra_data_bytes()),
),
};
Ok(payload_builder)

View File

@@ -44,7 +44,7 @@ fn main() {
// create a new subscription to pending transactions
let mut pending_transactions = node.pool.new_pending_pool_transactions_listener();
// get an instance of the `trace_` API handler
// get an instance of the `eth_` API handler
let eth_api = node.rpc_registry.eth_api().clone();
println!("Spawning trace task!");
@@ -106,13 +106,13 @@ fn main() {
/// Our custom cli args extension that adds one flag to reth default CLI.
#[derive(Debug, Clone, Default, clap::Args)]
struct RethCliTxpoolExt {
/// The addresses of the recipients that we want to trace.
/// The addresses of the recipients that we want to inspect.
#[arg(long, value_delimiter = ',')]
pub recipients: Vec<Address>,
}
impl RethCliTxpoolExt {
/// Check if the recipient is in the list of recipients to trace.
/// Check if the recipient is in the list of recipients to inspect.
pub fn is_match(&self, recipient: &Address) -> bool {
self.recipients.is_empty() || self.recipients.contains(recipient)
}

View File

@@ -5,6 +5,7 @@ use crate::{
CustomNode,
};
use alloy_eips::eip2718::WithEncoded;
use alloy_primitives::Bytes;
use op_alloy_rpc_types_engine::{OpExecutionData, OpExecutionPayload};
use reth_engine_primitives::EngineApiValidator;
use reth_ethereum::{
@@ -54,6 +55,10 @@ impl ExecutionPayload for CustomExecutionData {
None
}
fn block_access_list(&self) -> Option<&Bytes> {
None
}
fn parent_beacon_block_root(&self) -> Option<revm_primitives::B256> {
self.inner.parent_beacon_block_root()
}

View File

@@ -62,7 +62,8 @@ where
ctx.provider().clone(),
pool,
evm_config,
EthereumBuilderConfig::new(),
EthereumBuilderConfig::new()
.with_extra_data(ctx.payload_builder_config().extra_data_bytes()),
);
let conf = ctx.payload_builder_config();