1. 程式人生 > >以太坊:呼叫已部署的智慧合約

以太坊:呼叫已部署的智慧合約

轉載請註明出處:https://www.cnblogs.com/zhizaixingzou/p/10122350.html

 

目錄

 

 

1. 呼叫已部署的智慧合約

相關截圖來自:

https://solidity-cn.readthedocs.io/zh/develop/abi-spec.html

https://github.com/ethereum/ethereumj

1.1. ABIpayload編碼的依據

ABI,應用二進位制介面(Application Binary Interface)。它是從區塊鏈外部與合約進行互動以及合約與合約間進行互動的一種標準方式。

根據它可以得到函式簽名編碼同樣根據它以及實際引數得到引數編碼,兩部分的位元組序列拼接而得payload

1.2. 函式簽名編碼

函式簽名編碼又叫函式選擇器函式簽名的 KeccakSHA-3)雜湊的前 4 位元組。函式簽名:函式名稱加上由括號括起來的引數型別列表,引數型別間由一個逗號分隔開,且沒有空格。函式的返回型別並不是這個簽名的一部分。

1.3. 引數編碼

引數型別可以分為:

基礎型別,如uint<M>int<M>addressuintintboolfixed<M>x<N>ufixed<M>x<N>

fixedufixedbytes<M>function

定長陣列型別:<type>[M]

非定長陣列型別:bytesstringUTF-8)、<type>[]

 

以下型別被稱為動態

l bytes

l string

任意型別 T 的變長陣列 T[]

任意動態型別 T 的定長陣列 T[k] k >= 0

由動態的 Ti 1 <= i <= k)構成的元組 (T1,...,Tk)

所有其他型別都被稱為

靜態

 

不同型別的引數編碼方式不一樣,下面以實際例子講解。

1.4. payload編碼原理

合約如下:

pragma solidity ^0.4.16;

 

contract Foo {

  function baz(uint32 x, bool y) public pure returns (bool r) { r = x > 32 || y; }

  function bar(bytes3[2]) public pure {}

  function sam(bytes, bool, uint[]) public pure {}

}

 

想用 69 true 做引數呼叫 baz,我們總共需要傳送 68 位元組,可以分解為:

l 0xcdcd77c0:方法ID。這源自ASCII格式的 baz(uint32,bool) 簽名的 Keccak 雜湊的前 4 位元組。

l 0x0000000000000000000000000000000000000000000000000000000000000045:第一個引數,一個被用 0 值位元組補充到 32 位元組的 uint32 69

l 0x0000000000000000000000000000000000000000000000000000000000000001:第二個引數,一個被用 0 值位元組補充到 32 位元組的 boolean true

合起來就是:

0xcdcd77c000000000000000000000000000000000000000000000000000000000000000450000000000000000000000000000000000000000000000000000000000000001

如它返回 false,那麼它的輸出將是一個位元組陣列,一個bool

0x0000000000000000000000000000000000000000000000000000000000000000

 

想用 ["abc", "def"] 做引數呼叫 bar,我們總共需要傳送68位元組,可以分解為:

l 0xfce353f6:方法ID。源自 bar(bytes3[2]) 的簽名。

l 0x6162630000000000000000000000000000000000000000000000000000000000:第一個引數的第一部分,一個 bytes3 "abc" (左對齊)。

l 0x6465660000000000000000000000000000000000000000000000000000000000:第一個引數的第二部分,一個 bytes3 "def" (左對齊)。

合起來就是:

0xfce353f661626300000000000000000000000000000000000000000000000000000000006465660000000000000000000000000000000000000000000000000000000000

 

想用 "dave"true [1,2,3] 作為引數呼叫 sam,我們總共需要傳送 292 位元組,可以分解為:

l 0xa5643bf2:方法ID。源自 sam(bytes,bool,uint256[]) 的簽名。注意,uint 被替換為了它的權威代表 uint256

l 0x0000000000000000000000000000000000000000000000000000000000000060:第一個引數(動態型別)的資料部分的位置,即從引數編碼塊開始位置算起的位元組數。在這裡,是 0x60

l 0x0000000000000000000000000000000000000000000000000000000000000001:第二個引數:boolean true

l 0x00000000000000000000000000000000000000000000000000000000000000a0:第三個引數(動態型別)的資料部分的位置,由位元組數計量。在這裡,是 0xa0

l 0x0000000000000000000000000000000000000000000000000000000000000004:第一個引數的資料部分,以位元組陣列的元素個數作為開始,在這裡,是 4

l 0x6461766500000000000000000000000000000000000000000000000000000000:第一個引數的內容:"dave" UTF-8 編碼(在這裡等同於 ASCII 編碼),並在右側(低位)用 0 值位元組補充到 32 位元組。

l 0x0000000000000000000000000000000000000000000000000000000000000003:第三個引數的資料部分,以陣列的元素個數作為開始,在這裡,是 3

l 0x0000000000000000000000000000000000000000000000000000000000000001:第三個引數的第一個陣列元素。

l 0x0000000000000000000000000000000000000000000000000000000000000002:第三個引數的第二個陣列元素。

l 0x0000000000000000000000000000000000000000000000000000000000000003:第三個引數的第三個陣列元素。

合起來就是:

0xa5643bf20000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000000464617665000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000003

 

實際上,函式返回值和事件的引數也會被用同樣的方式進行編碼

1.5. ABIJSON表示

一個描述函式的ABIJSON表示含如下構成:

 

當原始碼被編譯後,就能得到函式的ABI,如Remix上的情況如下:

1.6. EthereumJ內建的payload編碼工具

JavaEthereum已經實現了編碼功能的內建

可以看到,內建的功能可以直接輸入ABIJSON表示,然後給出實際的引數就可以得到整個的payload

1.7. 合約執行過程及結果解析

合約執行過程分析:

如果data小於4位元組,則返回程式結果Return為空,並置為REVERT

載入data,取出前4位元組,與合約的所有方法的4位元組碼編碼對比,匹配上則跳轉到指定方法的入口並執行然後執行,如果有返回值,則得到後放到程式結果的Return中。

 

Abi.FunctiondecodeResult方法就可以解析返回的Return資料,得到的是List<?>

1.8. 參考資料

https://solidity-cn.readthedocs.io/zh/develop/abi-spec.html

https://github.com/ethereum/ethereumj