ABI全稱Application Binary Interface, 是調(diào)用智能合約函數(shù)以及合約之間函數(shù)調(diào)用的消息編碼格式定義,也可以理解為智能合約函數(shù)調(diào)用的接口說明. 類似Webservice里的SOAP協(xié)議一樣;也就是定義操作函數(shù)簽名,參數(shù)編碼,返回結(jié)果編碼等。
使用ABI協(xié)議時(shí)必須要求在編譯時(shí)知道類型,即強(qiáng)類型相關(guān).
智能合約的ABI接口定義
當(dāng)一個(gè)智能合約編譯出來(lái)后, 他的abi接口定義就確定了. 比如下面的智能合約:
contract myContract {
event Log_lotus(bytes32 _id, bytes32[] users);
uint k=0;
function lotus(uint a, bytes32 b, bytes32[] c) public {
k=a;
Log_lotus(b,c);
}
}
生成的字節(jié)碼:
606060405260008055341561001357600080fd5b610176806100226000396000f300606060405260043610610041576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff168063e17b829214610046575b600080fd5b341561005157600080fd5b6100b4600480803590602001909190803560001916906020019091908035906020019082018035906020019080806020026020016040519081016040528093929190818152602001838360200280828437820191505050505050919050506100b6565b005b826000819055507fa756dbb78a87f957869324b276aea920464fad0955c2e8dc59f80bef9ceb8343828260405180836000191660001916815260200180602001828103825283818151815260200191508051906020019060200280838360005b83811015610131578082015181840152602081019050610116565b50505050905001935050505060405180910390a15050505600a165627a7a723058202b9bfbc1e3fc4afd0d10fce971ba4109707f356f5c5c3f8ac7601f76b819b7330029
生成的abi定義:
[
{
"constant": false, //方法修飾符,false表示函數(shù)內(nèi)可以修改狀態(tài)變量
"inputs": [ //方法參數(shù),它是一個(gè)對(duì)應(yīng)數(shù)組,數(shù)組里的每個(gè)對(duì)象都是一個(gè)參數(shù)說明
{
"name": "a", //第一個(gè)參數(shù)的名字
"type": "uint256" //第一個(gè)參數(shù)的類型
},
{
"name": "b", //第二個(gè)參數(shù)的名字
"type": "bytes32" //第二個(gè)參數(shù)的類型
},
{
"name": "c", //第三個(gè)參數(shù)的名字
"type": "bytes32[]" ////第三個(gè)參數(shù)的類型
}
],
"name": "lotus", //方法名
"outputs": [], //方法返回值,格式和inputs類型相同
"payable": false,
"stateMutability": "nonpayable",
"type": "function" //方法類型,function, constructor, fallback,event
},
{
"anonymous": false,
"inputs": [
{
"indexed": false,
"name": "_id",
"type": "bytes32"
},
{
"indexed": false,
"name": "users",
"type": "bytes32[]"
}
],
"name": "Log_lotus",
"type": "event"
}
]
可以看出, 生成abi包含了2個(gè)定義: 函數(shù) lotus , 事件 Log_lotus , 各個(gè)字段含義見上. 根據(jù)該abi定義,就可以生成調(diào)用該智能合約函數(shù)的abi格式的數(shù)據(jù)了.
格式簡(jiǎn)單的可以表示為: 函數(shù)選擇器+參數(shù)編碼
函數(shù)選擇器
一個(gè)函數(shù)調(diào)用的前四個(gè)字節(jié)數(shù)據(jù)指定了要調(diào)用的函數(shù)簽名。計(jì)算方式是使用函數(shù)簽名的 keccak256 的哈希,取4個(gè)字節(jié)。
bytes4(keccak256("foo(uint32,bool)"))
函數(shù)名如果有多個(gè)參數(shù)使用,隔開,要去掉表達(dá)式中的所有空格。在geth客戶端,通過命令可以得到hash:
web3.sha3("foo(uint32,bool)")
"0xcc822237a37f9290b70dab4d640156d816bf8abdb959b5971d803a639dadef98"
//截取前4個(gè)字節(jié) 即0xcc822237
參數(shù)編碼
由于前面的函數(shù)簽名使用了四個(gè)字節(jié),參數(shù)的數(shù)據(jù)將從第五個(gè)字節(jié)開始。
根據(jù)參數(shù)類型,編碼規(guī)則有所區(qū)別:
-
uint<M>:M為integer類型代表M bits,0 < M <= 256,M % 8 == 0,如uint32,uint8,uint256。 int<M>:同上。同為從8到256位的無(wú)符號(hào)整數(shù)。uint和int:整型,分別是uint256和int256的別名。注意: 函數(shù)參數(shù)類型是uint,轉(zhuǎn)sha3碼時(shí)要變成uint256。address:地址,20個(gè)字節(jié),160bits。bool:布爾類型,1個(gè)字節(jié),true:1,false:0。bytes<M>:固定大小的字節(jié)數(shù)組,0<M<=32,byte都是bytes1的別名。bytes:動(dòng)態(tài)分配大小字節(jié)數(shù)組。不是一個(gè)值類型!string:動(dòng)態(tài)大小UTF8編碼的字符串,不是一個(gè)值類型!
除了bytes,和string, 其他類型的數(shù)據(jù)不足32字節(jié)長(zhǎng)度的需要加0補(bǔ)足32字節(jié). 動(dòng)態(tài)長(zhǎng)度的編碼在例子中介紹.
舉例
函數(shù): function baz(uint32 x, bool y) :
調(diào)用: baz(69, true)
生成的數(shù)據(jù)如下:
- 0xcdcd77c0: 使用函數(shù)選擇器確定的函數(shù)ID。通過
bytes4(keccak256("baz(uint32,bool)"))生成。 - 0x0000000000000000000000000000000000000000000000000000000000000045。第一個(gè)參數(shù),uint32位的值
69,并補(bǔ)位到32字節(jié)。 - 0x0000000000000000000000000000000000000000000000000000000000000001。第二值
boolean類型值true。補(bǔ)位到32字節(jié)。
返回結(jié)果是一個(gè)bool值,在這里,返回的是false:
- 0x0000000000000000000000000000000000000000000000000000000000000000
函數(shù): f(uint,uint32[],bytes10,bytes)
調(diào)用: (0x123, [0x456, 0x789], "1234567890", "Hello, world!")
函數(shù)選擇器: bytes4(sha3("f(uint256,uint32[],bytes10,bytes)"))
對(duì)于 固定大小的類型 值 uint256 和 bytes10 ,直接編碼值。
對(duì)于 動(dòng)態(tài)內(nèi)容類型 值 uint32[] 和 bytes ,我們先 編碼偏移值 ,偏移值是整個(gè)值編碼的開始到真正存這個(gè)數(shù)據(jù)的偏移值(這里不計(jì)算頭四個(gè)用于表示函數(shù)簽名的字節(jié))。
所以參數(shù)編碼數(shù)據(jù)依次為:
- 0x0000000000000000000000000000000000000000000000000000000000000123,32字節(jié)的
0x123。 - 0x0000000000000000000000000000000000000000000000000000000000000080 (第二個(gè)參數(shù)的由于是動(dòng)態(tài)內(nèi)容類型,所以這里存儲(chǔ)偏移值,4*32 字節(jié),剛好是頭部部分的大?。?/li>
- 0x3132333435363738393000000000000000000000000000000000000000000000 (“1234567890” 在右側(cè)補(bǔ)0到32字節(jié)大小)
- 0x00000000000000000000000000000000000000000000000000000000000000e0 (第四個(gè)參數(shù)的偏移 = 第一個(gè)動(dòng)態(tài)參數(shù)的偏移值 + 第一個(gè)動(dòng)態(tài)參數(shù)的大小 = ** **
4*32 + 3*32** ** 動(dòng)態(tài)長(zhǎng)度的計(jì)算見后)
尾部部分的第一個(gè)動(dòng)態(tài)參數(shù), [0x456, 0x789] 編碼拆解如下:
- 0x0000000000000000000000000000000000000000000000000000000000000002 (整個(gè)數(shù)組的長(zhǎng)度,2)。
- 0x0000000000000000000000000000000000000000000000000000000000000456 (第一個(gè)元素)
- 0x0000000000000000000000000000000000000000000000000000000000000789(第二個(gè)元素)
最后我們來(lái)看看第二個(gè)動(dòng)態(tài)參數(shù)的的編碼, Hello, world! 。
- 0x000000000000000000000000000000000000000000000000000000000000000d (元素的字節(jié)長(zhǎng)度,13)
- 0x48656c6c6f2c20776f726c642100000000000000000000000000000000000000 (“Hello, world!” 補(bǔ)位到32字節(jié),里面是按ascii編碼的,可以查查對(duì)應(yīng)的編碼。)
所以最終結(jié)果是:
0x8be65246
0000000000000000000000000000000000000000000000000000000000000123
0000000000000000000000000000000000000000000000000000000000000080
3132333435363738393000000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000000000000000000000e0
0000000000000000000000000000000000000000000000000000000000000002
0000000000000000000000000000000000000000000000000000000000000456
0000000000000000000000000000000000000000000000000000000000000789
000000000000000000000000000000000000000000000000000000000000000d
48656c6c6f2c20776f726c642100000000000000000000000000000000000000