【区块链笔记】ETH_Solidity函数(08)

Page content

这篇文章简单的整理了Solidity函数相关的内容。

1.Solidity函数声明和类型

函数的值类型有两类:- 内部(internal)函数和外部(external) 函数

  • 内部函数只能在当前合约内被调用(更具体来说,在当前代码块内,包括内部库函数和继承的函数中),
    因为它们不能在当前合约上下文的外部被执行。
    调用一个内部函数是通过跳转到它的入口标签来实现的,就像在当前合约的 内部调用一个函数。
  • 外部函数由一个地址和一个函数签名组成,可以通过外部函数调用传递或者 返回
  • 调用内部函数:直接使用名字 f
  • 调用外部函数:this.f(当前合约),a.f(外部合约)

2.Solidity函数可见性

函数的可见性可以指定为 external,public ,internal 或者 private;
对于状态变量,不能设置为 external ,默认是 internal。

  • external:外部函数作为合约接口的一部分,意味着我们可以从其他合 约和交易中调用。
    一个外部函数 f不能从内部调用(即 f 不起作用,但this.f()可以)。
    当收到大量数据的时候,外部函数有时候会更有效 率。
  • public:public 函数是合约接口的一部分,可以在内部或通过消息调用。
    对于public状态变量,会自动生成一个getter函数。
  • internal:这些函数和状态变量只能是内部访问(即从当前合约内部或从它派生的合约访问),不使用this调用。
  • private:private函数和状态变量仅在当前定义它们的合约中使用,并 且不能被派生合约使用
pragma solidity >=0.4.0 <0.6.0;

contract C {
    uint256 private data;

    function f(uint256 a) private pure returns (uint256 b) {
        return a + 1;
    }
    function setData(uint256 a) public {
        data = a;
    }
    function getData() public view returns (uint256) {
        return data;
    }
    function compute(uint256 a, uint256 b) internal pure returns (uint256) {
        return a + b;
    }
}

contract D {
    function readData() public {
        C c = new C();
        uint256 local = c.f(7); //private类型是不能这么调用,会报错
        c.setData(3);
        local = c.getData();
        local = c.compute(3, 5); //internal类型是需要继承后才能调用
    }
}

contract E is C {
    function g() public {
        C c = new C();
        uint256 val = c.compute(3, 5); //继承的是直接调用应该写成 c.compute(3, 5) => compute(3, 5)
    }
}
pragma solidity >=0.4.16 <0.6.0;

contract C {
    function f(uint256 a) private pure returns (uint256 b) {
        return a + 1;
    }

    function setData(uint256 a) internal {
        data = a;
    }

    uint256 public data;

    function x() public {
        data = 3; // 内部访问
        uint256 val = this.data(); // 外部访问 uint val2 = f(data);
    }
}

3.Solidity函数状态可变性

  • pure:纯函数,不允许修改或访问状态
  • view:不允许修改状态
  • payable:允许从消息调用中接收以太币Ether。
  • constant:与view相同,一般只修饰状态变量,不允许赋值 (除初始化以外)

以下情况被认为是修改状态

  • 修改状态变量
  • 产生事件。
  • 创建其它合约。
  • 使用selfdestruct。
  • 通过调用发送以太币。
  • 调用任何没有标记为view或者pure的函数。
  • 使用低级调用。
  • 使用包含特定操作码的内联汇编。

以下被认为是从状态中进行读取

  • 读取状态变量。
  • 访问 this.balance 或者 <address>.balance。
  • 访问 block,tx,msg中任意成员 (除 msg.sig 和 msg.data 之外)。
  • 调用任何未标记为pure的函数。
  • 使用包含某些操作码的内联汇编。

4.函数修饰器(modifier)

  • 使用修饰器modifier可以轻松改变函数的行为。
    例如,它们可以在执行函数之前自动检查某个条件。
    修饰器modifier是合约的可继承属性,并可能被派生合约覆盖
  • 如果同一个函数有多个修饰器modifier,它们之间以空格隔开,修饰 器modifier会依次检查执行。
