科普 | 為什麼要使用 transaction data?

可能你也注意到了,在跟智能合約交互(例如發送 token)時,你的事務會自動包含 input data(“輸入數據”)。在 MyCrypto 錢包界面,這些數據有個簡單的標籤:“Data(數據)” —— 它是做什麼的呢?

這篇文章就是從技術上解釋事務輸入數據是怎麼一回事,它實質是什麼,又是怎麼工作的。

科普 | 為什麼要使用 transaction 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

科普 | 為什麼要使用 transaction data?

– Input Data 分解 –

如果你在 Etherscan 上查看這些數據,你會看到它以下文這個形式呈現:

Function: transfer(address _to, uint256 _value)

MethodID: 0xa9059cbb

[0]: 0000000000000000000000004bbeeb066ed09b7aed07bf39eee0460dfa261520

[1]: 00000000000000000000000000000000000000000000000002a34892d36d6c74

十六進制是啥?

十六進制是一種計數系統,就像十進制和二進制一樣;十六進制使用數字 0 到 9 和字母 A 到 F(不區分大小寫),來對應表示十進制的 0 到 15。下面這種圖展現的就是這樣的對應關係。十六進制常常用來更直觀地表示大數字。

科普 | 為什麼要使用 transaction data?

– 十進制數字與對應的十六進制字符 –

單個十六進制字符所能表示的最大數值是 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 字節之後開始。

000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000003

科普 | 為什麼要使用 transaction data?

– 例子:input 數據要按照 32 字節一組來切分 –

因為字符串的長度是任意的(可能長過 32 字節),它們要按 32 字節一組來切分,處理方式跟數組相同。

像 Etherscan 這樣的網站是如何解碼 input data 的?

哈希函數是單向函數,所以如果你只有函數簽名的哈希值,是不可能會恢復出函數簽名的(你要試試暴力破解嗎老弟)。合約的所有者可以將合約的 ABI 作為 JSON 文件上傳,就像這個例子,這可以用來拿到函數簽名的哈希值。

即使合約的所有者不上傳合約的 ABI,也能夠解碼 input 數據(對大多數合約而言)。因為,ERC-20 合約函數的簽名都是一樣的,因此 Etherscan 只需使用一個預定義的合約 ABI 即可服務大部分合約。舉個例子,ERC 20 合約的轉賬函數的合約 ABI 如下文所示

科普 | 為什麼要使用 transaction data?

如果輸入數據里的簽名與任意一個預定義的函數相匹配,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。

科普 | 為什麼要使用 transaction data?

– 特定區塊的 Gas 上限 –

只需將鼠標停留在 “Gas Limit” 部分的某個區塊上,就可以看到其 Gas 上限。

更多信息

  • 合約 ABI 規範
  • ERC-20 Token 標準
  • 以太坊虛擬機

參考

  • 以太坊黃皮書
  • Solidity 文檔

(完)

(文內有許多超鏈接,可點擊左下 ”閱讀原文“ 從 EthFans 網站上獲取)

原文鏈接:

https://blog.mycrypto.com/why-do-we-need-transaction-data-/

作者: Maarten Zuidhoorn

翻譯: 阿劍

本文鏈接:https://www.8btc.com/article/6633644

轉載請註明文章出處

(0)
上一篇 2021-05-13 18:21
下一篇 2021-05-13 18:30

相关推荐