應用二進制接口描述

翻譯原文

date:20170801

基本設計

在以太坊生態(tài)系統(tǒng)中,應用二進制接口是跟合約交互的標準途徑充坑。從區(qū)塊鏈外部或者合約之間的交互都可以通過這個方式肩刃。數據根據它的類型編碼腥泥,我們將在這個文章中詳細論述奏篙。編碼不是自我描述的宋光,所以如果要解碼就需要模式(schema)情连。

我們假設合約的接口函數是強類型的叽粹,在編譯的時候推斷,且是靜態(tài)的却舀。沒有提供反省機制虫几。我們假設所有的合約都定義這樣的接口,使得任何合約都可以在編譯的時候調用挽拔。

該描述文檔不描述接口是動態(tài)的合約或者另一種情況--在運行的時候才知道持钉。這些情況是很重要的,因為他們可以構建以太坊生態(tài)系統(tǒng)的豐富的內建設施篱昔。(每强?This specification does not address contracts whose interface is dynamic or otherwise known only at run-time. Should these cases become important they can be adequately handled as facilities built within the Ethereum ecosystem.)

函數選擇器

函數調用的調用數據的前四個字節(jié)指出了所調用的函數。它是函數簽名的keccak(SHA-3)的前四個字節(jié)(左邊州刽,高位空执,大端存儲)。簽名被定義為基本原型的權威表達式穗椅。例如辨绊,函數名稱和用括號括起來的參數類型。參數類型通過一個逗號隔開-沒有使用空格匹表。

參數編碼

從第五個字節(jié)開始门坷,就是參數的編碼。這個編碼除了用在前四個字節(jié)指定函數外袍镀,也用在了其他地方默蚌。例如,返回值和時間參數也是用同樣的方式苇羡。

類型

有下面幾種基礎類型:

  • uint<M>:M位的無符號類型的整形绸吸,0 < M <= 256,M % 8 == 0,例如,uint32设江,uint8锦茁,uint256。
  • int<M>:M位的有符號整形叉存,0 < M <= 256,M % 8 == 0码俩。
  • address:等價于uint160,except for the assumed interpretation and language typing.
  • uint歼捏,int:各自是uint256,int256的同義詞(不是用來計算函數選擇器)
  • bool:等價于 uint8稿存,值被限制為0和1够傍。
  • fixed<M>x<N>:M位的有符號,固定點的小數挠铲,0 < M <= 256,M % 8 == 0,且0 < N <= 80, 意味著值v為v/(10 ** N).(冕屯?signed fixed-point decimal number of M bits, 0 < M <= 256, M % 8 ==0, and 0 < N <= 80, which denotes the value v as v / (10 ** N).)
  • ufixed<M>x<N>fixed<M>x<N>的無符號變量
  • fixedufixed:各自等價于fiexed128x19拂苹,ufixed128x19(不是用來計算函數選擇器)
  • bytes<M>:M位的二進制類型安聘,0 < M <= 32.
  • function:等價于bytes24,一個地址瓢棒,加函數選擇器

下面是(固定大小的)數組類型:

  • <type>[M]:給定類型的固定長度的數組

下面是非固定大小的類型:

  • bytes: 動態(tài)大小的字符串
  • string:動態(tài)大小的unicode字符串浴韭,假設是UTF-8編碼
  • <type>[]:給定類型的動態(tài)長度的數組

類型可以結合為匿名結構體,通過把有限數量的參數用圓括號包圍脯宿,通過逗號隔開:

  • (T1,T2,...,Tn):匿名結構體(有序元組)念颈,由類型T1,...连霉,Tn榴芳,n >= 0

可以組合成結構體有結構體,結構體數組等結構跺撼。

編碼的正式描述(?該章節(jié)尚未理解窟感,翻譯出錯的概率很大,讀者可以直接查看原文)

我們現在開始正式描述編碼歉井,它遵循下面的準則柿祈。如果參數中有嵌套數組,它們非常有用:

