Ethereum Indexer比較
WIP
概要
ブロックチェーンの情報をDBにエクスポートするにあたって既存ツールの調査・検証・比較を行う。
ブロック生成(できればイベント発火)毎にクエリしたい。
調査・検証内容
- 各主要ツールの一覧と特徴を整理
実際につかってみる、ドキュメントや記事をよむ、コードを読む etc..
- アーキテクチャを理解する
制御情報(途止めた場合。どのブロックまでsyncしたかなど)
定期実行の仕組み
- データ(特にevent)がどういった形で格納されるか確認する
どういった単位でsyncされるか:block単位?event単位?
できればeventごとにDBスキーム決めたい
reorgを実験する
eventeum sqlを動かす
グラフ更新
TL;DR
https://gyazo.com/4a2d580c16592ed30e435c4a26352cd4
ether-sql
全体的に使いやすいが、同期が30秒(2ブロック)毎と少しリアルタイム性に欠ける。統計、解析向き。
Ethereum-etl
セットアップが簡単でエンジニアでなくても扱いやすい。CSVへのエクスポートなど統計、解析向き。Google BigQueryで使われている。
eth.events
APIキーだけもらってSQLを操作する。パブリックな情報を解析するサービス向け。
eventeum
WebSocketでイベントの発火を取得してくれる。SQLもサポート予定。
The Graph
WebSocketでイベントの発火を取得してくれる。クエリはGrapsQLを使用。
事前整理
- EventWatcherってナニ?
ブロックチェーン上にデプロイされたコントラクトから発火するEventを拾って、何らかの後続処理に投げるコンポーネント
- indexerってナニ?
受け取ったイベントデータ(RLP形式)をデータベースのテーブルスキーマに合致する形にパースし永続化させる
- どういう時に使える?
ブロックチェーンの情報を取得してDBに格納、解析などしたい時。
ブロックチェーンの情報をトリガーにして何かしらのイベントを起こしたい時(bridgeなど)
各ツール整理
ether-sql (python)
- 概要
analysether社が開発するethereum blockchainのデータをsqlデータベースにプッシュするPythonライブラリ。INFURA(かローカルのノード) , postgreSQL, RabbitMQを利用する。ブログもgithubも1年前から更新がない。
- アーキテクチャ
https://gyazo.com/88d9dd1dba27326c1709033acc86b667
ブロックチェーンの情報は2つのcelery queueを使うことで随時取得可能。
①1つ目のqueueは30秒毎に(約2ブロック毎)新しいブロックを探し、(遅くないか?)
②新しく発見されたものを2つ目のqueueにプッシュする。
③2つ目のqueueは1つ目からプッシュされた最新の情報をもとにノードからブロックデータを取得し、
④詳細な情報をデータベースにプッシュする。
プッシュ時はデータベースの最後のブロックを確認し、後続のブロックをプッシュしていく。
queue1はひたすらブロックの最新状況をチェックするマン、queue2はqueue1からの情報をもとにDBにプッシュするマンと役割分担している模様。reorgへの言及はなし。
- 制御情報
テーブル一覧
https://gyazo.com/7b8a3717981d15e4848639d6b7feccce
logsというテーブルはあるがeventまでデコードした情報がないのでイベントを検知する場合は
① logsのtopicとコントラクトaddressで合致するイベントを探索
② 検知したトランザクションのデータをデコード (自分で実装)
の流れになりそう。
- データ
上述のようにeventの情報はない。Logsのテーブルは以下のようになっていて、イベントデータの形式はLogをデコードする過程で自分で決める。
https://gyazo.com/9c57889195c5eb3dcba879fdbc4f304b
0xB49180D443dC4Ca6028de0031Ac09337891fd8cEというコントラクトからCoinTransfer (address sender, address receiver, uint256 amount)というイベントを検出する場合対応するtopicは0x16cdf1707799c6655baac6e210f52b94b7cec08adcaf9ede7dfe8649da926146なので以下のようになる。
https://gyazo.com/a8b691d840c52a50cf5ae43de54270e4
dataをデコードすれば必要な情報は得られる。
- メモ
postgreSQLの環境構築
celery queues周りの解説
②の参考
Deep dive into Ethereum logs (ログをデコードする)
👆の参考
Technical Introduction to Events and Logs in Ethereum (イベントとログの役割)
ethereum-etl (python) / BigQuery
- 概要
Ethereumの様々なデータ(blocks, transactions, ERC20 / ERC721 tokens, transfers, receipts, logs, contracts, internal transactions)を抽出、変換、ロードできるpythonスクリプト。Google BigQueryで利用可能。google大先生公認というだけあって非常に情報がまとまっている。
- アーキテクチャ
https://gyazo.com/f5dfb56c51e8cb93d09f97fff6614b46
上図のようにGoogle Cloud Composerによって全体は制御され、Ethereum-etl (export)とGoogle BigQuery (load)の2つで仕事が分けられる。
Ethereum-etl (export)
EthereumブロックチェーンのデータをCSVまたはJSONでエクスポートできるオープンソースツール。JSON RPC APIでparity nodeに接続し、随時ブロックとトランザクションのデータを取得する。
https://gyazo.com/ccf85d5f45d211cf728c37012b0f7ae9
ethereum-etlではまずblocks_and_transctionsをexport_receipts_and_logsで生成したファイルから指定のイベントログでフィルターにかけエクスポートすることが可能。extract_token_transfers の場合はexport_receipts_and_logs, で生成されたファイルをtransferイベントのログでフィルターにかけエクスポートする。(valueのところのロジックをいじればidと名前とかに変更できそう)
Google BigQuery (load)
ethereum-etlで取得したデータをGoogle Cloud Storageで移し、BigQueryにloadする。BigQueryコンソールやAPIによってデータをクエリできる。
https://gyazo.com/52cef57972b14797c05aedb833541a6d
wait_* :tasks wait for an export file on a particular day.
load_* :tasks load the data from a Cloud Storage bucket to BigQuery tables.
enrich_* :tasks join multiple tables and cast columns to different types.
verify_* :tasks run SQLs that verify the consistency of the data.
- 制御情報
イベントの検出
token_transfersとlogsというテーブルはあるが全てのeventをデコードした情報がないのでether-sqlと同様
① logsのtopicとaddressでイベントを検知
② 検知したイベントの引数をデコード (自分で実装)
の流れになりそう。
ERC20、721トークンの場合はtoken_transfersから検出が可能。
Streaming (lambda architecture)
どこまでのデータをsyncしたかはlast_synced_blockで保存される。--lag オプションによってブロックを遡ることが可能。これによりre-orgにも対応可。
- データ
https://gyazo.com/700bb264408c04102938aa973e6887cf
上述のようにイベントの情報はない。Logsはある。イベントデータの形式はLogをデコードする過程で自分で決める。
ログからイベントのの検出例
https://gyazo.com/00c308c359f8c4fb0c015889f9d89071
- メモ
Ethereum BigQuery ドキュメント
llambda architecture
eth.events
- 概要
eth.eventsはホストされたEthereumベースのブロックチェーンのイベントをフィルタリングして検索するAPIを提供する。Elasticsearch と PostgreSQLベース。
ユーザー登録するとAPIキーがもらえる。
https://gyazo.com/a49cacd96aa2873e55a49e1263415e5c
ノードの運用でデータがバレる?
- アーキテクチャ
https://gyazo.com/bc6c11a9b11f1fbcf37966c48026aec3
- 制御情報
上図のようにテーブルがBlock, TX, Log, Event, Call, Trace, Contract, Tokenの8種類 + methodsという謎のテーブルの計9種類用意されており、クエリが可能。サーバーのインデックスは新しいブロックが生成される度にリアルタイムでアップデートされる。新しいブロック生成時にSQLのインデックスも更新され、関係のない情報は除かれるためre-orgへの耐性もある。
- データ
eventのデータ構造&型(一部)
https://gyazo.com/22af6edfd8e7d414f177150254e62cca
例)Struct {uint id; string: name;} の構造体を生成した場合(rinkby)
https://gyazo.com/f64ef2e0e8c93bb771de4c5d5ac47f0c
- メモ
イベントのデコードはABIを提出することで可能に
2019年4月3日のブログによるとlogの91%デコード済みとのこと。自分でeth.eventsを運用することができないのでABIを提出することでデコードしてもらう必要がある。ABIを提出したくなければ自分でlogからデコードするのみ。
Logをクエリをした時ダブりが出た
https://gyazo.com/49144e4cb7778f38b02408bf58781d7d
Eventeum(Java)
- 概要
Ethereumのイベント監視サービス。スマートコントラクトとミドルウェアレイヤーをbridgeする。ユーザーはイベントを直接監視することなくイベントに関する情報をsubscribeできる。Kafka/ZookepperとMongoDBを使用。kauriによって開発されており、Consensysに採択されてるプロジェクトでもある。
- アーキテクチャ
https://gyazo.com/431912f64d9f2f32f30b9272a57455ec
まずEventeumがブロックチェーンのデータをsubscribeし、特定のイベント発火時はユーザーのミドルウェアにこのイベントの詳細を含むメッセージをMessage Bus(Kafka またはRabbitMQ)に伝播してくれる。上図には記されていないが、複数のノードからデータを取得することが可能。
ノードのsubscribeはweb3jで行われる。ethereum clientはeventをsubscribeできるようにpub/subメカニズムを実装しているおり、web3jではpollingをより効率的に行うためにHTTP protocolの代わりにWebSocketを利用している。
https://gyazo.com/938c984b675dfb86d459d86b293bde28
- 制御情報
取得できる情報は主にコントラクトイベント、ブロックイベント、トランザクションイベントの3種類。
コントラクトイベント
指定したイベントが発火するとJSON messageがkafka topic または rabbit exchange (contract-events by default)に以下のフォーマットで伝播される
https://gyazo.com/2f5a1add406c5ba42611be68ffd251b9
ブロックイベント
新しいブロックが生成されるとJSON messageがkafka topic または rabbit exchange (contract-events by default)に以下のフォーマットで伝播される
https://gyazo.com/4f27d9f3e4f9c2c93cd3324933c6b7ab
トランザクションイベント(開発中)
モニターと合致するトランザクションが生成されるとJSON messageがkafka topic または rabbit exchange (contract-events by default)に以下のフォーマットで伝播される
https://gyazo.com/4a2bd99dd1bf758c9a01f28895b014be
データ
eventに関するテーブルはcontractEventDetails、contractEventFilter、latestBlockの3つ。
https://gyazo.com/f7ffeca5602ab770c63ac0a49ef17992
- contractEventDetails
イベントの詳細の一覧。トランザクションデータはnonIndexedParametersに格納。事前に指定した型も教えてくれる。
https://gyazo.com/77ef60d39a7b077e03113235ba64c8c1
- contractEventFilter
イベントのidごとにイベントを分類したもの
https://gyazo.com/d785c026ae16942e73b3f66bd018deb2
- latestBlock
最新ブロックの情報👈どこまでのブロックを取得しているかというメタデータ
https://gyazo.com/eed9cdb7fd5283fab2f7dd5ac155191b
- メモ
SQLフォーマット
ドキュメントにはまだ書かれてないがSQLフォーマットに対応するコミットが2019年8月13日にマージされている。
version 0.7.0よりSQLフォーマットをサポート
https://gyazo.com/c63ebcefa0d06f5333326718ccaac739
Fork が起きた場合
Eventeumは完全に承認されるまでのブロック数を指定することができる。仮に承認されるまでの間にフォークが起きた場合、マイナーなチェーンのTransactionをINVALIDとみなすまでのブロック数を指定することができる。
https://gyazo.com/6396ba4984667da9451bf11c96cf7325
Listening for Ethereum Smart Contract Events in Java
Apache Kafkaの概要とアーキテクチャ
The graph(Rust)
概要
The Graphはブロックチェーン上のデータをインデックス化やしたりクエリしてりするための分散プロトコル。IPFS node、Ethereum node、Store(PostgreSQL)によって運用されている。現在はゴリゴリ開発中のため、APIの仕様などはコロコロ変わる可能性があるとのこと。
アーキテクチャー
https://gyazo.com/5326d80ceb3ad7a3defd1fd7e160034b
①dAppがEthereum上のデータを作成/変更するトランザクションを生成する
②スマートコントラクトがイベントを発火する
③The Graph Nodeが特定のイベントを検知し、ユーザーによって定められたmappingのhandlerを呼ぶ
④mappingはWASM runtimeで実行されるWASMモジュールであり、 Ethereumのeventに応じてトランザクションをいくつか生成し、storeを更新する。
⑤storeは更新、インデックス化される
⑥dAppはthe Graph Nodeにクエリし、インデックス化されたブロックチェーンのデータを ノードのGraphQL endpointを利用して取得しようとする。The Graph Nodeはそのクエリをstoreに対してのクエリに変換し、情報を取得する。
⑦dAppはuser-friendlyなフォーマットで情報を表示する。
イベントが発火されるとその瞬間graph nodeがそれを検知し、ブロックに取り込まれた時にstoreは更新される。
制御情報
subgraph.yaml
https://gyazo.com/f1ce1cad26a6aa19bd49f2e9bfb95124
ブロックチェーンからデータを取得する際の制御情報はsubgraph manifestと呼ばれ、上図のsubgraph.yamlに格納される。subgraphはIPFSに保存後にgraph nodeへのデプロイが可能になり、the Graph Explorerでstateを確認することができる。ブロックチェーンとの同期はgenesisブロックから行われ、指定したイベントを取得していく。subgraphに変更を加えた場合は新しいsubgraphをデプロイすることで更新することが可能であり、この場合syncがgenesisブロックから再び行われる。nodeのsyncを中断した場合も続きからまたsyncが始まる。イベントを取得したい場合はsubgraphの以下の項目に欲しいイベント名などを指定する。ただし、subgraphを作成するコマンドでコントラクトから全てのイベントは拾うように自動で設定してくれるのいじる必要はあまりなさそう。
https://gyazo.com/1a75d4bb6383d53edc929594e472218b
reorg が起きた場合はロールバックしてstoreを更新する
graphQL
storeからGraphQLでデータを取得する。subgraphのschema.graphqlというファイルで取得する情報と型を指定。クエリする情報はここで決める。
https://gyazo.com/489e8d8a1ab0763a835de519da8aa323
データ
Store(Postgre)に保存されるデータ
テーブル一覧
https://gyazo.com/3f5238692c77f7adc315bc6f6ef5479c
ブロック情報はあったがデコードされた情報は見当たらないのでクエリ時にデコードされる?(まだ探せてないだけかも)
メモ
- the Graph Explorer(Log)
https://gyazo.com/c5e333e14c76ef4c60e0d55888105054
- graphqlによるクエリ
https://gyazo.com/35408b2c7e5f27e0c4a76fea666aa993
- graph protocol Medium
番外編 ~bridge調査~
Parity bridge(Rust)
parity bridgeってナニ? → まず👆のgithubのread.me or etaro.icon氏による神記事(wipだけど) Parity Bridgeを読んでください - なんで調べるの?
Parity bridgeはブリッジする両チェーンのeventに関する情報ををEvent watcherで監視/検知しており参考になりそう。
MainからSideへのRelay
https://gyazo.com/b8516a12dfc2f0a98c778eed3d617221
1:senderが " Main.relayMessage(data, recipient)を ポチッ"と実行
2: RelayMessage( messageID, sender, recipient)イベント発火
3: sideのauthorityが2のイベント発火をチェックしたら、1RelayMessageイベントにつき一回Side.acceptMessage(transactionHash, data, sender, recepient)を実行
4: SideのAuthoritiesのうち、コンセンサスに必要な署名数が集まった(Side.requiredSignaturesに達した)段階で 、次へ
5:recipientにaddressを指定したコントラクト上のacceptMessage(data,sender)を実行する
6: AcceptedMessage(messageID, sender, recipient)が発火
SideからMainへのRelay
https://gyazo.com/14368ffe71c8da267b1dc5aeee0f2107
1; senderが " Side.relayMessage(data, recipient) をポチッ"と実行
2: Side.RelayMessage( messageID, sender, recipient)イベント発火
3: 2を検知したsideのauthoritiesが、1RelayMessageイベントにつき一回、 Side.submitSignedMessage(signature, message)を(署名とともに)実行
4: SideのAuthoritiesのうち、コンセンサスに必要な署名数が集まった(Side.requiredSignaturesに達した)段階で Side.SignedMessage(authorityThatSubmittedLastSignature, messageHash)イベントが発火
5: 4を検知したAuthoritiesは、MessageHashから message とsignatureを取り出し、そのdataを持って、Main上のMain.acceptMessage(vs, rs, ss, transactionHash, data, sender, recipient)を実行
6: Main.acceptMessage(vs, rs, ss, transactionHash, data, sender, recipient)上で、authoritesのリストのうち、コンセンサスに必要なだけの署名数が存在するかをチェックし、OKだったら 次へ
7: recipientにaddressを指定したコントラクト上のacceptMessage(data,sender)を実行する
8: AcceptedMessage(messageID, sender, recipient)が発火
Tokenbridge(Javascript)
- 概要
POA TokenBridgeはEthereumエコシステムの2つのチェーン間での資産の移転を可能にします。
Oracleは、ネットワーク内の指定されたバリデーターノード(コントラクト内でで指定されたアドレスの秘密鍵を持つノードのみ)にデプロイされます。Remote Procedure Call (RPC)を介して2つのチェーンに接続し、ブリッジコントラクトに関連するイベントの監視と資産の移転を承認するトランザクションの送信を行う。
- アーキテクチャ
Native-to-ERC20
https://gyazo.com/0716fddad983cbc5818a91e1abcf3de7
Watcher
watcherは特定のイベントを監視し、queue内に適切なjobを作成します。これらのjobには、トランザクションデータ(ナンスなし)およびイベントのトランザクションハッシュが含まれます。watcherは指定された頻度で実行され、最新のブロックを追跡します。
- 3つのWatcher
Signature Request Watcher: Home networkのUserRequestForSignatureイベントを監視
Collected Signatures Watcher: Home networkのCollectedSignaturesイベントを監視
Affirmation Request Watcher: ブリッジのモードに依存
Native-to-ERC20: bridge contractの UserRequestForAffirmation を監視
ERC20-to-ERC20 and ERC20-to-Native: token contractのTransferイベントを監視
Sender
送信者はqueueをサブスクライブしながらナンスを追跡します。Senderはqueueからjobを取得し、トランザクションデータを抽出し、適切なナンスを追加して、ネットワークに送信します。
- 2つのSender
Home Sender: Home networkにトランザクションを送る
Foreign Sender: Foreign networkにトランザクションを送る
- メモ
POA Tokenbridge / Oracle
参考
Ethereumでコントラクトの監視