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