屬性:

1. 為了獲取一個值而讀取的次數哩至,差不多是值在參數數組結構里的深度躏嚎。例如,如果要獲取a_i[k][l][r]菩貌,就要讀取4次卢佣。在之前版本的ABI中,最壞的情況下菜谣,讀取次數和動態(tài)參數的總數線性相關珠漂。
2.變量的值或者數組元素不會插入其他值晚缩,而且可以重新定位尾膊。例如,它只使用相關的“addresses”荞彼。

我們區(qū)分了靜態(tài)和動態(tài)類型冈敛。靜態(tài)類型編碼在當前位置。而動態(tài)類型編碼在當前區(qū)塊之后的單獨分配的位置鸣皂,

定義:以下的類型被稱為是“動態(tài)的”:* bytes* string* 對于任意類型T的數組T[] 任意動態(tài)類型T的數組T[k]抓谴,且K > 0* (T1,....,Tk),對于1 <= i <= k,如果Ti都是動態(tài)的暮蹂。

所有其他的類型稱為“靜態(tài)”。

定義:len(a)是任意字符串 a 的字節(jié)數癌压。len(a)的類型假設為uint256仰泻。

我們定義enc,真實的編碼滩届,作為ABI類型的映射值到二進制字符串集侯,所以len(enc(X)),當且僅當X是動態(tài)的時候帜消,依賴于X的值棠枉。

定義:對于任意的ABI的值X,我們遞歸定義enc(X)依賴于X的類型

  • 對于任意k>=0泡挺,任意類型的T1,...Tk
    enc(X) = head(X(1)) ... head(X(k-1)) tail(X(0)) .... tail(X(k-1))
    其中X(i)是組件的第i個值辈讶,head和tail定義為Ti的靜態(tài)類型,為
    head(X(i)) = enc(X(i)) and tail(X(i)) = “” (空字符串)
    并且
    head(X(i)) = enc(len(head(X(0)) ... head(X(k-1)) tail(X(0)) ... tail(X(i-1)))) tail(X(i)) = enc(X(i))
    另外一種情況娄猫,例如 如果Ti是一個動態(tài)類型贱除。
    注意,在動態(tài)的情況下媳溺,head(X(i))是很清晰勘伺,因為頭部的長度只依賴于類型而不是值。值是tail(X(i))起始的偏移相對于enc(X)的起始褂删。
  • 對于任意的T和k飞醉,T[k]
    enc(X) = enc((X[0],...,X[k-1]))
    例如,他會被當做具有相同類型的屯阀,k個元素的匿名數組
  • T[]缅帘,X有k個元素(k假設為uint256的類型):
    enc(X) = enc(k) enc([X[1],...,X[k]])
    例如,它會被當做靜態(tài)大小的數組來編碼
  • k長度(被假設為uint256)的bytes
    enc(X) = enc(k) pad_right(X),例如难衰,bytes的數量被編碼為a
    uint256 繼承了真實的值X作為byte序列钦无,繼承了最小數量的零字節(jié),因此len(enc(X))是32的倍數盖袭。
  • string:enc(X)=enc(enc_utf8(X))失暂,例如X是utf-8編碼,值是bytes類型鳄虱,并且更進一步的編碼弟塞。注意,子字符串編碼的長度是utf-8編碼的bytes數拙已,而不是字符數决记。
  • uint<M>:enc(X)是大端編碼的X,對于負數左側填充0xff倍踪,正數左側填充0系宫,最后長度為32的倍數
  • address: 與uint160一致
  • int<M>: enc(X)是x的大端2進制補碼編碼索昂,對于負數左側填充0xff,正數左側填充0扩借,最后長度為32的倍數
  • bool:與uint8一致椒惨,1為true,0為false潮罪。
  • fixed<M>x<N>:enc(X) 是 enc(X * 10N)框产,其中X * 10N 是int256的解釋
  • fixed:和fixed128x19一致
  • unfixed<M>x<N>: enc(X)和enc(X * 10 ** N)一直,其中 X * 10 ** N是uint256的解釋
  • ufixed: 和ufixed128x19一樣
  • bytes<M>: enc(X)是一系列的字節(jié)X错洁,通過零字節(jié)填充的32長度的序列秉宿。

