BOOK THIS SPACE FOR AD
ARTICLE ADArticle written by Hubert Ritzdorf, Senior Engineer for ChainSecurity
Over the past years, Ethereum has seen tremendous adoption. This adoption has come in the form of more smart contracts and more addresses. While that is certainly a great success, it clashed with one of the design choices leading to recurring issues. We will briefly review this design and a short history of how we got to where we are today.
Ethereum’s persistent storage can simply be seen as a database. Among other things, it stores all the addresses along with their ETH balances. Some of these addresses happen to be smart contracts. For those, the storage also contains their code and all their persistent variables.
The growth in Ethereum adoption translated into growth of the database, as illustrated in the table below:
With the growth of this database, database lookups started to take longer and longer. Database lookups are necessary in all kinds of situations, but in particular they are necessary when one smart contract calls another (e.g., through the CALL opcode). Then, the EVM needs to load information about the called contract. These lookups are also required whenever a smart contract loads one of its persistent variables through the SLOAD opcode.
All of the EVM operations come with a certain gas cost. The relevant opcodes discussed here (e.g., CALL and SLOAD) generally had a fixed gas cost [1]. Fixed costs are obviously great in terms of simplicity, but clearly clash with the previously described growing execution costs that nodes experience over time.
Historically, this was first adjusted during EIP-150 in October 2016, when the cost of CALL went from 40 to 700 and the cost of SLOAD grew from 50 to 200. This came as a response to different attacks that had severely degraded network performance.
In 2019, Martin Holst Swende conducted an analysis highlighting that Ethereum nodes spent increasing amounts of time with SLOAD operations. Hence, the balance between gas cost and execution cost was no longer correct. Consequently, EIP-1884 raised the gas cost of SLOAD from 200 to 800.
Earlier that year, Daniel Perez and Benjamin Livshits published an early version of their “Broken Metre: Attacking Resource Metering in EVM” paper, which outlines how to generate transactions with high execution times. As we reproduced their results, we realized that some of those were highly cache-dependent and/or block-height-dependent, meaning that a transaction which is slow to execute in one block might be fast in another.
Hence, we set out to find a more general attack vector, which would make the attack more realistic. We realized that inexistent account lookups were quite costly for all the available clients. Hence, we came up with a smart contract doing the following:
Pushing a “random” address to the stack through the GAS opcodeMeasuring the balance of that addressIgnoring the result by removing it from the stackOur contract would just keep doing these three steps repeatedly until it would run out of gas. Given the huge address space, it is expected that almost none of the addresses would exist. Hence, lots of costly lookups of inexistent accounts were necessary.
As detailed in our submission to the Ethereum Foundation, this led to execution times between 75 and 90 seconds for a single block depending on the used node. EIP-1884 improved the situation but still allowed execution times above 30 seconds.
Different solutions for the problem were discussed, including dynamic opcode pricing based on the database structure. In the end, a two-fold strategy was chosen:
go-ethereum implemented a state snapshot that allowed constant-time lookups at the cost of a higher storage consumption.EIP-2929 increased the gas costs of SLOAD, CALL and related opcodes while also mirroring the execution costs more closely by differentiating between “warm” and “cold” states. This EIP became active as part of the Berlin hardfork in April 2021.Together these two solutions provide a very powerful protection, while also enabling some additional benefits.
Lessons Learned
For an active, rapidly evolving network such as Ethereum, it is hard to balance gas and execution costs. This balance, important for efficiency and security of the network, needs to be reestablished from time to time, which can cause issues for developers and users.
For a considerable time, state growth has been a concern for DLT systems. In Ethereum’s case, it led to a dangerous imbalance between gas and execution costs. We believe that other systems are currently suffering from similar problems, or will be soon. While a few have paid close attention and have taken measures to limit state growth, others might be forced to make design or engineering changes.
Finally, we would like to thank the Ethereum Foundation and in particular Martin Holst Swende and Péter Szilágyi for the incredibly engaging and informative interactions and their great work in managing the situation.
See more information in Ethereum foundations’ articleSee more information in our disclosure report[1] The gas cost of CALL actually depends on different conditions, but simply calling another contract had been associated with a fixed cost.
ChainSecurity’s mission is to build trust within the blockchain ecosystem, to allow this emerging technology to reach its potential among established organizations, governments and blockchain companies alike.
We have the capabilities to guide and support you wherever you are on the path — from exploring how you can use DLT and smart contracts and assessments of your smart contracts before they launch, to monitoring your smart contracts once they’re in place. If you want to know more about smart contract security or are interested in working with us, visit www.chainsecurity.com