fix: resolve eth_feeHistory base fee per query instead of pinning a container clone - #1295
Merged
Conversation
…ontainer clone Closes #1282. The RPC server held a one-time BaseFeeContainer clone for its worker. GasAccumulator::set_num_workers truncates slots on shrink and creates fresh default containers on regrow, so for worker ids >= 1 the held clone detached from the slot the epoch schedule writes, and eth_feeHistory / eth_blobBaseFee quoted a permanently stale fee. Add WorkerBaseFee, a per-query handle that resolves the worker's slot through the accumulator on every read, and pass it (instead of a clone) through initialize_worker_components into get_rpc_server and FeeHistoryWithEpochBaseFee. A read while the slot is truncated answers MIN_PROTOCOL_BASE_FEE, the value the regrown slot is reborn with, so a fee quote never panics a running node. Tests: three tn-types unit tests (stale-clone repro, truncated-slot default, pure-grow agreement) and a tn-reth RPC regression test that shrinks and regrows the worker set and asserts eth_feeHistory quotes the live fee. Each new test confirmed by in-diff mutation. Signed-off-by: Onyeka Obi <softwareengineerasaservant@isurvivable.cv>
…-network into fix/1282-rpc-base-fee-lookup
The merge of main brought in the eth_syncing test fix (#1285), which passes BaseFeeContainer::default() to get_rpc_server. On this branch get_rpc_server takes a WorkerBaseFee handle (#1282) and the test module no longer imports BaseFeeContainer, so the workspace test build failed with E0433. Hand the test a one-worker accumulator handle, matching the other fee-history tests in the module. Signed-off-by: Onyeka Obi <softwareengineerasaservant@isurvivable.cv>
MavenRain
requested a deployment
to
merge-into-main
August 27, 2026 15:08 — with
GitHub Actions
Waiting
MavenRain
requested a deployment
to
merge-into-main
August 27, 2026 15:08 — with
GitHub Actions
Waiting
sstanfield
previously approved these changes
Aug 27, 2026
5 tasks
…-lookup Signed-off-by: Onyeka Obi <softwareengineerasaservant@isurvivable.cv> # Conflicts: # crates/node/src/engine/mod.rs # crates/node/src/manager/node/start_epoch.rs # crates/tn-reth/src/env/rpc.rs
…-lookup Signed-off-by: Onyeka Obi <softwareengineerasaservant@isurvivable.cv>
…-lookup Signed-off-by: Onyeka Obi <softwareengineerasaservant@isurvivable.cv>
…-lookup Signed-off-by: Onyeka Obi <softwareengineerasaservant@isurvivable.cv> # Conflicts: # crates/node/src/engine/inner.rs # crates/node/src/engine/mod.rs
MavenRain
added a commit
that referenced
this pull request
Sep 5, 2026
…c-endpoints Round 6: main 66e0d14, 8 commits past the round-5 target 87364b2 (#1270, #1295, #1291, #1288, #1280, #1268, #1294 and one fork merge; 38 files). Two files overlap the PR. engine/inner.rs auto-merged: main's WorkerBaseFee handle at get_rpc_server, the PR's worker_id at start_rpc. env/rpc.rs needed two hand reconciliations because #1295 changed get_rpc_server to take a WorkerBaseFee handle instead of a BaseFeeContainer clone: - The tn_types import: keep the PR's WorkerId and take main's WorkerBaseFee. - The PR's start_worker_rpc test helper: take a &GasAccumulator and pass worker_id's container to init_txn_pool and its worker_base_fee handle to get_rpc_server. The two-worker IPC test builds one GasAccumulator::new(2) so each worker resolves its own slot, matching main's fee_history_methods_for_worker helper. Every other file is byte-identical to origin/main. Signed-off-by: Onyeka Obi <softwareengineerasaservant@isurvivable.cv>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #1282.
Problem
The RPC server received a one-time
BaseFeeContainerclone at worker startup and held it for its whole life.GasAccumulator::set_num_workerstruncates the per-worker slots on a shrink and creates fresh default containers on a regrow, so for worker ids >= 1 the held clone detached from the slot the accumulator owns. From then on the epoch schedule (EpochBaseFees::apply) wrote the new container whileeth_feeHistoryandeth_blobBaseFeekept quoting the orphaned one: a permanently stale base fee for the rest of the node's life. No attacker is required; a routine committee-driven worker-count shrink and regrow is enough.Fix
Hand the RPC server a lookup, not a clone (option 1 from the issue). A new
WorkerBaseFeehandle wraps theGasAccumulatorplus aWorkerIdand resolves the worker's slot on every read, so a long-lived holder always sees the container the accumulator currently owns. The handle is total: a read while the slot is truncated answersMIN_PROTOCOL_BASE_FEE, which is exactly the value the regrown slot is reborn with, so a fee quote never panics a running node (in contrast toinc_block, whose out-of-range panic is by design on the consensus path).initialize_worker_componentsandget_rpc_servernow take the handle instead of a pinned container clone; the read site inFeeHistoryWithEpochBaseFeeis unchanged (self.base_fee.base_fee()at query time).Changes
crates/types/src/gas_accumulator.rs: addWorkerBaseFeehandle andGasAccumulator::worker_base_fee; document the staleness hazard onGasAccumulator::base_feecrates/tn-reth/src/rpc_fee_history.rs:FeeHistoryWithEpochBaseFeestores aWorkerBaseFeeinstead of aBaseFeeContainerclonecrates/tn-reth/src/env/rpc.rs:get_rpc_servertakes the handle; add a shrink-and-regrow RPC regression testcrates/node/src/engine/inner.rs,crates/node/src/engine/mod.rs:initialize_worker_componentstakes the handlecrates/node/src/manager/node/start_epoch.rs: build the handle from the accumulator and pass it throughcrates/node/tests/it/main.rs: source the test's base fee through the accumulator and pass a handleTesting
tn-typesunit tests:held_container_clone_goes_stale_after_shrink_and_regrow(reproduces the bug and shows the handle survives it),worker_base_fee_answers_rebirth_default_while_truncated, andworker_base_fee_and_clone_agree_across_a_pure_grow.tn-rethregression testtest_fee_history_quote_tracks_accumulator_across_shrink_and_regrow: builds the production RPC server for worker 1 of 2, shrinks to 1 and regrows to 2, writes a fresh epoch fee, and assertseth_feeHistoryquotes the live fee.cargo fmt -- --check,cargo +1.94 check --workspace --all-targets, scopedclippy --no-depson the touched crates.