注意,對于任意X屯碴,len(enc(x))都是32的整數倍描睦。

函數選擇器和參數編碼

總之,調用f函數导而,并傳遞a_1,...,a_n參數會被編碼為

function_selector(f) enc((a_1,...,a_n))

并且f的返回值v_1,...,v_k會被編碼為

enc((v_1,...v_k))

例如忱叭,返回值會組合為一個數組并且編碼。

例子

給定的合約如下所示:

pragma solidity ^0.4.0;

contract Foo {
  function bar(bytes3[2] xy) {}
  function baz(uint32 x, bool y) returns (bool r) { r = x > 32 || y; }
  function sam(bytes name, bool z, uint[] data) {}
}

因此今艺,對于Foo這個例子韵丑,如果我們想要調用baz,參數為69true虚缎。我們就得傳遞總共68字節(jié)撵彻,可以分解為:

  • 0xcdcd77c0:方法的ID,它來自于ASCII編碼的baz(uint32,bool)的keccak哈希的前4個字節(jié)实牡。
  • 0x0000000000000000000000000000000000000000000000000000000000000045陌僵,第一個參數,類型為uint32创坞,值為69碗短。
  • 0x0000000000000000000000000000000000000000000000000000000000000001,第二個字節(jié),布爾量true题涨,填充為32位偎谁。

合并為

0xcdcd77c000000000000000000000000000000000000000000000000000000000000000450000000000000000000000000000000000000000000000000000000000000001

返回值為一個布爾量,例如纲堵,如果返回false巡雨,它的輸出為單個字節(jié)數組
,0x0000000000000000000000000000000000000000000000000000000000000000婉支,一個布爾量鸯隅。

如果我們想要調用bar,參數為["abc","def"]向挖,我們就要傳遞總共68個字節(jié)蝌以,可以分解為

  • 0xfce353f6:方法ID。它來自于對bar(bytes3[2])的簽名何之。
  • 0x6162630000000000000000000000000000000000000000000000000000000000
  • 0x6465660000000000000000000000000000000000000000000000000000000000

合并為

0xfce353f661626300000000000000000000000000000000000000000000000000000000006465660000000000000000000000000000000000000000000000000000000000

如果我們想要調用sam跟畅,參數為"dave",true和[1,2,3]溶推。我們總共會傳遞292個字節(jié)徊件,可以分解為:

  • 0xa5643bf2:方法ID,來自于sam(bytes,bool,uint256[])的簽名蒜危,注意uint被替換為它的同義表述虱痕,uint256
  • 0x0000000000000000000000000000000000000000000000000000000000000060:第一個個參數(動態(tài)類型)的偏移位置,單位為bytes辐赞,從參數塊的起始開始部翘。這個例子為,0x60响委。
  • 0x0000000000000000000000000000000000000000000000000000000000000001:第二個參數新思,布爾量true。
  • 0x00000000000000000000000000000000000000000000000000000000000000a0:第三個參數(動態(tài)類型)的偏移位置赘风,單位為bytes夹囚。這個例子為,0xa0邀窃。
  • 0x0000000000000000000000000000000000000000000000000000000000000004:第一個參數數據的一部分荸哟,它代表字節(jié)數組的長度。 這個例子是4.
  • 0x6461766500000000000000000000000000000000000000000000000000000000:第一個參數的內容:“dave”的UTF-8(這個例子等價于ASCII)編碼瞬捕,用0填充為32字節(jié)敲茄。
  • 0x0000000000000000000000000000000000000000000000000000000000000003:第三個元素的數據的一部分,它代表數組長度的山析。這個例子為堰燎,3.
  • 0x0000000000000000000000000000000000000000000000000000000000000001:第三個參數的第一個元素
  • 0x0000000000000000000000000000000000000000000000000000000000000002:第三個參數的第二個元素
  • 0x0000000000000000000000000000000000000000000000000000000000000003:第三個參數的第三個元素

