智能合約基本上由狀態變量和函數組成。有些函數是私有的,只能從合約內部訪問,但是許多函數是公共的,可以從合約外部訪問。也就是說,應用程序(和人)可以向合約發送數據並從合約中檢索數據。
要將數據發送到合約,我們需要以合約可以讀取的方式發送數據。也就是說,它們需要被編碼。如何執行這種編碼的規則由以太坊虛擬機(EVM) 的實現定義。可以在 此處找到規範的詳細信息。
在本文中,我們將學習一些關於編碼規則的知識,以及如何使用solidity 對必須作為函數參數發送的數據進行編碼和解碼。
使用abi.encode() 對函數的參數進行編碼
Solidity 有一個名為abi的全局變量 ,它有一個encode 方法,所以我們可以用它來編碼任何函數的參數。讓我們從一個簡單的例子開始。假設我們有以下功能
function myFunction(address _myAddress, uint _myNumber)…
我們感興趣的只是對函數的參數進行編碼,即地址和整數。我們可以使用remix 來創建一個執行此操作的函數。
部署此合約,並使用以下地址和無符號整數值調用函數 encode(…) :(0x5B38Da6a701c568545dCfcB03FcB875f56beddC4, 127),我們得到結果
0x0000000000000000000000005b38da6a701c568545dcfcb03fcb875f56beddc4000000000000000000000000000000000000000000000000000000000000007f
對結果的快速分析表明它有64 個字節。這是因為編碼發生在32 字節的倍數中:
0000000000000000000000005b38da6a701c568545dcfcb03fcb875f56beddc4000000000000000000000000000000000000000000000000000000000000007f
前32 個字節包含地址(20 個字節),其他32 個字節包含整數7f。編碼始終為十六進制,十六進制中的7f 為127。
使用abi.decode() 解碼函數的參數
現在讓我們使用solidity 來解碼函數的參數。請注意,沒有必要識別我們正在處理的函數,因為它的簽名將在編碼參數之前。為了讓問題稍微複雜一點,讓我們使用動態變量作為參數。
讓我們使用以下合約來解碼一組值:字符串、uint 和字符串。
contract Encode {function encode(string memory _string1, uint _uint, string memory _string2) public pure returns (bytes memory) { return (abi.encode(_string1, _uint, _string2)); }function decode(bytes memory data) public pure returns (string memory _str1, uint _number, string memory _str2) { (_str1, _number, _str2) = abi.decode(data, (string, uint, string)); }}
部署,然後使用以下參數(João,3,Paulo)調用函數 encode(…) ,我們得到以下返回:
0x0000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000054a6fc3a36f00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000055061756c6f000000000000000000000000000000000000000000000000000000
也許你可能期望返回是96 字節(3 x 32),因為我們有3 個變量。但是3個變量中有2個是動態的,動態變量的編碼就沒那麼簡單了。讓我把上面的值分解成32 字節的數據塊。
0000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000054a6fc3a36f00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000055061756c6f000000000000000000000000000000000000000000000000000000
我將快速解釋在字符串的情況下如何進行編碼。第一行指代第一個變量,第二行指代第二個變量,第三行指代第三個變量。由於第二個變量是value 類型,我們可以 在第二行直接檢索它的值3 。
在變量類型 string的情況下,第一行中包含的數據是關於第一個字符串的信息。在此示例中,數據為 十六進制的60 ,即十進制的 96 。但是這是什麼意思?這意味著關於第一個字符串的信息是在數據開頭的96個字節之後找到的。
在92 字節之後,32 字節的塊有一個數字:5。這是字符串佔用的字節數,從下一行開始,utf-8 編碼: 4a6fc3a36f。 從十六進制轉換為UTF-8,我們檢索到單詞“João”(巧合的是我的名字)。
按照相同的模式,可以在a0 字節之後檢索第三個字符串,即從數據開始的160 字節之後。它說它也有5 個字節,它的值是 5061756c6f, 是’Paulo’ 的UTF-8 編碼(也是我的名字,有多大的機率)。
現在你已經了解瞭如何手動執行此操作,讓我們看看如何通過solidity 執行此操作。使用我們在合約中編寫的函數 decode(bytes memory data) ,可以檢索編碼信息:
其他庫也可用於編碼和解碼變量,例如 web3.eth.abi。目前還有一個可以用於此目的的在線網站 abi.hashex.org/。