长篇干货|以太坊智能合约 —— ***安全开发指南(附代码) (3)

无论是使用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;

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/zgxdjj.html