美文网首页
Solidity - Contracts

Solidity - Contracts

作者: Llewyn_Davis | 来源:发表于2018-10-26 14:45 被阅读0次

    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

    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

          本文链接:https://www.haomeiwen.com/subject/vkxktqtx.html