Ethereum - 利用 web3j 開發以太坊相關應用教學

在開始介紹之前,建議如果對 Ethereum Blockchain 還不太熟悉的話,可以先去看一下相關文獻。我如果有空在補 Ethereum Blockchain 本身架構的介紹,區塊鏈本身的底層技術其實不是很好理解,現在大部分的區塊鏈應用比較著重在 dapp 的設計,也就是透過 Smart Contract 來建立出一個分散式應用程式,藉此打破以前中心化應用程式的架構,我也是新手,純紀錄分享走過的坑。

介紹 web3j 程式庫

web3j 是由一個 Java 撰寫的程式庫,主要是用來提供處理以太坊智慧合約及以太坊網路上的節點,簡單來說它是一個 Ethereum Client Library,方便透過該程式庫去與以太坊進行溝通。web3j 除了提供原生 Java 的程式庫也提供可在 android 上進行使用。

安裝 web3j CLI

這個是 web3j 提供 CLI 程式庫,這個是方便我們在 CLI 有一些便捷的指令,其中最重要的是可以在 CLI 中將我們 Smart Contract 編譯出來的.abi、.bin 檔案轉成 Java Class 進行對應,這樣當我們操作區塊鏈上的智能合約就能用 Java Class 對應智能合約進行操作。

在安裝之前要先確保電腦上有 JDK Version8 + 環境,若糾結 JDK 環境要安裝哪個可參考:JDK 安裝教學介紹

Linux 安裝方式

輸入以下指令:

1
curl -L get.web3j.io | sh

當安裝完的時候,它會提醒要在輸入以下指令:

1
source $HOME/.web3j/source.sh

意思就是幫你把 web3j CLI 的環境變數放置進去,這樣每次在 CLI 使用的時候就不用重新設定環境變數。

接著就能檢查是否安裝成功:

1
web3j version

會出現 logo 及版本資訊。

Windows 安裝方式

開啟 Windows 內建的 PowerShell,並輸入以下指令:

1
Set-ExecutionPolicy Bypass -Scope Process -Force; iex ((New-Object System.Net.WebClient).DownloadString('https://raw.githubusercontent.com/web3j/web3j-installer/master/installer.ps1'))

當下載完成後就會幫你設定好環境變數,因此打開 windows cmd,輸入以下指令就能檢查是否安裝成功:

1
web3j version

一樣會出現 logo 及版本資訊。

web3j 基本操作介紹

安裝 web3j

首先 web3j 提供兩種方式安裝:

  1. maven

    1
    2
    3
    4
    5
    <dependency>
    <groupId>org.web3j</groupId>
    <artifactId>core</artifactId>
    <version>4.5.12</version>
    </dependency>
  2. gradle

    1
    compile ('org.web3j:core:4.5.12')

這邊的示範採用 Gradle Project~

設定 Ganache 環境

根據我寫的這篇文章內容,先把 Ganache 環境架起來:

Ganache - 快速開發 Ethereum-Blockchain 的工具

這樣的做法因為我們要透過 web3j 連接到 Ganache 架起來的節點。

web3j 連接 Ganache

同步連接

直接上程式碼:

1
2
3
4
5
6
7
8
9
public class Main {
public static void main(String[] args) throws Exception {
// send synchronous requests
Web3j web3 = Web3j.build(new HttpService("http://127.0.0.1:7545"));
Web3ClientVersion web3ClientVersion = web3.web3ClientVersion().send();
String clientVersion = web3ClientVersion.getWeb3ClientVersion();
System.out.println(clientVersion);
}
}

先說當執行這段程式碼,雖然程式可以跑,但是你會發現執行結果出現一個紅色錯誤:

1
2
3
SLF4J: Failed to load class "org.slf4j.impl.StaticLoggerBinder".
SLF4J: Defaulting to no-operation (NOP) logger implementation
SLF4J: See http://www.slf4j.org/codes.html#StaticLoggerBinder for further details.

這個我的理解是 web3j 內建有用到 slf4j 的一個 log library,這個錯誤是在提醒你,該程式庫沒有被實作,因此你要在額外添加一個 library:

1
compile ('org.slf4j:slf4j-nop:1.7.25')

而 maven 就是依照其格式同樣加入該 library 即可。

接著講解上面的程式碼:

  1. 透過 HttpService 實例化我們要連接的網址,這個網址就是對應於 Ganache 上面的 RPC Server 的網址。
  2. 此外,該程式碼是同步化的作法,也就是它會一直 block 住,直到拿到 response,因此會採用 send ()
  3. 這邊就是進行連線然後拿取 web3ClientVersion 而已。

輸出結果:

1
EthereumJS TestRPC/v2.8.0/ethereum-js

實際上這個就是 Ganache 底層是透過這個來建立起 RPC 節點的。

異步連接

1
2
3
4
5
6
7
8
9
10
11
public class Main {

public static void main(String[] args) throws Exception {
// send asynchronous requests using a CompletableFuture
Web3j web3 = Web3j.build(new HttpService("http://127.0.0.1:7545"));
Web3ClientVersion web3ClientVersion = web3.web3ClientVersion().sendAsync().get();
String clientVersion = web3ClientVersion.getWeb3ClientVersion();
System.out.println(clientVersion);
}
}

這邊是使用異步連接,所以你會發現是使用 sendAsync (),當執行這段程式會發現一樣會出現上面的輸出結果,而如果今天是一個耗時的操作是不會 block 住的會直接繼續往下執行其它程式碼。

開啟 Remix 編輯 Smart Contract

這邊突然跳到這邊,是因為要介紹如何透過 web3j 去佈署 smart contract 及連接 smart contract。這邊就需要先寫一些 solidity。

這邊講一下,Remix 是一個線上 IDE,就是一個 web 網頁可以編輯 solidity code。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
pragma solidity ^0.4.25;

// Modified Greeter contract. Based on example at https://www.ethereum.org/greeter.

contract Mortal {
/* Define variable owner of the type address*/
address owner;

/* this function is executed at initialization and sets the owner of the contract */
constructor () public { owner = msg.sender; }

/* Function to recover the funds on the contract */
function kill() public { if (msg.sender == owner) selfdestruct(owner); }
}

contract Greeter is Mortal {
/* define variable greeting of the type string */
string greeting;

/* this runs when the contract is executed */
constructor (string _greeting) public {
greeting = _greeting;
}

function newGreeting(string _greeting) public {
emit Modified(greeting, _greeting, greeting, _greeting);
greeting = _greeting;
}

/* main function */
function greet() public constant returns (string) {
return greeting;
}

/* we include indexed events to demonstrate the difference that can be
captured versus non-indexed */
event Modified(
string indexed oldGreetingIdx, string indexed newGreetingIdx,
string oldGreeting, string newGreeting);
}

這邊直接拿 web3j 官方提供的範例 XD,這邊不多加講解 smart contract code 在做甚麼,因為我覺得過於簡單,不熟悉語法可以再去參考官方文檔。

編譯 solidity

  • 直接透過 remix 編譯
  • 在電腦上安裝 solc 編譯器,在電腦上自己手動編譯。

我個人是推薦第一種,因為 remix 寫 code 比較方便所以一併編譯會比較理想。

透過 remix 編譯 solidity 可以在編譯頁面看到:

假設該 smart contract 叫做 Greeter.sol。在左邊可以看到編譯按鈕,當編譯完成後看到左下方的 ABI 跟 Bytecode,可以各自複製,這時候需要將這兩個複製的內容個創建兩個檔案:

  • Greeter.abi
  • Greeter.bin

因為等等要透過 web3j CLI 來針對這兩個檔案產生 smart contract 對應的 Java Class。

web3 CLI 轉換 smart contract

將 Greeter.abi、Greeter.bin 放置專案根目錄下,並執行以下指令:

1
web3j solidity generate -b ./Greeter.bin -a ./Greeter.abi -o ./src/main/java -p kenny.home
  • -b:Greeter.bin 的檔案路徑
  • -a:Greeter.abi 的檔案路徑
  • -o:產生出來對應的 Java 檔案要放置在哪
  • -p:你的專案 package name

簡單來說你產生出來的 java 檔案會放在 - o 路徑下的 - p 下。

