feat: preexecute transactions on followers and send access list to leader - #2664
feat: preexecute transactions on followers and send access list to leader#2664carneiro-cw wants to merge 35 commits into
Conversation
|
Forwarding benchmark: Git Info:
Follower Stats (transactions sent to follower): Plots: |
|
Forwarding benchmark: Git Info:
Follower Stats (transactions sent to follower): Plots: |
|
Forwarding benchmark: Git Info:
Follower Stats (transactions sent to follower): Plots: |
|
Forwarding benchmark: Git Info:
Follower Stats (transactions sent to follower): Plots: |
7354462 to
9ff339a
Compare
|
Forwarding benchmark: Git Info:
Follower Stats (transactions sent to follower): Plots: |
|
Forwarding benchmark: Git Info:
Follower Stats (transactions sent to follower): Plots: |
|
Forwarding benchmark: Git Info:
Follower Stats (transactions sent to follower): Plots: |
|
Forwarding benchmark: Git Info:
Follower Stats (transactions sent to follower): Plots: |
|
Forwarding benchmark: Git Info:
Follower Stats (transactions sent to follower): Plots: |
|
Forwarding benchmark: Git Info:
Follower Stats (transactions sent to follower): Plots: |
|
Forwarding benchmark: Git Info:
Follower Stats (transactions sent to follower): Plots: |
|
Forwarding benchmark: Git Info:
Follower Stats (transactions sent to follower): Plots: |
|
Forwarding benchmark: Git Info:
Follower Stats (transactions sent to follower): Plots: |
|
Forwarding benchmark: Git Info:
Follower Stats (transactions sent to follower): Plots: |
Codex Review SummaryThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
There was a problem hiding this comment.
Summary
Thanks for the substantial refactor — the direction (pre-exec on follower + forwarding access list, unified BlockInfo, and middleware-owned eth_sendRawTransaction path) is clear and valuable. I found one blocking correctness issue tied to RPC response identity handling in the new async eth_send_raw_transaction path.
Blocking
eth_send_raw_transactioncan return a response withId::Nullonspawn_blockingjoin failure, losing request/response correlation
Insrc/eth/rpc/server.rs, the new middleware path captures the real request id (let id = request.id().into_owned();) and uses it for success responses, but in the error branch of.map(...)it buildsMethodResponse::error(Id::Null, ...). This breaks JSON-RPC id correlation exactly in the error scenario and can confuse clients/retries in production under runtime pressure. The error response should preserve the originalidrather than forcingNull.
If this is fixed, the rest of the sampled changes look consistent with the new architecture.
PR Reviewer Guide 🔍Here are some key observations to aid the review process:
|
PR Code Suggestions ✨Explore these optional code suggestions:
|
There was a problem hiding this comment.
💡 Codex Review
stratus/src/eth/storage/stratus_storage.rs
Line 584 in e351474
After save_block drops the latest-state write guard, the cached header/state already represents the new block but read_mined_block_number() still returns the preceding block until this statement runs. An explicit-number eth_call arriving in that interval can therefore have its old requested number classified as Latest by read_block_info, combining historical state selection with the new block's execution header. Update the mined number before releasing the same publication lock, or base the classification solely on the guarded block info.
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
There was a problem hiding this comment.
Summary
Thanks for the substantial refactor — the overall direction (custom eth_sendRawTransaction middleware path, follower pre-exec access-list forwarding, and BlockInfo unification) is good. I found one blocking correctness issue that can cause rejected clients’ transactions to still execute/forward.
Blocking
eth_sendRawTransactionwork starts before client rejection is enforced
In the middlewarecallpath,eth_send_raw_transaction(request, ...)is invoked beforereject_client(...)for this method path is guaranteed, and that function immediately spawnstokio::task::spawn_blockingwith the full execution/forwarding logic. Because spawning is eager and replacing the returned future does not cancel the blocking task, a blocked/unknown client can receive a rejection response while the transaction is still processed in the background. This is a behavior/security mismatch for admission control and should be fixed by ensuring rejection happens before spawning any raw-tx work (or by moving rejection inside the spawned closure before side effects).
There was a problem hiding this comment.
Summary
Good progress overall on the BlockInfo unification and follower pre-exec path, but I found one blocking correctness issue in the new eth_sendRawTransaction async path.
Blocking
- Error responses from the spawned blocking task lose JSON-RPC request identity.
In src/eth/rpc/server.rs, eth_send_raw_transaction correctly captures the original request id (let id = request.id().into_owned();) and uses it for successful responses, but in the spawn_blocking join-error branch it builds MethodResponse::error(Id::Null, ...). This breaks request/response correlation exactly on runtime failure paths and can mis-handle client retries/dispatch; the error response should preserve the original id instead of forcing Id::Null.
There was a problem hiding this comment.
Summary
Good progress on the middleware split and follower pre-exec flow; the client-rejection ordering issue appears addressed. I found one blocking correctness issue still present in the new async eth_sendRawTransaction path.
Blocking
eth_send_raw_transactionstill returnsId::Nullonspawn_blockingjoin failure, which breaks JSON-RPC request/response correlation on error paths. The request id is captured (let id = request.id().into_owned()), but the join-error branch buildsMethodResponse::error(Id::Null, ...)instead of reusing that id; clients can no longer reliably match failures to requests under runtime pressure.
Please preserve the original id in that error response branch.
PR Type
Enhancement
Description
Replace
PendingBlockHeaderwithBlockInfofor executionsAdd precomputed access list forwarding and cache warm-up
Introduce semaphore for transaction warmup in executor
Enhance storage caching with batch missing-keys logic
File Walkthrough
18 files
Simplify call execution input with BlockInfoUnify transaction input creation with BlockInfoDerive AccessListOutput iterator and cloneAdd semaphore and support access_list paramForward access list when sending to leaderExtend RPC call to include access_list paramAdapt local transaction call to new signatureImplement `forward_access_list` in consensusParse and trace `access_list` in eth_sendRawTransactionExpose try_get and contains for cache lookupsReplace transient lock guard with LatestStateReadGuardReplace transient lock with `latest_state_lock` and load access listAdd batched `read_slots` and release orderingImplement multi‐get `read_slots` and simplify `read_accounts`Use `BlockInfo` when serializing mined transactionsSwitch pending header to `BlockInfo`Batch retain missing accounts/slots in temp storageImplement `Semaphore` and `Permit` for warmup1 files
Add `forward_access_list` CLI config option1 files
Rename supervisor module and exports13 files