アルゴリズムとかオーダーとか

仕事で勉強したことなどをまとめてます

web3.eth.filterとweb3.eth.subscribe

web3.jsは現状2つのバージョンが開発されています。1つはweb3@0.x.x(現行は0.20.5)とweb3@1.0.0です。truffleが利用しているのが0.x.x系で、npm install web3 するとインストールされるのがweb3@1.0.0系です。
0.x.xがstableバージョンであり、1.0.0はまだβバージョンです。
両者の大きな違いは、0.x.x系はcontractのfunction呼び出し等にcallbackが必須となっていますが、1.0.0系ではpromisefyされているのでコールバックは必要なく、Promiseを返してくれるため簡単に同期/非同期処理が書けます。
また、もう一つ大きな違いが、タイトルにあるようにイベントフィルタの仕組みです。1.0.0ではイベントの監視処理がリニューアルされています。

今回はこのイベントフィルタの仕組みについて調べたことをまとめます。

web3.eth.filter@0.x.x

概要

node上で発生したイベントを取得するための機能です。取得したいイベントに対するtopicを設定し、nodeに登録することで変更通知を受け取れるようになります。

機能

web3.eth.filterではlogsイベントのみ取得できます。eth_newBlockFilterとeth_newPendingTransactionFilterは実装していないようです。
web3.eth.filterでイベントを取得するために2つのメソッドが用意されています。

filter.get

fromBlockからtoBlockまでの間にtopicに一致するログを取得して返します。ログを一括で取得したいときに使います。

filter.watch

fromBlockからtoBlockまでの間にtopicに一致するログを取得します。getと違いwatchを呼び出した後、toBlockに到達するまで定期的にnodeを監視して、ログが発生するたびに通知を受け取ります。

https://github.com/ethereum/wiki/wiki/JavaScript-API#web3ethfilter

問題点

web3.eth.filterはpollingの問題があります。web3.eth.filterではwatchを設定したときにnodeからイベントの通知を受け取るために定期的にnodeに対してeth_getFilterChangesを呼び出しています。以下は該当部分の抜粋です。

元のソースへのリンクも張っておきます。
web3.js filter.watch
web3.js filter.pollFilter
web3.js requestManager polling

pollingは500ms毎に呼び出されています。これはDappsとnode双方に無駄に負荷をかけることになります。
そのため、web3.js@1.0.0では新しくPUB/SUBパターンのイベント通知機能に一新されました。

web3.eth.subscribe@1.0.0

概要

web3.eth.filter#watchでの問題点を改善したイベント通知機能です。この新しいPUB/SUB機能についてはERCとして提案されていました。
github.com
このERCはgo-ethereumですでに実装されています。
github.com

機能

web3.eth.subscribeは0.x.xでいうところのwatch機能のみを抜き出したものです。そのため機能としてはイベント監視だけとなります。以下の4つのイベントが監視できます。

  • newBlockHeader:新しいブロックが追加されたイベント
  • pendingTransaction: pendingTransactionが追加されたイベント
  • syncing: 同期処理で新しいデータを受け取ったときと同期が完了したイベント
  • logs: transactionReceiptにlogが記録されたイベント
https://web3js.readthedocs.io/en/1.0/web3-eth-subscribe.html

さらに、ContractのEvent関連処理もリニューアルされており、いくつか便利なメソッドが追加されています。

  • web3.eth.Contract.events.once: 1度だけeventを受け取る。Contract creationの時とかいろいろと1度だけ受け取りたいシーンは多いので専用のメソッドが用意されたみたいです。
  • web3.eth.Contract.events.myEvent: 特定のeventをsubscribeします
  • web3.eth.Contract.allEvents: Contractのすべてのイベントをsubscribeします
  • web3.eth.Contract.getPastEvents: web3.eth.filter#getと同じ動作。指定されたfromBlockからtoBlockの間に発生したイベントを返します
https://web3js.readthedocs.io/en/1.0/web3-eth-contract.html

問題点

この機能はサーバからのpush通知が必要なためwebsocket上で動くように実装されています。なので利用するためにはnodeがwebsocketでRPCサーバを起動できる必要があります。EIPとしても採択されていないため現状利用できるnodeは限られています。(とはいえgo-ethereumで利用できるのでそれほど問題にはならないかも?)

なお、go-ethereumはこの機能を実装済みです。--wsオプションを付けることでwebsocketサーバが起動します。
またinfura.ioでも最近websocketのエンドポイントが実装されました。
github.com

まとめ

web3.eth.subscribeについてはまだドキュメントとソースを読んだだけで実際に動かして試していないため間違い等あるかもしれません。
web3.eth.filter.watchをganacheに対して発行したときは、eth_getFilterChangesのログがすごい勢いで流れて、ノードの挙動が不安定になっていました。なので、web3.eth.filter#watchの問題は実感しており、web3.eth.subscribeを使えばどれくらい安定するのか楽しみです。