SWC-116/使用区块值作为时间计量
合同通常需要访问时间值以执行某些类型的功能。block.timestamp和block.number这样的值 可以使你了解当前时间或时间增量,但是,在大多数情况下使用它们并不安全。
对于block.timestamp,开发人员经常尝试使用它来触发时间相关的事件。由于以太坊是去中心化的 节点只能在某种程度上同步时间。此外,恶意矿工可以更改其区块的时间戳,特别是如果他们可以 通过这样做获益的话。但是,矿工不能将时间戳设置为比前一个时间戳小(否则该区块将被拒绝), 也不能将时间戳设置得太远。考虑到以上所有因素,开发人员无法依赖所提供时间戳的准确性。
至于block.number,以太坊的区块时间通常约为14秒,因此可以预测区块之间的时间增量。但是, 区块时间不是恒定的,并且由于各种原因(例如,分叉重组和难度炸弹)可能会发生变化。由于 块时间可变,因此也不应该依赖于block.number进行精确的时间计算。
CWE漏洞分类
整改方案
开发人员应在编写智能合约时注意区块值并不精确,使用它们会导致意想不到的结果。作为替代方案, 在需要使用精确时间时,可以使用预言机/Oracle。
参考文献
合约示例
time_lock.sol
/*
* @author: Kaden Zipfel
*/
pragma solidity ^0.5.0;
contract TimeLock {
struct User {
uint amount; // amount locked (in eth)
uint unlockBlock; // minimum block to unlock eth
}
mapping(address => User) private users;
// Tokens should be locked for exact time specified
function lockEth(uint _time, uint _amount) public payable {
require(msg.value == _amount, 'must send exact amount');
users[msg.sender].unlockBlock = block.number + (_time / 14);
users[msg.sender].amount = _amount;
}
// Withdraw tokens if lock period is over
function withdraw() public {
require(users[msg.sender].amount > 0, 'no amount locked');
require(block.number >= users[msg.sender].unlockBlock, 'lock period not over');
uint amount = users[msg.sender].amount;
users[msg.sender].amount = 0;
(bool success, ) = msg.sender.call.value(amount)("");
require(success, 'transfer failed');
}
}
time_lock.yaml
description: Time lock contract using block number as proxy for time
issues:
- id: SWC-116
count: 1
locations:
- bytecode_offsets: {}
line_numbers:
time_lock.sol: [18]
timed_crowdsale.sol
pragma solidity ^0.5.0;
contract TimedCrowdsale {
event Finished();
event notFinished();
// Sale should finish exactly at January 1, 2019
function isSaleFinished() private returns (bool) {
return block.timestamp >= 1546300800;
}
function run() public {
if (isSaleFinished()) {
emit Finished();
} else {
emit notFinished();
}
}
}
timed_crowdsale.yaml
description: Timestamp Dependence
issues:
- id: SWC-116
count: 1
locations:
- bytecode_offsets:
'0x68b8b78a805010c7bf2cd5f199e998845c732f1b8265677d4069efd67dca84d8': [63]
line_numbers:
timed_crowdsale.sol: [14]