Traditional blockchains like Ethereum require every node to store and compute against the complete world state, creating a significant bottleneck for scalability. Partial State Execution fundamentally changes this model. Instead of processing all state, a node executing a transaction only needs access to the specific state it will read or modify. This is analogous to a database query that fetches only the required rows, not the entire table. The concept is central to scaling solutions like stateless clients and zk-rollups, where proving correctness replaces the need for full state replication.
How to Prepare for Partial State Execution
Introduction to Partial State Execution
Partial State Execution (PSE) is a paradigm shift for blockchain scalability, allowing nodes to process transactions without needing the entire global state. This guide explains its principles and how to prepare your dApp.
The technical foundation of PSE relies on cryptographic accumulators, primarily Verkle Trees or vector commitments, which replace traditional Merkle Patricia Tries. These structures allow for compact proofs (witnesses) that a specific piece of state exists and is valid within the larger state root. When a user submits a transaction, they must provide a witness containing the state their transaction will access. The executing node verifies this proof against the known state root and then processes the transaction locally, updating only the relevant state fragments. This drastically reduces the data bandwidth and storage requirements for network participants.
For developers, preparing for a PSE environment means architecting state access to be explicit and minimal. Smart contracts should avoid unnecessary storage reads and writes across disparate slots, as each accessed key requires a proof. Gas costs will increasingly reflect the cost of witness generation and verification. Tools like the Ethereum Stateless Ethereum Prototype or zk-rollup SDKs (e.g., Starknet, zkSync) provide early testing grounds. Begin by auditing your contracts for state access patterns and exploring libraries for witness management, as this will be a core client-side responsibility in stateless paradigms.
The transition to PSE enables several key advancements: light clients can fully validate execution, block propagation becomes orders of magnitude faster as blocks contain proofs instead of full state, and historical data storage can be decentralized. However, it introduces new challenges like witness size optimization and managing state expiry for obsolete data. Projects like Ethereum's Verkle Tree transition (EIP-6800) and Celestia's data availability layer are critical infrastructure pieces making this future possible. Understanding these components is essential for building the next generation of scalable dApps.
Prerequisites for Implementation
Before implementing partial state execution, you must establish a robust technical foundation. This involves configuring your execution client, understanding state management, and setting up essential tooling.
The core prerequisite is a properly configured execution client capable of handling state queries. For Ethereum, this means running a full node like Geth or Erigon with the --gcmode archive flag to ensure access to all historical state. You will need a synced node with the JSON-RPC API enabled, particularly the eth_getProof endpoint, which is essential for generating Merkle proofs. Sufficient storage (typically 2TB+ for an archive node) and reliable, high-bandwidth internet are non-negotiable for maintaining sync and serving requests with low latency.
You must have a deep understanding of the chain's state trie structure. Partial execution relies on fetching and verifying specific slices of the global state, represented as Merkle-Patricia Trie nodes. Familiarize yourself with the concepts of state roots, storage roots, and account proofs. Tools like the trie library from the EthereumJS suite or Go-Ethereum's trie package are invaluable for programmatically interacting with and verifying these data structures. Understanding the difference between a full node's state and an archive node's historical state is critical for determining data availability.
Develop a strategy for proof generation and verification. This involves writing or integrating code that can request a Merkle proof for a given account and storage slot at a specific block height, then verify it against a trusted block header. The verification logic must check the proof's consistency all the way up to the known state root. For development and testing, use a local testnet (e.g., launched with Hardhat or Ganache) before moving to public testnets like Sepolia. This allows you to experiment with state queries and proof validation in a controlled, cost-free environment.
Finally, establish a data pipeline for fetching and caching state data. Partial execution often requires aggregating data from multiple sources: your local node for recent blocks, services like Etherscan or Infura for broad queries, and potentially decentralized storage for historical proofs. Implement efficient caching layers for frequently accessed state (like popular smart contract storage) to minimize latency and RPC calls. Your implementation's reliability will depend on this foundational setup being both robust and performant.
Core Concepts: Statelessness and Witnesses
Partial state execution is a paradigm shift for blockchain scalability, separating transaction execution from state storage. This guide explains the core concepts of statelessness and the role of witnesses.
Statelessness is a design principle where a blockchain node validates transactions without maintaining the full global state locally. Instead of storing the entire state (e.g., all account balances and smart contract storage), a stateless client relies on a compact cryptographic proof, known as a witness, to verify state transitions. This dramatically reduces the hardware requirements for node operators, lowering the barrier to participation and enhancing network decentralization. The concept is central to Ethereum's Verkle tree roadmap and other scaling solutions like Stateless Ethereum.
A witness is the critical data structure that enables stateless verification. It contains the minimal set of Merkle or Verkle tree proofs required to prove the pre-state of the accounts and storage slots accessed by a specific block of transactions. When a block producer creates a block, they generate a witness. Validators and other nodes can then use this witness, alongside the block data, to execute transactions and verify the proposed new state root without needing the full state tree. This shifts the storage burden from all nodes to a smaller subset of block producers.
To prepare for partial state execution, developers must understand how their applications interact with state. Contracts that make widespread, random state accesses (e.g., looping over unbounded arrays) will generate larger, less efficient witnesses. Optimizing for state locality—grouping related data in contiguous storage slots—becomes a critical performance consideration. Tools like Ethereum execution APIs (eth_getProof) already allow you to fetch witnesses for specific accounts and storage keys, which is essential for building light clients or testing stateless execution logic in your development workflow.
The transition requires new standards and client behaviors. EIP-4444 (History Expiry) mandates nodes to stop serving historical block data beyond a certain age, pushing the ecosystem towards portal network clients that fetch data on-demand. For dApp developers, this means designing with the assumption that not all historical data is perpetually available from every node. Building with stateless friendliness in mind future-proofs applications against these coming architectural changes, ensuring they remain performant and cost-effective in a post-state-expansion era.
Implementation Patterns and Strategies
Prepare for the shift to modular blockchains by understanding the core patterns for managing and verifying partial state.
Partial State Execution: Protocol Comparison
Comparison of major protocols implementing partial state execution for scaling Ethereum.
| Feature / Metric | Optimism (OP Stack) | Arbitrum Nitro | zkSync Era | Starknet |
|---|---|---|---|---|
Execution Model | Optimistic Rollup | Optimistic Rollup | ZK Rollup | ZK Rollup |
State Access Pattern | Full state sync, selective fraud proofs | WASM-based VM, multi-threaded execution | State diffs, storage writes only | Cairo VM, fine-grained state updates |
Data Availability | Ethereum calldata | Ethereum calldata (compressed) | Ethereum calldata | Ethereum or Validium (optional) |
Time to Finality (L1) | ~7 days (challenge period) | ~7 days (challenge period) | < 1 hour | < 1 hour |
Gas Cost per State Write | $0.10 - $0.50 (varies) | $0.08 - $0.40 (varies) | $0.05 - $0.30 (varies) | $0.03 - $0.25 (varies) |
Developer Experience | EVM-equivalent | EVM-equivalent (ArbOS) | EVM-compatible (LLVM) | Cairo (custom VM) |
Native Account Abstraction | ||||
Trust Assumption | 1-of-N honest validator | 1-of-N honest validator | Cryptographic (ZK proofs) | Cryptographic (ZK proofs) |
Code Examples: Working with Witnesses
A practical guide to generating and handling witness data for off-chain computation and state verification.
In blockchain systems like zk-rollups or optimistic rollups, a witness is the set of private inputs and auxiliary data needed to generate a proof or verify a state transition without re-executing the entire transaction. It typically includes account states, storage slots, and transaction details that are not publicly available on-chain. Preparing this data correctly is the first step for any partial state execution, where only a subset of the global state is processed. This is essential for scaling solutions that compute transactions off-chain and submit compressed proofs or fraud challenges to a base layer.
To prepare a witness, you must first define the circuit or execution trace that your proof system will verify. For a simple token transfer, the witness might include the sender's private key (or signature), the recipient's address, the amount, and the sender's current nonce and balance. In a zkEVM context, this expands to include opcode execution traces, memory/stack states, and storage accesses. The data must be serialized into a format your proving system accepts, such as a JSON file for snarkjs or a binary buffer for direct circuit input. Tools like circom or Halo2 provide libraries to structure this data.
Here is a conceptual JavaScript example using a pseudo-SDK to collect witness data for a balance check. This simulates gathering the necessary off-chain state to prove a user has sufficient funds.
javascriptasync function prepareBalanceWitness(userAddress, tokenContractAddress, chainId) { // 1. Fetch the private state data (simulated off-chain database) const privateStorage = await offChainDB.getAccountState(userAddress); const balance = privateStorage.balances[tokenContractAddress]; const nonce = privateStorage.nonce; // 2. Fetch the public state root against which we are proving const latestBlockHeader = await publicRPC.getBlockHeader('latest'); const stateRoot = latestBlockHeader.stateRoot; // 3. Construct the witness object const witnessData = { publicInputs: { stateRoot: stateRoot, userAddress: userAddress, tokenContract: tokenContractAddress, chainId: chainId }, privateInputs: { balance: balance, nonce: nonce, // A Merkle proof would be included here in a real scenario balanceProof: privateStorage.merkleProof } }; return witnessData; }
This structure separates public inputs (known on-chain) from private inputs (the witness).
After generating the raw witness data, it must be formatted for your specific proof backend. For a Groth16 zk-SNARK proof via snarkjs, you would convert the JavaScript object into the wtns file format using the circuit's witness calculator. For STARKs or other VM-based proofs, the witness is often a direct execution trace. A critical best practice is to validate the witness against the public inputs before proof generation to catch errors early. This involves ensuring the witness satisfies all the circuit constraints locally, which is faster and cheaper than a failed on-chain verification.
Common pitfalls in witness preparation include data inconsistency (e.g., using a state root from a different block), serialization errors (incorrect field ordering or type), and omitting necessary pre-images for hash functions. Always use deterministic methods to fetch state and include cryptographic proofs of inclusion (like Merkle proofs) for any data claimed to be part of a public root. The integrity of the entire proof system depends on the correctness and completeness of this witness. Libraries such as ffjavascript for finite field serialization or vendor-specific SDKs from rollup providers abstract away some of this complexity.
The final step is integrating witness generation into your application flow. For a rollup sequencer, this is a automated process post-transaction execution. For a user proving membership, it's a client-side operation. The witness, once prepared, is fed into a proving key to generate a zero-knowledge proof or into a fraud-prover module to create a fraud proof. By mastering witness preparation, developers can build efficient layer-2 applications, privacy-preserving proofs, and custom verification logic, which are foundational to the next generation of scalable blockchain infrastructure.
Common Implementation Challenges
Partial state execution introduces unique complexities for developers. This section addresses the most frequent technical hurdles and questions encountered when building applications that leverage this pattern.
A state root mismatch occurs when the state transition computed off-chain does not match the on-chain verification. This is a critical security check that prevents invalid state updates.
Common causes include:
- Non-deterministic execution: Using off-chain data sources (e.g., Chainlink oracles) or system timestamps (
block.timestamp) in your state transition logic. The verifier's on-chain computation must be perfectly reproducible. - Incorrect pre-state: The
stateRootyou submit as the starting point for the execution does not correspond to the actual on-chain state at the referenced block. - Gas/Opcode differences: Execution paths that behave differently under varying gas limits or between the EVM and your off-chain execution environment (e.g., a Geth fork vs. a testing framework).
How to debug:
- Log and compare the full execution trace of your off-chain prover and the on-chain verifier.
- Ensure all external calls and precompiles are mocked or handled identically in both environments.
- Use a state proof to cryptographically verify the starting state was correct.
Tools and Development Libraries
Resources and libraries to help developers understand, simulate, and build for environments with partial state availability.
How to Prepare for Partial State Execution
Partial state execution introduces unique testing challenges. This guide outlines a strategy for simulating and validating these complex state transitions.
Partial state execution occurs when a smart contract processes a transaction that depends on state data not fully available on-chain at execution time. This is common in optimistic rollups like Optimism and Arbitrum, and validiums like StarkEx, where state is managed off-chain. Testing this requires simulating the interaction between the on-chain verifier contract and the off-chain state provider. You must validate that the contract correctly handles scenarios where required state roots or proofs are missing, invalid, or delayed.
Develop a modular testing suite that separates concerns. Unit tests should mock the off-chain state interface, allowing you to inject specific test vectors: valid proofs, invalid proofs, and missing data. Use a framework like Foundry's forge with vm.mockCall or Hardhat's hardhat-network-helpers to simulate these conditions. For example, you can mock a call to a StateCommitmentChain contract to return a specific historical state root, then test your contract's logic for accepting or rejecting a proof against it.
Integration testing requires a local development network that mimics the partial execution environment. Tools like Optimism's op-geth or Arbitrum Nitro's local devnode allow you to run a mini-sequencer and verifier. Deploy your contracts and simulate the full flow: submit a transaction to the sequencer, generate a state update, and then challenge it on the L1 verifier. This tests the liveness and security assumptions of your system under realistic network latency and adversarial conditions.
For advanced simulation, implement fuzz testing and differential testing. Fuzz tests (e.g., with Foundry's forge fuzz) can bombard your state verification function with randomly generated proofs and inputs to uncover edge cases. Differential testing involves comparing the output of your partial execution logic against a reference implementation or a simpler, fully on-chain version of the same logic. This helps ensure correctness even when the execution path is non-deterministic or depends on external data availability.
Finally, prepare for mainnet by conducting testnet deployments on networks like Sepolia, Goerli, or protocol-specific testnets (Optimism Sepolia, Arbitrum Sepolia). Use gas profiling to understand the cost of state verification under load and monitoring to track proof validation times. Your testing strategy should prove that your application remains secure and functional even when the underlying data availability layer experiences faults or delays, a core requirement for systems built on sovereign rollups or modular blockchains.
Risk and Trade-off Analysis
Comparison of state execution approaches for rollups, highlighting security, cost, and complexity trade-offs.
| Feature / Risk | Full State Execution | Partial State Execution | Stateless Clients |
|---|---|---|---|
State Data Availability | Full on-chain | Partial on-chain (e.g., via DACs) | Off-chain with validity proofs |
Client Hardware Requirements | High (Full node) | Medium (Partial verifier) | Low (Stateless verifier) |
Time to Finality | < 1 sec (optimistic) | 2-5 sec (with fraud proofs) | < 1 sec (with ZK proofs) |
Trust Assumptions | None (fully decentralized) | 1-of-N Data Availability Committee | Proof system security |
Prover/Sequencer Cost | Low (no proving) | Medium (proving for disputed state) | High (ZK proof generation) |
Cross-Chain Interop Complexity | Low (full state root) | Medium (state proof construction) | High (proof verification) |
Upgrade Flexibility | Low (hard fork required) | High (modular component upgrade) | Medium (circuit upgrade) |
Developer Experience | Standard EVM tooling | New tooling for state partitioning | Circuit writing required |
Frequently Asked Questions
Common questions and technical clarifications for developers implementing or interacting with partial state execution systems.
Partial state execution is a scaling technique where a blockchain processes only a subset of its global state for a given transaction. Instead of requiring every node to validate the entire world state, the system identifies and executes only the relevant state components (e.g., specific smart contract storage slots or account balances) touched by the transaction.
Key components include:
- State Witnesses: Cryptographic proofs (like Merkle proofs) that attest to the current value of off-chain state.
- Execution Environment: A verifiable runtime (e.g., a zkVM, optimistic VM) that processes the transaction against the provided witness.
- State Commitment: A cryptographic root (like a Merkle root) that commits to the entire state, allowing the witness to be verified.
This model is foundational to validiums, zk-rollups, and certain optimistic execution layers, enabling higher throughput by decoupling execution from full-state availability.
Further Resources and Documentation
Primary specifications, research notes, and tooling documentation for developers preparing systems and codebases for partial state execution and stateless execution models.