How safe is it to use block.timestamp as contract expiration time? If a miner provides an incorrect timestamp in a block header, how much can it be off before it is rejected by other nodes? Is there a strategy to make a contract more robust to this?
3 Answers
Not especially, but it depends on what the use case is.
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.
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.
First, a basic: using strict equality == block.timestamp
would not be safe, since a block with that exact timestamp may never get mined. So use >= block.timestamp
Now it depends on what happens when the contract "expiration time" is reached. For reasons similar to the safe use of BLOCKHASH, block.timestamp
should only be safely used if the total amount of value resting on what the contract does at "expiration time", is lower than what a miner earns by mining a single block. If the outcome of a lottery worth millions depends on block.timestamp
, the timestamp will probably be manipulated.
The Yellow Paper does not have any answer to "how much can it be off before it is rejected by other nodes". If block.timestamp
is used, the only guarantee (equation 43) is that block.timestamp
is greater than that of its parent. But there are game theoretic reasons why block.timestamp
won't be significantly wrong.
From the Solidity docs another thing to watch for is:
You publish a transaction at a time X, this transaction contains some code that calls block.timestamp
and is included in a block whose timestamp is Y and this block is included into the canonical chain (published) at a time Z. The value of block.timestamp
will be identical to Y, and X <= Y <= Z
-
Maybe I should elaborate more in my answer: there are ways for larger miners to manipulate timestamps while incurring lower costs than the value of a single block reward. So the use of block.timestamp cannot be made strictly safe by only using low values the way BLOCKHASH randomness can be. Commented Jan 23, 2016 at 0:19
-
I can edit or strikethrough my answer and refer to the ways larger miners can manipulate timestamps with low costs... what should I refer to?– eth ♦Commented Jan 23, 2016 at 0:50
A more secure solution would be to rely on block.number and an estimated time per block increment. Let's say you wanted to create a lease for a contract with a specific end date. You could calculate the approximate time with:
estimated_time_in_blocks = lease_length_in_seconds / average_block_time_in_seconds
lease_end_block_number = current_block + estimated_time_in_blocks
In your contract you can then check:
block.number <= lease_end_block_number
The example in Mastering Ethereum is:
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 uses this approach