pragma solidity >=0.4.22 <0.6.0; 
contract Purchase {
    address public seller;
    modifier onlySeller() { // Modifier
        require( msg.sender == seller, "Only seller can call." );
        _; 
    }

    function abort() public view onlySeller returns(uint) { // Modifier usage
        return 200;
    }
}

5.回退函数(fallback)

  • 回退函数(fallback function)是合约中的特殊函数;
    没有名字,不能有参数也不能有返回值
  • 如果在一个到合约的调用中,没有其他函数与给定的函数标识符匹配(或没有提供调用数据),
    那么这个函数(fallback 函数)会被执行
  • 每当合约收到以太币(没有任何数据),回退函数就会执行。
    此外,为了接收以太币,fallback 函数必须标记为 payable。
    如果不存在这 样的函数,则合约不能通过常规交易接收以太币
  • 在上下文中通常只有很少的gas可以用来完成回退函数的调用,所以使fallback函数的调用尽量廉价很重要
pragma solidity >0.4.99 <0.6.0;

contract Sink {
    function() external payable {}
}
contract Test {
    function() external { x = 1; }
    uint256 x;
}
contract Caller {
    function callTest(Test test) public returns (bool) {
        (bool success, ) = address(test).call(
            abi.encodeWithSignature("nonExistingFunction()")
        );
        require(success);
        address payable testPayable = address(uint160(address(test)));
        return testPayable.send(2 ether);
    }
}

6.事件(event)

  • 事件是以太坊EVM提供的一种日志基础设施。
    事件可以用来做操作记录,存储为日志。
    也可以用来实现一些交互功能,比如通知UI,返回函数调用结果等
  • 当定义的事件触发时,我们可以将事件存储到EVM的交易日志中,日志是区块链中的一种特殊数据结构;
    日志与合约关联,与合约的存储合并存入区块链中;
    只要某个区块可以访问,其相关的日志就可以访 问;
    但在合约中,我们不能直接访问日志和事件数据
  • 可以通过日志实现简单支付验证SPV(SimplifiedPayment Verification),
    如果一个外部实体提供了一个带有这种证明的合约,
    它可以检查日志是否真实存在于区块链中

7.Solidity异常处理

  • Solidity使用“状态恢复异常”来处理异常。
    这样的异常将撤消对当前调用(及其所有子调用)中的状态所做的所有更改,并且向 调用者返回错误。
  • 函数assert和require可用于判断条件,并在不满足条件时抛出异 常
  • assert()一般只应用于测试内部错误,并检查常量
  • require() 应用于确保满足有效条件(如输入或合约状态变量),或验证调用外部合约的返回值
  • revert()用于抛出异常,它可以标记一个错误并将当前调用回退

8. Solidity中的单位

以太币(ether)

  • 以太币 Ether 单位之间的换算就是在数字后边加上 wei、 finney、 szabo 或 ether 来实现的,
    如果后面没有单位,缺 省为 Wei。例如 2 ether == 2000 finney 的逻辑判断值为 true
Unit Wei Value Wei
wei 1 1 wei
Kwei (babbage) 1e3 wei 1,000
Mwei (lovelace) 1e6 wei 1,000,000
Gwei (shannon) 1e9 wei 1,000,000,000
microether (szabo) 1e12 wei 1,000,000,000,000
milliether (finney) 1e15 wei 1,000,000,000,000,000
ether 1e18 wei 1,000,000,000,000,000,000

时间

秒是缺省时间单位,在时间单位之间,数字后面带 有 seconds、 minutes、 hours、 days、 weeks 和 years 的可以进 行换算,基本换算关系如下:

  • 1 == 1 seconds
  • 1 minutes == 60 seconds
  • 1 days == 24 hours
  • 1 weeks == 7 days
  • 1 years == 365 days
  • 1 hours == 60 minutes

这些后缀不能直接用在变量后边。
如果想用时间单位(例如 days)来将输入变量 换算为时间,你可以用如下方式来完成:

function f(uint start, uint daysAfter) public {
    if (now >= start + daysAfter * 1 days) { // ... }
}

欢迎大家的意见和交流

email: li_mingxie@163.com