產生出來的 Java 檔案如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
public class Greeter extends Contract {
private static final String BINARY = "{\r\n"
+ "\t\"linkReferences\": {},\r\n"
+ "\t\"object\": \"608060405234801561001057600080fd5b506040516106c23803806106c283398101806040528101908080518201929190505050336000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055508060019080519060200190610089929190610090565b5050610135565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f106100d157805160ff19168380011785556100ff565b828001600101855582156100ff579182015b828111156100fe5782518255916020019190600101906100e3565b5b50905061010c9190610110565b5090565b61013291905b8082111561012e576000816000905550600101610116565b5090565b90565b61057e806101446000396000f300608060405260043610610057576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff16806341c0e1b51461005c5780634ac0d66e14610073578063cfae3217146100dc575b600080fd5b34801561006857600080fd5b5061007161016c565b005b34801561007f57600080fd5b506100da600480360381019080803590602001908201803590602001908080601f01602080910402602001604051908101604052809392919081815260200183838082843782019150505050505091929192905050506101fd565b005b3480156100e857600080fd5b506100f161040b565b6040518080602001828103825283818151815260200191508051906020019080838360005b83811015610131578082015181840152602081019050610116565b50505050905090810190601f16801561015e5780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614156101fb576000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16ff5b565b806040518082805190602001908083835b602083101515610233578051825260208201915060208101905060208303925061020e565b6001836020036101000a0380198251168184511680821785525050505050509050019150506040518091039020600160405180828054600181600116156101000203166002900480156102bd5780601f1061029b5761010080835404028352918201916102bd565b820191906000526020600020905b8154815290600101906020018083116102a9575b505091505060405180910390207f047dcd1aa8b77b0b943642129c767533eeacd700c7c1eab092b8ce05d2b2faf560018460405180806020018060200183810383528581815460018160011615610100020316600290048152602001915080546001816001161561010002031660029004801561037b5780601f106103505761010080835404028352916020019161037b565b820191906000526020600020905b81548152906001019060200180831161035e57829003601f168201915b5050838103825284818151815260200191508051906020019080838360005b838110156103b557808201518184015260208101905061039a565b50505050905090810190601f1680156103e25780820380516001836020036101000a031916815260200191505b5094505050505060405180910390a380600190805190602001906104079291906104ad565b5050565b606060018054600181600116156101000203166002900480601f0160208091040260200160405190810160405280929190818152602001828054600181600116156101000203166002900480156104a35780601f10610478576101008083540402835291602001916104a3565b820191906000526020600020905b81548152906001019060200180831161048657829003601f168201915b5050505050905090565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f106104ee57805160ff191683800117855561051c565b8280016001018555821561051c579182015b8281111561051b578251825591602001919060010190610500565b5b509050610529919061052d565b5090565b61054f91905b8082111561054b576000816000905550600101610533565b5090565b905600a165627a7a72305820c9ef43bfc7be6a77def404c505dcd9ae4f88a22c2a5b9ecaa581c675a2993b700029\",\r\n"
+ "\t\"opcodes\": \"PUSH1 0x80 PUSH1 0x40 MSTORE CALLVALUE DUP1 ISZERO PUSH2 0x10 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH1 0x40 MLOAD PUSH2 0x6C2 CODESIZE SUB DUP1 PUSH2 0x6C2 DUP4 CODECOPY DUP2 ADD DUP1 PUSH1 0x40 MSTORE DUP2 ADD SWAP1 DUP1 DUP1 MLOAD DUP3 ADD SWAP3 SWAP2 SWAP1 POP POP POP CALLER PUSH1 0x0 DUP1 PUSH2 0x100 EXP DUP2 SLOAD DUP2 PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF MUL NOT AND SWAP1 DUP4 PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF AND MUL OR SWAP1 SSTORE POP DUP1 PUSH1 0x1 SWAP1 DUP1 MLOAD SWAP1 PUSH1 0x20 ADD SWAP1 PUSH2 0x89 SWAP3 SWAP2 SWAP1 PUSH2 0x90 JUMP JUMPDEST POP POP PUSH2 0x135 JUMP JUMPDEST DUP3 DUP1 SLOAD PUSH1 0x1 DUP2 PUSH1 0x1 AND ISZERO PUSH2 0x100 MUL SUB AND PUSH1 0x2 SWAP1 DIV SWAP1 PUSH1 0x0 MSTORE PUSH1 0x20 PUSH1 0x0 KECCAK256 SWAP1 PUSH1 0x1F ADD PUSH1 0x20 SWAP1 DIV DUP2 ADD SWAP3 DUP3 PUSH1 0x1F LT PUSH2 0xD1 JUMPI DUP1 MLOAD PUSH1 0xFF NOT AND DUP4 DUP1 ADD OR DUP6 SSTORE PUSH2 0xFF JUMP JUMPDEST DUP3 DUP1 ADD PUSH1 0x1 ADD DUP6 SSTORE DUP3 ISZERO PUSH2 0xFF JUMPI SWAP2 DUP3 ADD JUMPDEST DUP3 DUP2 GT ISZERO PUSH2 0xFE JUMPI DUP3 MLOAD DUP3 SSTORE SWAP2 PUSH1 0x20 ADD SWAP2 SWAP1 PUSH1 0x1 ADD SWAP1 PUSH2 0xE3 JUMP JUMPDEST JUMPDEST POP SWAP1 POP PUSH2 0x10C SWAP2 SWAP1 PUSH2 0x110 JUMP JUMPDEST POP SWAP1 JUMP JUMPDEST PUSH2 0x132 SWAP2 SWAP1 JUMPDEST DUP1 DUP3 GT ISZERO PUSH2 0x12E JUMPI PUSH1 0x0 DUP2 PUSH1 0x0 SWAP1 SSTORE POP PUSH1 0x1 ADD PUSH2 0x116 JUMP JUMPDEST POP SWAP1 JUMP JUMPDEST SWAP1 JUMP JUMPDEST PUSH2 0x57E DUP1 PUSH2 0x144 PUSH1 0x0 CODECOPY PUSH1 0x0 RETURN STOP PUSH1 0x80 PUSH1 0x40 MSTORE PUSH1 0x4 CALLDATASIZE LT PUSH2 0x57 JUMPI PUSH1 0x0 CALLDATALOAD PUSH29 0x100000000000000000000000000000000000000000000000000000000 SWAP1 DIV PUSH4 0xFFFFFFFF AND DUP1 PUSH4 0x41C0E1B5 EQ PUSH2 0x5C JUMPI DUP1 PUSH4 0x4AC0D66E EQ PUSH2 0x73 JUMPI DUP1 PUSH4 0xCFAE3217 EQ PUSH2 0xDC JUMPI JUMPDEST PUSH1 0x0 DUP1 REVERT JUMPDEST CALLVALUE DUP1 ISZERO PUSH2 0x68 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH2 0x71 PUSH2 0x16C JUMP JUMPDEST STOP JUMPDEST CALLVALUE DUP1 ISZERO PUSH2 0x7F JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH2 0xDA PUSH1 0x4 DUP1 CALLDATASIZE SUB DUP2 ADD SWAP1 DUP1 DUP1 CALLDATALOAD SWAP1 PUSH1 0x20 ADD SWAP1 DUP3 ADD DUP1 CALLDATALOAD SWAP1 PUSH1 0x20 ADD SWAP1 DUP1 DUP1 PUSH1 0x1F ADD PUSH1 0x20 DUP1 SWAP2 DIV MUL PUSH1 0x20 ADD PUSH1 0x40 MLOAD SWAP1 DUP2 ADD PUSH1 0x40 MSTORE DUP1 SWAP4 SWAP3 SWAP2 SWAP1 DUP2 DUP2 MSTORE PUSH1 0x20 ADD DUP4 DUP4 DUP1 DUP3 DUP5 CALLDATACOPY DUP3 ADD SWAP2 POP POP POP POP POP POP SWAP2 SWAP3 SWAP2 SWAP3 SWAP1 POP POP POP PUSH2 0x1FD JUMP JUMPDEST STOP JUMPDEST CALLVALUE DUP1 ISZERO PUSH2 0xE8 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH2 0xF1 PUSH2 0x40B JUMP JUMPDEST PUSH1 0x40 MLOAD DUP1 DUP1 PUSH1 0x20 ADD DUP3 DUP2 SUB DUP3 MSTORE DUP4 DUP2 DUP2 MLOAD DUP2 MSTORE PUSH1 0x20 ADD SWAP2 POP DUP1 MLOAD SWAP1 PUSH1 0x20 ADD SWAP1 DUP1 DUP4 DUP4 PUSH1 0x0 JUMPDEST DUP4 DUP2 LT ISZERO PUSH2 0x131 JUMPI DUP1 DUP3 ADD MLOAD DUP2 DUP5 ADD MSTORE PUSH1 0x20 DUP2 ADD SWAP1 POP PUSH2 0x116 JUMP JUMPDEST POP POP POP POP SWAP1 POP SWAP1 DUP2 ADD SWAP1 PUSH1 0x1F AND DUP1 ISZERO PUSH2 0x15E JUMPI DUP1 DUP3 SUB DUP1 MLOAD PUSH1 0x1 DUP4 PUSH1 0x20 SUB PUSH2 0x100 EXP SUB NOT AND DUP2 MSTORE PUSH1 0x20 ADD SWAP2 POP JUMPDEST POP SWAP3 POP POP POP PUSH1 0x40 MLOAD DUP1 SWAP2 SUB SWAP1 RETURN JUMPDEST PUSH1 0x0 DUP1 SWAP1 SLOAD SWAP1 PUSH2 0x100 EXP SWAP1 DIV PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF AND PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF AND CALLER PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF AND EQ ISZERO PUSH2 0x1FB JUMPI PUSH1 0x0 DUP1 SWAP1 SLOAD SWAP1 PUSH2 0x100 EXP SWAP1 DIV PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF AND PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF AND SELFDESTRUCT JUMPDEST JUMP JUMPDEST DUP1 PUSH1 0x40 MLOAD DUP1 DUP3 DUP1 MLOAD SWAP1 PUSH1 0x20 ADD SWAP1 DUP1 DUP4 DUP4 JUMPDEST PUSH1 0x20 DUP4 LT ISZERO ISZERO PUSH2 0x233 JUMPI DUP1 MLOAD DUP3 MSTORE PUSH1 0x20 DUP3 ADD SWAP2 POP PUSH1 0x20 DUP2 ADD SWAP1 POP PUSH1 0x20 DUP4 SUB SWAP3 POP PUSH2 0x20E JUMP JUMPDEST PUSH1 0x1 DUP4 PUSH1 0x20 SUB PUSH2 0x100 EXP SUB DUP1 NOT DUP3 MLOAD AND DUP2 DUP5 MLOAD AND DUP1 DUP3 OR DUP6 MSTORE POP POP POP POP POP POP SWAP1 POP ADD SWAP2 POP POP PUSH1 0x40 MLOAD DUP1 SWAP2 SUB SWAP1 KECCAK256 PUSH1 0x1 PUSH1 0x40 MLOAD DUP1 DUP3 DUP1 SLOAD PUSH1 0x1 DUP2 PUSH1 0x1 AND ISZERO PUSH2 0x100 MUL SUB AND PUSH1 0x2 SWAP1 DIV DUP1 ISZERO PUSH2 0x2BD JUMPI DUP1 PUSH1 0x1F LT PUSH2 0x29B JUMPI PUSH2 0x100 DUP1 DUP4 SLOAD DIV MUL DUP4 MSTORE SWAP2 DUP3 ADD SWAP2 PUSH2 0x2BD JUMP JUMPDEST DUP3 ADD SWAP2 SWAP1 PUSH1 0x0 MSTORE PUSH1 0x20 PUSH1 0x0 KECCAK256 SWAP1 JUMPDEST DUP2 SLOAD DUP2 MSTORE SWAP1 PUSH1 0x1 ADD SWAP1 PUSH1 0x20 ADD DUP1 DUP4 GT PUSH2 0x2A9 JUMPI JUMPDEST POP POP SWAP2 POP POP PUSH1 0x40 MLOAD DUP1 SWAP2 SUB SWAP1 KECCAK256 PUSH32 0x47DCD1AA8B77B0B943642129C767533EEACD700C7C1EAB092B8CE05D2B2FAF5 PUSH1 0x1 DUP5 PUSH1 0x40 MLOAD DUP1 DUP1 PUSH1 0x20 ADD DUP1 PUSH1 0x20 ADD DUP4 DUP2 SUB DUP4 MSTORE DUP6 DUP2 DUP2 SLOAD PUSH1 0x1 DUP2 PUSH1 0x1 AND ISZERO PUSH2 0x100 MUL SUB AND PUSH1 0x2 SWAP1 DIV DUP2 MSTORE PUSH1 0x20 ADD SWAP2 POP DUP1 SLOAD PUSH1 0x1 DUP2 PUSH1 0x1 AND ISZERO PUSH2 0x100 MUL SUB AND PUSH1 0x2 SWAP1 DIV DUP1 ISZERO PUSH2 0x37B JUMPI DUP1 PUSH1 0x1F LT PUSH2 0x350 JUMPI PUSH2 0x100 DUP1 DUP4 SLOAD DIV MUL DUP4 MSTORE SWAP2 PUSH1 0x20 ADD SWAP2 PUSH2 0x37B JUMP JUMPDEST DUP3 ADD SWAP2 SWAP1 PUSH1 0x0 MSTORE PUSH1 0x20 PUSH1 0x0 KECCAK256 SWAP1 JUMPDEST DUP2 SLOAD DUP2 MSTORE SWAP1 PUSH1 0x1 ADD SWAP1 PUSH1 0x20 ADD DUP1 DUP4 GT PUSH2 0x35E JUMPI DUP3 SWAP1 SUB PUSH1 0x1F AND DUP3 ADD SWAP2 JUMPDEST POP POP DUP4 DUP2 SUB DUP3 MSTORE DUP5 DUP2 DUP2 MLOAD DUP2 MSTORE PUSH1 0x20 ADD SWAP2 POP DUP1 MLOAD SWAP1 PUSH1 0x20 ADD SWAP1 DUP1 DUP4 DUP4 PUSH1 0x0 JUMPDEST DUP4 DUP2 LT ISZERO PUSH2 0x3B5 JUMPI DUP1 DUP3 ADD MLOAD DUP2 DUP5 ADD MSTORE PUSH1 0x20 DUP2 ADD SWAP1 POP PUSH2 0x39A JUMP JUMPDEST POP POP POP POP SWAP1 POP SWAP1 DUP2 ADD SWAP1 PUSH1 0x1F AND DUP1 ISZERO PUSH2 0x3E2 JUMPI DUP1 DUP3 SUB DUP1 MLOAD PUSH1 0x1 DUP4 PUSH1 0x20 SUB PUSH2 0x100 EXP SUB NOT AND DUP2 MSTORE PUSH1 0x20 ADD SWAP2 POP JUMPDEST POP SWAP5 POP POP POP POP POP PUSH1 0x40 MLOAD DUP1 SWAP2 SUB SWAP1 LOG3 DUP1 PUSH1 0x1 SWAP1 DUP1 MLOAD SWAP1 PUSH1 0x20 ADD SWAP1 PUSH2 0x407 SWAP3 SWAP2 SWAP1 PUSH2 0x4AD JUMP JUMPDEST POP POP JUMP JUMPDEST PUSH1 0x60 PUSH1 0x1 DUP1 SLOAD PUSH1 0x1 DUP2 PUSH1 0x1 AND ISZERO PUSH2 0x100 MUL SUB AND PUSH1 0x2 SWAP1 DIV DUP1 PUSH1 0x1F ADD PUSH1 0x20 DUP1 SWAP2 DIV MUL PUSH1 0x20 ADD PUSH1 0x40 MLOAD SWAP1 DUP2 ADD PUSH1 0x40 MSTORE DUP1 SWAP3 SWAP2 SWAP1 DUP2 DUP2 MSTORE PUSH1 0x20 ADD DUP3 DUP1 SLOAD PUSH1 0x1 DUP2 PUSH1 0x1 AND ISZERO PUSH2 0x100 MUL SUB AND PUSH1 0x2 SWAP1 DIV DUP1 ISZERO PUSH2 0x4A3 JUMPI DUP1 PUSH1 0x1F LT PUSH2 0x478 JUMPI PUSH2 0x100 DUP1 DUP4 SLOAD DIV MUL DUP4 MSTORE SWAP2 PUSH1 0x20 ADD SWAP2 PUSH2 0x4A3 JUMP JUMPDEST DUP3 ADD SWAP2 SWAP1 PUSH1 0x0 MSTORE PUSH1 0x20 PUSH1 0x0 KECCAK256 SWAP1 JUMPDEST DUP2 SLOAD DUP2 MSTORE SWAP1 PUSH1 0x1 ADD SWAP1 PUSH1 0x20 ADD DUP1 DUP4 GT PUSH2 0x486 JUMPI DUP3 SWAP1 SUB PUSH1 0x1F AND DUP3 ADD SWAP2 JUMPDEST POP POP POP POP POP SWAP1 POP SWAP1 JUMP JUMPDEST DUP3 DUP1 SLOAD PUSH1 0x1 DUP2 PUSH1 0x1 AND ISZERO PUSH2 0x100 MUL SUB AND PUSH1 0x2 SWAP1 DIV SWAP1 PUSH1 0x0 MSTORE PUSH1 0x20 PUSH1 0x0 KECCAK256 SWAP1 PUSH1 0x1F ADD PUSH1 0x20 SWAP1 DIV DUP2 ADD SWAP3 DUP3 PUSH1 0x1F LT PUSH2 0x4EE JUMPI DUP1 MLOAD PUSH1 0xFF NOT AND DUP4 DUP1 ADD OR DUP6 SSTORE PUSH2 0x51C JUMP JUMPDEST DUP3 DUP1 ADD PUSH1 0x1 ADD DUP6 SSTORE DUP3 ISZERO PUSH2 0x51C JUMPI SWAP2 DUP3 ADD JUMPDEST DUP3 DUP2 GT ISZERO PUSH2 0x51B JUMPI DUP3 MLOAD DUP3 SSTORE SWAP2 PUSH1 0x20 ADD SWAP2 SWAP1 PUSH1 0x1 ADD SWAP1 PUSH2 0x500 JUMP JUMPDEST JUMPDEST POP SWAP1 POP PUSH2 0x529 SWAP2 SWAP1 PUSH2 0x52D JUMP JUMPDEST POP SWAP1 JUMP JUMPDEST PUSH2 0x54F SWAP2 SWAP1 JUMPDEST DUP1 DUP3 GT ISZERO PUSH2 0x54B JUMPI PUSH1 0x0 DUP2 PUSH1 0x0 SWAP1 SSTORE POP PUSH1 0x1 ADD PUSH2 0x533 JUMP JUMPDEST POP SWAP1 JUMP JUMPDEST SWAP1 JUMP STOP LOG1 PUSH6 0x627A7A723058 KECCAK256 0xc9 0xef NUMBER 0xbf 0xc7 0xbe PUSH11 0x77DEF404C505DCD9AE4F88 LOG2 0x2c 0x2a JUMPDEST SWAP15 0xca 0xa5 DUP2 0xc6 PUSH22 0xA2993B70002900000000000000000000000000000000 \",\r\n"
+ "\t\"sourceMap\": \"476:753:0:-;;;635:75;8:9:-1;5:2;;;30:1;27;20:12;5:2;635:75:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;325:10;317:5;;:18;;;;;;;;;;;;;;;;;;694:9;683:8;:20;;;;;;;;;;;;:::i;:::-;;635:75;476:753;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;:::o;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;:::o;:::-;;;;;;;\"\r\n"
+ "}";

public static final String FUNC_KILL = "kill";

public static final String FUNC_NEWGREETING = "newGreeting";

public static final String FUNC_GREET = "greet";

public static final Event MODIFIED_EVENT = new Event("Modified",
Arrays.<TypeReference<?>>asList(new TypeReference<Utf8String>(true) {}, new TypeReference<Utf8String>(true) {}, new TypeReference<Utf8String>() {}, new TypeReference<Utf8String>() {}));
;

@Deprecated
protected Greeter(String contractAddress, Web3j web3j, Credentials credentials, BigInteger gasPrice, BigInteger gasLimit) {
super(BINARY, contractAddress, web3j, credentials, gasPrice, gasLimit);
}

protected Greeter(String contractAddress, Web3j web3j, Credentials credentials, ContractGasProvider contractGasProvider) {
super(BINARY, contractAddress, web3j, credentials, contractGasProvider);
}

@Deprecated
protected Greeter(String contractAddress, Web3j web3j, TransactionManager transactionManager, BigInteger gasPrice, BigInteger gasLimit) {
super(BINARY, contractAddress, web3j, transactionManager, gasPrice, gasLimit);
}

protected Greeter(String contractAddress, Web3j web3j, TransactionManager transactionManager, ContractGasProvider contractGasProvider) {
super(BINARY, contractAddress, web3j, transactionManager, contractGasProvider);
}

public RemoteFunctionCall<TransactionReceipt> kill() {
final Function function = new Function(
FUNC_KILL,
Arrays.<Type>asList(),
Collections.<TypeReference<?>>emptyList());
return executeRemoteCallTransaction(function);
}

public RemoteFunctionCall<TransactionReceipt> newGreeting(String _greeting) {
final Function function = new Function(
FUNC_NEWGREETING,
Arrays.<Type>asList(new org.web3j.abi.datatypes.Utf8String(_greeting)),
Collections.<TypeReference<?>>emptyList());
return executeRemoteCallTransaction(function);
}

public RemoteFunctionCall<String> greet() {
final Function function = new Function(FUNC_GREET,
Arrays.<Type>asList(),
Arrays.<TypeReference<?>>asList(new TypeReference<Utf8String>() {}));
return executeRemoteCallSingleValueReturn(function, String.class);
}

public List<ModifiedEventResponse> getModifiedEvents(TransactionReceipt transactionReceipt) {
List<Contract.EventValuesWithLog> valueList = extractEventParametersWithLog(MODIFIED_EVENT, transactionReceipt);
ArrayList<ModifiedEventResponse> responses = new ArrayList<ModifiedEventResponse>(valueList.size());
for (Contract.EventValuesWithLog eventValues : valueList) {
ModifiedEventResponse typedResponse = new ModifiedEventResponse();
typedResponse.log = eventValues.getLog();
typedResponse.oldGreetingIdx = (byte[]) eventValues.getIndexedValues().get(0).getValue();
typedResponse.newGreetingIdx = (byte[]) eventValues.getIndexedValues().get(1).getValue();
typedResponse.oldGreeting = (String) eventValues.getNonIndexedValues().get(0).getValue();
typedResponse.newGreeting = (String) eventValues.getNonIndexedValues().get(1).getValue();
responses.add(typedResponse);
}
return responses;
}

public Flowable<ModifiedEventResponse> modifiedEventFlowable(EthFilter filter) {
return web3j.ethLogFlowable(filter).map(new io.reactivex.functions.Function<Log, ModifiedEventResponse>() {
@Override
public ModifiedEventResponse apply(Log log) {
Contract.EventValuesWithLog eventValues = extractEventParametersWithLog(MODIFIED_EVENT, log);
ModifiedEventResponse typedResponse = new ModifiedEventResponse();
typedResponse.log = log;
typedResponse.oldGreetingIdx = (byte[]) eventValues.getIndexedValues().get(0).getValue();
typedResponse.newGreetingIdx = (byte[]) eventValues.getIndexedValues().get(1).getValue();
typedResponse.oldGreeting = (String) eventValues.getNonIndexedValues().get(0).getValue();
typedResponse.newGreeting = (String) eventValues.getNonIndexedValues().get(1).getValue();
return typedResponse;
}
});
}

public Flowable<ModifiedEventResponse> modifiedEventFlowable(DefaultBlockParameter startBlock, DefaultBlockParameter endBlock) {
EthFilter filter = new EthFilter(startBlock, endBlock, getContractAddress());
filter.addSingleTopic(EventEncoder.encode(MODIFIED_EVENT));
return modifiedEventFlowable(filter);
}

@Deprecated
public static Greeter load(String contractAddress, Web3j web3j, Credentials credentials, BigInteger gasPrice, BigInteger gasLimit) {
return new Greeter(contractAddress, web3j, credentials, gasPrice, gasLimit);
}

@Deprecated
public static Greeter load(String contractAddress, Web3j web3j, TransactionManager transactionManager, BigInteger gasPrice, BigInteger gasLimit) {
return new Greeter(contractAddress, web3j, transactionManager, gasPrice, gasLimit);
}

public static Greeter load(String contractAddress, Web3j web3j, Credentials credentials, ContractGasProvider contractGasProvider) {
return new Greeter(contractAddress, web3j, credentials, contractGasProvider);
}

public static Greeter load(String contractAddress, Web3j web3j, TransactionManager transactionManager, ContractGasProvider contractGasProvider) {
return new Greeter(contractAddress, web3j, transactionManager, contractGasProvider);
}

public static RemoteCall<Greeter> deploy(Web3j web3j, Credentials credentials, ContractGasProvider contractGasProvider, String _greeting) {
String encodedConstructor = FunctionEncoder.encodeConstructor(Arrays.<Type>asList(new org.web3j.abi.datatypes.Utf8String(_greeting)));
return deployRemoteCall(Greeter.class, web3j, credentials, contractGasProvider, BINARY, encodedConstructor);
}

public static RemoteCall<Greeter> deploy(Web3j web3j, TransactionManager transactionManager, ContractGasProvider contractGasProvider, String _greeting) {
String encodedConstructor = FunctionEncoder.encodeConstructor(Arrays.<Type>asList(new org.web3j.abi.datatypes.Utf8String(_greeting)));
return deployRemoteCall(Greeter.class, web3j, transactionManager, contractGasProvider, BINARY, encodedConstructor);
}

@Deprecated
public static RemoteCall<Greeter> deploy(Web3j web3j, Credentials credentials, BigInteger gasPrice, BigInteger gasLimit, String _greeting) {
String encodedConstructor = FunctionEncoder.encodeConstructor(Arrays.<Type>asList(new org.web3j.abi.datatypes.Utf8String(_greeting)));
return deployRemoteCall(Greeter.class, web3j, credentials, gasPrice, gasLimit, BINARY, encodedConstructor);
}

@Deprecated
public static RemoteCall<Greeter> deploy(Web3j web3j, TransactionManager transactionManager, BigInteger gasPrice, BigInteger gasLimit, String _greeting) {
String encodedConstructor = FunctionEncoder.encodeConstructor(Arrays.<Type>asList(new org.web3j.abi.datatypes.Utf8String(_greeting)));
return deployRemoteCall(Greeter.class, web3j, transactionManager, gasPrice, gasLimit, BINARY, encodedConstructor);
}

public static class ModifiedEventResponse extends BaseEventResponse {
public byte[] oldGreetingIdx;

public byte[] newGreetingIdx;

public String oldGreeting;

public String newGreeting;
}
}

