perf: remove beneficiary commit barriers - #119
Merged
Merged
Conversation
AshinGau
force-pushed
the
main
branch
2 times, most recently
from
August 1, 2026 03:22
6d696e7 to
62742b1
Compare
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 Galxe/gravity-audit#773.
Closes Galxe/gravity-audit#775.
Summary
This PR removes two commit-prefix barriers from Grevm's speculative execution path while preserving upstream
revmsemantics:StorageReset(address)instead of a special self-destruct path that can only be resolved from committed state.It also replaces the former
CacheDBabstraction withIncarnationDb, which owns the complete database lifecycle of one Block-STM transaction incarnation.The optimization is intentionally narrow: incremental balance handling applies only to the protocol beneficiary reward. Ordinary value transfers, sender and recipient balances, and transaction nonce semantics remain unchanged.
Motivation
Every successful transaction may reward the block beneficiary. Treating that reward as an ordinary absolute account write creates an almost block-wide dependency chain. The previous workaround skipped beneficiary MV-memory writes, but explicit beneficiary reads could then need the committed prefix to obtain an authoritative balance.
Account deletion had a similar problem. A special
SelfDestructedmarker required later account or storage reads to wait for commit before continuing safely. That path also mixed together fork-specificSELFDESTRUCTbehavior, EIP-161 empty-account clearing, creation, storage lifetime, and balance ordering.Finally, the old execution interface exposed a reusable database through several independent
take_*calls plus a separate MV-memory update. This made the incarnation boundary implicit and allowed dependency metadata and write publication to drift apart.Design
1. Beneficiary reward history
The execution handler remains the single source of truth for reward calculation and disposition:
revmhook, preserving load, touch, and EIP-161 behavior;revm, preserving journal order with explicit beneficiary writes andSELFDESTRUCT;DeferredBeneficiaryReward.A deferred reward contains only the non-zero reward amount. The beneficiary address is a block invariant already owned by
BeneficiaryandOrderedCommitter, so it is not duplicated in every speculative result.Each transaction owns one preallocated entry in
BeneficiaryHistory:Rewardapplies a protocol reward to the preceding beneficiary value.Snapshot(Some(account))records an explicit finalized beneficiary update and terminates the preceding reward chain.Snapshot(None)records an actual deletion or finalized empty-account clearing.Unchangedrecords an exact no-op, allowing later readers to distinguish it from an unresolved estimate.Reads walk backward to the newest snapshot or the immutable block-start anchor, then apply contributing rewards in transaction order. Validation compares the complete contributing incarnation chain, not only the newest writer.
flowchart TD A[Execute transaction incarnation] --> B{Beneficiary reward path} B -->|Fee charging disabled| C[No reward] B -->|Zero reward| D[Apply upstream revm hook] B -->|Beneficiary already in journal| D B -->|Positive reward and not journaled| E[Return DeferredBeneficiaryReward] C --> F[Finalize EVM state] D --> F E --> F F --> G[Publish ordinary MV-memory writes] G --> H[Publish exact BeneficiaryHistory effect] H --> I[Validate complete origin chain] I --> J[Ordered commit] J --> K[Fold deferred reward into EVM state] K --> L[Commit transaction state once]Publication remains ordered: ordinary MV-memory writes become visible before the beneficiary entry becomes exact. Failed, blocked, or conflicting incarnations publish an estimate, and stale executions or validations cannot overwrite a newer incarnation.
At ordered commit, the reward is checked-added to the authoritative beneficiary account and inserted into the same finalized
EvmState. The transaction state is then committed once, preserving upstream overflow and account-materialization behavior without a separate balance-increment commit.2. Account lifecycle and storage reset
FinalizedAccountcentralizes the lifecycle classification already produced byrevm:Unchanged: merely loaded, with no consensus-visible write;Deleted: actualSELFDESTRUCTor finalized EIP-161 empty-account removal;Created: a newly created account whose previous storage is cleared;Updated: an update to an existing account.IncarnationDbpublishesStorageReset(address)for both deletion and creation. Storage reads independently resolve:flowchart LR A[Finalized revm account] --> B{Lifecycle} B -->|Deleted| R[Publish StorageReset] B -->|Created| R B -->|Updated EIP-7702 delegation| C[Publish Code without reset] S[Read address and slot] --> W[Latest preceding slot write] S --> X[Latest preceding StorageReset] W --> D{Which version is newer?} X --> D D -->|Slot write at or after reset| V[Return slot value] D -->|Reset is newer| Z[Return zero] D -->|Neither exists| DB[Read backing database]Both locations enter the read set, so a newly discovered earlier reset invalidates a speculative reader through normal MV-memory validation.
This delegates hardfork semantics to finalized
revmstate:SELFDESTRUCTof a pre-existing account remains a balance transfer;SELFDESTRUCTin the same transaction remains an actual deletion;Beneficiary storage stays on this generic MV-memory path and is independent from beneficiary reward history. A storage-only access therefore does not wait for unresolved beneficiary balance rewards.
3. Explicit incarnation lifecycle
The former
CacheDBis nowIncarnationDb: a reusablerevm::Databaseadapter for the currently executing transaction incarnation.IncarnationDbowns:finish_incarnationderives the MV-memory estimate flag directly from the blocker set, publishes writes, and returns allIncarnationAccessesatomically.discard_incarnationpublishes no EVM writes but preserves discovered blockers and retains scratch allocation capacity for reuse.GrevmExecutornow encapsulates the entire begin/execute/finish-or-discard lifecycle. The scheduler no longer obtains a mutable database handle or sequences multipletake_*calls.Component boundaries
account.rsrevmaccount lifecyclebeneficiary/reward.rsrevmsemanticsbeneficiary/history.rsbeneficiary.rsincarnation_db.rsStorageResetscheduler/executor.rsscheduler/ordered_commit.rsCustom precompile contract
This PR does not turn arbitrary shared stateful closures into rollback-aware precompiles. Custom precompiles supplied to the parallel scheduler must remain concurrent and retry safe:
Under this integration invariant, shallow cloning of a custom precompile is intentional and is not a missing rollback mechanism.
Correctness coverage
The test suite covers:
SELFDESTRUCT;SELFDESTRUCT;Validation
cargo test --all-targets --all-featurescargo clippy --all-targets --all-features -- -D warningsrevmfor Frontier, Spurious Dragon, Shanghai, Cancun, Prague, and Amsterdam23,352,851..=23,352,950) replayed successfully with parallel results and bundle state matching sequentialrevmIn that 100-block replay sample, aggregate in-memory execution and bundle extraction took
1.118 swith sequentialrevmand0.679 swith Grevm, approximately1.65xfaster. This is an illustrative replay result, not a benchmark guarantee.