天天看點

solidity智能合約簡析1,智能合約的前世今生2,simplestorage合約3,solidity和其它程式設計語言的不同:4,solidity進階文法:繼承,接口, 庫

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指令調轉到庫的位址,進而調用庫函數

繼續閱讀