合并為:

0xa5643bf20000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000000464617665000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000003

動態(tài)類型的使用

調用一個函數,f(uint,uint32[],bytes10,bytes)笋轨,參數為(0x123,[0x456,0x789],"1234567890","Hello,world!")秆剪,會通過下面的方式編碼:

我們取sha3(“f(uint256,uint32[],bytes10,bytes)”)的前四個字節(jié),例如0x8be65246爵政。然后我們編碼四個參數的頭部仅讽。對于靜態(tài)類型的uint256bytes10,直接傳遞他們的值钾挟。但是對于動態(tài)類型uint32[]bytes10洁灵,我們使用他們在數據區(qū)域的偏移,測量值編碼的起始位置(例如,不包含函數簽名hash的前四個字節(jié))徽千。它們是:

  • 0x0000000000000000000000000000000000000000000000000000000000000123:(0x123擴展為32字節(jié))
  • 0x0000000000000000000000000000000000000000000000000000000000000080:(第二個參數在數據區(qū)域的偏移苫费,4*32bytes,其實是頭部的大小)
  • 0x3132333435363738393000000000000000000000000000000000000000000000:("1234567890"右填充為32個字節(jié))
  • 0x00000000000000000000000000000000000000000000000000000000000000e0:(第四個參數在數據區(qū)域的偏移 = 第一個動態(tài)參數的數據的偏移 + 第一個動態(tài)參數的數據的大小 = 4*32 + 3 * 32)

這之后双抽,后面緊跟第一個動態(tài)參數的數據部分百框,[0x456,0x789]:

  • 0x0000000000000000000000000000000000000000000000000000000000000002:(數組的長度,2)
  • 0x0000000000000000000000000000000000000000000000000000000000000456:(第一個參數)
  • 0x0000000000000000000000000000000000000000000000000000000000000789:(第二個參數)

最后我們會編碼第二個動態(tài)參數的數據部分牍汹,”Hello world铐维!“

  • 0x000000000000000000000000000000000000000000000000000000000000000d:(元素的數量(這個例子為bytes):13)
  • 0x48656c6c6f2c20776f726c642100000000000000000000000000000000000000:(”Hello,world!“右填充為32個字節(jié))

合而為一,編碼如下(函數選擇器之后要換行慎菲,為了清晰嫁蛇,每行32字節(jié)):

0x8be65246
  0000000000000000000000000000000000000000000000000000000000000123
  0000000000000000000000000000000000000000000000000000000000000080
  3132333435363738393000000000000000000000000000000000000000000000
  00000000000000000000000000000000000000000000000000000000000000e0
  0000000000000000000000000000000000000000000000000000000000000002
  0000000000000000000000000000000000000000000000000000000000000456
  0000000000000000000000000000000000000000000000000000000000000789
  000000000000000000000000000000000000000000000000000000000000000d
  48656c6c6f2c20776f726c642100000000000000000000000000000000000000

事件

事件是以太坊日志/事件監(jiān)聽協(xié)議的抽象。日志實體提供了合約的地址露该,一系列但最多4個主題和任意長度的二進制數據睬棚。事件整合已存在的ABI,將他(和接口描述一起)翻譯為合適類型的結構體有决。(闸拿?Events leverage the existing function ABI in order to interpret this (together with an interface spec) as a properly typed structure.)

給定一個事件名稱和一系列的事件參數,我們可以將它分為兩個子系列:被索引的和沒有被索引的书幕。被索引的新荤,最多3個,通過事件簽名的Keccak哈希來組成日志實體的主題台汇。另外沒有索引的組成事件的byte數組苛骨。