恩… 非常的龐大,基本上你不太會需要去看裡面的程式碼實作,而是知道怎麼呼叫這 class 裡面的 method 就可以了,因為它就是對應於 smart contract 定義的函式,當你呼叫這邊,就是幫你呼叫 smart contract 的函式。所以其實要理解的是你 smart contract 函式怎麼實作的~

web3j 佈署 smart contract

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class Main {

public static void main(String[] args) throws Exception {
// deploy smart contract
Web3j web3 = Web3j.build(new HttpService("http://127.0.0.1:7545"));
Credentials credentials = Credentials.create("92891ae4dc8b974bcda5ca84f962a4ad8671c17d32d8e9c889c692bf3f2d35d6");
ContractGasProvider provider = new StaticGasProvider(BigInteger.valueOf(20000000000L), BigInteger.valueOf(6721975L));
Greeter contract = Greeter.deploy(
web3, credentials, provider, "test"
).send();
String contractAddress = contract.getContractAddress();
System.out.println(contractAddress);
}
}

  1. 首先要知道部署 smart contract 是需要用到帳戶資訊的,由於我們這邊是採用 Ganache 架設環境,因此這邊是直接拿帳戶的私鑰當字串建立 Credentials 物件。

  2. 如果不是用 Ganache,也可以用以下的方式:

    1
    Credentials credentials = WalletUtils.loadCredentials("password", "/path/to/walletfile");
  3. 然後需要建立 ContractGasProvider 物件,因為佈署 smart contract 需要設定 Gas Price 跟 Gas Limit,這邊直接拿 Ganache 提供的預設值的數字。

  4. 接著透過剛剛產生出來的 Greeter Class,建立該物件,事實上就是對應於 smart contract,利用 deploy (),進行佈署,而 provider 後面的參數是代表可以填入 smart contract 建構子的值。

  5. 透過 getContractAddress () 可以取得合約位址,如果佈署成功就會成功取到。

