Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(tree): make state methods work for historical blocks #11265

Merged
merged 10 commits into from
Oct 7, 2024

Conversation

Rjected
Copy link
Member

@Rjected Rjected commented Sep 26, 2024

This fixes #11251 by allowing fetching state from disk OR from in-memory state.

Closes: #10875
Closes: #8305

@rkrasiuk rkrasiuk added C-bug An unexpected or incorrect behavior A-consensus Related to the consensus engine labels Sep 26, 2024
@Rjected Rjected force-pushed the dan/fix-state-methods branch 4 times, most recently from e177f80 to 5436da2 Compare September 30, 2024 12:35
crates/chain-state/src/in_memory.rs Outdated Show resolved Hide resolved
// We are not removing block meta as it is used to get block changesets.
let mut block_bodies = Vec::new();
for block_num in range.clone() {
let Some(block_body) = self.block_body_indices(block_num)? else { break };
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should we error here instead of providing only partial response to range query?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think so, although current range methods on the db provider provide partial response over erroring, so we may want to refactor those separately

/// 3. Set the local state to the value in the changeset
///
/// If the range is empty, or there are no blocks for the given range, then this returns `None`.
pub fn get_state(
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

akin to checks in historical provider this method should check lowest available history blocks in case of a pruned node

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Now done in ChangeSetReader and StorageChangeSetReader, ensuring we don't create a db provider without checking prune checkpoints first: d7e6542

Copy link
Collaborator

@joshieDo joshieDo Sep 30, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  • can we add to the docs that it's only for historical (or persistence storage), if thats the case? doesn't seem to be the case
  • this is creating a ton of new database transactions. a lil worried that this might also have some unwanted effects since they do not share the same view. if this is only for historical (is it?), maybe it would be better moved to DatabaseProvider?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can we add to the docs that it's only for historical (or persistence storage), if thats the case?

Yes, this is only when we're using a databaseprovider, so only for persisted storage

this is creating a ton of new database transactions. a lil worried that this might also have some unwanted effects since they do not share the same view. if this is only for historical (is it?), maybe it would be better moved to DatabaseProvider?

You mean the provider calls which hit the DB? ie:

            let block_body = self
                .block_body_indices(block_num)?
                .ok_or(ProviderError::BlockBodyIndicesNotFound(block_num))?;
// ^ one db tx
            let changeset =
                self.account_block_changeset(block_num)?.into_iter().map(|elem| (block_num, elem));
// ^ may or may not be one db tx
            let changeset = self.storage_changeset(block_num)?;
// ^ also may or may not be one db tx

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Rjected a valid point from @joshieDo here. in scope of this function we perform a ton of lookups by block number, but there is no consistency guarantee here

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This now uses one database tx

Vec::new(),
receipts.into(),
start_block_number,
Vec::new(),
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what should we do with requests here?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should really be fetching them using the RequestsProvider, but that method requires a timestamp, which I'm not sure how to deal with yet

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

whether we actually need to store requests is still tbd so imo this is fine, but please track so we don't forget

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Member

@fgimenez fgimenez left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

awesome work, lgtm!

@Rjected Rjected marked this pull request as ready for review September 30, 2024 18:19
@joshieDo
Copy link
Collaborator

joshieDo commented Sep 30, 2024

might be wrong but this feels unsafe when it touches in-memory ranges.

every query to self.canonical_in_memory_state.get/fetch/state should be viewed as a DatabaseTransaction in which the view might change in the next one.

Example:

We fetch block changesets on a loop which underneath calls self.canonical_in_memory_state.state_by_number . every call is basically another view decoupled from the previous call.

Example:

main: b1,b2,b3
reorg: b1',b2',b3'

we might end up with weird changeset lists like:

b1, b2', b3'

@Rjected
Copy link
Member Author

Rjected commented Sep 30, 2024

might be wrong but this feels unsafe when it touches in-memory ranges.

When this is used outside of the tree, yes, calling get_state in a loop could result in different views. I think most of the provider methods suffer from this. In those cases we need to provide a safer API that is aware of the CanonicalInMemoryState locks. For example, right now it is unsafe to call most combinations of providers in sequence. For a safer API, do we want to put the provider methods behind a lock? Similar to how the DatabaseProvider works:

let blockchain_provider = BlockchainProvider::new(/* stuff */);
let provider_ro = blockchain_provider.read(); // acquires ro `CanonicalInMemoryState` lock
for block in blocks {
    let state = provider_ro.get_state(block);
}

Copy link
Collaborator

@mattsse mattsse left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

pending @rkrasiuk

Vec::new(),
receipts.into(),
start_block_number,
Vec::new(),
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

whether we actually need to store requests is still tbd so imo this is fine, but please track so we don't forget

Comment on lines +1473 to +1585
let changesets = state
.block()
.execution_output
.bundle
.reverts
.clone()
.into_plain_state_reverts()
.storage
.into_iter()
.flatten()
.flat_map(|revert: PlainStorageRevert| {
revert.storage_revert.into_iter().map(move |(key, value)| {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

-.-

/// 3. Set the local state to the value in the changeset
///
/// If the range is empty, or there are no blocks for the given range, then this returns `None`.
pub fn get_state(
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Rjected a valid point from @joshieDo here. in scope of this function we perform a ton of lookups by block number, but there is no consistency guarantee here

@Rjected Rjected force-pushed the dan/fix-state-methods branch 3 times, most recently from ecb6f09 to eb3b137 Compare October 2, 2024 23:10
@mattsse mattsse added this to the engineV2 default milestone Oct 3, 2024
Copy link
Collaborator

@mattsse mattsse left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is this good to go?

imo the memory concerns that @joshieDo raised are valid but not an issue for the engine, because by the time this is called no disk io should be in progress.
I'd like to get this in and then look how to make it sound

Comment on lines 87 to 88
///
/// This gives the provider back if creating the historical state provider is not successful.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
///
/// This gives the provider back if creating the historical state provider is not successful.

@mattsse mattsse dismissed rkrasiuk’s stale review October 7, 2024 09:12

will be done separately

@mattsse mattsse added this pull request to the merge queue Oct 7, 2024
Merged via the queue into main with commit 4b12f32 Oct 7, 2024
35 checks passed
@mattsse mattsse deleted the dan/fix-state-methods branch October 7, 2024 09:34
Thegaram added a commit to scroll-tech/reth that referenced this pull request Oct 10, 2024
* chore(provider): use `get_in_memory_or_storage` on `transactions_by_block_range` (paradigmxyz#11453)

* chore(exex): adjust WAL gauge metric names (paradigmxyz#11454)

* chore(provider): use `get_in_memory_or_storage_by_block` on `fn block_body_indices` (paradigmxyz#11452)

* chore(provider): find `last_database_block_number` with `BlockState` anchor instead (paradigmxyz#11455)

* feat: add metrics for failed deliveries (paradigmxyz#11456)

* chore: release 1.0.8 (paradigmxyz#11457)

* chore(provider): use `block_ref` instead on `BlockState` (paradigmxyz#11458)

* feat(rpc): Add codes in execution witness return (paradigmxyz#11443)

* fix: ensure the request's gas limit does not exceed the target gas limit (paradigmxyz#11462)

* feat(grafana): ExEx WAL (paradigmxyz#11461)

* fix: use correct rpc errors (paradigmxyz#11463)

* chore(provider): clone after filtering on `sealed_headers_while`  (paradigmxyz#11459)

      Co-authored-by: Matthias Seitz <[email protected]>

* Map `TransferKind::EofCreate` => `OperationType::OpEofCreate` (paradigmxyz#11090)

Co-authored-by: Matthias Seitz <[email protected]>

* fix: windows build (paradigmxyz#11465)

* chore: use `block_ref` on `CanonicalInMemoryState`  (paradigmxyz#11467)

* chore(rpc): remove include_preimage param on debug_execution_witness (paradigmxyz#11466)

* feat: make addons stateful (paradigmxyz#11204)

Co-authored-by: Arsenii Kulikov <[email protected]>

* Relax Trait Bounds on TransactionPool::Transaction and EthPoolTransaction (paradigmxyz#11079)

Co-authored-by: Matthias Seitz <[email protected]>

* test: add unit tests for `CanonicalChain` (paradigmxyz#11472)

* feat(perf): integrate OnStateHook in executor (paradigmxyz#11345)

* feat: cleaned up prepare_call_env() (paradigmxyz#11469)

Co-authored-by: Matthias Seitz <[email protected]>

* chore: use block.body directly (paradigmxyz#11474)

* feat: Add metrics to track transactions by type in txpool  (paradigmxyz#11403)

Co-authored-by: garwah <garwah@garwah>
Co-authored-by: Matthias Seitz <[email protected]>

* chore: op chainspec (paradigmxyz#11415)

* chore(exex): more backfill debug logs (paradigmxyz#11476)

* fix(exex): use thresholds in stream backfill (paradigmxyz#11478)

* chore(db): capture tx opening backtrace in debug mode (paradigmxyz#11477)

* chore(sdk): `SealedHeader` generic over header (paradigmxyz#11429)

* chore(lint): fix lint primitives (paradigmxyz#11487)

* chore(provider): add more test coverage on `BlockchainProvider::*by_tx_range` queries (paradigmxyz#11480)

* test: ensure default hash matches (paradigmxyz#11486)

* chore: rm deposit contract config for op (paradigmxyz#11479)

* chore(lint): fix lint storage (paradigmxyz#11485)

* chore(provider): add more test coverage on `BlockchainProvider::*by_block_range` queries (paradigmxyz#11488)

* fix(rpc-eth-types): incorrect error msg(; -> :) (paradigmxyz#11503)

Signed-off-by: jsvisa <[email protected]>

* Reexport optimism specific crates from `op-reth` (paradigmxyz#11499)

* feat: rpc replace function created (paradigmxyz#11501)

Co-authored-by: Matthias Seitz <[email protected]>

* Add metrics for failed deliveries to Grafana dashboard (paradigmxyz#11481)

* chore: Remove duplicate EthereumChainSpecParser in favor of existing EthChainSpecParser  (paradigmxyz#11412)

Co-authored-by: garwah <garwah@garwah>
Co-authored-by: Matthias Seitz <[email protected]>

* chore(lint): fix `clippy::needles_lifetimes` (paradigmxyz#11496)

* chore: rm from genesis impl (paradigmxyz#11509)

* fix: cap gas limit properly (paradigmxyz#11505)

* feat: add PoolBuilderConfigOverrides (paradigmxyz#11507)

* feat: expose Op node network_config helper (paradigmxyz#11506)

* chore(deps): weekly `cargo update` (paradigmxyz#11518)

Co-authored-by: github-merge-queue <[email protected]>

* test: add unit tests for `PruneLimiter` (paradigmxyz#11517)

* feat(provider): add `test_race` to `BlockchainProvider2` tests (paradigmxyz#11523)

* fix(tree): make state methods work for historical blocks (paradigmxyz#11265)

Co-authored-by: Roman Krasiuk <[email protected]>
Co-authored-by: Federico Gimenez <[email protected]>

* rpc: use `eth_api()` method (paradigmxyz#11516)

* chore: delete rpc-types (paradigmxyz#11528)

* feat: add get_highest_tx_by_sender to pools (paradigmxyz#11514)

Co-authored-by: Matthias Seitz <[email protected]>

* ci: add `windows` cargo check  (paradigmxyz#11468)

* fix: acquire permit first (paradigmxyz#11537)

* chore: dont fail on ttd (paradigmxyz#11539)

* grafana: add metrics of all transactions in pool by type  (paradigmxyz#11515)

Co-authored-by: Emilia Hane <[email protected]>
Co-authored-by: Emilia Hane <[email protected]>

* feat(exex): subscribe to notifications with head using `ExExContext` (paradigmxyz#11500)

* chore: enforce window (paradigmxyz#11540)

* Refactor get_payload_bodies_by_hash_with to be non-blocking (paradigmxyz#11511)

Co-authored-by: Matthias Seitz <[email protected]>

* Introduce Eth PayloadTypes Impl (paradigmxyz#11519)

Co-authored-by: Matthias Seitz <[email protected]>

* fix(op-reth): add jemalloc feature to optimism-cli for version (paradigmxyz#11543)

* fix(grafana): remove rate function from panel "Transactions by Type in Pool" (paradigmxyz#11542)

* chore: move ethfiltererror (paradigmxyz#11552)

* chore: rm redundant type hint (paradigmxyz#11557)

* Introduce Op PayloadTypes Impl (paradigmxyz#11558)

Co-authored-by: Matthias Seitz <[email protected]>

* chore: chain manual serialisation implementation (paradigmxyz#11538)

* chore: rm unused optimism feature (paradigmxyz#11559)

* feat(trie): expose storage proofs (paradigmxyz#11550)

* chore: rm unused optimism feature from compat (paradigmxyz#11560)

* Added InternalBlockExecutionError to execute.rs exports (paradigmxyz#11525)

Co-authored-by: Matthias Seitz <[email protected]>

* docs(exex): include code for ExEx book from real files (paradigmxyz#11545)

* fix: actually configure the custom gas limit (paradigmxyz#11565)

* chore: relax trait bound for `EthTransactions` (paradigmxyz#11571)

* fix(provider): fix sub overflow on `tx_range` queries for empty blocks (paradigmxyz#11568)

* chore(provider): add more test coverage on `BlockchainProvider` non-range queries (paradigmxyz#11564)

* feat: adding a new method to network config builder (paradigmxyz#11569)

* chore: rm unused optimism feature from engine api (paradigmxyz#11577)

* chore: replace some revm deps (paradigmxyz#11579)

* chore: rm bad cap function (paradigmxyz#11562)

* feat: impl `Encodable2718` and `Decodable2718` for `PooledTransactionsElement` (paradigmxyz#11482)

* fix(exex): exhaust backfill job when using a stream (paradigmxyz#11578)

* fix: in-memory trie updates pruning (paradigmxyz#11580)

Co-authored-by: Matthias Seitz <[email protected]>

* chore(providers): test race condition on all `BlockchainProvider2` macro tests (paradigmxyz#11574)

* chore: also derive arb for test (paradigmxyz#11588)

* chore: bump alloy primitives 0 8 7 (paradigmxyz#11586)

* chore(ci): remove expected failures related to checksummed addresses (paradigmxyz#11589)

* chore(rpc): use `block_hash` instead on fetching `debug_trace_block` block (paradigmxyz#11587)

* feat: add mul support for SubPoolLimit (paradigmxyz#11591)

Co-authored-by: Dan Cline <[email protected]>

* docs: delete missing part path (paradigmxyz#11590)

Co-authored-by: Dan Cline <[email protected]>

* fix: simplify reorg handling (paradigmxyz#11592)

* fix: use original bytes for codes (paradigmxyz#11593)

* perf(rpc): use `Arc<BlockWithSenders` on `full_block_cache` (paradigmxyz#11585)

* fix: active inflight count (paradigmxyz#11598)

* fix(net): max inflight tx reqs default (paradigmxyz#11602)

* fix: set deposit gasprice correctly (paradigmxyz#11603)

* fix: set system tx correctly (paradigmxyz#11601)

* fix(trie): prefix set extension (paradigmxyz#11605)

* feat: add tx propagation mode (paradigmxyz#11594)

* feat: add helper function to provde the tx manager config (paradigmxyz#11608)

* fix(grafana): set instance variable from `reth_info` metric (paradigmxyz#11607)

* fix(net): add concurrency param from config to `TransactionFetcherInfo` (paradigmxyz#11600)

Co-authored-by: Matthias Seitz <[email protected]>

* perf(rpc): optimistically retrieve block if near the tip on `eth_getLogs` (paradigmxyz#11582)

* update fork base commit

* remove new book tests

---------

Signed-off-by: jsvisa <[email protected]>
Co-authored-by: joshieDo <[email protected]>
Co-authored-by: Alexey Shekhirin <[email protected]>
Co-authored-by: Matthias Seitz <[email protected]>
Co-authored-by: Francis Li <[email protected]>
Co-authored-by: Emilia Hane <[email protected]>
Co-authored-by: Arsenii Kulikov <[email protected]>
Co-authored-by: Eric Woolsey <[email protected]>
Co-authored-by: Thomas Coratger <[email protected]>
Co-authored-by: Federico Gimenez <[email protected]>
Co-authored-by: Varun Doshi <[email protected]>
Co-authored-by: garwah <[email protected]>
Co-authored-by: garwah <garwah@garwah>
Co-authored-by: greged93 <[email protected]>
Co-authored-by: Delweng <[email protected]>
Co-authored-by: Parikalp Bhardwaj <[email protected]>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: github-merge-queue <[email protected]>
Co-authored-by: Dan Cline <[email protected]>
Co-authored-by: Roman Krasiuk <[email protected]>
Co-authored-by: crazykissshout <[email protected]>
Co-authored-by: tedison <[email protected]>
Co-authored-by: David <[email protected]>
Co-authored-by: Emilia Hane <[email protected]>
Co-authored-by: Steven <[email protected]>
Co-authored-by: Debjit Bhowal <[email protected]>
Co-authored-by: Oliver <[email protected]>
Co-authored-by: Luca Provini <[email protected]>
Co-authored-by: John <[email protected]>
vandenbogart pushed a commit to testmachine-ai/reth that referenced this pull request Oct 14, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-consensus Related to the consensus engine C-bug An unexpected or incorrect behavior
Projects
None yet
5 participants