Timestamp Dependence

Timestamp Dependence¶

Be aware that the timestamp of the block can be manipulated by the miner, and all direct and indirect uses of the timestamp should be considered.

Block timestamps have historically been used for a variety of applications, such as entropy for random numbers (see the Entropy Illusion for further details), locking funds for periods of time, and various state-changing conditional statements that are time-dependent.

From Solidity docs:

Do not rely on block.timestamp, now and blockhash as a source of randomness, unless you know what you are doing. [..] The current block timestamp must be strictly larger than the timestamp of the last block, but the only guarantee is that it will be somewhere between the timestamps of two consecutive blocks in the canonical chain.

From Ethereum Stack Exchange question:

Block times are subject to the following constraints:

• If you stamp your block with a time too far in the future, no one else will build on it (miners will not build on a block timestamped "from the future"). • Your block time cannot be stamped with an earlier time than its parent. • Difficulty is kept lowest (best deal for miners) by not stamping blocks as earlier than they actually occur. • using strict equality == block.timestamp would not be safe, since a block with that exact timestamp may never get mined. So use >= block.timestamp

Taken together, these factors suggest that most blocks produced by small hashrate miners who are not trying to manipulate anything will be pretty close to accurate. However, it is trivial for a more powerful miner to manipulate timestamps over short periods, especially if there is something to be gained by doing this.

Ultimate, there is no cryptographic way to verify the timestamp itself--only the ordering of certain cryptographic structures. Therefore block.timestamp needs to be supplemented with some other strategy in the case of high-value/risk applications.

block.timestamp and its alias now can be manipulated by miners if they have some incentive to do so. Let’s construct a simple game, shown in roulette.sol, that would be vulnerable to miner exploitation.

Block Timestamp vs Block Number

block.timestamp can be manipulated by miners. The only constraint is that a timestamp has to be larger than the previous block's timestamp. Timestamps too far in the future are usually ignored by the network, but the miner usually has a 30-second window within which to choose a timestamp. Watch out for the use of block.timestamp for anything that is time-sensitive, like auctions or lottery ends. Contracts should proceed without assuming timestamp accuracy.

block.number can also lead to problems. There's no way to safely predict when a specific block will be mined.

As a rule of thumb, the block.timestamp is generally preferred, subject to accommodation of its inaccurate nature.

Example

roulette.sol from Mastering Ethereum book

contract Roulette {
    uint public pastBlockTime; // forces one bet per block
    constructor() public payable {} // initially fund contract
    // fallback function used to make a bet
    function () public payable {
        require(msg.value == 10 ether); // must send 10 ether to play
        require(now != pastBlockTime); // only 1 transaction per block
        pastBlockTime = now;
        if(now % 15 == 0) { // winner
            msg.sender.transfer(this.balance);
        }
    }
}

This contract behaves like a simple lottery. One transaction per block can bet 10 ether for a chance to win the balance of the contract. The assumption here is that block.timestamp’s last two digits are uniformly distributed. If that were the case, there would be a 1 in 15 chance of winning this lottery.

However, as we know, miners can adjust the timestamp should they need to. In this particular case, if enough ether pools in the contract, a miner who solves a block is incentivized to choose a timestamp such that block.timestamp or now modulo 15 is 0. In doing so they may win the ether locked in this contract along with the block reward. As there is only one person allowed to bet per block, this is also vulnerable to front-running attacks (see Race Conditions/Front Running for further details).

In practice, block timestamps are monotonically increasing and so miners cannot choose arbitrary block timestamps (they must be later than their predecessors). They are also limited to setting block times not too far in the future, as these blocks will likely be rejected by the network (nodes will not validate blocks whose timestamps are in the future).

Example

From Consensys recommendations

Be aware that the timestamp of the block can be manipulated by a miner. Consider this contract

uint256 constant private salt =  block.timestamp; // ! not random
function random(uint Max) constant private returns (uint256 result){
    //get the best seed for randomness
    uint256 x = salt * 100/Max;
    uint256 y = salt * block.number/(salt % 5) ;
    uint256 seed = block.number/3 + (salt % 300) + Last_Payout + y;
    uint256 h = uint256(block.blockhash(seed));
    return uint256((h / x)) % Max + 1; //random number between 1 and Max
}

When the contract uses the timestamp to seed a random number, the miner can actually post a timestamp within 15 seconds of the block being validated, effectively allowing the miner to precompute an option more favorable to their chances in the lottery. Timestamps are not random and should not be used in that context.

The 15-second Rule

The Yellow Paper (Ethereum's reference specification) does not specify a constraint on how much blocks can drift in time, but it does specify that each timestamp should be bigger than the timestamp of its parent. Popular Ethereum protocol implementations Geth and Parity both reject blocks with timestamp more than 15 seconds in future. Therefore, a good rule of thumb in evaluating timestamp usage is:

Real-World Example: GovernMental

GovernMental, the old Ponzi scheme mentioned above, was also vulnerable to a timestamp-based attack. The contract paid out to the player who was the last player to join (for at least one minute) in a round. Thus, a miner who was a player could adjust the timestamp (to a future time, to make it look like a minute had elapsed) to make it appear that they were the last player to join for over a minute (even though this was not true in reality). More detail on this can be found in the “History of Ethereum Security Vulnerabilities, Hacks and Their Fixes” post by Tanya Bahrynovska.

References

https://consensys.github.io/smart-contract-best-practices/known_attacks/#timestamp-dependence https://github.com/ethereumbook/ethereumbook/blob/develop/09smart-contracts-security.asciidoc#block-timestamp-manipulation https://ethereum.stackexchange.com/questions/413/can-a-contract-safely-rely-on-block-timestamp

Preventative Techniques

To avoid miner manipulation in random number generation, there are a few solutions:

  • A commitment scheme such as RANDAO, a DAO where the random number is generated by all participants in the DAO.

  • External sources via oracles, e.g. Oraclize.

  • Using Bitcoin block hashes, as the network is more decentralized and blocks are more expensive to mine.

Timestamp Dependence

There are three main considerations when using a timestamp to execute a critical function in a contract, especially when actions involve fund transfer.

Time-sensitive logic is sometimes required; e.g., for unlocking contracts (time-locking), completing an ICO after a few weeks, or enforcing expiry dates. It is sometimes recommended to use block.number and an average block time to estimate times; with a 10 second block time, 1 week equates to approximately, 60480 blocks. Thus, specifying a block number at which to change a contract state can be more secure, as miners are unable easily to manipulate the block number. The BAT ICO contract employed this strategy.

It is possible to estimate a time delta using the block.number property and average block time, however this is not future proof as block times may change (such as fork reorganisations and the difficulty bomb). In a sale spanning days, the 15-second rule allows one to achieve a more reliable estimate of time.

Note If the scale of your time-dependent event can vary by 15 seconds and maintain integrity, it is safe to use a block.timestamp.

Block Timestamp vs Block Number

block.timestamp can be manipulated by miners. The only constraint is that a timestamp has to be larger than the previous block's timestamp. Timestamps too far in the future are usually ignored by the network, but the miner usually has a 30-second window within which to choose a timestamp. Watch out for the use of block.timestamp for anything that is time-sensitive, like auctions or lottery ends. Contracts should proceed without assuming timestamp accuracy.

block.number can also lead to problems. There's no way to safely predict when a specific block will be mined.

As a rule of thumb, the block.timestamp is generally preferred, subject to accommodation of its inaccurate nature.

Resources

Last updated