但是執行這段程式碼可能會得到以下錯誤:

1
Caused by: java.lang.RuntimeException: Error processing transaction request: VM Exception while processing transaction: stack underflow

這個原因是因為透過 web3 CLI 產生的 Java 檔案裏面有一些錯誤。這可能是 web3j 潛在的 bug。

首先看 Greeter.java 裡面的一個 Binary 變數:

1
2
3
4
5
6
private static final String BINARY = "{\r\n"
+ "\t\"linkReferences\": {},\r\n"
+ "\t\"object\": \"608060405234801561001057600080fd5b506040516106c23803806106c283398101806040528101908080518201929190505050336000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055508060019080519060200190610089929190610090565b5050610135565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f106100d157805160ff19168380011785556100ff565b828001600101855582156100ff579182015b828111156100fe5782518255916020019190600101906100e3565b5b50905061010c9190610110565b5090565b61013291905b8082111561012e576000816000905550600101610116565b5090565b90565b61057e806101446000396000f300608060405260043610610057576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff16806341c0e1b51461005c5780634ac0d66e14610073578063cfae3217146100dc575b600080fd5b34801561006857600080fd5b5061007161016c565b005b34801561007f57600080fd5b506100da600480360381019080803590602001908201803590602001908080601f01602080910402602001604051908101604052809392919081815260200183838082843782019150505050505091929192905050506101fd565b005b3480156100e857600080fd5b506100f161040b565b6040518080602001828103825283818151815260200191508051906020019080838360005b83811015610131578082015181840152602081019050610116565b50505050905090810190601f16801561015e5780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614156101fb576000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16ff5b565b806040518082805190602001908083835b602083101515610233578051825260208201915060208101905060208303925061020e565b6001836020036101000a0380198251168184511680821785525050505050509050019150506040518091039020600160405180828054600181600116156101000203166002900480156102bd5780601f1061029b5761010080835404028352918201916102bd565b820191906000526020600020905b8154815290600101906020018083116102a9575b505091505060405180910390207f047dcd1aa8b77b0b943642129c767533eeacd700c7c1eab092b8ce05d2b2faf560018460405180806020018060200183810383528581815460018160011615610100020316600290048152602001915080546001816001161561010002031660029004801561037b5780601f106103505761010080835404028352916020019161037b565b820191906000526020600020905b81548152906001019060200180831161035e57829003601f168201915b5050838103825284818151815260200191508051906020019080838360005b838110156103b557808201518184015260208101905061039a565b50505050905090810190601f1680156103e25780820380516001836020036101000a031916815260200191505b5094505050505060405180910390a380600190805190602001906104079291906104ad565b5050565b606060018054600181600116156101000203166002900480601f0160208091040260200160405190810160405280929190818152602001828054600181600116156101000203166002900480156104a35780601f10610478576101008083540402835291602001916104a3565b820191906000526020600020905b81548152906001019060200180831161048657829003601f168201915b5050505050905090565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f106104ee57805160ff191683800117855561051c565b8280016001018555821561051c579182015b8281111561051b578251825591602001919060010190610500565b5b509050610529919061052d565b5090565b61054f91905b8082111561054b576000816000905550600101610533565b5090565b905600a165627a7a72305820c9ef43bfc7be6a77def404c505dcd9ae4f88a22c2a5b9ecaa581c675a2993b700029\",\r\n"
+ "\t\"opcodes\": \"PUSH1 0x80 PUSH1 0x40 MSTORE CALLVALUE DUP1 ISZERO PUSH2 0x10 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH1 0x40 MLOAD PUSH2 0x6C2 CODESIZE SUB DUP1 PUSH2 0x6C2 DUP4 CODECOPY DUP2 ADD DUP1 PUSH1 0x40 MSTORE DUP2 ADD SWAP1 DUP1 DUP1 MLOAD DUP3 ADD SWAP3 SWAP2 SWAP1 POP POP POP CALLER PUSH1 0x0 DUP1 PUSH2 0x100 EXP DUP2 SLOAD DUP2 PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF MUL NOT AND SWAP1 DUP4 PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF AND MUL OR SWAP1 SSTORE POP DUP1 PUSH1 0x1 SWAP1 DUP1 MLOAD SWAP1 PUSH1 0x20 ADD SWAP1 PUSH2 0x89 SWAP3 SWAP2 SWAP1 PUSH2 0x90 JUMP JUMPDEST POP POP PUSH2 0x135 JUMP JUMPDEST DUP3 DUP1 SLOAD PUSH1 0x1 DUP2 PUSH1 0x1 AND ISZERO PUSH2 0x100 MUL SUB AND PUSH1 0x2 SWAP1 DIV SWAP1 PUSH1 0x0 MSTORE PUSH1 0x20 PUSH1 0x0 KECCAK256 SWAP1 PUSH1 0x1F ADD PUSH1 0x20 SWAP1 DIV DUP2 ADD SWAP3 DUP3 PUSH1 0x1F LT PUSH2 0xD1 JUMPI DUP1 MLOAD PUSH1 0xFF NOT AND DUP4 DUP1 ADD OR DUP6 SSTORE PUSH2 0xFF JUMP JUMPDEST DUP3 DUP1 ADD PUSH1 0x1 ADD DUP6 SSTORE DUP3 ISZERO PUSH2 0xFF JUMPI SWAP2 DUP3 ADD JUMPDEST DUP3 DUP2 GT ISZERO PUSH2 0xFE JUMPI DUP3 MLOAD DUP3 SSTORE SWAP2 PUSH1 0x20 ADD SWAP2 SWAP1 PUSH1 0x1 ADD SWAP1 PUSH2 0xE3 JUMP JUMPDEST JUMPDEST POP SWAP1 POP PUSH2 0x10C SWAP2 SWAP1 PUSH2 0x110 JUMP JUMPDEST POP SWAP1 JUMP JUMPDEST PUSH2 0x132 SWAP2 SWAP1 JUMPDEST DUP1 DUP3 GT ISZERO PUSH2 0x12E JUMPI PUSH1 0x0 DUP2 PUSH1 0x0 SWAP1 SSTORE POP PUSH1 0x1 ADD PUSH2 0x116 JUMP JUMPDEST POP SWAP1 JUMP JUMPDEST SWAP1 JUMP JUMPDEST PUSH2 0x57E DUP1 PUSH2 0x144 PUSH1 0x0 CODECOPY PUSH1 0x0 RETURN STOP PUSH1 0x80 PUSH1 0x40 MSTORE PUSH1 0x4 CALLDATASIZE LT PUSH2 0x57 JUMPI PUSH1 0x0 CALLDATALOAD PUSH29 0x100000000000000000000000000000000000000000000000000000000 SWAP1 DIV PUSH4 0xFFFFFFFF AND DUP1 PUSH4 0x41C0E1B5 EQ PUSH2 0x5C JUMPI DUP1 PUSH4 0x4AC0D66E EQ PUSH2 0x73 JUMPI DUP1 PUSH4 0xCFAE3217 EQ PUSH2 0xDC JUMPI JUMPDEST PUSH1 0x0 DUP1 REVERT JUMPDEST CALLVALUE DUP1 ISZERO PUSH2 0x68 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH2 0x71 PUSH2 0x16C JUMP JUMPDEST STOP JUMPDEST CALLVALUE DUP1 ISZERO PUSH2 0x7F JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH2 0xDA PUSH1 0x4 DUP1 CALLDATASIZE SUB DUP2 ADD SWAP1 DUP1 DUP1 CALLDATALOAD SWAP1 PUSH1 0x20 ADD SWAP1 DUP3 ADD DUP1 CALLDATALOAD SWAP1 PUSH1 0x20 ADD SWAP1 DUP1 DUP1 PUSH1 0x1F ADD PUSH1 0x20 DUP1 SWAP2 DIV MUL PUSH1 0x20 ADD PUSH1 0x40 MLOAD SWAP1 DUP2 ADD PUSH1 0x40 MSTORE DUP1 SWAP4 SWAP3 SWAP2 SWAP1 DUP2 DUP2 MSTORE PUSH1 0x20 ADD DUP4 DUP4 DUP1 DUP3 DUP5 CALLDATACOPY DUP3 ADD SWAP2 POP POP POP POP POP POP SWAP2 SWAP3 SWAP2 SWAP3 SWAP1 POP POP POP PUSH2 0x1FD JUMP JUMPDEST STOP JUMPDEST CALLVALUE DUP1 ISZERO PUSH2 0xE8 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH2 0xF1 PUSH2 0x40B JUMP JUMPDEST PUSH1 0x40 MLOAD DUP1 DUP1 PUSH1 0x20 ADD DUP3 DUP2 SUB DUP3 MSTORE DUP4 DUP2 DUP2 MLOAD DUP2 MSTORE PUSH1 0x20 ADD SWAP2 POP DUP1 MLOAD SWAP1 PUSH1 0x20 ADD SWAP1 DUP1 DUP4 DUP4 PUSH1 0x0 JUMPDEST DUP4 DUP2 LT ISZERO PUSH2 0x131 JUMPI DUP1 DUP3 ADD MLOAD DUP2 DUP5 ADD MSTORE PUSH1 0x20 DUP2 ADD SWAP1 POP PUSH2 0x116 JUMP JUMPDEST POP POP POP POP SWAP1 POP SWAP1 DUP2 ADD SWAP1 PUSH1 0x1F AND DUP1 ISZERO PUSH2 0x15E JUMPI DUP1 DUP3 SUB DUP1 MLOAD PUSH1 0x1 DUP4 PUSH1 0x20 SUB PUSH2 0x100 EXP SUB NOT AND DUP2 MSTORE PUSH1 0x20 ADD SWAP2 POP JUMPDEST POP SWAP3 POP POP POP PUSH1 0x40 MLOAD DUP1 SWAP2 SUB SWAP1 RETURN JUMPDEST PUSH1 0x0 DUP1 SWAP1 SLOAD SWAP1 PUSH2 0x100 EXP SWAP1 DIV PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF AND PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF AND CALLER PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF AND EQ ISZERO PUSH2 0x1FB JUMPI PUSH1 0x0 DUP1 SWAP1 SLOAD SWAP1 PUSH2 0x100 EXP SWAP1 DIV PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF AND PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF AND SELFDESTRUCT JUMPDEST JUMP JUMPDEST DUP1 PUSH1 0x40 MLOAD DUP1 DUP3 DUP1 MLOAD SWAP1 PUSH1 0x20 ADD SWAP1 DUP1 DUP4 DUP4 JUMPDEST PUSH1 0x20 DUP4 LT ISZERO ISZERO PUSH2 0x233 JUMPI DUP1 MLOAD DUP3 MSTORE PUSH1 0x20 DUP3 ADD SWAP2 POP PUSH1 0x20 DUP2 ADD SWAP1 POP PUSH1 0x20 DUP4 SUB SWAP3 POP PUSH2 0x20E JUMP JUMPDEST PUSH1 0x1 DUP4 PUSH1 0x20 SUB PUSH2 0x100 EXP SUB DUP1 NOT DUP3 MLOAD AND DUP2 DUP5 MLOAD AND DUP1 DUP3 OR DUP6 MSTORE POP POP POP POP POP POP SWAP1 POP ADD SWAP2 POP POP PUSH1 0x40 MLOAD DUP1 SWAP2 SUB SWAP1 KECCAK256 PUSH1 0x1 PUSH1 0x40 MLOAD DUP1 DUP3 DUP1 SLOAD PUSH1 0x1 DUP2 PUSH1 0x1 AND ISZERO PUSH2 0x100 MUL SUB AND PUSH1 0x2 SWAP1 DIV DUP1 ISZERO PUSH2 0x2BD JUMPI DUP1 PUSH1 0x1F LT PUSH2 0x29B JUMPI PUSH2 0x100 DUP1 DUP4 SLOAD DIV MUL DUP4 MSTORE SWAP2 DUP3 ADD SWAP2 PUSH2 0x2BD JUMP JUMPDEST DUP3 ADD SWAP2 SWAP1 PUSH1 0x0 MSTORE PUSH1 0x20 PUSH1 0x0 KECCAK256 SWAP1 JUMPDEST DUP2 SLOAD DUP2 MSTORE SWAP1 PUSH1 0x1 ADD SWAP1 PUSH1 0x20 ADD DUP1 DUP4 GT PUSH2 0x2A9 JUMPI JUMPDEST POP POP SWAP2 POP POP PUSH1 0x40 MLOAD DUP1 SWAP2 SUB SWAP1 KECCAK256 PUSH32 0x47DCD1AA8B77B0B943642129C767533EEACD700C7C1EAB092B8CE05D2B2FAF5 PUSH1 0x1 DUP5 PUSH1 0x40 MLOAD DUP1 DUP1 PUSH1 0x20 ADD DUP1 PUSH1 0x20 ADD DUP4 DUP2 SUB DUP4 MSTORE DUP6 DUP2 DUP2 SLOAD PUSH1 0x1 DUP2 PUSH1 0x1 AND ISZERO PUSH2 0x100 MUL SUB AND PUSH1 0x2 SWAP1 DIV DUP2 MSTORE PUSH1 0x20 ADD SWAP2 POP DUP1 SLOAD PUSH1 0x1 DUP2 PUSH1 0x1 AND ISZERO PUSH2 0x100 MUL SUB AND PUSH1 0x2 SWAP1 DIV DUP1 ISZERO PUSH2 0x37B JUMPI DUP1 PUSH1 0x1F LT PUSH2 0x350 JUMPI PUSH2 0x100 DUP1 DUP4 SLOAD DIV MUL DUP4 MSTORE SWAP2 PUSH1 0x20 ADD SWAP2 PUSH2 0x37B JUMP JUMPDEST DUP3 ADD SWAP2 SWAP1 PUSH1 0x0 MSTORE PUSH1 0x20 PUSH1 0x0 KECCAK256 SWAP1 JUMPDEST DUP2 SLOAD DUP2 MSTORE SWAP1 PUSH1 0x1 ADD SWAP1 PUSH1 0x20 ADD DUP1 DUP4 GT PUSH2 0x35E JUMPI DUP3 SWAP1 SUB PUSH1 0x1F AND DUP3 ADD SWAP2 JUMPDEST POP POP DUP4 DUP2 SUB DUP3 MSTORE DUP5 DUP2 DUP2 MLOAD DUP2 MSTORE PUSH1 0x20 ADD SWAP2 POP DUP1 MLOAD SWAP1 PUSH1 0x20 ADD SWAP1 DUP1 DUP4 DUP4 PUSH1 0x0 JUMPDEST DUP4 DUP2 LT ISZERO PUSH2 0x3B5 JUMPI DUP1 DUP3 ADD MLOAD DUP2 DUP5 ADD MSTORE PUSH1 0x20 DUP2 ADD SWAP1 POP PUSH2 0x39A JUMP JUMPDEST POP POP POP POP SWAP1 POP SWAP1 DUP2 ADD SWAP1 PUSH1 0x1F AND DUP1 ISZERO PUSH2 0x3E2 JUMPI DUP1 DUP3 SUB DUP1 MLOAD PUSH1 0x1 DUP4 PUSH1 0x20 SUB PUSH2 0x100 EXP SUB NOT AND DUP2 MSTORE PUSH1 0x20 ADD SWAP2 POP JUMPDEST POP SWAP5 POP POP POP POP POP PUSH1 0x40 MLOAD DUP1 SWAP2 SUB SWAP1 LOG3 DUP1 PUSH1 0x1 SWAP1 DUP1 MLOAD SWAP1 PUSH1 0x20 ADD SWAP1 PUSH2 0x407 SWAP3 SWAP2 SWAP1 PUSH2 0x4AD JUMP JUMPDEST POP POP JUMP JUMPDEST PUSH1 0x60 PUSH1 0x1 DUP1 SLOAD PUSH1 0x1 DUP2 PUSH1 0x1 AND ISZERO PUSH2 0x100 MUL SUB AND PUSH1 0x2 SWAP1 DIV DUP1 PUSH1 0x1F ADD PUSH1 0x20 DUP1 SWAP2 DIV MUL PUSH1 0x20 ADD PUSH1 0x40 MLOAD SWAP1 DUP2 ADD PUSH1 0x40 MSTORE DUP1 SWAP3 SWAP2 SWAP1 DUP2 DUP2 MSTORE PUSH1 0x20 ADD DUP3 DUP1 SLOAD PUSH1 0x1 DUP2 PUSH1 0x1 AND ISZERO PUSH2 0x100 MUL SUB AND PUSH1 0x2 SWAP1 DIV DUP1 ISZERO PUSH2 0x4A3 JUMPI DUP1 PUSH1 0x1F LT PUSH2 0x478 JUMPI PUSH2 0x100 DUP1 DUP4 SLOAD DIV MUL DUP4 MSTORE SWAP2 PUSH1 0x20 ADD SWAP2 PUSH2 0x4A3 JUMP JUMPDEST DUP3 ADD SWAP2 SWAP1 PUSH1 0x0 MSTORE PUSH1 0x20 PUSH1 0x0 KECCAK256 SWAP1 JUMPDEST DUP2 SLOAD DUP2 MSTORE SWAP1 PUSH1 0x1 ADD SWAP1 PUSH1 0x20 ADD DUP1 DUP4 GT PUSH2 0x486 JUMPI DUP3 SWAP1 SUB PUSH1 0x1F AND DUP3 ADD SWAP2 JUMPDEST POP POP POP POP POP SWAP1 POP SWAP1 JUMP JUMPDEST DUP3 DUP1 SLOAD PUSH1 0x1 DUP2 PUSH1 0x1 AND ISZERO PUSH2 0x100 MUL SUB AND PUSH1 0x2 SWAP1 DIV SWAP1 PUSH1 0x0 MSTORE PUSH1 0x20 PUSH1 0x0 KECCAK256 SWAP1 PUSH1 0x1F ADD PUSH1 0x20 SWAP1 DIV DUP2 ADD SWAP3 DUP3 PUSH1 0x1F LT PUSH2 0x4EE JUMPI DUP1 MLOAD PUSH1 0xFF NOT AND DUP4 DUP1 ADD OR DUP6 SSTORE PUSH2 0x51C JUMP JUMPDEST DUP3 DUP1 ADD PUSH1 0x1 ADD DUP6 SSTORE DUP3 ISZERO PUSH2 0x51C JUMPI SWAP2 DUP3 ADD JUMPDEST DUP3 DUP2 GT ISZERO PUSH2 0x51B JUMPI DUP3 MLOAD DUP3 SSTORE SWAP2 PUSH1 0x20 ADD SWAP2 SWAP1 PUSH1 0x1 ADD SWAP1 PUSH2 0x500 JUMP JUMPDEST JUMPDEST POP SWAP1 POP PUSH2 0x529 SWAP2 SWAP1 PUSH2 0x52D JUMP JUMPDEST POP SWAP1 JUMP JUMPDEST PUSH2 0x54F SWAP2 SWAP1 JUMPDEST DUP1 DUP3 GT ISZERO PUSH2 0x54B JUMPI PUSH1 0x0 DUP2 PUSH1 0x0 SWAP1 SSTORE POP PUSH1 0x1 ADD PUSH2 0x533 JUMP JUMPDEST POP SWAP1 JUMP JUMPDEST SWAP1 JUMP STOP LOG1 PUSH6 0x627A7A723058 KECCAK256 0xc9 0xef NUMBER 0xbf 0xc7 0xbe PUSH11 0x77DEF404C505DCD9AE4F88 LOG2 0x2c 0x2a JUMPDEST SWAP15 0xca 0xa5 DUP2 0xc6 PUSH22 0xA2993B70002900000000000000000000000000000000 \",\r\n"
+ "\t\"sourceMap\": \"476:753:0:-;;;635:75;8:9:-1;5:2;;;30:1;27;20:12;5:2;635:75:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;325:10;317:5;;:18;;;;;;;;;;;;;;;;;;694:9;683:8;:20;;;;;;;;;;;;:::i;:::-;;635:75;476:753;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;:::o;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;:::o;:::-;;;;;;;\"\r\n"
+ "}";

