Solidity - Contracts

2018-10-26笔记


合约相当于一个类,包含有使用持久性存储的状态变量,用来修改状态变量的函数。调用其他合约的函数,会切换当前上下文环境到新合约中。

创建合约

在创建合的时候,会执行合约的constructor,其中constructor可有可无,如果有只能有一个。当节点执行完构造函数后,状态变量跟函数会部署到节点中,构造函数与只在构造函数中使用的内部函数不会被部署到节点中。

成员作用域

Solidity只有两种函数调用方式,内部调用和外部调用(即通过消息调用的方式)。但是,合约的状态变量与方法共有4种可见范围。

  • external只可以通过外部调用的方式使用,如果合约内部需要使用一个external成员,需要使用this,这个主要用来与其他外部合约对接使用。
  • public:这个标识的可以通过内部调用或者外部调用的方式来访问成员。对状态变量使用public会为其生成一个getter
  • internal:这个标识的只能内部访问,且派生合约可以访问,相当于protected
  • private:这个就是private了,同样不可以被派生类访问。

Getter方法

声明为public的状态变量,编译器会为其制动生成一个同名getter方法,当没有使用this(这个表示合约实例对象)的是否,是通过internal的方式进行访问,反之则是使用external的方式。例如下面这个例子:

pragma solidity >=0.4.0 <0.6.0;

contract C {
    uint public data;
    function f() public view returns(uint) {
        data = 10; //内部访问方式
        return data;

        //return this.data;
        //编译器报错,因为使用this相当于外部访问
        //需要用函数调用的形式访问变量
        //即 return this.data(); 的方式代替
    }
}

contract Caller {
    C c = new C();
    function f() public view returns (uint) {
        //在Caller中调用C长度data状态变量,需要使用.data()
        //如果去掉(),则编译器会报错,无法将c.data转换成uint类型返回
        return c.data(); 
    }
}
c.data报错

Function Modifiers

这个有点像是python的装饰器,可以给函数增加一些功能,看一下官方的例子:

contract owned {
    constructor() public { owner = msg.sender; }
    address payable owner;
    modifier onlyOwner {
        require(
            msg.sender == owner,
        );
        _; //这个表示被装饰的函数调用位置
    }
}

contract mortal is owned {
    function close() public onlyOwner {    //这里表示使用了onlyOwner这个Modifier
        selfdestruct(owner); 
    }
}

上面的例子就相当于

contract mortal {
    function close() public {
        require(
            msg.sender == owner,
        );
        selfdestruct(owner); 
    }
}

如果一个函数要使用多个Modifier,则使用空格分隔各个Modifier,并且Modifiers会按序执行。举个例子,

pragma solidity >0.4.99 <0.6.0;

contract A1 {
    uint a;
    
    modifier Func1 {
        require(a > 0, "\n**********\nneed a > 0\n**********\n");
        _;
    }
    
    modifier Func2 {
        if(a == 0) {
            a = 10;
        }
        _;
    }
    
    function f1() public view returns(uint) {
        return a;
    }
    
    function f2() public Func1 Func2 returns(uint) {
        return a;
    }
    
    function f3() public Func2 Func1 returns(uint) {
        a = 1;
        return a;
    }
    
}

首先查看f1()的的结果,在不使用Modifier的情况下,会直接返回a的值0

f1().png

再看f2()的结果,发现出现了Error,原因是require的时候,a的值还是0

Solidity - Contracts_第1张图片
f2().png

最后看看f3()的结果,

f3().png

综合上面三个输出结果,可分析得出结论,执行顺序与列举Modifier的顺序是一致的,首先执行 modifier Func2将a的值变成了 10,然后再 modifier Func1中满足了 require要求,最后才执行 f3()的函数体,这时候 a被赋值为 1

函数类型

合约函数总共有两种类型,可以理解成用来表示function对调用者的承诺。
view:表示该函数不会读取状态变量,但承诺不会去修改状态变量,相当于是一个只读函数。
pure:表示该函数不会访问状态变量,就只使用从用户处接收的变量。

Fallback Function

一个合约只能有一个Fallback Funciton,这个函数没有函数名,external作用域,不能接收参数(依旧可以使用msg这个特殊的用户参数),当调用合约方法的时候,找不到一个方法名的时候,就会调用该方法(当合约接收eth的时候contract_address.transfer(1),就会调用这个方法,不过有一个前提,该Fallback Function需要是payable的)

函数重载

合约可以含有多个同名函数,但是要求是他们的参数类型要不一样,返回参数不作为参考标准。意思就是只要传送的参数能够没有歧义地调用正确函数就可以了,比如说contract A就可以,他们参数个数不一样,但是contract B却不行,因为address变量跟合约都是20字节的地址变量(我们通过合约地址来表示合约)。

pragma solidity >=0.4.16 <0.6.0;

contract A {
    function f(uint _in) public pure returns (uint out) {
        out = _in;
    }

    function f(uint _in, bool _really) public pure returns (uint out) {
        if (_really)
            out = _in;
    }
}

// This will not compile
contract B {
    function f(C _in) public pure returns (C out) {
        out = _in;
    }

    function f(address _in) public pure returns (address out) {
        out = _in;
    }
}

contract C {
}

抽象合约

当合约函数中,至少存在一个函数没有实现,那么这个合约就被称为抽象合约,它的作用是告诉别人,如果要从我这里派生新的合约,必须自己实现这个方法。
注:抽象合约不能被编译

pragma solidity >=0.4.0 <0.6.0;

contract Feline {
    function utterance() public returns (bytes32);
}

contract Cat is Feline {
    function utterance() public returns (bytes32) { return "miaow"; }
}

interface

接口跟抽象合约类似,但是接口中的函数全部都不可以实现,并且不能有constructor,不能有状态变量,函数必须都是external,暂时不太清楚它作用是什么。

Library

顾名思义,函数库。设计出来的目的是为了代码重用,使一些相同的合约可以从函数库中调用,这样可以减少以太坊链上的合约代码量。比如,A合约跟B合约都使用了相同的函数f,那么可以将f放在library中,然后让A和B调用,减少了代码冗余。

Using For

这个用起来就相当于Python中,类实例调用方法一样,实例作为第一个参数自动被传入,比如

pragma solidity >=0.4.16 <0.6.0;

library BigInt {
    struct bigint {
        uint[] limbs;
    }

    function fromUint(uint x) internal pure returns (bigint memory r) {
        //...
    }

    function add(bigint memory _a, bigint memory _b) internal pure returns (bigint memory r) {
               //...
    }

    function limb(bigint memory _a, uint _limb) internal pure returns (uint) {
               //...
    }

    function max(uint a, uint b) private pure returns (uint) {
                //...
    }
}

contract C {
    using BigInt for BigInt.bigint;

    function f() public pure {
        BigInt.bigint memory x = BigInt.fromUint(7);
        BigInt.bigint memory y = BigInt.fromUint(uint(-1));
        BigInt.bigint memory z = x.add(y); // 不用使用BigInt.add(x, y)的方式
        assert(z.limb(1) > 0); // 同样不需要通过BigInt.limb(z, 1)的方式
    }
}

你可能感兴趣的:(Solidity - Contracts)