Skip to content

Conversation

@Raneet10
Copy link
Member

@Raneet10 Raneet10 commented Nov 6, 2025

Description

This PR enables prefetching of trie nodes before execution of block. It adds a waitforwarm config which if enabled will block execution of block until the warming finishes.

Changes

  • Bugfix (non-breaking change that solves an issue)
  • Hotfix (change that solves an urgent issue, and requires immediate attention)
  • New feature (non-breaking change that adds functionality)
  • Breaking change (change that is not backwards-compatible and/or changes current functionality)
  • Changes only for a subset of nodes

Checklist

  • I have added at least 2 reviewer or the whole pos-v1 team
  • I have added sufficient documentation in code
  • I will be resolving comments - if any - by pushing each fix in a separate commit and linking the commit hash in the comment reply
  • Created a task in Jira and informed the team for implementation in Erigon client (if applicable)
  • Includes RPC methods changes, and the Notion documentation has been updated

Testing

  • I have added unit tests
  • I have added tests to CI
  • I have tested this code manually on local environment
  • I have tested this code manually on remote devnet using express-cli
  • I have tested this code manually on amoy
  • I have created new e2e tests into express-cli

@Raneet10 Raneet10 requested a review from a team November 6, 2025 10:03
header := pool.currentHead.Load()
pool.mu.RUnlock()

if header == nil || len(txs) == 0 {
Copy link
Collaborator

Choose a reason for hiding this comment

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

Shall we move the txs len check on top of the function?
Just an optimization to reduce lock contention. Since txs is an input and doesn’t depend on pool state, you can fast-return before touching any shared data.

storageCacheHitMeter = metrics.NewRegisteredMeter("chain/storage/reads/cache/process/hit", nil)
storageCacheMissMeter = metrics.NewRegisteredMeter("chain/storage/reads/cache/process/miss", nil)

accountCacheHitPrefetchMeter = metrics.NewRegisteredMeter("chain/account/reads/cache/prefetch/hit", nil)
Copy link
Collaborator

Choose a reason for hiding this comment

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

Shall we remove all the unused metrics here? Lint's complaining

defer cancel()

if followupInterrupt == nil {
followupInterrupt = &atomic.Bool{}
Copy link
Collaborator

Choose a reason for hiding this comment

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

is linter right 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.

Not sure. I didn't make this change.

vmCfg := bc.cfg.VmConfig
vmCfg.Tracer = nil
bc.prefetcher.Prefetch(block, throwaway, vmCfg, followupInterrupt)
// go func(start time.Time, throwaway *state.StateDB, block *types.Block) {
Copy link
Collaborator

Choose a reason for hiding this comment

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

Is this worth deleting?
Or - alternatively - have a comment to explain why it's disabled

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 commented it out so that we don't run multiple prefetches together. I'll probably remove this once we finalize all the optimizations.

return nil, 0
}
vmCfg := bc.cfg.VmConfig
vmCfg.Tracer = nil
Copy link
Collaborator

Choose a reason for hiding this comment

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

why is this required? Same for PrefetchFromTxpool

// Append cache hit/miss percentages
if bc != nil {
if total := bc.lastProcAccHit + bc.lastProcAccMiss; total > 0 {
pct := int64(100 * bc.lastProcAccHit / total)
Copy link
Collaborator

Choose a reason for hiding this comment

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

seems like this (and others in the same func) conversion is pleonastic

Copy link
Member Author

Choose a reason for hiding this comment

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

Yeah right. I don't think these will make it to develop anyway.


// Note: Change the go image version in Dockerfile if you change this.
go 1.24.6
go 1.24.8
Copy link
Collaborator

Choose a reason for hiding this comment

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

worth updating in the CI yml fles as well.
Also, since we're bumping, maybe we can use 1.24.9 to solve the govulncheck issues (I have that on my upstream PR as well)

Copy link
Member Author

Choose a reason for hiding this comment

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

Could do yeah but anyway this isn't getting merged to develop just yet.

core/analyzer.sh Outdated
@@ -0,0 +1,102 @@
# ./log_analyzer.sh "3 days ago" # Scans the last 3 days
Copy link
Contributor

Choose a reason for hiding this comment

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

This should be excluded from this PR I guess?

Comment on lines 114 to 123
// Aggregate touched pre-state from stateCpy and populate the process reader cache directly
acc, stor := stateCpy.PrefetchTouched()
for a := range acc {
reader.Account(a)
}
for a, bucket := range stor {
for k := range bucket {
reader.Storage(a, k)
}
}
Copy link
Contributor

Choose a reason for hiding this comment

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

What's the rationale of this change? IntermediateRoot also fetches touched account/storages.

Copy link
Member Author

Choose a reason for hiding this comment

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

Yes but it also calculates state root which we strictly don't need. Pre-execution should fill up the accounts/storages. Do you think it still should be there ?

Copy link
Contributor

Choose a reason for hiding this comment

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

It should be there. Simply loading account/storage isn't sufficient, because in order to calculate the final state root hash, some sibling nodes will need to be retrieved, even though they aren't related to touched accounts.

vmCfg.Tracer = nil
synthetic := types.NewBlockWithHeader(header).WithBody(types.Body{Transactions: txs})
var noop atomic.Bool
bc.prefetcher.Prefetch(synthetic, warmdb, vmCfg, &noop)
Copy link
Contributor

Choose a reason for hiding this comment

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

This call execute transactions without taking fees from transactions, which introduces a DoS vector. Malicious users can submit resource-expensive transaction and replace them with a bit higher gas continuously without paying the gas fee.

For example, if the current base gas price is 50Gwei, I can submit a transaction with 32M gas of SLOAD, with a initial gas price of 1Gwei, and continuously replace the existing tx by bumping up the gas by 10% for 41 times before reaching the 50Gwei base price (1Gwei * 1.1^41 = 49Gwei). This will result in the node to fully execute this transaction 41 times before the transaction is actually included in a block.

Copy link
Member Author

Choose a reason for hiding this comment

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

Good catch. Though I am planning to remove PrefetchFromTxpool altogether as I didn't see any impact of prefetching from txpool. Perhaps running it in worker (which is present currently) is more deterministic ?

@codecov
Copy link

codecov bot commented Nov 7, 2025

Codecov Report

❌ Patch coverage is 49.66667% with 151 lines in your changes missing coverage. Please review.
⚠️ Please upload report for BASE (src-optimizations@a63c799). Learn more about missing BASE report.

Files with missing lines Patch % Lines
core/blockchain.go 49.45% 39 Missing and 7 partials ⚠️
core/state/reader.go 2.70% 36 Missing ⚠️
core/state/statedb.go 0.00% 22 Missing ⚠️
core/blockchain_insert.go 57.50% 13 Missing and 4 partials ⚠️
miner/worker.go 63.41% 10 Missing and 5 partials ⚠️
core/state_prefetcher.go 86.53% 7 Missing ⚠️
eth/ethconfig/gen_config.go 0.00% 6 Missing ⚠️
eth/backend.go 0.00% 1 Missing ⚠️
miner/fake_miner.go 0.00% 1 Missing ⚠️
Additional details and impacted files
@@                 Coverage Diff                  @@
##             src-optimizations    #1871   +/-   ##
====================================================
  Coverage                     ?   47.92%           
====================================================
  Files                        ?      844           
  Lines                        ?   143880           
  Branches                     ?        0           
====================================================
  Hits                         ?    68959           
  Misses                       ?    70402           
  Partials                     ?     4519           

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@sonarqubecloud
Copy link

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants