以太坊的ABI編碼

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ì)于 固定大小的類型uint256bytes10 ,直接編碼值。

對(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
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容