對就是這麼長,正確的話應該裡面只會有 object 裡面的值,因此改成:

1
2
private static final String BINARY = "608060405234801561001057600080fd5b506040516106c23803806106c283398101806040528101908080518201929190505050336000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055508060019080519060200190610089929190610090565b5050610135565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f106100d157805160ff19168380011785556100ff565b828001600101855582156100ff579182015b828111156100fe5782518255916020019190600101906100e3565b5b50905061010c9190610110565b5090565b61013291905b8082111561012e576000816000905550600101610116565b5090565b90565b61057e806101446000396000f300608060405260043610610057576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff16806341c0e1b51461005c5780634ac0d66e14610073578063cfae3217146100dc575b600080fd5b34801561006857600080fd5b5061007161016c565b005b34801561007f57600080fd5b506100da600480360381019080803590602001908201803590602001908080601f01602080910402602001604051908101604052809392919081815260200183838082843782019150505050505091929192905050506101fd565b005b3480156100e857600080fd5b506100f161040b565b6040518080602001828103825283818151815260200191508051906020019080838360005b83811015610131578082015181840152602081019050610116565b50505050905090810190601f16801561015e5780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614156101fb576000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16ff5b565b806040518082805190602001908083835b602083101515610233578051825260208201915060208101905060208303925061020e565b6001836020036101000a0380198251168184511680821785525050505050509050019150506040518091039020600160405180828054600181600116156101000203166002900480156102bd5780601f1061029b5761010080835404028352918201916102bd565b820191906000526020600020905b8154815290600101906020018083116102a9575b505091505060405180910390207f047dcd1aa8b77b0b943642129c767533eeacd700c7c1eab092b8ce05d2b2faf560018460405180806020018060200183810383528581815460018160011615610100020316600290048152602001915080546001816001161561010002031660029004801561037b5780601f106103505761010080835404028352916020019161037b565b820191906000526020600020905b81548152906001019060200180831161035e57829003601f168201915b5050838103825284818151815260200191508051906020019080838360005b838110156103b557808201518184015260208101905061039a565b50505050905090810190601f1680156103e25780820380516001836020036101000a031916815260200191505b5094505050505060405180910390a380600190805190602001906104079291906104ad565b5050565b606060018054600181600116156101000203166002900480601f0160208091040260200160405190810160405280929190818152602001828054600181600116156101000203166002900480156104a35780601f10610478576101008083540402835291602001916104a3565b820191906000526020600020905b81548152906001019060200180831161048657829003601f168201915b5050505050905090565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f106104ee57805160ff191683800117855561051c565b8280016001018555821561051c579182015b8281111561051b578251825591602001919060010190610500565b5b509050610529919061052d565b5090565b61054f91905b8082111561054b576000816000905550600101610533565b5090565b905600a165627a7a72305820c9ef43bfc7be6a77def404c505dcd9ae4f88a22c2a5b9ecaa581c675a2993b700029";

