[Ethereum] コントラクトをJSON-RPCから呼び出す

# 概要
他サービスと連携させると、web3.jsを使えないケースが往々にしてある。
json-rpcでコントラクトを呼び出せると、できることの幅が広がる。

というわけでやる。
今回は燃料不要なメソッド(view)だけ

- contract.totalSuppry()
- name()
- contract.getMaru(uint256)


# 下準備

[gethのインストール 〜 トークン作成まで](https://qiita.com/marutake/items/4236217e5203b5a8ceb7)

## json-rpcに必要な情報を取得する

rpcから起動するにはシグネチャが必要らしい
[How to call a contract method using the eth_call JSON-RPC API](https://ethereum.stackexchange.com/questions/3514/how-to-call-a-contract-method-using-the-eth-call-json-rpc-api)

めんどいのでgethから```contractObj.methodName.getData(methodParams)``` で取得する。


1.gethコンソールにいく

```
geth --networkid "19" --nodiscover --datadir "eth_private"  --rpc --rpcaddr "localhost" --rpcport "8545" --rpcapi "eth,net,web3,personal,accounts,miner,admin" --rpccorsdomain "*" --unlock "0"  console 2>> eth_private/geth_err.log

> Unlocking account 0 | Attempt 1/3
> Passphrase: 
> Welcome to the Geth JavaScript console!

#改行を削除したabiをここにベタっと貼る
abi　=  [{"constant":true,"in... "event"}]
#コントラクトのアドレス
address = "0x9876..."

test=eth.contract(abi).at(address)

#２枚買っとく
test.mint({from:eth.accounts[0],value:web3.toWei(1),gas:30000000})
test.mint({from:eth.accounts[0],value:web3.toWei(1),gas:30000000})

#掘る(自動化推奨）
miner.start(1)
miner.stop()

# json-rpcでの呼び出しに必要な情報の取得
test.totalSuppry.getData()
> "0x18160ddd"
test.name.getData()
> "0x06fdde03"
test.getMaru.getData(1)
> "0x1e20aef70000000000000000000000000000000000000000000000000000000000000001"
```

ここでgetDataで取得した値は、後でコントラクトのメソッドを叩く時に使う。
ファンクションシグネチャとか言うらしい。
まぁ何だろう、コントラクトはブロックチェーン上にデプロイされていてbytecodeになっているので、そのメソッドをrpcで叩きたいならbytecodeでやらないといかん的なあれそれ。

多分関数のアドレスなのかな？引数がある時はお尻につく。

例：
getMaruは_tokenIndexを引数にしているので、getDataにその引数を渡してやる。
すると、得られるbytecodeの末尾に引数の1が追加されている。

# json-rpcを叩いてみる

- 前提
  - eth.accounts[0]のアドレスは"0x12345..." とする
  - コントラクトのアドレスは        "0x9876..." とする

ターミナルを開く

```

#まず繋がってるか確認
 curl -H "Content-type: application/json" -H "Accept: application/json" -X POST http://localhost:8545  --data '{"jsonrpc":"2.0","method":"web3_clientVersion","params":[],"id":1}'
> {"jsonrpc":"2.0","id":1,"result":"Geth/v1.8.2-stable/darwin-amd64/go1.10"}


## 大丈夫そうならコントラクトを叩く

＃contract.totalSuppry() == 6　の時
curl -H 'Origin: localhost' -H "Content-type: application/json" -H "Accept: application/json" -X POST http://localhost:8545  --data '{"jsonrpc":"2.0","method":"eth_call","params":[{"from":"0x12345...", "to":"0x9876...", "data":"0x18160ddd"},"latest"],"id":1}'
> {"jsonrpc":"2.0","id":1,"result":"0x0000000000000000000000000000000000000000000000000000000000000006"}

＃contract.name() == "NewMaruToken"の時
curl -H 'Origin: localhost' -H "Content-type: application/json" -H "Accept: application/json" -X POST http://localhost:8545  --data '{"jsonrpc":"2.0","method":"eth_call","params":[{"from":"0x12345...", "to":"0x9876...", "data":"0x06fdde03"},"latest"],"id":1}'
> {"jsonrpc":"2.0","id":1,"result":"0x0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000c4e65774d617275546f6b656e0000000000000000000000000000000000000000"}

#contract.getMaru(1) == ["0x12345...", "foo", 3, 0, 1522406948, 1000000000000000000, 2] の時
 curl -H 'Origin: localhost' -H "Content-type: application/json" -H "Accept: application/json" -X POST http://localhost:8545  --data '{"jsonrpc":"2.0","method":"eth_call","params":[{"from":"0x12345...", "to":"0x9876...", "data":"0x1e20aef70000000000000000000000000000000000000000000000000000000000000001"},"latest"],"id":1}'
> {"jsonrpc":"2.0","id":1,"result":"0x00000000000000000000000075e2ce17d5b38bd1982f209d089d76797afc736600000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005abe16240000000000000000000000000000000000000000000000000de0b6b3a764000000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000003666f6f0000000000000000000000000000000000000000000000000000000000"}

```

完


## 戻り値がちょっとわかんない

戻り値のresultは見たまんまバイト列なのでweb3.toDecimalとかweb3.toUtf8とかかけるといいらしい

```
# case:1 totalSuppry() のresult ==> 6 
"0x0000000000000000000000000000000000000000000000000000000000000006"
↓
web3.toDecimal("0x0000000000000000000000000000000000000000000000000000000000000006")
> 6


# case:2 name() のresult ==> "NewMaruToken" 
"0x0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000c4e65774d617275546f6b656e0000000000000000000000000000000000000000"

web3.toUtf8("0x0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000c4e65774d617275546f6b656e0000000000000000000000000000000000000000")
> ""
web3.toAscii("")
> "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00 \x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\fNewMaruToken\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"

```

いきなり躓く。
toAsciiの奥の方に一応　"fNewMaruToken"　なるものがあったが、こんなん貰ってもしようもない。


## ちょっと考える

送信がbyteだから戻りもbyteだろう。
んで固定長だろう。
totalSuppryの長さで改行してみる。0xは除去

```
# nameのresult

0000000000000000000000000000000000000000000000000000000000000020
000000000000000000000000000000000000000000000000000000000000000c
4e65774d617275546f6b656e0000000000000000000000000000000000000000

```

"NewMaruToken" が12文字で16進だと"c"。
"4e65774d617275546f6b656e0000000000000000000000000000000000000000"
は分からんけど文字な気がする

```
web3.toUtf8("4e65774d617275546f6b656e0000000000000000000000000000000000000000")
> "NewMaruToken"
```

解決。```020```は何だろう、行番号かな？
とりあえずgetMaruも改行してみる。

```
# getMaru(1)
# ["0x12345...", "foo", 3, 0, 1522406948, 1000000000000000000, 2]

00000000000000000000000012345...................................
00000000000000000000000000000000000000000000000000000000000000e0
0000000000000000000000000000000000000000000000000000000000000003
0000000000000000000000000000000000000000000000000000000000000000
000000000000000000000000000000000000000000000000000000005abe1624
0000000000000000000000000000000000000000000000000de0b6b3a7640000
0000000000000000000000000000000000000000000000000000000000000002
0000000000000000000000000000000000000000000000000000000000000003
666f6f0000000000000000000000000000000000000000000000000000000000
↓
#行番号入れてみる
000 : 00000000000000000000000012345...................................
020 : 00000000000000000000000000000000000000000000000000000000000000e0
040 : 0000000000000000000000000000000000000000000000000000000000000003
060 : 0000000000000000000000000000000000000000000000000000000000000000
080 : 000000000000000000000000000000000000000000000000000000005abe1624
0a0 : 0000000000000000000000000000000000000000000000000de0b6b3a7640000
0c0 : 0000000000000000000000000000000000000000000000000000000000000002
0e0 : 0000000000000000000000000000000000000000000000000000000000000003
100 : 666f6f0000000000000000000000000000000000000000000000000000000000
```

行番号くさい。数値の32(e0)と番地指定のe0はどう見分けるのか。
いや多分型を知ってる前提なのだろう。stringの場合は文字数が格納されてるアドレスへの参照で、文字の実体は次に存在するみたいな。

## 確かめる

文字列返しまくりなtestメソッドを作成する

```solidity:ERC721MaruToken.sol
    function testMaru() public view returns(string,string,string,string,string,string,string){
        return ('いっち','2','three','四','5','6','7');
    }
```

↓
compile
↓
runタブ移ってcreate
↓
gethでabiとaddress設定して、いつもの

```
#geth
abi=/* 今作ったやつのabi */
address=/* 今作ったやつのaddress */
test=eth.contract(abi).at(address)
test.testMaru.getdata()
> 0x54a70de6
```

んでcurlからの整列

```
# testMaru() ==> ["いっち", "2", "three", "四", "5", "6", "7"]

curl -H 'Origin: localhost' -H "Content-type: application/json" -H "Accept: application/json" -X POST http://localhost:8545  --data '{"jsonrpc":"2.0","method":"eth_call","params":[{"from":"0x75e2ce17d5b38bd1982f209d089d76797afc7366", "to":"0xed2e9733bedb0a6ae5647a632626bf892549f918", "data":"0x54a70de6"},"latest"],"id":1}'
> {"jsonrpc":"2.0","id":1,"result":"0x00000000000000000000000000000000000000000000000000000000000000e00000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000016000000000000000000000000000000000000000000000000000000000000001a000000000000000000000000000000000000000000000000000000000000001e0000000000000000000000000000000000000000000000000000000000000022000000000000000000000000000000000000000000000000000000000000002600000000000000000000000000000000000000000000000000000000000000009e38184e381a3e381a1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000013200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000574687265650000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003e59b9b0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000135000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001360000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000013700000000000000000000000000000000000000000000000000000000000000"}

↓

00000000000000000000000000000000000000000000000000000000000000e0
0000000000000000000000000000000000000000000000000000000000000120
0000000000000000000000000000000000000000000000000000000000000160
00000000000000000000000000000000000000000000000000000000000001a0
00000000000000000000000000000000000000000000000000000000000001e0
0000000000000000000000000000000000000000000000000000000000000220
0000000000000000000000000000000000000000000000000000000000000260
0000000000000000000000000000000000000000000000000000000000000009
e38184e381a3e381a10000000000000000000000000000000000000000000000
0000000000000000000000000000000000000000000000000000000000000001
3200000000000000000000000000000000000000000000000000000000000000
0000000000000000000000000000000000000000000000000000000000000005
7468726565000000000000000000000000000000000000000000000000000000
0000000000000000000000000000000000000000000000000000000000000003
e59b9b0000000000000000000000000000000000000000000000000000000000
0000000000000000000000000000000000000000000000000000000000000001
3500000000000000000000000000000000000000000000000000000000000000

↓

000 : 00000000000000000000000000000000000000000000000000000000000000e0
020 : 0000000000000000000000000000000000000000000000000000000000000120
040 : 0000000000000000000000000000000000000000000000000000000000000160
060 : 00000000000000000000000000000000000000000000000000000000000001a0
080 : 00000000000000000000000000000000000000000000000000000000000001e0
0a0 : 0000000000000000000000000000000000000000000000000000000000000220
0c0 : 0000000000000000000000000000000000000000000000000000000000000260
0e0 : 0000000000000000000000000000000000000000000000000000000000000009
100 : e38184e381a3e381a10000000000000000000000000000000000000000000000
120 : 0000000000000000000000000000000000000000000000000000000000000001
140 : 3200000000000000000000000000000000000000000000000000000000000000
160 : 0000000000000000000000000000000000000000000000000000000000000005
180 : 7468726565000000000000000000000000000000000000000000000000000000
1a0 : 0000000000000000000000000000000000000000000000000000000000000003
1c0 : e59b9b0000000000000000000000000000000000000000000000000000000000
1e0 : 0000000000000000000000000000000000000000000000000000000000000001
200 : 3500000000000000000000000000000000000000000000000000000000000000
220 : 0000000000000000000000000000000000000000000000000000000000000001
240 : 3600000000000000000000000000000000000000000000000000000000000000
260 : 0000000000000000000000000000000000000000000000000000000000000001
280 : 3700000000000000000000000000000000000000000000000000000000000000
```

目が痛い。番地で間違いなさそう。

# まとめ

- getDataは便利
- ブロックチェーン上のコントラクトがbytecodeなのでjson-rpcでやる時もbytecode
- params、resultに型情報は入ってない（だからabiが必要なのやも）
- 可変長のデータはstring同様、参照が入ってるはず


```
#curl書式
curl -H "Content-type: application/json" -H "Accept: application/json" -X POST http://localhost:8545  --data '{"jsonrpc":"2.0","method":"eth_call","params":[{"from":sender_address, "to":contract_address, "data":function_bytecode},"latest"],"id":1}'
```

# 参考
[EthereumノードにJSON-RPC API経由でアクセスする](https://qiita.com/kanehara/items/5f09401813b52a326ac5)