solidity智能合约简析

   日期:2020-05-09     浏览:99    评论:0    
核心提示:1,智能合约的前世今生从比特币到以太坊,从简单的脚本到图灵完备的合约,从签名,多签到合约2,simplestorage合约从simplestorage入门, 看合约layout。从remix编译和调试结果引出gas, EVM, memory, storage和calldata3,solidity和其它编程语言的不同:区块链共识的一致性特性决定了:(1)不能使用本机随机数(2)不能访问...区块链

1,智能合约的前世今生

从比特币到以太坊,从简单的脚本到图灵完备的合约,从签名,多签到合约

2,simplestorage合约

从simplestorage入门, 看合约layout。从remix编译和调试结果引出gas, EVM, memory, storage和calldata

pragma solidity >=0.4.16 <0.7.0;

contract SimpleStorage {
    uint storedData;

    function set(uint x) public {
        storedData = x;
    }

    function get() public view returns (uint) {
        return storedData;
    }
}

3,solidity和其它编程语言的不同:

区块链共识的一致性特性决定了:
(1)不能使用本机随机数
(2)不能访问IO
区块链的可用性决定了:
(1)资源收费,抵御粉尘攻击 gas 基于数据存储和指令执行收费
(2)运行合约需要沙盒环境 EVM

对照表:
OS -> 区块链
database -> mpt
APP -> 智能合约
多语言互操作:protobuf -> abi
memory -> memory
固化存储: file -> storage
常数: const -> calldata

从gas看设计:
gas花费是solidity代码设计和编译器优化的重点。

storage: 可读可写, 最烧gas,是优化的重点。编译时分配,删除即赋值。
calldata:常数,只读。数据为0, 1 byte消耗4 gas; 数据非零, 1 byte消耗68 gas。合约bytecode和调用数据均属于calldata类型

从gas消耗看调用数据的layout设计,为什么只取function selector的前4个字节。

keccak256("set(uint256)")
60fe47b16ed402aae66ca03d2bfc51478ee897c26a1158669c7058d5f24898f4

只取前4个字节可以节省: 68 * 32 - 68 * 4 = 1904 gas

4,solidity高级语法:继承,接口, 库

继承

// Test Inherit
contract BaseA {
    uint256 a;
    
    constructor(uint256 y) internal {
        a = y;
    }
    
    function set(uint256 x) public {
        a = x;
    }
}

// expect that constructor() of BaseA contract will execute first and then its constructor()
// The same function will override
contract TestInherit is BaseA {
    // explicit specify the param of base contract's constructor
    constructor() public BaseA(1) {
        a = 2;
    }
    
    // explicit call method of base contract
    function set(uint256 x) public {
        BaseA.set(x);
    }
}

基类合约和子类合约编译到一块。部署时只需要部署子类合约。

接口

//
// Test interface call
interface InterfaceA {
    function set(uint256 x) external;
}

contract TestA {
    uint256 a;
    
    constructor() public {
        a = 1;
    }
    
    // visibility compatiable: public against external(interface)
    function set(uint256 x) public {
        a = x;
    }
}

contract TestInterfaceA {
    constructor(address _addr) public {
        InterfaceA testA = InterfaceA(_addr);
        testA.set(2);
    }
}

首先部署实现接口的合约,将合约地址传入或者硬编码到调用合约。调用合约通过接口即可调用,调用方法和调用基类合约一样。

library basicMath {
	function add(uint x, uint y) public pure returns (uint z) {
		z = x + y;
	}

	function subtract(uint x, uint y) public pure returns (uint z) {
		z = x - y;
	}
}

contract TestUseLibrary {
    uint256 a;
    constructor() public {
        a = basicMath.add(1, 2);
    }
}

库的调用有2种方式:
1,不需要修改状态(库函数view/pure),直接调用/using for
(1), 库函数是internal函数,直接编入调用合约,通过JUMP跳转调用
(2), 否则,通过DELEGATECALL指令调转到库的地址(需要先部署库合约),从而调用库函数
2,需要修改状态的,通过addr.delegatecall调用

下面的库函数indexOf是view,但不是internal,所以是1.2中的情况

pragma solidity >=0.0.0;

library Search {
    function indexOf(uint[] storage self, uint value) public view returns (uint) {
        for (uint i = 0; i < self.length; i++)
            if (self[i] == value) return i;
        return uint(-1);
    }
}

编译调用合约

solc.exe --bin consuming-contract.sol

======= consuming-contract.sol:C =======
Binary:
608060405234801561001057600080fd5b50610218806100206000396000f3fe608060405234801561001057600080fd5b50600436106100365760003560e01c8063e33b87071461003b578063e81cf24c14610069575b600080fd5b6100676004803603602081101561005157600080fd5b81019080803590602001909291905050506100a1565b005b61009f6004803603604081101561007f57600080fd5b8101908080359060200190929190803590602001909291905050506100d0565b005b600081908060018154018082558091505090600182039060005260206000200160009091929091909150555050565b60008073__$501e46c9438942acd821033cbc3fe1b072$__6324fef5c89091856040518363ffffffff1660e01b8152600401808381526020018281526020019250505060206040518083038186803b15801561012b57600080fd5b505af415801561013f573d6000803e3d6000fd5b505050506040513d602081101561015557600080fd5b810190808051906020019092919050505090507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8114156101c15760008290806001815401808255809150509060018203906000526020600020016000909192909190915055506101de565b81600082815481106101cf57fe5b90600052602060002001819055505b50505056fea265627a7a723158206dd50d8c9a5b8f6daf20ab2be9e6446b5fcf427150b19033657f1f148bb7f29d64736f6c63430005110032

// $501e46c9438942acd821033cbc3fe1b072$ -> single-lib.sol:Search

======= single-lib.sol:Search =======
Binary:
610124610026600b82828239805160001a60731461001957fe5b30600052607381538281f3fe730000000000000000000000000000000000000000301460806040526004361060335760003560e01c806324fef5c8146038575b600080fd5b606b60048036036040811015604c57600080fd5b8101908080359060200190929190803590602001909291905050506081565b6040518082815260200191505060405180910390f35b600080600090505b838054905081101560c4578284828154811060a057fe5b9060005260206000200154141560b8578091505060e9565b80806001019150506089565b507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff90505b9291505056fea265627a7a72315820f7867af2de0fc9c862047dbea0e2e67ddf478cde90d2f1c59d758d70e2f458d564736f6c63430005110032

上述bytecode中" 501 e 46 c 9438942 a c d 821033 c b c 3 f e 1 b 072 501e46c9438942acd821033cbc3fe1b072 501e46c9438942acd821033cbc3fe1b072"在链接的时候会被替换为库合约的地址,调用库的合约会通过DELEGATECALL指令调转到库的地址,从而调用库函数

 
打赏
 本文转载自:网络 
所有权利归属于原作者,如文章来源标示错误或侵犯了您的权利请联系微信13520258486
更多>最近资讯中心
更多>最新资讯中心
0相关评论

推荐图文
推荐资讯中心
点击排行
最新信息
新手指南
采购商服务
供应商服务
交易安全
关注我们
手机网站:
新浪微博:
微信关注:

13520258486

周一至周五 9:00-18:00
(其他时间联系在线客服)

24小时在线客服