はじめに
前回Geth起動時にHTTP-RPCサーバーを有効にしてローカル環境からEthereumノードのアカウントにリクエストを出すのを試してみました。 ⇒ [Ethereum]curlでアカウントの値を取得する
今回はスマートコントラクトを作成し、このスマートコントラクトへのcallとsendTransactionを試してみます。
コントラクトを作成しデプロイ
まずはsolidityで簡単なコントラクトを作成。
/* 文字列"Hello World"を返すコントラクト */
contract SampleJsonrpc {
function getHello() public pure returns (string memory){
return "Hello World";
}
}
コンパイル後、Gethを起動しデプロイします。
Geth起動時にHTTP-RPCを有効にするのを忘れないでくださいね。よく分からない方は ⇨ https://qiita.com/y_kitchens/items/7df48bb2bf438200d983 を参照してみてください。
ブロックチェーンに取り込まれたらこんな感じになっていると思います。
{
abi: [{
inputs: [],
name: "getHello",
outputs: [{...}],
stateMutability: "pure",
type: "function"
}],
address: "0x907d1d0ddd9430600b34731d88810688bfbc9937",
transactionHash: "0x43bb321b8f87a46af4162352bec8af478fd31c05cdc19da400975d369fb79dc0",
allEvents: function(),
getHello: function()
}
これで準備出来ました。
Gethは起動したまま新しいターミナルを立ち上げる等でローカル環境に戻ります。
function getHelloにcallしてみる
それではローカル環境からEthereumノードのスマートコントラクト内のgetHello()にリクエストを送りレスポンスをもらいます。
json-rpcを記述する
-
コントラクト内の状態を変更せず単に値だけを取得する場合、"eth_call"メソッドを指定します。
-
パラメーターには、送付元アドレス、送付先アドレス、送付データのオブジェクトとオプションを指定します。
- 送付元アドレス: EOAのアドレス
- 送付先アドレス: スマートコントラクトのアドレス
- 送付データ: 呼び出したいfunctionのsignature
[{"from": "0x9f9d52e422572aef077fa89ad9ef55044305ae4c", "to": "0x907d1d0ddd9430600b34731d88810688bfbc9937", "data": "0x8da9b772"}, "pending"]
- signatureの求め方: 呼び出したいfunction名(引数がある場合引数の型含む)をkeccak-256でハッシュ化した値の先頭4byte(8桁)に0xを付加する。
- 今回呼び出したいfunction名 → getHello()
- keccak256(getHello()) → 8da9b772fde003bec0c56958dc8f754856a07b6760d305ee06bc3b02ef954fd6
- 先頭4byte(8桁)を取り出し、0xを付加する → 0x8da9b772
となります。
json-rpc全体ではこんな感じになります。
{"jsonrpc": "2.0", "method": "eth_call", "params": [{"from": "0x9f9d52e422572aef077fa89ad9ef55044305ae4c", "to": "0x907d1d0ddd9430600b34731d88810688bfbc9937", "data": "0x8da9b772"}, "pending"], "id": "1"}
curlでリクエストを送る
リクエストに必要なデータは作成できたのでcurlコマンドでリクエストを送ってみます。
$ curl -X POST -H "Content-Type: application/json" --data '{"jsonrpc": "2.0", "method": "eth_call", "params": [{"from": "0x9f9d52e422572aef077fa89ad9ef55044305ae4c", "to": "0x907d1d0ddd9430600b34731d88810688bfbc9937", "data": "0x8da9b772"}, "pending"], "id": "1"}' http://localhost:8545
レスポンスデータ
リクエストを送るとすぐレスポンスデータが返ってくると思います。
{"jsonrpc":"2.0","id":"1","result":"0x0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000b48656c6c6f20576f726c64000000000000000000000000000000000000000000"}
"result"にfunctionを実行した結果が16進数表記で格納されています。
"result":"0x0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000b48656c6c6f20576f726c64000000000000000000000000000000000000000000"
Ethereumクライアントからの戻り値は固定長か可変長かで形式が違います。
今回はstring型なので可変長の形式で返ってきているので、見やすくするため0xを除いた値を32byte(64桁)ごとに区切り改行します。
0000000000000000000000000000000000000000000000000000000000000020
000000000000000000000000000000000000000000000000000000000000000b
48656c6c6f20576f726c64000000000000000000000000000000000000000000
データの内訳は
・1行目: データのバイトオフセット値
・2行目: データ量
・3行目: データ内容(stringの場合32byteに足りない分は0埋め)
となっていますので、3行目の48656c6c6f20576f726c64をutf-8形式にデコードします。
48656c6c6f20576f726c64 → Hello World
無事Hello Worldが確認できました。
おわりに
本当はsendTransactionも書きたかったけど時間がなくなってしまったのでまた次回書こうと思います。
また、signatureを求めるためにkeccak-256を使用しましたが、SHA3-256とkeccak-256の違いがよく分かっていないのでこの辺も調べてみないといけないかと思います。
Ethereumのアプリ開発はまだ取り組み始めたばかりなので、表現等おかしな箇所があればご指摘いただければありがたいです。
参考サイト
今回json-rpcを使用するにあたり下記サイトを参考にさせていただきました。
ありがとうございます。
[EthereumノードにJSON-RPC API経由でアクセスする]
(https://qiita.com/kanehara/items/5f09401813b52a326ac5)
[[Ethereum] スマートコントラクトを呼び出してみよう(call編)]
(https://medium.com/nayuta-inc/ethereum-%E3%82%B9%E3%83%9E%E3%83%BC%E3%83%88%E3%82%B3%E3%83%B3%E3%83%88%E3%83%A9%E3%82%AF%E3%83%88%E3%82%92%E5%91%BC%E3%81%B3%E5%87%BA%E3%81%97%E3%81%A6%E3%81%BF%E3%82%88%E3%81%86-call%E7%B7%A8-3c6eaf289be7)