web3j入門

[web3j](https://web3j.readthedocs.io/en/latest/)は、Ethereumのクライアント（ノード）とJSON-RPCで通信できるJavaライブラリです。
この記事では、web3jの基本的な使い方を紹介します。

＜トピック＞

1. Ethereumクライアントへの接続
1. EthereumクライアントとJSON-RPCで通信
    1. web3_clientVersion
    1. eth_getBlockByNumber
    1. eth_sendTransaction
1. 便利な機能の紹介
    1. Walletファイルを使った処理
    1. コントラクトのラッパークラス
    1. イベントをリアクティブに処理

## Ethereumクライアントへの接続
まずはEthereumクライアントを起動し、JSON-RPCによる通信が可能な状態にしましょう。
例えば`Geth`を起動し、HTTPによるJSON-RPC通信が可能な状態にします。

準備ができたら、web3jを使ってEthereumクライアントに接続します。

```java
// HTTPによる接続
Web3j web3j = Web3j.build(new HttpService("http://<ip address>:<port>"));

// IPCによる接続(Unix)
Web3j web3 = Web3j.build(new UnixIpcService("/path/to/socketfile"));

// IPCによる接続(Windows)
Web3j web3 = Web3j.build(new WindowsIpcService("/path/to/namedpipefile"));
```
## EthereumクライアントとJSON-RPCで通信
Ethereumクライアントへの接続が成功したら、次はJSON-RPCで通信してみましょう。
web3jによるJSON-RPC通信はとても簡単です。

### web3_clientVersion
例えばJSON-RPCの`web3_clientVersion`メソッドを呼び出す場合は、下記のように記述します。
（`web3_clientVersion`は、Ethereumクライアントのバージョンを確認するメソッドです）

```java
// Ethereumクライアントと接続
Web3j web3j = Web3j.build(new HttpService("http://localhost:8545"));
// JSON-RPCのリクエストを送信し、レスポンスを受信する
Web3ClientVersion response = web3j.web3ClientVersion().send();
// 結果を出力
System.out.println(response.getResult());
```

```:出力(例)
Geth/v1.8.17-stable-8bbe7207/windows-amd64/go1.11.1
```

このように、web3jでは、JSON-RPCの各メソッドをリクエストするための専用のJavaメソッドが用意されています。

### eth_getBlockByNumber
次に、メソッド引数を指定する必要のある例を紹介します。
ここでは、JSON-RPCの`eth_getBlockByNumber`をメソッドを使って、最新（"latest"）のブロック情報を取得してみましょう。

```java
// Ethereumクライアントと接続
Web3j web3j = Web3j.build(new HttpService("http://localhost:8545"));
// JSON-RPCのリクエストを送信し、レスポンスを受信する
EthBlock response = web3j.ethGetBlockByNumber(
    DefaultBlockParameterName.LATEST, // block number。ここでは最新のブロックを表すタグである"latest"を指定する
    false // trueの場合、トランザクションの詳細を取得する。falseの場合、トランザクションハッシュのみ取得する
    ).send();
// 結果を出力
Block block = response.getResult();
System.out.println("hash:" + block.getHash());
System.out.println("number:" + block.getNumber());
```

```:出力(例)
hash:0x7abc74f4b77054a378cd8969b77ca207ed74ff925bd751314186180d37818d33
number:7311
```

このように、メソッド引数もJSON-RPCの仕様に沿って指定できますので、とても分かりやすいと思います。

### eth_sendTransaction
次に、トランザクションの発行を伴う例を紹介します。
ここでは、JSON-RPCの`eth_sendTransaction`を使って、Etherを送金しましょう。

ただしその前に、アカウントをアンロックする必要があります。
Ethereumクライアントが`Geth`の場合、JSON-RPCで`personal_unlockAccount`メソッドが提供されていますので、これを利用してアカウントをアンロックした後に、Etherを送金しましょう。

```java
// Ethereumクライアントと接続
// AdminはWeb3jのサブクラスで、"personal_unlockAccount"のリクエスト処理が実装されています。
Admin web3j = Admin.build(new HttpService("http://localhost:8545"));

// "personal_unlockAccount"のリクエストを送信し、レスポンスを受信する
PersonalUnlockAccount unlockAccountResponse = web3j.personalUnlockAccount(
    "0x766a1c4737970feddde6f2b8659fca05bd0339ab", // アドレス
    "pass1" // パスワード
    ).send();

// アンロックが成功していたら、Etherを送金する
if (unlockAccountResponse.getResult()) {
    // "eth_sendTransaction"の引数に渡すオブジェクトを作成
    Transaction transaction = Transaction.createEtherTransaction(
        "0x766a1c4737970feddde6f2b8659fca05bd0339ab", // from
        null, // nonce。今回は指定しないため、null
        null, // gasPrice。今回は指定しないため、null
        null, // gasLimit。今回は指定しないため、null
        "0x0cbc0fe59d39e58d7369ca436fe5c6b4b71341d5", // to
        BigInteger.valueOf(100) // value
        );
    
    // "eth_sendTransaction"のリクエストを送信し、レスポンスを受信する
    EthSendTransaction sendTransactionResponse = web3j.ethSendTransaction(transaction).send();
    // 結果を出力
    System.out.println(sendTransactionResponse.getResult());
}
```

```:出力(例)
0x3557db13b5ac56f1e3e23ab9eeccd11b2c4daf73e416496d53c5bba377202a61
```

## 便利な機能の紹介
web3jは、単にJSON-RPCのメソッドを簡単に呼び出すだけではない、便利な機能を提供しています。
ここでは、下記3つの機能について紹介します。

- Walletファイルを使った処理
- コントラクトのラッパークラス
- イベントをリアクティブに処理

### Walletファイルを使った処理
Walletファイルってなんだ？　と思うかもしれません。
これは`Geth`では「keyファイル」と呼ばれているものです。keyファイルは`geth account new`を実行した際に作成されます。
通常`Geth`では「\<datadir\>/keystore」にkeyファイルが配置されています。
この「keyファイル」のことを、web3jでは「Walletファイル」と呼んでいます。

web3jでは、Walletファイルを利用することで、アカウントのアンロックに関わる処理をシンプルにしています。
ここでは、Walletファイルを利用してEtherを送金してみましょう。

まずは、Walletファイルを、web3jのプログラムを実行する環境からアクセスできる場所に配置（コピー）しましょう。
準備ができたら、Walletファイルを利用してEtherを送金します。

```java
// Ethereumクライアントと接続
Web3j web3j = Web3j.build(new HttpService("http://localhost:8545"));

// Walletファイルを読み込み
Credentials credentials = WalletUtils.loadCredentials(
    "pass1", // アカウントのパスワード
    "/pass/to/walletfile"
    );

// Etherを送金
TransactionReceipt receipt = Transfer.sendFunds(
    web3j,
    credentials,
    "0xdc87fc3ac1ec09ed43623dd9da4c6f3d792a88f5", // to
    new BigDecimal("1000"), // value
    Unit.ETHER // unit
    ).send();
```

一度Walletファイルをロードすれば、何度でも利用することができます。
複数のトランザクションを発行したい場合に、何度も`personal_unlockAccount`をリクエストする必要がないため、処理がシンプルになります。

### コントラクトのラッパークラス
web3jでは、コントラクトのラッパークラスを生成するツール（コマンドラインツール）を提供しています。
ラッパークラスを利用すれば、コントラクトのデプロイやメソッド呼び出しがとてもシンプルになります。
（もちろん、ラッパークラスを利用せずに、JSON-RPCの`eth_sendTransaction`や`eth_call`をリクエストしてコントラクトのデプロイやメソッド呼び出しを行うことも可能です。）

ここでは、サンプルとして、Solidityで書いた下記のソースのラッパークラスを生成します。

```Solidity:Token.sol
pragma solidity ^0.5.0;

contract Token {
    uint256 public totalSupply;
    mapping (address => uint256) public balanceOf;

    constructor (uint256 _initialSupply) public {
        totalSupply = _initialSupply;
        balanceOf[msg.sender] = _initialSupply;
    }

    function transfer(address _to, uint256 _value) public {
        require(balanceOf[msg.sender] >= _value);
        require(balanceOf[_to] + _value >= balanceOf[_to]);
        balanceOf[msg.sender] -= _value;
        balanceOf[_to] += _value;
    }
}
```

まずは[オンラインのコンパイラー](http://remix.ethereum.org/)などを利用して、ソースをコンパイルしましょう。
ソースをコンパイルすると、ABIとバイトコードを取得できます。
取得したABIとバイトコードをファイルに保存してください。

サンプルでは、以下のようにABIとバイトコードをファイルに保存しました。

```:Token.abi
[{"constant":true,"inputs":[],"name":"totalSupply","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"","type":"address"}],"name":"balanceOf","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_to","type":"address"},{"name":"_value","type":"uint256"}],"name":"transfer","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"inputs":[{"name":"_initialSupply","type":"uint256"}],"payable":false,"stateMutability":"nonpayable","type":"constructor"}]
```

```:Token.bin
0x608060405234801561001057600080fd5b506040516020806103a28339810180604052602081101561003057600080fd5b81019080805190602001909291905050508060008190555080600160003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002081905550506103068061009c6000396000f3fe608060405260043610610051576000357c01000000000000000000000000000000000000000000000000000000009004806318160ddd1461005657806370a0823114610081578063a9059cbb146100e6575b600080fd5b34801561006257600080fd5b5061006b610141565b6040518082815260200191505060405180910390f35b34801561008d57600080fd5b506100d0600480360360208110156100a457600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050610147565b6040518082815260200191505060405180910390f35b3480156100f257600080fd5b5061013f6004803603604081101561010957600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff1690602001909291908035906020019092919050505061015f565b005b60005481565b60016020528060005260406000206000915090505481565b80600160003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054101515156101ad57600080fd5b600160008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205481600160008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054011015151561023c57600080fd5b80600160003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206000828254039250508190555080600160008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008282540192505081905550505056fea165627a7a72305820f97c3c938c56e1b594db9c9b681f3a08bb4ef81ef0fa5888d5982034167a44ba0029
```

次に、コマンドラインツールを入手しましょう。
コマンドラインツールはGitHubの[リリースページ](https://github.com/web3j/web3j/releases)からダウンロードできます。

ダウンロードした圧縮ファイルを解凍し、binディレクトリ配下にある実行ファイルを以下の通り実行します。

```
web3j solidity generate -a=/path/to/Token.abi -b=/path/to/Token.bin -o=/path/to/src/main/java -p=package.name
```

コマンドが無事に成功すると、-oと-pで指定したディレクトリに「Token.java」という名前でラッパークラスが生成されます。

準備が完了しましたので、ラッパークラスを利用して、コントラクトをデプロイとメソッド呼び出しを行ってみましょう。

```java
// Ethereumクライアントと接続
Web3j web3j = Web3j.build(new HttpService("http://localhost:8545"));

// Walletファイルを読み込み
Credentials credentials = WalletUtils.loadCredentials(
    "pass1", // アカウントのパスワード
    "/pass/to/walletfile"
    );	

// コントラクトをデプロイ
Token token = Token.deploy(
    web3j,
    credentials,
    BigInteger.valueOf(1000000000L), // gasPrice
    BigInteger.valueOf(4700000L), // gasLimit
    BigInteger.valueOf(100000000000L) // initialSupply（コンストラクタ引数）
    ).send();

// コントラクトのアドレスを出力
System.out.println("contract address: " + token.getContractAddress());

// メソッド呼び出し（トランザクションなし）
System.out.println("totalSupply: " + token.totalSupply().send());
System.out.println("balance of user1: " + token.balanceOf("0x766a1c4737970feddde6f2b8659fca05bd0339ab").send());

// メソッド呼び出し（トランザクションあり）
System.out.println("Sending ether to user2...");
TransactionReceipt tran = token.transfer("0xdc87fc3ac1ec09ed43623dd9da4c6f3d792a88f5", BigInteger.valueOf(1000L)).send();

// 結果出力
System.out.println("transaction hash: " + tran.getTransactionHash());
System.out.println("balance of user1: " + token.balanceOf("0x766a1c4737970feddde6f2b8659fca05bd0339ab").send());
System.out.println("balance of user2: " + token.balanceOf("0xdc87fc3ac1ec09ed43623dd9da4c6f3d792a88f5").send());
```

```:出力(例)
contract address: 0x7b1530efbcbc4e9588fea3e505a95a3aeb84f3ed
totalSupply: 100000000000
balance of user1: 100000000000
Sending ether to user2...
transaction hash: 0x47cea479cd5aa3b4fc2fda5a0fcf6f5edc83ad2692a10355338b43aad9de41ef
balance of user1: 99999999000
balance of user2: 1000
```

コントラクトのデプロイやメソッド呼び出しを、とてもシンプルに記述できているのが分かると思います。

### イベントをリアクティブに処理
最後にイベント監視の機能を紹介します。
web3jでは、Ethereumのイベントをリアクティブに処理する機能を提供しています。

JSON-RPCを使ったイベント監視は、通常であれば、まず`eth_newBlockFilter`や`eth_newFilter`等を使ってフィルターを登録し、次に`eth_getFilterChanges`を使ってイベントをポーリングする、という流れになります。
これはとても面倒ですよね。

web3jが提供する機能を利用すれば、イベント監視は以下のように記述できます。

```
// Ethereumクライアントと接続
Web3j web3j = Web3j.build(new HttpService("http://localhost:8545"));

// フィルターを登録（イベント監視を開始）
Disposable disposable = web3j.blockFlowable(false).subscribe(ethBlock -> {
    // 新しいブロックが発生した際に、以下の処理を行う
    Block block = ethBlock.getBlock();
    System.out.println(block.getNumber());
});

// フィルターを削除（イベント監視を終了）
disposable.dispose();
```

とてもシンプルに書けますね。