EthereumノードにJSON-RPC API経由でアクセスする

go-ethereum（gethクライアント）のHTTP-RPCに接続し、コントラクトにアクセスするまでの手順のメモです。
https://github.com/ethereum/wiki/wiki/JSON-RPC

## ノードを起動する

gethコマンド https://github.com/ethereum/go-ethereum/blob/master/README.md のコマンドラインオプションで、HTTP-RPCを有効にするのに関係するものは、以下の通り。

| オプション | 説明 |
|:-------------------|:-----|
| --rpc	| HTTP-RPCサーバを有効にする。 |
| --rpcaddr _value_ | エンドポイントのホストアドレスの指定。無指定の場合、"localhost"。|
| --rpcport _value_ | エンドポイントのポート番号の指定。無指定の場合、8545。|
| --rpcapi _value_                                   | APIで受け付けるHTTP-RPCインターフェースの種類の指定。インターフェース名をコンマ区切りのリストで記述する。無指定の場合、"eth,net,web3"。|

HTTP-RPCには、アクセス制御がないため、他のホストからアクセスされた場合、無制限に制御されてしまう恐れがある。故に"localhost"以外を指定することは、まず無い。

rpcapiオプションで指定できるインターフェース名は、admin, debug, eth, miner, net, personal, rpc, txpool, web3。

## APIへのアクセス形式

JSONRPC 2.0形式でアクセスする。http://www.jsonrpc.org/specification

要点は、

* HTTPプロトコルのPOSTメソッドで、JSON形式のリクエストを送信する。
* 実行を要求するメソッドはObject型で送信する。Object型のデータの要素は以下の通り、

| リクエスト要素 | 説明 |
|:----|:-----|
| "jsonrpc" | "2.0" （固定値）|
| "method" | 実行するメソッド名。|
| "params" | メソッドのパラメータ。パラメータのリストをArray型で書く。パラメータがない場合は、省略可。|
| "id" | 整数、または、文字列。サーバは、リクエストと同じ値をレスポンスに含める。|

* レスポンスは、JSON形式で、以下の要素を含むオブジェクト。

| レスポンス要素 | 説明 |
|:------------|:-----|
| "jsonrpc" | "2.0" |
| "result" | レスポンスの値。エラーのときは、出力しない。|
| "error" | エラーのときの値。正常時は出力しない。|
| "id" | リクエストに含まれていた値。|

リクエストのオブジェクトをArray型のデータにまとめて送信しても良い。サーバは、Array型のデータにまとめてレスポンスを返す。ただし、要素の順序は保たれないので、オブジェクトのid要素を見て対応させる。

```json:リクエストの例
{"jsonrpc":"2.0","id":59113867,"method":"eth_getBalance","params":["0xb10f7d9592127d769fd8f291da8d96e716d74f2e","latest"]}
```

```json:レスポンスの例
{"jsonrpc":"2.0","id":59113867,"result":"0x7509a969b7e7129e0"}
```

## データのエンコーディング

リクエストパラメータやレスポンスに含まれる数値や文字列のデータは、HEX value encodingによりエンコードされる。

### 数値

* 先頭に"0x"を付加した16進数形式。
* 最上位桁に"0"をつけない。"0x0400"は不可。"0x400"が正解。
* ただし、0の場合は、"0x0"。

### 生データ

* 先頭に"0x"を付加した16進形式。
* 1バイトに付き、2桁の16進数。
* 桁数は、必ず偶数になる。
* "0x41746f5a"は、4バイトの"AtoZ"。
* 長さ0バイトのデータは、"0x"。
* 文字列、アドレス、ハッシュ値などは、この形式で表す。

### ブロックパラメータ

メソッド eth_getBalance、eth_getCode、eth_getTransactionCount、eth_getStorageAt、eth_callに指定するブロックパラメータは、以下のいずれか。

1. ブロック番号（上記エンコーディング形式）
2. "earliest"（エンコードせず、そのままの文字列）
3. "latest"
4. "pending"

## curlコマンドでアクセスする

gethクライアントのJSON-RPC APIエンドポイントに対して、JSON形式のリクエストを送る。

```bash:
curl -s -X POST --data '{"jsonrpc":"2.0","id":67867379,"method":"eth_getBalance","params":["0xb10f7d9592127d769fd8f291da8d96e716d74f2e","latest"]}' http://localhost:8545
```

正常に実行されると、以下の様な実行結果が返る。

```
{"jsonrpc":"2.0","id":67867379,"result":"0x7509a969b7e7129e0"}
```

エラーがある場合、以下の様な実行結果が返る。

```
{"jsonrpc":"2.0","id":67867379,"error":{"code":-32602,"message":"invalid argument 0: hex string has length 0, want 40 for Address"}}
```

