Skip to content

feat: restrict parallel precompile state access - #120

Merged
AshinGau merged 1 commit into
Galxe:mainfrom
AshinGau:main
Aug 5, 2026
Merged

feat: restrict parallel precompile state access#120
AshinGau merged 1 commit into
Galxe:mainfrom
AshinGau:main

Conversation

@AshinGau

@AshinGau AshinGau commented Aug 5, 2026

Copy link
Copy Markdown
Collaborator

close https://github.com/Galxe/gravity-audit/issues/1089

Summary

This PR implements the Grevm side of Galxe/gravity-audit#1089 by replacing unrestricted parallel custom precompiles with a capability-restricted API.

  • Adds ParallelPrecompile and DynParallelPrecompile as the registration boundary for custom precompiles used by the parallel scheduler.
  • Exposes call metadata through ParallelPrecompileInput without exposing Alloy PrecompileInput, raw EvmInternals, or db_mut().
  • Exposes only reviewed, journal-aware state operations through ParallelPrecompileState: balance, sload, set_balance, and sstore.
  • Updates Scheduler and worker EVM construction to accept the restricted type and perform the Alloy adapter conversion internally.
  • Preserves beneficiary storage publication and validation while keeping beneficiary balance history and raw storage resolution correctly layered.
  • Documents the new integration boundary and adds comprehensive unit and end-to-end coverage.

Motivation

Alloy PrecompileInput exposes the full mutable EVM internals. A custom precompile could therefore read or write through db_mut() and bypass the revm journal.

In parallel execution, bypassing the journal can break:

  • same-transaction read-your-writes semantics;
  • call-frame rollback behavior;
  • stable reads within one speculative incarnation;
  • Grevm read/write tracking, conflict detection, and retry correctness;
  • static-call state-change enforcement.

The new API makes journal-aware access the only way for a scheduler-registered custom precompile to interact with EVM state.

Design

Restricted precompile interface

ParallelPrecompileInput provides:

  • calldata, forwarded gas, and the EIP-8037 reservoir;
  • caller and call value;
  • target and bytecode addresses, including direct-call detection;
  • static-context metadata;
  • a restricted mutable state facade.

The state facade deliberately does not expose the raw database, mutable journal accounts, code mutation, checkpoints, logs, transient storage, or a conversion back to EvmInternals.

All state operations go through revm journal APIs. Storage access preserves normal account-first semantics, including beneficiary-history coordination. The lower-level database storage path remains independent of beneficiary reward history as a correctness fallback.

Error and execution semantics

  • State changes during STATICCALL are recorded as sticky halts, even if an implementation ignores the returned error and reports success.
  • Database failures are recorded as sticky fatal errors.
  • Precompile Revert and Halt results follow normal call-frame rollback semantics.
  • Fatal execution discards the speculative incarnation and does not publish its writes.
  • The Alloy input-only precompile cache is disabled because restricted precompiles may depend on journal state that is not represented by the cache key.

Precompile implementations remain responsible for gas validation and complete PrecompileOutput accounting. They must also remain safe to call concurrently and more than once because speculative executions may be discarded and retried.

Integration impact

This is an intentional API change for custom precompile users:

Option<Arc<Vec<(Address, DynPrecompile)>>>

becomes:

Option<Arc<Vec<(Address, DynParallelPrecompile)>>>

Integrations should implement ParallelPrecompile directly or construct a DynParallelPrecompile from a restricted closure. Built-in precompiles and scheduler users that do not register custom precompiles are unaffected.

Testing

Coverage includes:

  • compile-time rejection of raw internals access;
  • metadata forwarding, direct-call detection, and cache disabling;
  • same-transaction read-your-writes through sload/sstore;
  • journal checkpoint rollback;
  • sticky static-call and database-error handling;
  • suffix fallback with the restricted adapter installed;
  • deterministic beneficiary reward and storage conflicts with retry;
  • non-zero custom precompile gas charged exactly once after retry;
  • committed-state sinks proving stale incarnation writes are discarded;
  • precompile Revert, Halt, and fatal-error atomicity;
  • beneficiary storage MV-memory publication without a beneficiary Basic write.

Related

@AshinGau
AshinGau merged commit baaffa1 into Galxe:main Aug 5, 2026
2 checks passed
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.

1 participant