描述:以太坊智能合约中使用block.timestamp来向合约提供当前区块的时间戳,并且这个变量通常被用于计算随机数、锁定资金等。但是区块的打包时间并不是系统设定的,而是可以由矿工在一定的幅度内进行自行调整。因此,一旦时间戳使用不当,则会引起漏洞
核心问题:矿工操纵时间戳生成对自己有利的随机数,或者来解除合约的时间限制
pragma solidity ^0.4.24;
contract TimeGame1{
uint public lastBlockTime;
function lucky() public payable{
require(msg.value == 100 wei);
require(lastBlockTime != block.timestamp); //block.timestamp获取当前区块的时间戳
lastBlockTime = block.timestamp;
if(lastBlockTime % 10 == 5){
msg.sender.transfer(address(this).balance);
}
}
}
合约讲解:合约通过交易发送所在区块时间戳来决定是否获奖,每个区块中只允许第一笔交易获奖,若区块时间戳的十进制表示最低位是5,交易发送者即可获奖。
漏洞点:由于矿工有个0~900s的任意设置时间戳的权限,导致矿工可以非常轻易的来设置满足交易的时间戳。普通用户可以自己写一个攻击合约来调用lucky(),也是可以自由设置满足交易的时间戳
pragma solidity ^0.4.24;
contract TimeGame2{
bool public neverPlayed=true;
function check(uint answer) public returns(bool){
return true;
}
function play() public {
require(now > 1577808000 && neverPlayed == true); //now即为block.timestamp的另一种写法
if (check(233) == true){
neverPlayed = false;
msg.sender.transfer(1500 ether);
}
}
}
合约讲解:开奖时间被硬编码到合约中,只有等到开奖时间到来之后才能开奖
漏洞点:矿工可以在时间戳即将到来之前,将包含该笔交易的区块时间戳稍微提前,就可以提前开奖