RustAG
Core concepts

The faithful pre-state model

RustAG produces a content-addressed, tamper-evident pre-state closure of every account a proposed transaction will touch. This closure is the cryptographic foundation of the EvidenceBundle — it's what makes a rehearsal independently verifiable by anyone.

The lazy account mirror

The lazy account mirror is the core idea of RustAG. Rather than copying all of mainnet up front, it fetches the exact accounts a transaction touches — on first access — and caches them locally.

How it works

When a transaction reads account X:

  1. Local hit? Return the stagenet's local copy.
  2. Miss? Fetch it from mainnet → cache it → mark it Clean → return it.
  3. A transaction writes X? Mark it Dirty — it is now frozen from mainnet sync forever, so your local changes are never clobbered.
the lazy-mirror decision flow
Local hit?  → return local copyMiss?       → fetch from mainnet → cache → mark Clean → returnWrite to X? → mark Dirty (frozen from mainnet sync forever) Background: re-fetch Clean ORACLE accounts every 30s            Dirty + Pinned accounts: never overwritten

A background task re-fetches Clean oracle accounts every 30 seconds (the default interval), so Oracle prices stay fresh. In the transaction path, a pre-load step batch-fetches any static account key that is not already loaded and not Dirty, loading it into EVM sandbox as Clean; fetch failures are logged and tolerated.

Why this matters on the EVM

This is how “mainnet replay” works on Robinhood Chain. EVM tools (Tenderly, Anvil's --fork-url) fork at a block hash and pull state from that fixed point; the EVM has no equivalent block to fork from. So RustAG instead fetches accounts on demand and tracks every write, so it always knows what it may and may not refresh from mainnet.

The mirror itself (rustag-mirror) is a deliberately dependency-light read side: given pubkeys, it returns current mainnet state via a raw getMultipleAccounts JSON-RPC call over reqwest (≤100 keys per call), avoiding solana-rpc-client so it doesn't fork the Robinhood Chain crate versions EVM sandbox 0.12 unifies on.

Known limitation (early access)
The mirror loads program accounts verbatim, so they are readable and present, but Phase 1 does not yet extract and JIT-load BPF bytecode from the separate program-data account. Your own deployed program can read real mainnet state today; invoking a foreign program like a full Uniswap swap end-to-end needs the fuller program-loading planned for Phase 2.

Account state machine

Every account in a stagenet carries one of four sync states — the AccountSync enum in crates/rustag-core/src/account_state.rs. The state decides whether the background scheduler is allowed to overwrite the account from mainnet.

StateMeaningBackground sync?
UnknownNever fetched. Resolved on first access during closure resolution.Never
CleanContent-addressed from mainnet at a known slot. Used as the sealed pre-state root.Yes
DirtyModified by the rehearsed payload. Captured in the post-state root.Never
PinnedExplicitly patched by forensics mode. Locked to the overridden ELF/data.Never

Clean carries a fetched_at timestamp and Dirty carries a modified_at timestamp; Unknown and Pinned are plain variants. An account is_syncable() only when it is Clean or Unknown — exactly the set the background oracle loop is allowed to refresh.

crates/rustag-core/src/account_state.rs
pub enum AccountSync {    /// Never fetched. Will be fetched lazily on first access.    Unknown,    /// Fetched from mainnet. May be re-synced by the background scheduler.    Clean { fetched_at: DateTime<Utc> },    /// Modified by a local transaction. Never overwritten by mainnet sync.    Dirty { modified_at: DateTime<Utc> },    /// Explicitly set by the user via the override API. Immune to everything.    Pinned,}

Transitions

  • Unknown → Clean: first access misses locally, so RustAG fetches from mainnet, caches it, and stamps it Clean (from_remote / mark_clean).
  • Clean → Clean (refreshed): the background oracle sync re-fetches Clean oracle accounts every 30s, re-stamping fetched_at.
  • Clean / Unknown → Dirty: a local transaction writes the account. Writable accounts are derived from the message header's (num_required_signatures, num_readonly_signed, num_readonly_unsigned) layout and marked Dirty; their post-state is persisted. Read-only accounts (programs, oracles, sysvars) stay Clean and keep syncing.
  • any → Pinned: the override API (rustag override) calls pin(), making the account immune to everything — no background sync, no clobbering.

Once an account is Dirty or Pinned, the background mirror never touches it again, so user-modified and explicitly-pinned state is preserved deterministically.

Oracle freshness

Oracle accounts are the one category RustAG actively keeps fresh. A background loop (spawn_oracle_sync) re-fetches Clean oracle accounts on the default 30s interval (clamped to a 1s minimum), so Oracle prices don't go stale under your tests.

Phase 2 · Preview A push path over the standard accountSubscribe WebSocket — the protocol Geyser/Yellowstone providers serve — drops oracle staleness to a p99 target of under 2 seconds. It is behind the realtime cargo feature; build with --features realtime.

The invariant that never bends
Dirty and Pinned accounts are never overwritten by any sync — neither the 30s poll nor the realtime push path. Whatever you write or pin stays put, so a test stays deterministic.

Why this enables independent verification

The account sync-state machine is what makes an EvidenceBundle Grade A — deterministically re-executable by anyone, independent of the rehearser.

Because every account in the pre-state closure is Clean (content-addressed from mainnet at a known slot) or Pinned (explicitly set by the verifier), a third party can:

  1. Re-fetch the closure from their own RPC endpoint (not the rehearser's).
  2. Compare the SHA-256 pre_state_root with the bundle's claim.
  3. Re-execute the payload and compare post_state_root.
  4. Check the Ed25519 signature over all of the above.

A compromised proposer UI cannot produce a valid Grade A bundle for a different payload. The state machine ensures the pre-state is pinned before execution, sealed during, and signed after — with no escape hatch.

 Real mainnet pre-stateSigned attestationSemantic diffInvariant alarmsOffline verifiable
robinhood chain-test-validator
EVM sandbox / Bankrun (libs)
simulateTransaction (RPC)
RustAG GroundTruth

Because writes are tracked as Dirty and pins are honored, you can reproduce a mainnet incident locally — pin the exact account state and replay the failing transaction against a frozen snapshot. State persists across restarts (SQLite via sqlx), so a stagenet behaves like a real, always-on environment rather than a throwaway fixture.