Author: Roman Dvorkin
The Fireblocks research team has released an experimental open-source Chrome extension for the verification of token balances on block explorers and portfolio websites. The extension is the first real-world application on Ethereum which utilizes trustless light client technology, and is a step forward in our commitment to continuously advance blockchain security.
Etherscan is the largest block explorer for Ethereum. It is a popular and beloved resource in the Ethereum community that solves a critical requirement, and we all use it all the time. The data displayed on Etherscan is commonly treated as a synonym for blockchain data, and less knowledgeable users sometimes don’t know the difference between the two.
However, Etherscan is a centralized entity, and the blockchain ethos dictates applying a “Trust, but verify” approach. While there is no reason to doubt Etherscan, we believe in independent open-source verification. Some experienced users are already verifying Etherscan data, and this process is cumbersome; it is usually done by comparing to another centralized source or using various scripts for fetching blockchain data.
With the rise of maturity in light client technology in Ethereum, specifically in implementations like Lodestar light client, we saw an opportunity to solve the balance verification problem in a way that is easy to use with high trust guarantees. We implemented this solution in widely used websites such as Etherscan and the Metamask portfolio.
The Balance Verification Chrome Extension
Our solution is the first real-world application of Ethereum light clients. We developed a Chrome extension that uses Lodestar’s light client to verify balances on various websites such as Etherscan and Metamask Portfolio, with more websites to be added.
Download the extension here:
View the source code here:
It is important to note this is an experimental extension based on cutting-edge tech but it is not deemed production-ready and, as such, is not recommended for use in live production environments.
Vitaliks balances without the extension
Vitaliks balances with the extension
Collaborating with the light client technology community
This blog post outlines the results of our work, however, we want to share some of our experience working with light clients.
Developing a working application is often challenging when a technology is in its infancy. In the process of building the experimental Chrome extension, we attempted various light client implementations and actively engaged with developers to make adjustments and fixes in the code, such as adjusting the light client to work in the extension context. The teams developing light clients were helpful with knowledge sharing with our team, and have accomplished much. The Fireblocks team would like to express gratitude to the light client community for the collaboration, especially the team behind Lodestar, with whom we worked at developing the extension.
This extension and its open-source code has been released as part of Fireblocks’ commitment to continuously innovate and raise the bar for blockchain security. While it is currently intended only as an experimental extension, the technology itself holds much potential for wallet security at large. To further our research, we encourage feedback to foster a discussion about light clients outside of the Ethereum foundation to the broad EVM application ecosystem.
This launch is in a very basic MVP, and a lot of features can be added, such as:
- Similar verifications to other block explorers, portfolio websites, or any source displaying blockchain information (we are accepting PRs)
- Verify information other than balances. For example transaction inclusion – verifying a transaction shown in the UI occurred.
- Light clients on other blockchains. Research & development continues, and we expect to see similar improvements and capabilities shown in many blockchains – especially in EVM-compatible and L2 chains.
Technical Deep Dive
First, let’s review how light clients work and how they can keep track of the beacon chain without requiring heavy storage and computation resources.
Next, there’s a brief overview of some elements of the Ethereum consensus protocol and structures, and understand insights into how it all ties together and enables us to cryptographically prove execution layer information based on beacon chain information, provided by the light client.
Then we will discuss the two proving methods – one using storage slot proofs to prove some information about EVM storage (ERC20 balances are an example of such information) and the other is a more general method proving the execution of an EVM contract call (Used for example to prove the result of calling the function that returns the balance of ERC20 tokens).
The following sections are quite technical and assume some familiarity with topics such as Ethereum consensus, EVM, and Merkle trees. It is encouraged to look up unfamiliar terms and keep track of the general flow – what information we have in each step and what we are trying to achieve.
Light clients are not a new concept – a version of light clients in the form of SPV were proposed in Satoshi Nakamoto’s 2009 whitepaper.
Light clients provide a way to cryptographically verify a blockchain state with very low resource (memory, network bandwidth, CPU) requirements, enabling them to run in environments such as browsers, phones, and IoT devices.
How Do Light Clients Work In Ethereum?
Support for Ethereum light clients was introduced in the Altair hard fork in October 2021. The light clients use the sync protocol, utilizing a random subset of Ethereum validators called “sync committees” to keep track of beacon chain block headers.
The protocols work as follows:
- Every 256 epochs (~27 hours), a sync committee of 512 random validators is selected.
- The public keys of the sync committee members are included in the beacon chain.
- While the committee is active, its members sign beacon chain blocks, and those signatures are included in the beacon chain.
- When the committee changes, the old committee signs the members of the new committee.
Sync protocol visualized
This way, for a light client to keep track of the beacon chain, it only needs to verify the signatures of the sync committee, and when a switch happens it needs to verify the new members.
The traditional (non-light client) way to keep track of the beacon chain involves getting information about the block proposer and attestors and requires calculations on the entire validator set, and this requires more resources to compute. For comparison, light clients have storage requirements of just a few MBs, while regular nodes need many GBs. In addition, light clients require less CPU and less network bandwidth
|Resource||Light Clients||Regular Nodes|
While the protocol is relatively fast and syncing from the beginning of the chain to the latest block takes about one hour (in contrast to a few days for full nodes), it is still too slow for many applications.
So, instead of syncing from the beginning, light clients can sync from a trusted checkpoint, which is a block they trust was included in the past of the chain more recently.
There are many sync checkpoint providers, and the user is encouraged to query multiple providers for a trusted checkpoint.
From now on the client won’t have to trust anything – as long as the initial checkpoint is correct, all the verified information will be correct and verified cryptographically.
We now understand what makes light clients “light” and how they are able to run with very low resource requirements and sync with the beacon chain, and now we will learn how we can use them for practical applications.
There are multiple use cases for light clients, and the one we decided to focus on is using a light client to reduce trust in centralized actors, specifically those displaying account balances. When a user searches for his crypto balances on a block explorer or looks at his wallet UI he trusts this information and assumes its correctness. With light clients, users no longer need to blindly trust the displayed information and can verify it quickly.
We explained how the light client gets synced to the latest beacon block, and now we will explain how to use this to verify balance information.
First, we need a brief explanation of some terminology regarding Ethereum block structure & state architecture, which will be used later.
The ExecutionPayload is a structure included in the beacon block body. It contains Execution Layer (EL) information such as block number, transactions, and state root that can be used to verify balances and other information.
State Root, Storage Root
This blog post does an excellent job of explaining Ethereum’s trie architecture, which includes the various Merkle trees used to store data about the global state.
We are particularly interested in the state tree, whose root is included in the ExecutionPayload. The state tree stores each account their ETH balance and storage root. The storage tree contains all the information about an address’ (contract) storage.
This is all the information we need to know about Ethereum architecture to understand how it all ties together and we will be able to verify our balances!
Proofs and Verification
The eth_getProof rpc call is the basis for all our verifications. We can request from nodes Merkle proofs for the state trie and child storage tries. This way we can create proofs for an account ETH balance, and proofs for the storage value of contracts. The Merkle proofs will be verified against the state root that is included in the beacon block.
Using this method we can directly verify ETH balance, but verifying ERC20 balances requires some more steps. We will explain two methods of verifying ERC20 balances, one using storage slot proofs, and the other which is more general using contract call proofs.
Storage Slot Proofs
The balance of the token is usually stored in a mapping that stores the balances of all owners of this token.
For a mapping, the storage slot of balanceOf[address] is calculated as
where balanceOf_BaseStorageSlotIndex is the index of the mapping in the contract’s memory layout. That is if the mapping is the first defined variable in the contract, the index would be zero , and in WETH case this index is three because it is defined in the fourth 4th storage slot.
Note: counting the number of variables isn’t always correct, due to packing. Read more on storage memory layout here.
* WETH Storage layout
The ERC20 standard does not define how the memory layout of tokens should look, so we need to find the balanceOf_BaseStorageSlotIndex ourselves.
A great idea described in this blog post by Euler Labs uses the method of brute forcing to map the storage layout of ERC20 contracts. This works as follows:
- Set the storage slot of keccack256(address,i) for 0 <= i <= 20 to some val_i,
- Query contract.balanceOf
- Return the i corresponding to val_i
We implemented this method here: https://github.com/fireblocks-labs/erc20-balance-storage-slot-finder.
This method is pretty fast and we can create a mapping of used tokens this way.
There are some downsides:
- We need to do this mapping for every new token.
- There are tokens for which the balance mapping isn’t standard.
Now that we have the storage slot mapping, we can verify balances with a light client using the previously explained eth_getProof rpc method.
Contract Call Proof
The previous method’s main problem is that memory layout isn’t standardized in ERC20, this is what required us to bruteforce the balance storage slots of different contracts. However, the function balanceOf is standardized, and this is the way commonly used to get ERC20 balances.
If you think it is possible to verify a contract call using our synced block data – you are correct!
The method works as follows:
- Call eth_createAccessList to get all the addresses and storage slots that will be accessed by this call
- Get the code of every address, the value of every storage slot, and their proofs
- Verify the proofs, essentially verifying the EVM state that is relevant for the contract call
- Execute the contract call with the verified state locally
This method is implemented by the prover library developed by Lodestar. The prover library is quite new and under active development so we are not using it yet in this initial release, but will likely adopt it once it’s matured a bit.
Balance Verification Summary
To summarize, the steps to verify Ethereum balances are:
1. Query multiple checkpoint providers for a checkpoint
2. Initialize the light client with the checkpoint, and run until it is synced to the latest block
3. Two options:
a. Storage slot proof
i. Find the balance storage slot of relevant ERC20 tokens
ii. Get proofs from a node for the ETH balance and additional ERC20 balance storage values with eth_getProof.
iii. Verify the proofs against the latest block information from the light client
b. Contract call proof
i. Call eth_createAccessList to get accessed addresses and storage slots for a contract call
ii. Query node for relevant proofs and contract codes via eth_getCode, eth_getProof
iii. Verify the proofs against the latest block information from the light client
iv. Set EVM state with relevant storage values and contract codes
v. Run the contract call locally with the verified state
Light client technology is getting mature enough that it is becoming interesting at the product layer and not just the protocol layer. It allows products to balance performance and trust and allows the open-source community to solve some of the trust challenges in widely adopted centralized tools.
We are excited to see how light client technology will be used in the future to make a wide range of products more trustless. We are getting close to the inflection point where it’s less about the Ethereum Foundation and Ethereum clients developers and more about the Ethereum blockchain and EVM ecosystems to make the next push of using this technology. We see a lot of promise in the wallet security space and welcome feedback on the open source and ideas shared in this post.