這個錯誤也有可能是因為透過 remix 編譯出來的問題,因為如果在電腦上透過 solc 編譯器進行編譯後,在用 web3 CLI 轉換不會遇到這種錯誤。

因此如果透過 remix 的話要記得修改 BINARY 變數裡面的值。

修改好後,再度執行程式碼:

1
0x03bf993df7c8811d00efe210ff582544ff245465

就會修出合約位址,可以去 Ganache 那邊去進行確認是否正確:

可以看到位址是一樣的~

web3j 載入 smart contract 並呼叫相關函式

1
2
3
4
5
6
7
8
9
10
11
12
13
public class Main {

public static void main(String[] args) throws Exception {
// load smart contract
Web3j web3 = Web3j.build(new HttpService("http://127.0.0.1:7545"));
Credentials credentials = Credentials.create("92891ae4dc8b974bcda5ca84f962a4ad8671c17d32d8e9c889c692bf3f2d35d6");
ContractGasProvider provider = new StaticGasProvider(BigInteger.valueOf(20000000000L), BigInteger.valueOf(6721975L));
Greeter contract = Greeter.load("0xdBc7112B5C2E37d14977a2AcD8bC96FF807487a6", web3,
credentials, provider);
System.out.println(contract.greet().send());
}
}

由於剛剛成功佈署上去了,因此之後要獲取該 smart contract,就透過 load () 即可,裡面要放置 smart contract 的 address。

