正文
function split(address[] calldata recipients) external payable
{
require(msg.value > 0,"Please provide currency to be split among recipients");
uint amount = msg.value / recipients.length; // problem here if length is 0
for(uint index = 0; index < recipients.length; index++)
{
(bool success,) = payable(recipients[index]).callvalue:amount("");
require(success,"Could not send ether to recipient");
}
}
整数除法余数
这也是许多编程语言中常见的算术问题。Solidity 执行整数除法,这意味着除法操作的结果会被截断。这可能导致忽略除法余数,从而产生逻辑错误。以下代码片段提供一个示例:如果提供的金额不能被接收者的数量整除,那么部分加密货币可能会被锁定在合约中。
function split(address[] calldata recipients) external payable
{
require(recipients.length > 0,"Empty recipients list");
uint amountPerRecipient = msg.value / recipients.length; // remainder ???
require(amountPerRecipient > 0,"Amount must be positive");
for(uint index = 0; index < recipients.length; index++)
{
payable(recipients[index]).transfer(amountPerRecipient);
}
}
未初始化变量
未初始化的变量可能会导致逻辑错误或异常。如果变量未被初始化,根据其类型赋予的默认值很可能不适合该变量的用途。以下代码包含一个依赖于 owner 状态变量的访问修饰符。该变量是私有的,因此无法在合约外部访问或赋值。此外,构造函数中也没有对 owner 进行显式初始化。这会导致变量保持默认值,从而使所有带有 onlyOwner 修饰符的函数无法执行。
address private owner;
modifier onlyOwner() {
require(msg.sender == owner, "Only the owner of the contract has access");
_;
}
用户输入验证
参数验证或“净化”是每个方法开始时必须实现的过程。这确保了方法总是按预期执行。不能信任终端用户总是提供有效参数。如果缺少验证,而用户又不了解,甚至是恶意的,就可能导致关键性错误,从而产生意外结果或完全停止合约的执行。以下示例包含一个内部数组的 getter 方法。用户可以提供未经验证的索引,因此有可能越界访问。
uint256[] private _array= [10, 20, 30, 40, 50];
function getArrayElement(uint256 index) external view returns (uint256)
{
return _array[index];
}
类型不匹配
在 Solidity 中,枚举类型以无符号整数的形式存储。因此,它们可以与 uint 类型的变量进行比较和赋值。然而,这种情况可能很棘手,因为枚举的值域通常远小于无符号整数的值域。如果将一个超过枚举范围的变量赋值给枚举变量,则交易将被回滚。虽然回滚交易被认为是安全的,但这种情况表明合约代码存在逻辑错误,最好避免出现此类问题。
contract UnmatchedType {
enum Options { Candidate1, Candidate2, Candidate3 }
mapping(address => Options) private _votes;
mapping(Options => uint) private _votesCount;
function vote(uint option) external {
_votes[msg.sender] = Options(option);
_votesCount[Options(option)]++;
}
function getStatisticsForOption(uint option) external view returns(uint) {
return _votesCount[Options(option)];
}
}
本节简要介绍我们所进行的一些实验结果。
基本上,我们使用了一些工具对智能合约进行分析,以检查它们在第 2 节所示示例中的表现。
我们选择的工具如下所示。