實際上,使用ABI的日志實體被描述為:

  • address:合約的地址(以太坊本身支持)
  • topics[0]: keccak(EVENT_NAME+”(“+EVENT_ARGS.map(canonical_type_of).join(”,”)+”)”) (canonical_type_of 是一個函數苟呐,簡單的根據參數返回同義類型痒芝。例如,對于uint索引的foo牵素,它會返回 uint256严衬。)如果事件被描述為anonymoustopics[0]不會生成笆呆。
  • topics[n]: EVENT_INDEXED_ARGS[n - 1](EVENT_INDEXED_ARGS是一系列被索引的EVENT_ARGS)
  • data:abi_serialise(EVENT_NON_INDEXED_ARGS) (EVENT_NON_INDEXED_ARGS 是一系列沒有被索引的EVENT_ARGS请琳,abi_serialise是ABI序列化函數,用來返回一系列類型的函數返回赠幕,如上面所述)

JSON

合約接口的JSON形式通過一個函數數組 和/或 事件描述 給定俄精。函數描述是一個JSON對象,具有如下的字段:

  • type: "function","construct"或者"fallback"(沒有名字的默認函數)

  • name: 函數的名稱

  • inputs: 一個對象數組榕堰,每個對象包含:

    • name:參數名稱
    • type:參數類型的同義類型
  • outputs: 像inputs一樣的數組對象竖慧,如果沒有返回值,可以刪除這個字段

  • constant: 如果函數聲明為不改變區(qū)塊鏈狀態(tài)為true

  • payable: 如果函數接收以太幣,則為true圾旨,默認為false踱讨。

type可以刪除,默認為"function".

構造函數和fallback函數沒有名稱或者輸出碳胳。fallback函數也沒有輸入勇蝙。

發(fā)送非零個以太幣到非payable的函數會有異常沫勿,不要這么干挨约。

一個事件的描述,是一個差不多字段的JSON對象产雹。

  • type: 總是”event“

  • name: 事件的名稱

  • inputs: 一個對象數組诫惭,每個對象包含

    • name:參數名稱
    • type:參數類型的同義類型
    • indexed:如果字段是日志的topic,則為true蔓挖,如果是日志的數據部分夕土,則為false
  • anonymous: 如果事件被聲明為anonymous,則為true瘟判。

例如怨绣,

pragma solidity ^0.4.0;

contract Test {
  function Test(){ b = 0x12345678901234567890123456789012; }
  event Event(uint indexed a, bytes32 b)
  event Event2(uint indexed a, bytes32 b)
  function foo(uint a) { Event(a, b); }
  bytes32 b;
}

JSON形式為:

