Skip to content

perf(evmrpc): eliminate redundant block fetches in simulate backend#3208

Open
amir-deris wants to merge 6 commits intomainfrom
amir/plt-254-making-improvements-for-evmrpc-simulate
Open

perf(evmrpc): eliminate redundant block fetches in simulate backend#3208
amir-deris wants to merge 6 commits intomainfrom
amir/plt-254-making-improvements-for-evmrpc-simulate

Conversation

@amir-deris
Copy link
Copy Markdown
Contributor

@amir-deris amir-deris commented Apr 7, 2026

Summary

Eliminates redundant block fetches in the simulate backend hot path (eth_call, eth_estimateGas).

Single block fetch per request: Added getResultBlockByNumberOrHash so block number/hash (including latest) resolves once. The ResultBlock is passed into getHeader, removing the second blockByNumberRespectingWatermarks call across StateAndHeaderByNumberOrHash, HeaderByNumber, and BlockByNumber.

Request context in getHeader: getHeader now accepts ctx and uses it for the conditional block fetch and blockResultsWithRetry, so deadlines, cancellation, and tracing propagate from the RPC context instead of context.Background().

Stricter error handling: If the block cannot be loaded, the error surfaces rather than silently returning a header with DefaultBlockGasLimit. simulate_test updated accordingly (bcAlwaysFailClient).

Out of scope

BlockByHash still resolves by hash then calls BlockByNumber (two RPCs). Left unchanged to keep this PR small; can be deduplicated in a follow-up.

@github-actions
Copy link
Copy Markdown

github-actions bot commented Apr 7, 2026

The latest Buf updates on your PR. Results from workflow Buf / buf (pull_request).

BuildFormatLintBreakingUpdated (UTC)
✅ passed✅ passed✅ passed✅ passedApr 8, 2026, 8:43 PM

@codecov
Copy link
Copy Markdown

codecov bot commented Apr 7, 2026

Codecov Report

❌ Patch coverage is 87.50000% with 3 lines in your changes missing coverage. Please review.
✅ Project coverage is 58.78%. Comparing base (47afb6e) to head (fea1a20).
⚠️ Report is 2 commits behind head on main.

Files with missing lines Patch % Lines
evmrpc/simulate.go 87.50% 3 Missing ⚠️
Additional details and impacted files

Impacted file tree graph

@@            Coverage Diff             @@
##             main    #3208      +/-   ##
==========================================
- Coverage   58.93%   58.78%   -0.15%     
==========================================
  Files        2062     2054       -8     
  Lines      169019   168230     -789     
==========================================
- Hits        99612    98896     -716     
+ Misses      60686    60591      -95     
- Partials     8721     8743      +22     
Flag Coverage Δ
sei-chain-pr 64.02% <87.50%> (?)
sei-db 70.41% <ø> (ø)

Flags with carried forward coverage won't be shown. Click here to find out more.

Files with missing lines Coverage Δ
evmrpc/simulate.go 73.94% <87.50%> (+1.86%) ⬆️

... and 71 files with indirect coverage changes

🚀 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.

@amir-deris amir-deris changed the title Making improvements to evmrpc/simulate.go perf(evmrpc): eliminate redundant block fetches in simulate backend Apr 8, 2026
@amir-deris amir-deris self-assigned this Apr 8, 2026
@amir-deris amir-deris requested review from jewei1997 and masih April 8, 2026 16:40
return nil, 0, false, err
}
return block.Block.Height, false, nil
return block, block.Block.Height, false, nil
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

If the function returns block, why also return block.Block.Height? The caller can infer what they need from block right?

func (b *Backend) getBlockHeight(ctx context.Context, blockNrOrHash rpc.BlockNumberOrHash) (int64, bool, error) {
// getResultBlockByNumberOrHash resolves blockNrOrHash to a Tendermint ResultBlock in one RPC path
// (by hash or by number, including latest). Callers can pass tmBlock to getHeader to avoid a second block fetch.
func (b *Backend) getResultBlockByNumberOrHash(ctx context.Context, blockNrOrHash rpc.BlockNumberOrHash) (*coretypes.ResultBlock, int64, bool, error) {
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

I would drop Result from the name of this function.

}

func (b *Backend) getHeader(blockNumber *big.Int) *ethtypes.Header {
func (b *Backend) getHeader(ctx context.Context, blockNumber *big.Int, tmBlock *coretypes.ResultBlock) *ethtypes.Header {
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Similarly, if this function takes block there is no need to also pass in the hight, right? Since it can get what it needs from the block itself.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I believe the reason to have both blockNumber and tmBlock is in some methods that use the getHeader, the tmBlock is not available and only we have the blockNumber. For example in method CurrentHeader().

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants