概要
他サービスと連携させると、web3.jsを使えないケースが往々にしてある。
json-rpcでコントラクトを呼び出せると、できることの幅が広がる。
というわけでやる。
今回は燃料不要なメソッド(view)だけ
- contract.totalSuppry()
- name()
- contract.getMaru(uint256)
下準備
json-rpcに必要な情報を取得する
rpcから起動するにはシグネチャが必要らしい
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)
#2枚買っとく
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メソッドを作成する
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}'