[{
"type":"event",
"inputs": [{"name":"a","type":"uint256","indexed":true},{"name":"b","type":"bytes32","indexed":false}],
"name":"Event"
}, {
"type":"event",
"inputs": [{"name":"a","type":"uint256","indexed":true},{"name":"b","type":"bytes32","indexed":false}],
"name":"Event2"
}, {
"type":"function",
"inputs": [{"name":"a","type":"uint256"}],
"name":"foo",
"outputs": []
}]
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市拷获,隨后出現的幾起案子篮撑,更是在濱河造成了極大的恐慌,老刑警劉巖匆瓜,帶你破解...
    沈念sama閱讀 222,627評論 6 517
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件赢笨,死亡現場離奇詭異,居然都是意外死亡驮吱,警方通過查閱死者的電腦和手機茧妒,發(fā)現死者居然都...
    沈念sama閱讀 95,180評論 3 399
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來左冬,“玉大人桐筏,你說我怎么就攤上這事∧磁椋” “怎么了梅忌?”我有些...
    開封第一講書人閱讀 169,346評論 0 362
  • 文/不壞的土叔 我叫張陵,是天一觀的道長毕匀。 經常有香客問我铸鹰,道長,這世上最難降的妖魔是什么皂岔? 我笑而不...
    開封第一講書人閱讀 60,097評論 1 300
  • 正文 為了忘掉前任蹋笼,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘剖毯。我一直安慰自己圾笨,他們只是感情好,可當我...
    茶點故事閱讀 69,100評論 6 398
  • 文/花漫 我一把揭開白布逊谋。 她就那樣靜靜地躺著擂达,像睡著了一般。 火紅的嫁衣襯著肌膚如雪胶滋。 梳的紋絲不亂的頭發(fā)上板鬓,一...
    開封第一講書人閱讀 52,696評論 1 312
  • 那天,我揣著相機與錄音究恤,去河邊找鬼俭令。 笑死,一個胖子當著我的面吹牛部宿,可吹牛的內容都是我干的抄腔。 我是一名探鬼主播,決...
    沈念sama閱讀 41,165評論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼理张,長吁一口氣:“原來是場噩夢啊……” “哼赫蛇!你這毒婦竟也來了?” 一聲冷哼從身側響起雾叭,我...
    開封第一講書人閱讀 40,108評論 0 277
  • 序言:老撾萬榮一對情侶失蹤悟耘,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后拷况,有當地人在樹林里發(fā)現了一具尸體作煌,經...
    沈念sama閱讀 46,646評論 1 319
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 38,709評論 3 342
  • 正文 我和宋清朗相戀三年赚瘦,在試婚紗的時候發(fā)現自己被綠了粟誓。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,861評論 1 353
  • 序言:一個原本活蹦亂跳的男人離奇死亡起意,死狀恐怖鹰服,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情揽咕,我是刑警寧澤悲酷,帶...
    沈念sama閱讀 36,527評論 5 351
  • 正文 年R本政府宣布,位于F島的核電站亲善,受9級特大地震影響设易,放射性物質發(fā)生泄漏。R本人自食惡果不足惜蛹头,卻給世界環(huán)境...
    茶點故事閱讀 42,196評論 3 336
  • 文/蒙蒙 一顿肺、第九天 我趴在偏房一處隱蔽的房頂上張望戏溺。 院中可真熱鬧,春花似錦屠尊、人聲如沸旷祸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,698評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽托享。三九已至,卻和暖如春浸赫,著一層夾襖步出監(jiān)牢的瞬間闰围,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,804評論 1 274
  • 我被黑心中介騙來泰國打工掺炭, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留辫诅,地道東北人凭戴。 一個月前我還...
    沈念sama閱讀 49,287評論 3 379
  • 正文 我出身青樓涧狮,卻偏偏與公主長得像,于是被迫代替她去往敵國和親么夫。 傳聞我的和親對象是個殘疾皇子者冤,可洞房花燭夜當晚...
    茶點故事閱讀 45,860評論 2 361

推薦閱讀更多精彩內容

  • 第5章 引用類型(返回首頁) 本章內容 使用對象 創(chuàng)建并操作數組 理解基本的JavaScript類型 使用基本類型...
    大學一百閱讀 3,238評論 0 4
  • 一涉枫、基本數據類型 注釋 單行注釋:// 區(qū)域注釋:/* */ 文檔注釋:/** */ 數值 對于byte類型而言...
    龍貓小爺閱讀 4,268評論 0 16
  • 我是不是更像一個秘書,和彥哥語音腐螟,感覺他流露出對我前途的惋惜愿汰。他問,我已經有多久沒有寫trans了
    木同之閱讀 121評論 0 0
  • 我不喜歡把一輩子都給定格掉乐纸,但人不是有好幾個“一輩子”嘛衬廷,七年就是一輩子,對于接下來七年的這輩子的我來說最重要的三...
    米斯特左腦閱讀 766評論 7 4
  • 01 有人說跌宛,他能看到我的未來。我懷著好奇心积仗,問他:“那你幫我算一卦吧疆拘!我想知道我的未來是什么樣〖挪埽”對話框那邊停留...
    魅格體閱讀 1,206評論 0 2