无论是使用raw calls 或是contract calls,如果这个ExternalContract是不受信任的都应该假设存在恶意代码。即使ExternalContract不包含恶意代码,但它所调用的其他合约代码可能会包含恶意代码。一个具体的危险例子便是恶意代码可能会劫持控制流程导致竞态。
(浏览Race Conditions获取更多关于这个问题的讨论,
地址:https://github.com/ConsenSys/smart-contract-best-practices/#race-conditions)
对于外部合约优先使用pull 而不是push
外部调用可能会有意或无意的失败。为了最小化这些外部调用失败带来的损失,通常好的做法是将外部调用函数与其余代码隔离,最终是由收款发起方负责发起调用该函数。这种做法对付款操作尤为重要,比如让用户自己撤回资产而不是直接发送给他们。(译者注:事先设置需要付给某一方的资产的值,表明接收方可以从当前账户撤回资金的额度,然后由接收方调用当前合约提现函数完成转账)。
(这种方法同时也避免了造成 gas limit相关问题。
地址:https://github.com/ConsenSys/smart-contract-best-practices/#dos-with-block-gas-limit)
// bad contract auction { address highestBidder; uint highestBid;
function bid() payable {
if (msg.value < highestBid) throw;
if (highestBidder != 0) {
if
(!highestBidder.send(highestBid)) { // if
this call consistently fails, no one else can bid throw; } }
highestBidder = msg.sender; highestBid = msg.value; } }
// good contract auction { address highestBidder; uint highestBid; mapping(address => uint) refunds;
function bid() payable external {
if (msg.value < highestBid) throw;
if (highestBidder != 0) { refunds[highestBidder] +=
highestBid; // record the refund that this user can claim }
highestBidder = msg.sender; highestBid = msg.value; }
function withdrawRefund() external { uint refund = refunds[msg.sender]; refunds[msg.sender] = 0; if (!msg.sender.send(refund)) { refunds[msg.sender] = refund; //
reverting state because send failed } } }
标记不受信任的合约
当你自己的函数调用外部合约时,你的变量、方法、合约接口命名应该表明和他们可能是不安全的。
// bad Bank.withdraw(100); // Unclear whether
trusted or untrusted
function makeWithdrawal(uint amount) { //
Isn\'t clear that this function is potentially unsafe Bank.withdraw(amount);
}
// good
UntrustedBank.withdraw(100); // untrusted external call
TrustedBank.withdraw(100); // external but trusted bank contract maintained by XYZ Corp
function makeUntrustedWithdrawal(uint amount) { UntrustedBank.withdraw(amount);
}
**使用****assert()**强制不变性
当断言条件不满足时将触发断言保护 -- 比如不变的属性发生了变化。举个例子,代币在以太坊上的发行比例,在代币的发行合约里可以通过这种方式得到解决。断言保护经常需要和其他技术组合使用,比如当断言被触发时先挂起合约然后升级。(否则将一直触发断言,你将陷入僵局)
例如:
contract Token { mapping(address => uint) public balanceOf; uint public totalSupply;