## 種々のインターフェースのメソッド

### [JSON RPC APIドキュメント](https://github.com/ethereum/wiki/wiki/JSON-RPC)に記述されたメソッド

例示の通り、記述されたメソッドをJSON RPC形式で呼び出す。パラメータのデータ、レスポンスのデータは、上記のとおりにHEX value encodingする。

### [Management APIsドキュメント](https://github.com/ethereum/go-ethereum/wiki/Management-APIs)に記述されたメソッド

RPCメソッドとして記述されている形式で呼び出す。

```json:JSONリクエスト
{"jsonrpc":"2.0","id":5777140,"method":"personal_listAccounts","params":[]}
```

```json:レスポンス
{"jsonrpc":"2.0","id":5777140,"result":["0xb10f7d9592127d769fd8f291da8d96e716d74f2e","0x2e1bf5fa73d449f9d7af2b39fe8181fab8f919c0","0xaba5c2701430a0eede7854b5d0d6ba1361be0f4b","0x8019e8d64157f51c18d62fc8b8ca3365d64e1132"]}
```

ファンクションのパラメータ列は、Array型の列で記述する。

```js:javascript
personal.unlockAccount("0xb64568b46d1ff83d3ddd24bb25f73efdbb9b3cbb ", "passwd")
```

```json:JSONリクエスト
{"jsonrpc":"2.0","id":253498898,"method":"personal_unlockAccount","params":["0xb64568b46d1ff83d3ddd24bb25f73efdbb9b3cbb","passwd"]}
```

```json:レスポンス
{"jsonrpc":"2.0","id":253498898,"result":true}
```

## 送金のトランザクションを発行する

残高0のアカウント"0xb64568b46d1ff83d3ddd24bb25f73efdbb9b3cbb"に、アカウント"0xb10f7d9592127d769fd8f291da8d96e716d74f2e"から1,000,000wei 送金する。

eth_getBalanceメソッドの第2パラメータに"latest"を指定すると、発掘済みの最新の残高の取得、"pending"を指定すると、まだ発掘が済んでいないトランザクションを含んだ残高の取得になる。

送金額には、HEX value encodingの数値型で、"0xf4240"を指定する。

```json:JSONリクエスト
{"jsonrpc":"2.0","id":54533442,"method":"eth_getBalance","params":["0xb64568b46d1ff83d3ddd24bb25f73efdbb9b3cbb","latest"]}
{"jsonrpc":"2.0","id":76981351,"method":"personal_unlockAccount","params":["0xb10f7d9592127d769fd8f291da8d96e716d74f2e","passwd"]}
{"jsonrpc":"2.0","id":29141882,"method":"eth_sendTransaction","params":[{"from":"0xb10f7d9592127d769fd8f291da8d96e716d74f2e","to":"0xb64568b46d1ff83d3ddd24bb25f73efdbb9b3cbb","value":"0xf4240"}]}
{"jsonrpc":"2.0","id":204573526,"method":"eth_getBalance","params":["0xb64568b46d1ff83d3ddd24bb25f73efdbb9b3cbb","pending"]}
```

```json:レスポンス（順に）
{"jsonrpc":"2.0","id":54533442,"result":"0x0"}
{"jsonrpc":"2.0","id":76981351,"result":true}
{"jsonrpc":"2.0","id":29141882,"result":"0x35dfc1cd85ba4c5133d981d5522053424308f2ad8c464ab8497110578da96264"}
{"jsonrpc":"2.0","id":204573526,"result":"0xf4240"}
```

## コントラクトにアクセスする

既に、コントラクトをブロックチェーン上に作成し、アドレスを得ているものとする。

```js:コントラクト
pragma solidity ^0.4.0;

contract SimpleStorage {
    uint storedData;

    function set(uint x) {
        storedData = x;
    }

    function get() constant returns (uint) {
        return storedData;
    }
}
```

上記のコントラクトの get() ファンクションを呼び出すには、eth_callメソッドを使い、第1パラメータのオブジェクトの"from"要素に自身のアドレス、"to"要素にコントラクトのアドレス、"data"要素にABI形式のデータ（ https://github.com/ethereum/wiki/wiki/Ethereum-Contract-ABI ）で "get()" を記述する。

"data"要素の値は、"0x"で始まるABI形式のデータで、呼び出すファンクションのシグネチャと、それに続くファンクションのパラメータをエンコードしたものを連結したもの。

ファンクションのシグネチャデータは、以下の形式のデータ。

* ファンクションのプロトタイプを標準形式で書く。ファンクション名 + 開き括弧 + コンマ区切りパラメータのデータ型 + 閉じ括弧。空白は入れない。
* ファンクションのプロトタイプをSHA-3ハッシュ関数でハッシュ値を得る。
* ハッシュ値の最初の4バイトをファンクションのシグネチャデータとする。