再來就可以呼叫 contract 裡面的函式,裡面有個 greet (),可以獲取一個字串值,記得要加 send (),代表發送 request。還記得前面建構仔我們是放 "test" 字串,因此理論上輸出結果會是這個字串。

我們在加入這兩行程式碼:

1
2
TransactionReceipt transactionReceipt = contract.newGreeting("Hello World").send();
System.out.println(contract.greet().send());

由於只是讀取 smart contract 的值並不會有交易產生,而如果要有交易產生,可以呼叫 newGreeting (),這個是去修改剛剛輸出 test 值的變數。而產生的 TransactionReceipt 物件,事實上就是該交易的相關紀錄。

因此再度執行該程式碼,輸出結果應為如下:

1
2
test
Hello World

web3j 監聽 smart contract 的事件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class Main {

public static void main(String[] args) throws Exception {
// smart contract event listen
Web3j web3 = Web3j.build(new HttpService("http://127.0.0.1:7545"));
Credentials credentials = Credentials.create("92891ae4dc8b974bcda5ca84f962a4ad8671c17d32d8e9c889c692bf3f2d35d6");
ContractGasProvider provider = new StaticGasProvider(BigInteger.valueOf(20000000000L), BigInteger.valueOf(6721975L));
Greeter contract = Greeter.load("0x03bF993dF7C8811D00eFe210fF582544fF245465", web3,
credentials, provider);
EthFilter filter = new EthFilter(DefaultBlockParameterName.EARLIEST, DefaultBlockParameterName.LATEST, "0x03bF993dF7C8811D00eFe210fF582544fF245465");
contract.modifiedEventFlowable(filter).subscribe(log -> System.out.println(log.newGreeting));
}
}

