区块时间戳历来有各种应用,例如随机数的熵,锁定资金的时间和各种状态变化的条件语句等。如果在智能合约中不正确地使用区块时间戳,矿工稍微调整时间戳,就可能会带来相当危险后果。
坑点分析
正如上面所说,如果矿工动机不纯,就可以操纵block.timestamp。让我们构建一个简单的游戏,这个游戏很容易被矿工利用。 image
代码17
代码17的这个合约,就像一个简单的彩票系统。每个区块中的一个交易都可以赌10以太币来得到赢得合约余额。这里的假设是,block.timestamp对于最后两位数字是均匀分布的。如果是这样的话,那么中奖的几率将是1/15。
然而,正如我们上面所说,矿工可以根据需要调整时间戳。在这种情况下,如果合约中集合了足够多的以太币,那么一个生成区块的矿工就会有动力去选择一个时间戳。例如,block.timestamp或者now的是0的时间戳。
在这样做的时候,他们可能会赢得锁定在这份合约中的以太币,同时获得全部的回报。由于每个区块只允许一个人下注,这也很容易受到非法预先交易的攻击。
在实践中,区块时间戳是单调增加的,因此矿工不能选择任意的时间戳,它们的时间戳必须比他们的父时间戳要大)。
因此,它们也仅限于在不远的时间段内设置区块时间,否则这些区块将就很可能被网络拒绝,也就是说,节点将不会验证未来时间戳的区块。
避坑技巧
区块时间戳不应该用于熵或产生随机数,例如,它们不应成为(直接或通过某种推导)赢得一场比赛或改变一个重要的状态(如果假设是随机的)的决定性因素。
有敏锐的时间逻辑有时是必要的,例如解锁合约(timelocking)在几周后完成一个 ICO 或强制执行过期日期。有时建议使用block.number和一个平均区块时间来估计时间。
例如,一个星期零10秒钟的区块时间,相当于大约60480个区块生成时间。因为矿工无法轻易操纵区块序数,所以指定一个区块序数来更改合约状态可以更加安全,BAT ICO合约就采用了这一策略。
如果合约不是特别关注矿工操纵的区块时间戳,也可以不用这样做,但是在开发合约时需要注意这一点。
**真实案例:GovernMental **
同样以GovernMental来举例。这个合约的签订者是在一轮中最后加入的玩家(至少一分钟)。因此,作为一名玩家的矿工,可以调整时间戳(在未来的某个时间,使它看起来像一分钟已经过去了),使得看起来玩家是最后加入的(即使这在现实中是不正确的)