開(kāi)發(fā) DApp 時(shí)要調(diào)用在區(qū)塊鏈上的 Ethereum 智能合約谦去,就需要智能合約的 ABI慷丽。本文希望更多了解 ABI,如為什么需要 ABI鳄哭?如何解讀 Ethereum 的智能合約 ABI要糊?以及如何取得智能的 ABI?
ABI(Application Binary Interface)
如果理解 API 就很容易了解 ABI妆丘。簡(jiǎn)單來(lái)說(shuō)杨耙,API 是程序與程序間互動(dòng)的接口。這個(gè)接口包含程序提供外界存取所需的 functions飘痛、variables 等。ABI 也是程序間互動(dòng)的接口容握,但程序是被編譯后的 binary code宣脉。所以同樣的接口,但傳遞的是 binary 格式的信息剔氏。所以 ABI 就要描述如何 decode/encode 程序間傳遞的 binary 信息塑猖。下圖以 Linux 為例,描述 Linux 中 API谈跛、ABI 和程序的關(guān)系羊苟。
編譯和部署智能合約
在 Ethereum 智能合約可以被大家使用前,必須先被部署到區(qū)塊鏈上感憾。
從智能合約的代碼到使用智能合約蜡励,大概包含幾個(gè)步驟:
- 編寫(xiě)智能合約的代碼(一般是用 Solidity 寫(xiě))
- 編譯智能合約的代碼變成可在 EVM 上執(zhí)行的 bytecode(binary code)。同時(shí)可以通過(guò)編譯取得智能合約的 ABI
- 部署智能合約,實(shí)際上是把 bytecode 存儲(chǔ)在鏈上(通過(guò)一個(gè)transaction)凉倚,并取得一個(gè)專(zhuān)屬于這個(gè)合約的地址
- 如果要寫(xiě)個(gè)程序調(diào)用這個(gè)智能合約兼都,就要把信息發(fā)送到這個(gè)合約的地址(一樣的也是通過(guò)一個(gè) transaction)。Ethereum 節(jié)點(diǎn)會(huì)根據(jù)輸入的信息稽寒,選擇要執(zhí)行合約中的哪一個(gè) function 和要輸入的參數(shù)
而要如何知道這這個(gè)智能合約提供哪些 function 以及應(yīng)該要傳入什么樣的參數(shù)呢扮碧?這些信息就是記錄在智能合約的 ABI!
Ethereum 智能合約 ABI
Ethereum 智能合約 ABI 用一個(gè) array 表示杏糙,其中會(huì)包含數(shù)個(gè)用 JSON 格式表示的 Function 或 Event慎王。根據(jù)最新的 Solidity 文件:
Function
共有 7 個(gè)參數(shù):
name
:a string,function 名稱(chēng)type
:a string宏侍,"function", "constructor", or "fallback"-
inputs
:an array赖淤,function 輸入的參數(shù),包含:name
:a string负芋,參數(shù)名type
:a string漫蛔,參數(shù)的 data type(e.g. uint256)components
:an array,如果輸入的參數(shù)是 tuple(struct) type 才會(huì)有這個(gè)參數(shù)旧蛾。描述 struct 中包含的參數(shù)類(lèi)型
outputs
:an array莽龟,function 的返回值,和inputs
使用相同表示方式锨天。如果沒(méi)有返回值可忽略毯盈,值為[]
payable
:true
,function 是否可收 Ether病袄,預(yù)設(shè)為false
constant
:true
搂赋,function 是否會(huì)改寫(xiě)區(qū)塊鏈狀態(tài),反之為false
stateMutability
:a string益缠,其值可能為以下其中之一:"pure"(不會(huì)讀寫(xiě)區(qū)塊鏈狀態(tài))脑奠、"view"(只讀不寫(xiě)區(qū)塊鏈狀態(tài))、"payable" and "nonpayable"(會(huì)改區(qū)塊鏈狀態(tài)幅慌,且如可收 Ether 為 "payable"宋欺,反之為 "nonpayable")
仔細(xì)看會(huì)發(fā)現(xiàn) payable
和 constant
這兩個(gè)參數(shù)所描述的內(nèi)容,似乎已包含在 stateMutability
中胰伍。
事實(shí)也確實(shí)是這樣的齿诞,在 Solidity v0.4.16 中把 constant
這個(gè)修飾function 的 key words 分成: view
(neither reads from nor writes to the state)和 pure
(does not modify the state),并從 v0.4.17 開(kāi)始 Type Checker 會(huì)強(qiáng)制檢查骂租。constant
改為只用來(lái)修飾不能被修改的 variable祷杈。并在 ABI 中加入 stateMutability
這個(gè)參數(shù)統(tǒng)一表示,payable
和 constant
目前保留是為了向后兼容渗饮。這個(gè)改動(dòng)詳細(xì)的內(nèi)容和討論可參考:
https://github.com/ethereum/solidity/issues/992
Event
共有 4 個(gè)參數(shù):
name
: a string但汞,event 的名稱(chēng)type
: a string宿刮,always "event"-
inputs
: an array,輸入?yún)?shù)特占,包含:name
: a string糙置,參數(shù)名稱(chēng)type
: a string,參數(shù)的 data type(e.g. uint256)components
: an array是目,如果輸入?yún)?shù)是 tuple(struct) type 才會(huì)有這個(gè)參數(shù)谤饭。描述 struct 中包含的信息類(lèi)型indexed
:true
,如果這個(gè)參數(shù)被定義為 indexed 懊纳,反之為false
anonymous
:true
揉抵,如果 event 被定義為 anonymous
更新智能合約狀態(tài)需要發(fā)送 transaction,transaction 需要等待驗(yàn)證嗤疯,所以更新合約狀態(tài)是非同步的冤今,無(wú)法馬上取得返回值。使用 Event 可以在狀態(tài)更新成功后茂缚,將相關(guān)信息記錄到 Log戏罢,并讓監(jiān)聽(tīng)這個(gè) Event 的 DApp 或任何應(yīng)用這個(gè)接口的程序收到通知。每筆 transaction 都有對(duì)應(yīng)的 Log脚囊。
所以簡(jiǎn)單來(lái)說(shuō)龟糕,Event 可用來(lái):1. 取得 function 更新合約狀態(tài)的返回值 2. 也可作為合約另外的存儲(chǔ)空間。
Event 的參數(shù)分為:有 indexed
悔耘,和其他沒(méi)有 indexed
的讲岁。有 indexed
的參數(shù)可以使用 filter,例如同一個(gè) Event衬以,我可以選擇只監(jiān)聽(tīng)從特定 address 發(fā)出來(lái)的交易缓艳。每筆 Log 的信息同樣分為兩個(gè)部分:Topics(長(zhǎng)度最多為 4 的 array) 和 Data。有 indexed
的參數(shù)會(huì)存儲(chǔ)存在 Log 的 Topics看峻,其他的存在 Data阶淘。如果定義為 anonymous
,就不會(huì)產(chǎn)生以下示例中的 Topics[0]互妓,其值為 Event signature 的 hash舶治,作為這個(gè) Event 的 ID。
event Set(address indexed _from, uint value)
用一個(gè)簡(jiǎn)單的智能合約舉個(gè)例子
這個(gè)智能合約包含:
-
data
:一個(gè)可修改的 state variable车猬,會(huì)自動(dòng)產(chǎn)生一個(gè)只能讀取的data()
function -
set()
:一個(gè)修改data
值的 function -
Set()
:一個(gè)在每次修寫(xiě)data
時(shí)記錄 Log 的 event
智能合約 Source Code:
pragma solidity ^0.4.20;
contract SimpleStorage {
uint public data;
event Set(address indexed _from, uint value);
function set(uint x) public {
data = x;
Set(msg.sender, x);
}
}
智能合約 ABI:
[{
"constant": true,
"inputs": [],
"name": "data",
"outputs": [{"name": "","type": "uint256"}],
"payable": false,
"stateMutabㄒility": "view",
"type": "function"
},
{
"anonymous": false,
"inputs": [{"indexed": true,"name": "_from","type": "address"},{"indexed": false,"name": "value","type": "uint256"}],
"name": "Set",
"type": "event"
},
{
"constant": false,
"inputs": [{"name": "x","type": "uint256"}],
"name": "set",
"outputs": [],
"payable": false,
"stateMutability": "nonpayable",
"type": "function"
}]
取得 Ethereum 智能合約 ABI
Solidity Compiler
可以用 Solidity Compiler 取得合約 ABI,我使用 JavaScript 版本的 Compiler 為例尺锚。
安裝:
npm install solc -g
取得合約 ABI:
solcjs simpleStorage.sol --abi
會(huì)生成一個(gè) simpleStorage_sol_SimpleStorage.abi 文件珠闰,里面就是合約ABI 內(nèi)容。
也可以取得合約的 binary code:
solcjs your_contract.sol --bin
Remix
同樣的使用 Solidity Compiler瘫辩,也可以用 Remix伏嗜。在合約的 Details 可以看到完整的 ABI坛悉。可以在 Settings 中指定 Compiler 版本承绸。
Etherscan
許多知名合約會(huì)把合約 source code 放上 Etherscan 做驗(yàn)證裸影,可以同時(shí)看到h 合約ABI。
另外 Etherscan 提供 API军熏,可用來(lái)取得經(jīng)過(guò)驗(yàn)證的合約 ABI轩猩。