由於在實際的需求時常會有監聽 smart contract 的操作,因此這邊特別提出來講,可以透過 EthFilter 物件來達成。

也就是建立該物件需要三種參數:

  • 從哪個區塊開始監聽
  • 監聽到哪個區塊
  • smart contract address
1
contract.modifiedEventFlowable(filter).subscribe(log -> System.out.println(log.newGreeting));

這段程式碼這是開啟異步監聽,也就是執行到這裡並不會被 block 住,而是繼續往下執行程式碼,直到如果 smart contract 對應的事件發生,也就是針對 greeting 的值修改,就會監聽到,一旦監聽到這邊的操作就是會輸出結果。

當執行該程式碼後,此刻你有兩種做法測試:

  • 寫另外一個 Java 專案,並且是拿來提交交易
  • 使用 remix 去做以上的事情

由於方便,我們可以直接透過 remix 去操作:

畫面轉到這邊,可以看到 remix 這邊可以 load smart contract 等操作,其實就等同於在 web3j 的操作一樣。

在左邊 At Address 填入合約位址,就可以成功 load 進來,因此在左下方看到該 smart contract 的函式,透過點 greet 及可讀取該便數的值,並顯示在下面如 string: text

因此我們可以在 newGreeting 填入新的值,一旦提交此操作便會形成一個交易並觸動事件,照理說你的 Java 程式就會監聽到該操作,並且將值輸出。

假設將值填入 Hello World

基本上,輸出結果會輸出 testHello World

首先要知道以下幾件事情:

  1. 之所以會秀 test,是因為我們是監聽從第 0 塊到最後一塊,因此前面的交易紀錄會被輸出
  2. 你會發現 Hello World 並不是及時輸出,而是需要等待幾秒才會輸出,沒錯這是因為我們是用 HttpService 去連接而不是用 webSocket 的機制,無法讓 RPC Server 主動 push 給我們知道,HttpService 是透過輪詢 (polling) 的方式去得知,所以其資源耗用多也不即時。

web3j 連接 webSocket

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public class Main {

public static void main(String[] args) throws Exception {
// websocket
WebSocketClient webSocketClient = new WebSocketClient(new URI("ws://localhost:7545"));
boolean includeRawResponses = false;
WebSocketService webSocketService = new WebSocketService(webSocketClient, includeRawResponses);
// 注意該程式碼在官方文檔沒說,但必须要加上,否則會出現 WebsocketNotConnectedException。
webSocketService.connect();
Web3j web3 = Web3j.build(webSocketService);
Flowable<NewHeadsNotification> notifications = web3.newHeadsNotifications();
notifications.subscribe(event -> {
// get block hash
System.out.println(event.getParams().getResult().getHash());
});
web3.logsNotifications(Arrays.asList("0x7E70d99D8b710bd71095d24bbE8eB9f7F2d4fD7A"), Collections.emptyList()).subscribe(log -> {
// get transaction hash
System.out.println(log.getParams().getResult().getTransactionHash());
});
}

其實改成 websocket 也是很簡單的,就是要加上前面的一些程式碼,初始化 web3j 物件則改成丟進去 WebSocketService 物件。而 webSocketService.connect () 要記得加上,否則會出錯。

這邊主要是提供兩種 push 回來的資訊:

  1. Block Head
  2. Transaction log

但關於 websocket web3j 提供的功能在文檔寫得不甚清楚,因為好像是新出的功能,需要在鑽研一番~~

總結

基本上如果你是要開發 Ethereum Blockchain 的相關應用,透過 web3j 提供的功能大部分都能做到了,不過因為官方文檔也沒有寫得很清楚,需要多多去研究。

基本除了 web3j 的選擇,還有 web3 (JavaScript)、Golang (Ethereum Client) 原生。

下篇文章帶來如何運用在 quorum 區塊鏈上,因為有另外的程式庫替 web3j 添加了 quorum 的特性,但其實其它使用方法就如同這篇文章操作一樣。quorum 最特別的就在於可以發送私人交易給對方~才是最值得探討的。

最後最後!請聽我一言!

如果你還沒有註冊 Like Coin,你可以在文章最下方看到 Like 的按鈕,點下去後即可申請帳號,透過申請帳號後可以幫我的文章按下 Like,而 Like 最多可以點五次,而你不用付出任何一塊錢,就能給我寫這篇文章的最大的回饋!