Skip to content

Latest commit

 

History

History
67 lines (55 loc) · 4.06 KB

File metadata and controls

67 lines (55 loc) · 4.06 KB

控制结构与异常处理

控制结构

JavaScript 中的大部分控制结构在 Solidity 中都是可用的,除了 switch 和 goto。 因此 Solidity 中有 if,else,while,do,for,break,continue,return,? : 这些与在 C 或者 JavaScript 中表达相同语义的关键词。

用于表示条件的括号 不可以 被省略,单语句体两边的花括号可以被省略。

注意,与 C 和 JavaScript 不同,Solidity 中非布尔类型数值不能转换为布尔类型,因此 if (1) { ... } 的写法在 Solidity中无效。

下面是一个对0到100求和的合约:

pragma solidity ^0.5.0;

contract Sum {
    uint num = 0;
    function exec() public returns(uint) {
        for (uint i = 0; i < 100; i++) {
            num += i;
        }
        return num;
    }
}

错误处理及异常

Solidity通过Assert, Require, Revert三个关键字来进行异常处理,Solidity使用状态恢复来处理错误,简单来说就是撤销当前操作或者调用对合约状态所做的修改,并且向调用者表明错误。

其中函数assertrequire用于检查条件是否满足,如果不满足就抛出异常,revert 函数用来标记错误并恢复当前的调用,revert 调用中还可以包含有关错误的详细信息,这个消息会被返回给调用者。

assertrequire在以太坊拜占庭硬分叉之前行为是一致的,分叉后有细微的差别。 require 函数用于确认条件有效性,例如输入变量,或合约状态变量是否满足条件,或验证外部合约调用返回的值,而assert 应该用于检查内部错误,比如overflow。assert也被用来预防不应该发生的情况,当assert的错误发生意味着出现了一些出乎意料的错误,甚至是程序失控了,需要修复合约中的问题。

requirerevert调用失败以后会返回剩余的gas,但是assert并不会。

通过一个合约来看二者的区别。

pragma solidity >=0.5.0;

contract Sharer {
    function sendHalf(address addr) public payable returns (uint balance) {
        require(msg.value % 2 == 0, "Even value required.");
        uint balanceBeforeTransfer = this.balance;
        addr.transfer(msg.value / 2);//由于转移函数在失败时抛出异常并且不能在这里回调,因此我们应该没有办法仍然有一半的钱。
        assert(this.balance == balanceBeforeTransfer - msg.value / 2);
        return this.balance;
    }
}

在这个合约中,期望向指定地址转移所请求数值一半的数字资产,如果我们假设这个资产不能拆分到小数点后,只能拆分成整数,那么比如7,9,11这样的奇数就不符合要求。这种不符合要求的情况是符合我们的预期的,发送转移资产的请求可以是任何人,那么发送任何数量都不奇怪,所以在合约的开始通过 require(msg.value % 2 == 0, "Even value required.");来判断请求转移的资产是否可以对半拆分。

接着按照预期addr.transfer(msg.value / 2);会转移一半的资产,但是转移后剩余的资产应该是总资产减去转移了的资产,所以用assert(this.balance == balanceBeforeTransfer - msg.value / 2);来判断是不是符合预期,如果这里报错,那么一定是出现了我们意想不到的问题,可能是转移资产的时候出现了问题,或者什么灵异事件?

处理与 require() 同样的类型,但是需要更复杂处理逻辑的场景,如果有复杂的 if/else 逻辑流,那么应该考虑使用 revert() 函数而不是require()。复杂的场景意味着更多的代码,大量的require只会拖累合约的可读性,所以这是使用revert代替require的好机会。

pragma solidity >=0.5.0;

contract VendingMachine {
    function buy(uint amount) payable {
        if (amount > msg.value / 2 ether)
            revert("Not enough Ether provided.");
        // 下边是等价的方法来做同样的检查:
        require(
            amount <= msg.value / 2 ether,
            "Not enough Ether provided."
        );
        // 执行购买操作
    }
}