Cen*_*ngo 4 assert solidity evm
contract Sharer {
function sendHalf(address payable addr) public payable returns (uint balance) {
require(msg.value % 2 == 0, "Even value required.");
uint balanceBeforeTransfer = address(this).balance;
addr.transfer(msg.value / 2);
// Since transfer throws an exception on failure and
// cannot call back here, there should be no way for us to
// still have half of the money.
assert(address(this).balance == balanceBeforeTransfer - msg.value / 2);
return address(this).balance;
}
}
Run Code Online (Sandbox Code Playgroud)
对于上述合约,在什么情况下断言失败/address(this).balance 不会减少 (msg.value / 2)?为什么我们需要在这里断言?
这个断言是正确的,这正是它存在的原因。你用来assert()
声明你认为永远有效的事情。如果它们被证明是错误的,那么你的合同中就有错误。
断言不仅仅是一种幻想if
。虽然它确实执行运行时检查,但它也是提供形式验证目标的方法之一。Solidity 编译器中内置的SMTChecker等工具可以通过尝试证明有关代码的各种陈述来检测错误。问题是 - 这样的工具如何判断你得到的结果不是你想要的结果?用断言记录你的假设是一种非常简单的方法,可以为工具提供区分预期行为和有缺陷行为所需的额外信息。
此外,虽然现在合约很简单并且很容易看出它不会失败,但代码不会永远保持简单。该条件仅在合同没有其他应付功能的假设下成立。每次添加付费功能时你会记得修改这个功能吗?如果合约增长并且该函数被埋在文件底部的几个其他函数下怎么办?最重要的是——其他人将来修改代码怎么办?他们会注意到这个限制吗?断言是一种好方法,不必依赖任何人注意到这一点并将其转变为自动检查。
最后,这个断言是正确的,但它是显而易见的吗?实际上有很多假设:
sendHalf()
是这里唯一的一个receive()
或fallback()
函数 - 没有selfdestruct
成为另一份合同的接收者transfer()
无法回调sendHalf()
,因为transfer()
仅转发 2300 Gas,外部调用成本更高。transfer()
无法执行,selfdestruct
因为它花费了 5000 Gas。transfer()
不会以任何方式沉默,所以即使selfdestruct
改变未来的成本 <= 2300 Gas,发出它无论如何都会终止执行。这里有足够的假设,代码的作者可能根本无法 100% 确定他没有错过一些可能会变成安全漏洞的晦涩的极端情况。断言可以是明确排除这种可能性的简单而有效的方法。
归档时间: |
|
查看次数: |
518 次 |
最近记录: |