get()ファンクションの場合、標準形式で "get()"、よって、SHA-3ハッシュ値は、以下の通り。

```
SHA3("get()") = 0x6d4ce63caa65600744ac797760560da39ebd16e8240936b51f53368ef9e0e01f
```

ハッシュ値の最初の4バイトを取り出し、先頭に"0x"を付加して、最終的に、"0x6d4ce63c" となる。

ファンクションにパラメータがないので、ABI形式のデータは、このまま、"0x6d4ce63c" を指定する。

get()の戻り値は、uint型なので、ABI形式では、"0x"で始まる64桁の16進数。

```json:JSONリクエスト
{"jsonrpc":"2.0","id":200603988,"method":"eth_call","params":[{"from":"0xb10f7d9592127d769fd8f291da8d96e716d74f2e","to":"0x4bf6de3f5bb6a3f8532bd7002b8fcbf551dff46f","data":"0x6d4ce63c"},"pending"]}
```

```json:レスポンス
{"jsonrpc":"2.0","id":200603988,"result":"0x00000000000000000000000000000000000000000000000000000000000000f6"}
```

コントラクトの send(uint) ファンクションを呼び出して、 send(1800) を実行するとする。

このファンクションを実行するには、eth_sendTransactionメソッドを使いトランザクションを発行する。第1パラメータのオブジェクトの "from" 要素に自身のアドレス、"to" 要素にコントラクトのアドレス、"data" 要素にABI形式のデータで "send(1800)" を記述する。

ファンクションのシグネチャは、プロトタイプ "set(uint256)" のSHA-3ハッシュ関数の値。uint型は、標準形式では、"uint256"と書く。

```
SHA3("set(uint256)") = "0x60fe47b16ed402aae66ca03d2bfc51478ee897c26a1158669c7058d5f24898f4"
```

よって、シグネチャは、"60fe47b1"。

パラメータの1800は、ABI形式では、64桁の16進数で、"0000000000000000000000000000000000000000000000000000000000000708"となる。

ファンクションのシグネチャにパラメータを連結して、最終的に、"0x60fe47b10000000000000000000000000000000000000000000000000000000000000708" を"data"要素の値に指定する。

```json:JSONリクエスト
{"jsonrpc":"2.0","id":39140722,"method":"personal_unlockAccount","params":["0xb10f7d9592127d769fd8f291da8d96e716d74f2e","passwd"]}
{"jsonrpc":"2.0","id":151345098,"method":"eth_sendTransaction","params":[{"from":"0xb10f7d9592127d769fd8f291da8d96e716d74f2e","to":"0x4bf6de3f5bb6a3f8532bd7002b8fcbf551dff46f","data":"0x60fe47b10000000000000000000000000000000000000000000000000000000000000708"}]}
```

```json:レスポンス（順に）
{"jsonrpc":"2.0","id":39140722,"result":true}
{"jsonrpc":"2.0","id":151345098,"result":"0x03e1314a409e8cffa55c7adbc413cb98b6ad17c60ae2dabd6dc30bf88b1979a5"}
```

## ABI形式のファンクションパラメータ

### 基本型

| 型 | 例 |
|:-----:|:------|
| uint8 〜 uint256 |   0000000000000000000000000000000000000000000000000de0b6b3a7640000 |
| int8 〜 int256 | fffffffffffffffffffffffffffffffffffffffffffffffff21f494c589c0000 |
| bytes1 〜 bytes32 | バイトデータ（32バイト境界にパディング） 5468697320697320746573742e00000000000000000000000000000000000000 |
| string | uint形式のバイト長 + UTF-8のバイトデータ（32バイト境界にパディング） 000000000000000000000000000000000000000000000000000000000000001b e38193e3828ce381afe38386e382b9e38388e381a7e38199e380820000000000 |

### 固定長配列

要素の数だけデータを並べて連結する。

### 可変長配列

パラメータの数だけバイトオフセット値を並べ、その後配列のバイト長 + 要素の数だけのデータを並べる。

set(uint256,string)のファンクションに対して、set(12, "This is test.")を実行するとする。この時、"data"要素の値は、以下になる。（可読性のために改行を入れている）

```
"0x
64371977
000000000000000000000000000000000000000000000000000000000000000c
0000000000000000000000000000000000000000000000000000000000000040
000000000000000000000000000000000000000000000000000000000000000d
5468697320697320746573742e00000000000000000000000000000000000000"
```

2行目がファンクションシグネチャ、3行目がuint256形式の12、4行目が第2パラメータパラメータのオフセット値、5行目が文字列の長さのバイト数、6行目が文字列データで後ろに00をパディング。