Do Not Track Time With Block Numbers
Use of block number to track time is discouraged, because it implicitly and incorrectly assumes a constant block period. It is safer to track the passing of time using block timestamps, with the caveat that timestamps can be manipulated to some extent by miners.
Description
Tracking the passing of time is often fundamental for the business logic of smart contract systems. Common examples include the use of timelocks to allow / disallow execution of actions, tracking voting periods of a specific length, or token offerings based on vesting mechanisms.
In all cases, usage of the block number to track the passing of time is highly discouraged. Systems that rely on block numbers incorrectly assume that the block period is constant. Yet there might be minor delays in the time between blocks that, accummulated over time, can significantly affect behavior. Moreover, Ethereum’s “difficulty bomb” may make mining more difficult, thus increasing the average block time (see Etherscan’s average block time for reference). As a consequence, time between blocks could eventually be much larger than expected, affecting the system in unexpected and undesired ways.
A more reliable way of tracking time is using the timestamp of blocks. In Solidity, this can be achieved by reading the block.timestamp
global variable. Yet developers must be aware that timestamps can be manipulated in the order of seconds by miners. Therefore, as an abundance of caution, it is advised to only rely on block.timestamp
as long as time variations of five minutes or less do not significantly affect behavior nor open attack vectors in the system.
Currently, the OpenEthereum (Parity), Geth and Hyperledger Besu nodes allow timestamps up to 15 seconds in the future. But this is a simple convention not strictly enforced by standards, and could therefore change in the future. In earlier versions, nodes had a tolerance of up to 900 seconds (15 minutes).
Having considered this, if developers are still compelled to use the number of blocks to account for the passing of time, mechanisms to update the time between blocks must be put in place for a more robust implementation. Moreover, any assumptions made on the time between blocks must be made explicit with clear documentation, highlighting how block numbers relate to timestamps.
Example
The following contract implements a function that should allow users to withdraw funds from the contract on a weekly basis. It implicitly and incorrectly relies on a block period of 15 seconds. Therefore, it may lock users' funds for an unexpected amount of time if the block period changes. Note also that the number of blocks to be waited (40320) is not self-explanatory, as it cannot be easily related to a 7-days delay.
To fix this, the contract should instead rely on block timestamps:
Resources
Last updated