# Ethereum State is Too Big - Here’s How Partial Nodes can Fix It
Ethereum nodes today face a fundamental trade-off between independence and efficiency. A full node stores the entire blockchain state, including all accounts, contract code, and especially contract storage, which grows extremely large over time and now reaches hundreds of gigabytes. While this allows full verification and independence, it comes at a high cost in terms of storage, bandwidth, and hardware requirements. On the other end of the spectrum, light nodes avoid storing most of this data and instead rely on full nodes to answer queries, but this introduces dependency and limits their ability to independently verify and execute state.
To address this gap, new approaches like **partial statefulness ([EIP-7928](https://eips.ethereum.org/EIPS/eip-7928))** aim to provide a middle ground. Instead of storing everything or nothing, nodes can store only the parts of the state they care about, such as specific contracts, while still maintaining useful functionality. In geth, this is implemented by modifying snap sync to selectively download state using filters and skipping untracked data permanently. This approach relies on the snap protocol, which efficiently retrieves state data and verifies it using Merkle proofs against a trusted state root, ensuring correctness without downloading the entire trie .
However, this design has limitations because once data is skipped, it cannot be retrieved later. To overcome this, Erigon explores a different direction by combining partial statefulness with **sparse snapshots and BitTorrent-based data distribution**, allowing nodes to fetch missing data on demand instead of permanently ignoring it. This introduces a more flexible and decentralized model where data is not required to be stored locally but remains accessible across the network.
This blog explores why the Ethereum state becomes so large, how existing solutions like full nodes and light nodes handle this problem, how Geth implements partial statefulness using snap sync and Block Access Lists, and how Erigon extends this idea further by enabling dynamic, on-demand access to state data.
## Introduction - Why This Problem Exists
Ethereum is designed as a fully decentralized system where any node can independently verify the correctness of the blockchain. To achieve this, a traditional full node stores the entire state of the network, which includes all accounts, their balances and nonces, as well as all smart contract code and storage. While accounts themselves are relatively small, the storage associated with smart contracts grows continuously as more applications are deployed and used. Over time, this results in a very large state size, often hundreds of gigabytes, making it increasingly expensive and difficult for new nodes to join and participate fully in the network.
The main reason this problem exists is that Ethereum applications, especially decentralized finance (DeFi), NFTs, and other complex smart contracts, rely heavily on persistent storage. Every time a contract updates its state, new data is added or modified, and this data must be preserved so that future transactions can read and verify it. Unlike simple systems where old data can be discarded, Ethereum requires this state to remain available for correctness and verification. As a result, the state keeps growing, even if many parts of it are rarely accessed.
This creates a practical issue. Most users or applications do not need access to the entire Ethereum state. For example, a developer working only with a specific protocol like a decentralized exchange may only need data related to that protocol and not the entire ecosystem. However, in the traditional full node model, there is no option to selectively store only relevant data. Every node must carry the full burden of the entire network state, regardless of its actual needs.
To address this, lighter alternatives such as light nodes were introduced. Light nodes significantly reduce storage requirements by keeping only block headers and requesting state data from full nodes when needed. While this solves the storage problem, it introduces a new issue: dependency. Light nodes rely on external nodes to provide data, and although cryptographic proofs can verify correctness, they still lack the ability to independently execute transactions or fully participate in the network.
This leads to a fundamental gap in Ethereum’s design. On one side, full nodes provide complete independence but are resource-intensive. On the other side, light nodes are efficient but dependent and limited in functionality. There is no simple solution that provides both efficiency and independence at the same time. This gap is exactly what newer approaches like partial statefulness aim to address by allowing nodes to store only the data they care about while still maintaining a meaningful level of independence and functionality.
#### What is Snap Sync in Geth
Snap sync is a faster way for an Ethereum node to get the current state of the network without replaying all past transactions. In go-ethereum, instead of starting from the beginning of the blockchain and executing every transaction one by one, the node directly downloads the latest state - things like account balances, contract storage, and code - from other peers. This makes syncing much quicker and more practical.
The key idea is that the node does not blindly trust the data it receives. Each piece of state comes along with a Merkle proof, which is a cryptographic way to prove that the data actually belongs to the global Ethereum state. The node already knows a trusted state root from a recent block header, and it uses this proof to verify that the downloaded data matches that root. So even though the node is getting data from other peers, it can still independently check correctness without trusting them .
Another important detail is that snap sync does not download the entire internal structure of the Ethereum state (called the Merkle trie). Instead, it downloads only the useful data (accounts and storage) and rebuilds the structure locally. This reduces the amount of data transferred and speeds up synchronization significantly.
In simple terms, snap sync is like skipping the full history of how the state was built and instead downloading the final result directly, while still verifying that the result is correct using cryptographic proofs.
#### Staged Sync + Snapshots in Erigon
In Erigon, syncing the Ethereum blockchain is done using a combination of **staged sync** and **snapshots**, which together make the process much faster and more efficient compared to traditional methods.
The basic idea is simple. Instead of doing everything at once, Erigon breaks the syncing process into multiple **stages**, where each stage performs one specific task, such as downloading headers, fetching block data, recovering transaction senders, executing transactions, and finally computing the state root. These stages run one after another like a pipeline, and each stage completes its work fully before moving to the next. This structured approach avoids unnecessary work and makes syncing more stable and efficient.
At the same time, Erigon uses **snapshots**, which are prebuilt pieces of blockchain data stored in files (commonly **.kv** files). These snapshots contain important state data like accounts, contract storage, and code, as well as historical blockchain data. Instead of rebuilding everything from the beginning by executing all past transactions, the node can **start from these ready-made snapshots**, which significantly speeds up the syncing process. These snapshot files can also be shared and downloaded efficiently, even using decentralized methods like BitTorrent.
Another important detail is that Erigon separates transaction execution from the complex process of maintaining the Merkle Patricia Trie. It first updates a simpler flat database (key-value storage) during execution and computes the final state root afterward. This reduces computational overhead and improves performance.
Putting it all together, the syncing process looks like this: the node quickly gets a base state using snapshots, then processes new data step-by-step using staged sync, and finally stays up to date with the latest blocks. If something goes wrong, the system can resume from the last completed stage, making it robust.
In simple terms, **staged sync defines how the work is organized, and snapshots provide the data to start quickly**, allowing Erigon to sync faster, use less disk space, and handle large blockchain data more efficiently.
## What Makes Ethereum State So Heavy?
To understand why Ethereum state becomes so large, we first need to understand what “state” actually means. In simple terms, the Ethereum state is the current snapshot of everything the network knows at a given moment. This includes all accounts, their balances, nonces, smart contract code, and most importantly, the storage used by those contracts. This entire state is represented using a structure called the **Merkle Patricia Trie**, which allows nodes to verify correctness using a single hash called the state root.
Now, although accounts and contract code contribute to the state size, they are not the main problem. The real reason Ethereum state becomes heavy is **contract storage**. Every smart contract can store data in key-value form, and this storage grows over time as users interact with the contract. For example, a decentralized exchange stores user balances, liquidity positions, and trade data. An NFT contract stores ownership details for each token. Each of these interactions adds or modifies storage, and this data must remain available for future transactions to work correctly.
A key point is that Ethereum does not delete old state automatically. Even if some data is rarely used, it still remains part of the global state because any future transaction might depend on it. This means the state keeps growing continuously as more applications are deployed and used. Unlike systems where old data can be archived or removed easily, Ethereum must preserve this data to maintain correctness and verifiability.
Another factor that makes the state heavy is how it is stored and verified. The Merkle Patricia Trie structure introduces additional overhead because it stores not just the data but also hashes and paths needed for cryptographic verification. This increases the total size compared to a simple database. While this design is essential for security and trustlessness, it adds to the storage cost.
Over time, as millions of users interact with thousands of smart contracts, the total state size grows into hundreds of gigabytes. Most of this comes from storage that is rarely accessed but still needs to be maintained. This creates a situation where every full node must carry a large amount of data, even if it only needs a small portion of it.
In simple terms, Ethereum state is heavy because **smart contracts keep adding data that cannot be easily removed, and the system must store and verify all of it to remain correct and decentralized**.
### Existing Solutions (Before EIP-7928)
Before ideas like partial statefulness were introduced, Ethereum mainly had two practical ways for nodes to participate in the network: **full nodes** and **light nodes**. Each of these solves the problem of handling Ethereum’s large state in a different way, but both come with clear trade-offs.
A **full node** stores the entire Ethereum state, including all accounts, contract code, and contract storage. This means it has everything needed to independently verify the blockchain and execute transactions without relying on anyone else. Because of this, full nodes provide the highest level of trust and correctness. However, the downside is that they require a large amount of storage and resources, since the state keeps growing over time. As discussed earlier, most of this growth comes from contract storage, making full nodes increasingly heavy and harder to run for regular users.
On the other hand, **light nodes** take the opposite approach. Instead of storing the full state, they only keep a minimal amount of data, mainly block headers, and rely on full nodes to provide additional information when needed. When a light node wants to access something like an account balance or contract storage, it requests that data from a full node and verifies it using cryptographic proofs. This makes light nodes much more efficient in terms of storage and bandwidth. However, they are not fully independent because they depend on external nodes for data and cannot execute transactions or maintain the full state themselves.
This creates a clear trade-off between the two approaches. Full nodes are independent but resource-heavy, while light nodes are efficient but dependent and limited in functionality. There is no middle option in this model that allows a node to store only relevant parts of the state while still remaining mostly independent and capable.
In simple terms, before EIP-7928, Ethereum nodes had to choose between storing everything or relying on others, with no flexible way to balance efficiency and independence.
### The Missing Middle Ground
After understanding full nodes and light nodes, a clear gap starts to appear. Full nodes give complete independence but are expensive to run because they store the entire Ethereum state. Light nodes, on the other hand, are efficient but depend on other nodes and cannot fully execute or verify everything on their own. What is missing is a practical middle option where a node can remain useful and reasonably independent without carrying the full burden of the entire state.
The core problem is that most nodes do not actually need the entire Ethereum state. In real-world usage, a node is often interested in a specific subset of contracts or applications. However, before newer approaches, there was no way to express this preference at the protocol or client level. A node either had to sync everything or rely on others. There was no built-in mechanism to say, “store only what I care about, but still function as a meaningful participant in the network.”
This gap becomes even more important when we consider execution. Simply not storing data is not enough, because Ethereum nodes are expected to process blocks, update state, and stay in sync with the chain. If a node does not have the required state, it cannot correctly apply changes. So the challenge is not just reducing storage, but doing it in a way that still allows the node to follow the chain and maintain correctness.
This is exactly where the idea of partial statefulness comes in. Instead of treating the state as something that must be fully present or fully absent, it introduces the idea that a node can selectively maintain parts of the state while still being able to process updates. The key enabling concept here is the use of structured state changes, such as Block Access Lists (BAL), which describe what parts of the state are modified in each block. With this information, a node does not always need the entire state to stay updated; it can focus on the parts it tracks and handle the rest differently.
In simple terms, the missing middle ground is about moving away from an all-or-nothing model. It recognizes that nodes should be able to store less data without becoming fully dependent, and still remain functional within the network. This shift in thinking is what leads to designs like EIP-7928 and beyond, where efficiency and independence are no longer forced to be opposites.
### What is Partial Statefulness?
Partial statefulness is an idea that allows an Ethereum node to **store and work with only a selected part of the state**, instead of storing everything like a full node or almost nothing like a light node. It is introduced through proposals like EIP-7928 and implemented in clients such as geth.
In a traditional full node, the entire state is stored, including all accounts, contract code, and contract storage. In a light node, almost none of this is stored, and the node depends on others to fetch data when needed. Partial statefulness sits in between these two. A partially stateful node **always keeps all accounts**, but it only stores the **storage and code of selected contracts** that it cares about. Everything else is intentionally not stored.
The key challenge with storing only part of the state is: how can the node still stay in sync with the blockchain if it does not have all the data? This is solved using **Block Access Lists (BAL)**. A BAL describes which accounts and storage slots were accessed or modified in a block. Instead of re-executing every transaction and needing full state, the node can use this information to **apply state changes directly** for the parts it tracks. This allows the node to follow the chain without having complete state locally.
In go-ethereum, partial statefulness is implemented by modifying snap sync so that the node **downloads only the storage and code for selected contracts**, while skipping everything else. Once skipped, that data is not available later, and any request for it will result in an error. The node is effectively saying, “I only care about this subset of the state.”
This concept creates a new category of nodes. They are not fully independent like full nodes, but they are much more capable than light nodes. They can process blocks, maintain a consistent view of the chain, and serve useful queries for the parts of the state they track, all while using significantly less storage.
In simple terms, **partial statefulness means a node stores only the state it needs and ignores the rest, while still being able to stay synchronized with the blockchain using structured state updates like BAL**.
### How Geth Implements Partial Statefulness
In go-ethereum, partial statefulness is implemented by building on top of its existing sync system, especially **snap sync**, and adding a layer of filtering and structured state updates. The idea is not to redesign everything from scratch, but to modify how data is downloaded and how state is updated so that only a selected subset of the state is maintained.
The process starts with configuration. When running the node, the operator specifies which contracts they want to track. Based on this, Geth creates an internal filter that decides what data should be stored and what should be skipped. During sync, Geth still downloads all account data, but when it comes to contract storage and code, it only downloads the data for the selected contracts. Everything else is intentionally skipped using what are called **skip markers**, meaning the node knows that this data was deliberately not stored.
To make this work without breaking synchronization, Geth uses **Block Access Lists (BAL)**. These lists are provided along with block data and describe which parts of the state were touched in each block. Instead of re-executing every transaction with full state, the node uses BAL information to apply updates directly to the parts of the state it maintains. This allows the node to keep up with the blockchain even though it does not have the full state locally.
Another important part of the implementation is how Geth ensures correctness. Even though it does not store the full state, it still recomputes the state root for the data it tracks and relies on the account-level structure of the state to remain consistent. It also depends on trusted block headers and verification mechanisms similar to those used in snap sync to ensure that the data it processes is valid.
The RPC layer is also aware of this partial behavior. If a user queries data for a contract that is being tracked, the node responds normally. However, if the query involves an untracked contract, the node returns an error instead of trying to fetch or compute the missing data. This makes the behavior explicit and predictable.
One key limitation of Geth’s approach is that once data is skipped during sync, it is not available later. The node does not have a mechanism to go back and fetch missing storage or code from other peers. This means the decision of what to track must be made carefully at the beginning.
In simple terms, Geth implements partial statefulness by **filtering what state is downloaded during snap sync, using BAL to update only the tracked parts, and explicitly ignoring everything else**, creating a node that is efficient but limited to the data it chose to keep.
### Enter Erigon’s Approach
After seeing how geth implements partial statefulness, an important limitation becomes clear: once data is skipped during sync, it is **gone forever for that node**. This makes the system efficient, but also rigid. If the node later needs data from an untracked contract, it cannot recover it. This is where Erigon takes a different approach.
Instead of treating untracked data as something to permanently ignore, Erigon changes the mindset completely. It separates two ideas: **what is stored locally** and **what is accessible in the network**. A node does not need to store everything, but that does not mean it should lose access to it. This is the core idea behind Erigon’s design.
Erigon builds on its existing architecture of **staged sync and snapshot-based storage**, where state is organized into structured files (like `.kv` files). These files contain accounts, storage, and code in a format that is efficient to read and process. On top of this, Erigon introduces the concept of **sparse snapshots**, where only selected parts of the state are stored locally, and the rest can be fetched when needed.
To make this possible, Erigon uses a **BitTorrent-based distribution model**. Instead of requesting state directly from peers through a protocol like snap, the node identifies which file and which part of the file it needs, and then downloads that piece from any peer that has it. This allows missing data to be retrieved dynamically, even if it was not stored during initial sync.
This creates a major difference in behavior. In Geth, skipping data means losing it permanently. In Erigon, skipping data only means **not storing it locally**, while still keeping the ability to access it later. The node becomes more flexible, as it can start with a small local footprint and expand access as needed.
Erigon still uses structured updates like Block Access Lists to apply changes efficiently, but it enhances this with the ability to fetch missing state on demand. This makes the system more dynamic and better suited for real-world use cases where requirements can change over time.
In simple terms, **Erigon’s approach is to store less locally but never lose access to the rest**, combining efficient storage with a distributed data access model. This turns partial statefulness from a fixed configuration into a flexible system that adapts to what the node needs.
### Sparse Snapshots + BitTorrent (Core Idea)
In Erigon, the concept of **sparse snapshots combined with BitTorrent** is the core innovation that makes its approach to partial statefulness flexible and powerful. Instead of deciding once what data to keep and permanently ignoring the rest, Erigon separates **storage** from **access**.
The starting point is **snapshots**, which are structured files (commonly `.kv` files) that store Ethereum state data such as accounts, contract storage, and code. These files are organized in a way that allows efficient reading of specific parts without loading everything. In a traditional setup, a node would download and store all of these files locally. However, with sparse snapshots, Erigon allows a node to store only a **subset of this data locally**, while treating the rest as available externally.
This is where **BitTorrent** comes in. Instead of relying on direct peer-to-peer requests like in snap sync, Erigon distributes these snapshot files across the network using BitTorrent. Each file is divided into smaller pieces, and different nodes in the network may have different pieces. When a node needs some data that it does not have locally, it does not ask a specific peer for that exact data. Instead, it uses torrent metadata to identify which file and which piece contains the required data, discovers peers that have it, and downloads just that piece.
Because of this design, data is not tied to a single node. Any node that has a part of a snapshot file can serve it. This makes the system highly decentralized and scalable. A node can fetch missing storage or code on demand, and once downloaded, it can reuse or cache it locally if needed.
Another important detail is that filtering happens at the **read and write level**, not at the file level. Since a single `.kv` file may contain data for many contracts, Erigon does not split files per contract. Instead, it decides during execution whether to store certain data locally or fetch it from the network when needed.
In simple terms, **sparse snapshots mean storing only what you need locally, and BitTorrent ensures that the rest of the data is always reachable from the network**. This turns Ethereum state into something that is not just locally stored, but globally accessible on demand, making the system far more flexible than traditional approaches.
### The Magic — How Data is Found
At this point, the natural question is: if a node does not store all the data locally, how does it actually find and retrieve the missing pieces when needed? This is where the “magic” of Erigon’s design comes in, combining structured state storage with BitTorrent-style discovery.
When a node needs some specific data, such as a contract storage value, it does not randomly search the network. Instead, it already knows how Ethereum state is organized internally. Using this structure, the node can map what it needs step by step: from a contract and storage slot, to the corresponding data domain, then to the exact snapshot file (`.kv` file), and finally to the precise location (offset) inside that file. This mapping is deterministic, meaning the node can always figure out exactly where the required data should be.
Once the node identifies the file, it uses torrent metadata associated with that file. Each snapshot file is represented in the network by a unique identifier (often called an **infohash**), which acts like a global ID for that data. The node then uses BitTorrent’s discovery system to find peers that have this file. This is done through decentralized mechanisms like the Distributed Hash Table (DHT) and peer advertisements, where nodes announce what data they can serve.
After discovering peers, the node does not download the entire file. Instead, the file is divided into smaller pieces, and the node requests only the specific pieces that contain the data it needs. These pieces can even come from multiple peers at the same time, making the process efficient and scalable.
An additional layer, such as structured manifests (for example, chain metadata describing which nodes serve which data ranges), helps nodes quickly identify relevant peers instead of blindly searching the network. This makes data retrieval faster and more reliable.
The key insight here is that the node is not asking, “Does anyone have this data?” Instead, it is saying, “I know exactly where this data lives, now give me the specific piece.” This shift from blind querying to deterministic lookup is what makes the system efficient.
In simple terms, **Erigon finds missing data by mapping it to a specific file and piece, discovering peers that have it using BitTorrent mechanisms, and downloading only the required parts**, making data access both precise and decentralized.
### Geth vs Erigon — Key Difference
At a high level, both geth and Erigon are trying to solve the same problem: how to reduce the cost of storing and syncing Ethereum state while still remaining useful and correct. However, they take fundamentally different approaches, especially when it comes to how they handle data that is not stored locally.
In Geth’s partial-state design, the focus is on **selective syncing**. During sync, the node decides which contracts it wants to track and downloads only their storage and code. Everything else is skipped using filters. This makes the node much lighter, but also introduces a strict limitation: once data is skipped, it is not available anymore. If a user later asks for information about an untracked contract, the node cannot fetch it and simply returns an error. In this model, the node’s view of the state is intentionally limited and fixed based on its initial configuration.
In contrast, Erigon takes a more flexible approach by separating **local storage from global availability**. It still allows the node to store only selected parts of the state locally, but it does not treat the rest of the data as permanently inaccessible. Instead, using sparse snapshots and BitTorrent-based distribution, Erigon makes it possible to fetch missing data on demand from the network. This means a node can start with a smaller local footprint and still access other parts of the state when needed.
Another key difference lies in how data is obtained. Geth relies on the **snap protocol**, where the node requests state data directly from peers and verifies it using Merkle proofs. This works well for efficient initial sync, but it is limited to the sync phase and does not support dynamic retrieval later. Erigon, on the other hand, treats state as structured files distributed across the network. Instead of requesting data from a specific peer, it identifies the exact file and piece where the data exists and downloads it from any available peer using BitTorrent mechanisms.
This leads to a fundamental distinction in behavior. In Geth, skipping data means losing access to it permanently. In Erigon, skipping data only means not storing it locally, while still being able to retrieve it when needed. As a result, Geth offers a simpler but more rigid model, whereas Erigon provides a more dynamic and adaptable system.
In simple terms, **Geth reduces storage by limiting what the node knows, while Erigon reduces storage by limiting what the node stores but not what it can access**.