為什么要使用 transaction data?
可能你也注意到了,在跟智能合約交互(例如發送 token)時,你的事務會自動包含 input data(“輸入數據”)。在 MyCrypto 錢包界面,這些數據有個簡單的標籤:“Data(數據)” —— 它是做什么的呢?
這篇文章就是從技術上解釋事務輸入數據是怎么一回事,它實質是什么,又是怎么工作的。
- MyCrypto 錢包的高級事務設定 -
什么是 Input Data?
我們先來看看這筆 token 轉账交易。某個人發送了 0 ETH 到 0xd26114cd6ee289accf82350c8d8487fedb8a0c07(OmiseGo 合約地址),而且 Etherscan 網站呈現了這是一筆意圖發送 0.19 OMG token 到這個地址的事務。那么,EVM (以太坊虛擬機)究竟是怎么知道,這個人想要轉账某個數額的 token 到另一地址的呢?
你再仔細看 Etherscan,就能看到這筆事務帶着 input data。input data 是發送者為這筆事務附加的額外數據,既可以是普通的文本,也可以是數字(以十六進制的形式編碼)。但在這筆交易中,發送者使用這部分數據來 “告訴” 合約,讓合約運行特定的函數。智能合約本身是由一系列函數組成的。舉例而言,一個 ERC-20 token 合約使用比如 “transfer” 來把 token 從 A 账戶轉移到 B账戶,使用 “balancerOf” 函數來獲得某個地址的余額,等等。在我們研究的這筆交易中,你可以看到它調用了 transfer(address_to, uint256_value)
函數。
這筆事務的輸入數據為0xa9059cbb0000000000000000000000004bbeeb066ed09b7aed07bf39eee0460dfa26152000000000000000000000000000000000000000000000000002a34892d36d6c74
。你可以把這一長串的 十六進制 數據分解一下。开頭的 0x 表示這是一個十六進制數值,緊接着的 8 個字節(a9059cbb)是函數標識符,再然後就全部是以 32 字節(也就是 64 個 16 進制字符)為一組的函數參數。所以第一組是 0000000000000000000000004bbeeb066ed09b7aed07bf39eee0460dfa261520
而第二組是 000000000000000000000000000000000000000000000000002a34892d36d6c74
。
- Input Data 分解 -
如果你在 Etherscan 上查看這些數據,你會看到它以下文這個形式呈現
十六進制是啥?
十六進制是一種計數系統,就像十進制和二進制一樣;十六進制使用數字 0 到 9 和字母 A 到 F(不區分大小寫),來對應表示十進制的 0 到 15。下面這種圖展現的就是這樣的對應關系。十六進制常常用來更直觀地表示大數字。
- 十進制數字與對應的十六進制字符 -
單個十六進制字符所能表示的最大數值是 15,長度是 4 個比特(bit)。多個十六進制字符相連時,你要把每個字符的二進制表示前後拼接在一起,才能得到其十進制數值。舉個例子,0x5C,可以寫成 0101 (=5) 和 1100 (=C),前後拼接就是 01011100,這就是二進制形式的 92,所以十六進制數 0x5C 的數值就是 92。
大多數編程語言都使用前綴 0x 作為絕對標識符(arbitrary identifier),將十六進制數與其他的計數類型(比如普通的十進制、二進制等)區別开來。這個前綴本身沒有任何意義,只是為了清晰。我們這篇文章也會採取一樣的做法,十六進制數都用 0x 开頭。
講完這些,我們繼續。如果你還是沒能理解十六進制,也不用擔心 —— 對於理解 input data 來說不是必需的。
Input Data 與智能合約
Input Data 的首要用途就是與智能合約交互。大部分智能合約都使用 合約 ABI 規範,使得 Etherscan 這樣的網站能自動解碼 input data 並顯示事務所調用的具體操作。在我們上面那個例子中,這是一筆有關代幣合約的事務,而且代幣合約遵循 ERC-20 標准。這也就意味着,我們都知曉所有可能調用的函數,以及它們的 籤名。舉例,用於 ERC-20 合約的 transfer(轉账)函數的完整籤名總是 transfer(address, uint256)
,意味着這個函數需要兩個參數,所傳入的第一個參數會被解讀為一個地址,第二個參數會被解讀為一個未籤名的 256 位的數字(大小上限為 2256-1)。
Solidity 語言有多種參數類型。如果你有興趣學習 Solidity 語言和智能合約,你可以在Solidity 文檔頁面了解更多。
函數籤名
如你所見,transfer 函數的籤名是 transfer(address, uint256)
,這個對所有 ERC-20 合約都是一樣的。如果某個合約給轉账函數安排不一樣的參數類型,比如一個地址和一個 uint128(未籤名的 128 位整數),這個合約就不是 “ERC-20 兼容” 的。
要獲得一個函數的籤名的十六進制形式,我們先要獲得這個函數的 SHA-3(或者說 Keccak-256)哈希值的前面 4 個字節(也就是 8 個十六進制字符)。而要想知道一個數據的 Keccak-256 哈希值,你可以使用 JavaSceript 語言的 web3 庫,或者求助於這樣的在线工具。在這個工具頁面填入 transfer(address,uint256)
,它會顯示 0xa9059cbb2ab09eb219583f4a59a5d0623ade346d962bcd4e46b11da047c9049b
作為結果。取前 8 個字符(忽略掉 0x),就是 a9059cbb
,恰好跟上述事務的 MethodID 一致。
另一個例子:ERC-20 標准合約的 approve(許可)函數的函數籤名是 approve(address,uint256)
,其 SHA-3 哈希值是 0x095ea7b334ae44009aa867bfb386f5c3b4b443ac6f0ee573fa91c4608fbadfba
,首 8 個字符是 095ea7b3
,因此,調用許可函數的 input data 开頭就會是 0x095ea7b3。這筆發往 DAI token 合約的事務就是如此。
地址和數量
每一個參數(除了 列表/數組 和純文本 —— 這些我們後文再說)的長度都是 32 字節,或者說 64 個十六進制字符。但以太坊地址只有 40 個字節長(不算 0x 的話)。為了解決這個問題,地址參數要用 0 來填充。在十六進制裏面,0x0000123 和 0x123 是一樣的,因此 0x0000000000000000000000004bbeeb066ed09b7aed07bf39eee0460dfa261520
(上述事務中的地址參數)等同於 0x4bbeeb066ed09b7aed07bf39eee0460dfa261520
,而且 0x00000000000000000000000000000000000000000000000002a34892d36d6c74
也就等於 0x2a34892d36d6c74
。那為什么我們要填充這些 0 呢?
就像我們上面說到的,Solidity 合約可以接受的最大數值是 2256 - 1,剛好是 32 字節。使用固定的長度可以讓 EVM 和其他應用在解碼數據時候更輕松,因為你可以假設每一個參數的長度都是一樣的。
那數組和字符串呢?
如上所述,在 input data 中使用數組和字符串,情形會有些許不同。因為數組本質是多個東西組成的一個列表。舉個例子,1、2、3 三個數所組成的列表在大多數編程語言中都可以寫為 [1, 2, 3]。要在事務中發送這種數據,列表中的每一個對象都要作為 32 字節一組的數據發送,列在 input data 的結尾。指明數組長度的指針就作為參數。
假定我們有一個叫做 calledmyFunction
的函數,接收一個地址和數字的數組作為參數,即 myFunction(address,uint256[])
。該函數的函數籤名是 0x4b294170。地址這一項,我們照上面所說的操作。因為我們的數組包含 3 個對象,數組的長度用十六進制表示為 0x3。然後每個對象都要佔據恰好 32 自己的空間,且數組要放在所有其它參數之後,所以數組會從 32+32 = 64 字節之後开始。
- 例子:input 數據要按照 32 字節一組來切分 -
因為字符串的長度是任意的(可能長過 32 字節),它們要按 32 字節一組來切分,處理方式跟數組相同。
像 Etherscan 這樣的網站是如何解碼 input data 的?
哈希函數是單向函數,所以如果你只有函數籤名的哈希值,是不可能會恢復出函數籤名的(你要試試暴力破解嗎老弟)。合約的所有者可以將合約的 ABI 作為 JSON 文件上傳,就像這個例子,這可以用來拿到函數籤名的哈希值。
即使合約的所有者不上傳合約的 ABI,也能夠解碼 input 數據(對大多數合約而言)。因為,ERC-20 合約函數的籤名都是一樣的,因此 Etherscan 只需使用一個預定義的合約 ABI 即可服務大部分合約。舉個例子,ERC 20 合約的轉账函數的合約 ABI 如下文所示:
如果輸入數據裏的籤名與任意一個預定義的函數相匹配,Etherscan 都能解碼 input data。
input data 的大小有沒有什么限制?
既有,也沒有。以太坊協議沒有為 input data 的長度設固定的上限,但 input data 也消耗 gas。單個區塊可用的 Gas 數量是有上限的,在本文撰寫時是 800 萬(譯者注:原文撰寫於 2019 年 2 月,在 2021 年 4 月,已經上升到 1500 萬)。每一個 0 字節(0x00)都要消耗 4 gas,而非零的字節要消耗 68 gas。一筆標准的 ETH 轉账事務要消耗 21000 單位 gas,所以,如果不考慮調用合約的交易,當前 input data 的最大長度是 2 MB(全部由 0 組成),或者全部用非零字節的話,就是 0.12 MB。因為 input data 不會只有零,也不會一個 0 也沒有,所以實際的大小會在兩者之間。
如果你想看實時的 區塊 Gas 上限,可以看 ETHStats.net。
- 特定區塊的 Gas 上限 -
只需將鼠標停留在 “Gas Limit” 部分的某個區塊上,就可以看到其 Gas 上限。
更多信息
合約 ABI 規範
ERC-20 Token 標准
以太坊虛擬機
參考
以太坊黃皮書
Solidity 文檔
原文鏈接:
https://blog.mycrypto.com/why-do-we-need-transaction-data-/
作者: Maarten Zuidhoorn
翻譯: 阿劍
鄭重聲明:本文版權歸原作者所有,轉載文章僅為傳播信息之目的,不構成任何投資建議,如有侵權行為,請第一時間聯絡我們修改或刪除,多謝。
7月23:Mt. Gox 比特幣錢包在市場緊縮的情況下轉移了價值 28.2 億美元的 BTC
7月23:Mt. Gox 比特幣錢包在市場緊縮的情況下轉移了價值 28.2 億美元的 BTC一個引...
悅盈:比特幣68000的空完美落地反彈繼續看跌 以太坊破前高看回撤
一個人的自律中,藏着無限的可能性,你自律的程度,決定着你人生的高度。 人生沒有近路可走,但你走的每...