イーサリアムをエーテリウムと呼びたいブログ

Ether ってなんて呼んでますか?イーサですか?私はエーテルと呼びたいです。カッコいいから!

Infura 障害と Ethereum チェーンスプリット

coinpost.jp

先日発生した Infura の障害とその原因となった Ethereum のチェーンスプリットに関してまとめました。チェーンスプリットとは聞きなれない用語ですが、後述のとおり、ハードフォークというより意図しない分岐なのでチェーンスプリットが妥当かと思います。

Infura が障害を起こしたことにより、Metamask のユーザーなど、一部のユーザと言っても大きい範囲での Ethereum に関する障害が発生しました。ここぞとばかりにビットコイン信者は Ethereum 批判を行っていますが、個人的にはなぜこの問題が起こったか、批判がどの程度正しいのか検証の意味で一連の事象のまとめをここに行います。

Twitter上では、

  1. Ethereum は自社でまともに管理できないのか???
  2. Ethereum=Infura なので中央集権的
  3. サイレントコンセンサスアップグレードはダメ
  4. アーカイブノードは3台しかない(フルノードが3台しかない)

というような、昔からあるFUDや新しいものもありますが、個人的には Infura が落ちたのはあまり良い印象ではありませんでした。幸いにも Geth の開発者サイド、Infura サイドから詳細な報告書が提出されておりますので、こちらを読んでみましょう。

Péter Szilágyi の報告書

Péter Szilágyiから Ethereum のチェーンスプリットの分析が出ています。下にDeepL翻訳を記載します。

gist.github.com

この後反論やさらに反論が続いていますが、大分専門的な内容なので翻訳しても意味が良くわからないかもしれません。

重要な点は、チェーンスプリットは Geth の古いバージョンで引き起こされたもので、非 Geth ノードや新しいバージョンでは問題はなく、ネットワーク全体としてはダウンは行っておりませんでした。

Geth v1.9.17 ポストモーテム

昨日 - 2020年11月11日 - Ethereumネットワーク上で(意図的に)コンセンサス問題が発生しました。しかし、これらの通常のプレイアウトの方法とは対照的に、このコンセンサス問題は異なるクライアント間ではなく、むしろ同じクライアントの異なるバージョン、すなわちGeth間で発生しました。

Geth v1.9.7(2019年11月7日リリース)はEIP-211の実装を壊し、それによってメモリ領域が浅くコピーされ、範囲外の上書きを可能にしました。このバグ(※後述のshallow copy バグで詳細)は2020年7月15日にJohn Youngseok Yang氏によって報告され、5日後のGeth v1.9.17(2020年7月20日)で静かに修正されて出荷されました。この修正により、GethはBesu、Nethermind、OpenEthereum(およびEthereumの仕様自体)とのコンセンサスを取り戻したが、以前のGethのリリースとのコンセンサスを崩してしまった。

残念ながら、すべてのノード運営者が最近のリリースを実行しているわけではなく、昨日の朝、あるトランザクションがコンセンサスの問題を引き起こし、古いGethリリースをネットワークの他の部分から切り離してしまいました。Infuraも影響を受けた当事者の一社であり、彼らのクライアントベースを奪ってしまったため、これはより大きな問題となりました。

Twitter上では、主に2つのテーマを中心にいくつかの反発がありました。

  • なぜ Geth チームは一方的に「コンセンサスアップグレード」を行ったのか?
  • なぜ Geth チームはオペレータに警告するのではなく、この修正を黙って出荷したのか?

どちらの質問も有効ですが、いつものように、答えはツイッターのスレッドに収まるものよりもニュアンスのあるものです。

なぜGethチームは一方的に「コンセンサスアップグレード」を行ったのですか?

Gethチームはv1.9.17のリリースでコンセンサスの実装を変更しましたが、Ethereumコミュニティが知らなかった、あるいは同意しなかった新しいルールを作成したわけではありません。このルールはEIP-211で定義されており、3年前にネットワークがビザンチウムにフォークオーバーされた際にコミュニティが合意したものです。

偶然にバグを導入したことを「コンセンサス・アップグレード」と考えないのであれば、数ヶ月後にバグを修正したことを「コンセンサス・アップグレード」と考えるべきではありません。

なぜ Geth チームはオペレータに警告するのではなく、この修正を黙って出荷したのか?

これは少しグレーゾーンであり、ケースバイケースの議論が必要です。透明性が王様であり、それに向けて可能な限りの努力をすべきであることには誰もが同意していますが、頭を回転させる前にすべての詳細を見ておくことも重要です。

Ethereumのコンセンサスコードは比較的安定しているので、時間が経つにつれて壊れる確率は低くなります。しかし、ユーザーは常により速くすることを期待しているため、時折新しいバグが発生することもあります。これらのバグを修正することは難しくありません - この場合は1行のコードで済みました - しかし、修正したものを出荷することは、いくつかの興味深い問題を提起しています。

古典的なソフトウェアの世界では、一度セキュリティ修正が作成されると、プラットフォームのオペレータはすべてのノードを更新することができますし、ソフトウェアベンダはすべてのクライアントに更新をプッシュアウトすることができます。これにより、バグを知った攻撃者がバグを悪用できる時間を最小限に抑えることができます。(例: OpenSSLのHeartbleedバグは、誰もがそのことを公表する前に、かなりの数のインターネット大手がローカルインフラストラクチャでパッチを当てていた)。

Ethereumの場合、予定されているハードフォークでさえ、ノード運営者にアップデートしてもらうには、多くの時間(数週間、数ヶ月)が必要です。リリースに重要なコンセンサスやDoSの修正が含まれていることを強調すると、誰かがアップデータを叩きのめしてネットワークをダウンさせようとする危険性が常にあります。曖昧さを利用してセキュリティを確保することは、決して目指すべきことではありませんが、潜在的な攻撃を十分に遅らせて、ほとんどのノードオペレータが免疫を得られるようにすることは、透明性に対する一時的な「打撃」を与える価値があるかもしれません。

この特定の例では、コンセンサスバグは1年以上もの間、コードの中で休眠状態にありました。その間に誰かが誤って起動してしまう可能性はごくわずかです。それとは対照的に、セキュリティ上の問題として強調された場合、誰かが悪意を持ってバグを発動させる確率は、取るに足らないものではありません。Geth チームは、人々が最終的に修正を含むバージョンにアップグレードし、問題がネットワークから徐々に排出されることを期待して、この問題について言及しないという意識的な決断をしました。

あなたは「明らかにうまくいかなかった」と反論するかもしれません。

ほとんどのノードは実際に更新され、影響を受けませんでした。ネットワークの健全性の観点から見ると、戦略は意図した通りに機能し、Ethereumネットワークは意味のある問題なく存続していました。Infuraを使用している一部のプロジェクトは影響を受けましたが、結局のところ、Gethチームの第一の目標はEthereumネットワーク全体の健全性であり、個々の断片は二の次に過ぎません。

深刻なバグの詳細を公開するかどうかの決定は、両方のケースでどのような影響が出るか、そしてダメージが小さい方を選ぶかにかかっています。過去数年間、私たちは公開されなかったコンセンサスの問題をいくつか修正してきましたし、Nethermind、Besu、Parity でそのような問題を修正するのを手伝ってきました。これらすべてのケースにおいて、脚光を浴びることを避けることで、攻撃のリスクにさらされることなく、またノードオペレータを直ちにアップデートをしなければならないという非常事態に陥らせることなく、ネットワークをシームレスに進化させることができました。

しかし、Monero、ZCash、Bitcoinなどの他のプロジェクトが示すように、ある時は黙っていた方が良い場合もあります。

この特定の沈黙のコンセンサス修正は、昨日のネットワーク分割で予想外の展開を迎えましたが、振り返ってみると、これは正しい判断だったと考えています。我々は、事業者が常に最新のリリースに即座にアップデートすることを期待することはできないことを理解しており、我々の脆弱性管理とリリース構造にはブロックチェーンエコシステム特有のニュアンスがあることを理解していることに感謝しています。

Geth の shallow copy バグ

CVE-2020-28362と同時にチェーンスプリットを引き起こしたshallow copy バグの対応も行ったようです。shallow copy バグとCVE-2020-28362は全く別の問題と考えます。

blog.ethereum.org

以下翻訳です。

Geth security release Critical patch for CVE-2020-28362 - Geth shallow copy bug

影響を受けた版: 1.9.7 - 1.9.16

修正済み: 1.9.17

タイプ: コンセンサス脆弱性

2020-07-15、John Youngseok Yang (Software Platform Lab) が Geth のコンセンサス脆弱性を報告しました。

Geth のコンパイル済みの dataCopy(0x00...04) 契約は呼び出し時に浅いコピーを行いますが、Parity の契約は深いコピーを行います。攻撃者は

  • X を EVM メモリ領域 R に書き込みます。
  • Rを引数に0x00...04を呼び出します。
  • RをYに上書きします。
  • そして最後にRETURNDATACOPYオペコードを呼び出します。
  • このコントラクトが呼び出されると、Parity は EVM スタックの X をプッシュし、Geth は Y をプッシュします。
結果

これはEthereum Mainnetのブロック11234873トランザクション0x57f7f9で悪用されました。ノード<v1.9.18>がネットワークから脱落し、サイドチェーン上で~30ブロックが失われる原因となりました。また、Infuraがドロップオフする原因となり、バックエンドプロバイダとしてInfuraに依存していた多くの人々やサービスに問題が発生しました。

より多くのコンテキストは、Gethのポストモーテム(※上述している記事)とInfuraのポストモーテム(※後述している記事)とこちらで確認できます。

Infura の報告書

Infuraの初期の頃は、GethやParityチームが新しいリリースを出すとすぐにノードをアップグレードを行っていたようですが、後述の理由ためできなかったようです。

blog.infura.io

Infura Mainnet Outage Post-Mortem 2020-11-11

本日未明(2020-11-11)、Infura は4年間の運営の中で最も深刻なサービス中断を経験しました。私たちは、私たちが多くの素晴らしい製品やプロジェクトのための重要なインフラストラクチャの一部であることを認識しています。すべてのユーザーとエコシステムに謝罪したいと思います。私たちは、皆様が私たちを信頼してくださっていることを認識しており、それを軽んじることはありません。何が起こったのか透明性があるように、また、今後のサービスがより良く、より回復力のあるものになることを確信していただけるように、今回の事件の詳細を皆さんと共有したいと思っています。

07:13 UTC. Infuraの監視システムによると、当社のインフラへのピアツーピア侵入ポイントであるコアピアリングサブシステムは、チェーンの先端に遅れを取り始めています。

07:15 UTC: Infura監視システムは、当社のインフラストラクチャへのピアツーピア侵入ポイントであるコア・ピアリング・サブシステムがチェーンの先端に遅れ始めたことを示しました。自動アラートシステムは、アーカイブデータサブシステムやイベントログ処理パイプラインを含むいくつかの重要なサブシステムの完全な同期停止を検出しました。

07:15 UTC: 自動化されたアラートがオンコールエンジニアに送られ、トリアージ作業が開始されました。

07:56 UTC. 追加のエンジニアが調査のために呼ばれました。

08:04 UTC. 同期エラーの原因は、ブロック11234873のコンセンサス障害として確認された。

08:25 UTC: デバッグの修正により、ノードをこのブロックを通過させようとする最初の試みは効果がなかった。

09:45 UTC. クライアント v1.9.9.9 と v1.9.13 に影響を与えるコンセンサスバグを報告するために go-ethereum チームに連絡しました。

09:55 UTC. コンセンサスバグの修正を含むクライアントバージョンへのインフラの更新を開始。

10:30 UTC. サブシステムのアップグレードが完了。初期検証と互換性テストを開始。

10:38 UTC. システム統合テストで異常な動作が検出されました。

11:20 UTC: v1.9.9.9 と v1.1.9.19 の間の geth RPC パス処理の動作が変更されたため、統合に失敗した。

11:28 UTC: ホットフィックスの開発を開始。ホットフィックスの開発を開始。

12:00 UTC. ホットフィックスのロールアウトを開始しました。

12:20 UTC. サブシステムのホットフィックスのロールアウトが完了しました。

12:37 UTC。クライアントの再試行試行の増加が、許可サブシステムで下流のサービスの劣化を引き起こしました。

12:38 UTC: コア層アカウントの API アクセスは パーミッションサブシステムの復旧を迅速に行うため、コア層アカウントの API アクセスは一時的に無効化されました。

13:07 UTCJSON-RPC サブシステムは名目上の健全性に戻りました。コア層のアクセスが再び有効になりました。アーカイブデータサブシステムはまだ劣化した状態で動作しています。

14:28 UTCアーカイブ データ サブシステムが正常な状態に戻りました。

14:28 UTC。インシデントは解決済みとマークされました。

根本的な原因は何だったのか?

一部の内部システムで使用されている Geth (v1.9.9) および (v1.9.13) のバージョンに影響を及ぼすコンセンサスバグにより、これらのサブシステムのいくつかでブロック同期が停止していました。

Infuraは最新版が(v1.9.23)なのに、なぜgeth(v1.9.9)と(v1.9.13)を実行していたのですか?

Infuraの初期の頃は、GethやParityチームが新しいリリースを出すとすぐにノードをアップグレードしていました。私たちは最新のパフォーマンス向上、最新のAPIメソッド、そしてもちろんバグ修正を求めていました。しかし、これらの変更によって不安定性や重大な問題が発生し、ユーザーに悪影響を及ぼすことがあったため、それをやめました。時には、同期バグやピアリング動作の変更によりインフラストラクチャ内で予期せぬ問題が発生したり、JSON-RPCの動作にわずかな変更を加えただけで開発者がアプリケーションの変更を余儀なくされたりすることもありました。どんなソフトウェアもバグフリーではありませんし、すべてのリリースが計画通りに進むわけではありません。そのため、機能やパフォーマンスの微調整を得るために最新のクライアントバージョンを追跡するよりも、安定性の方が重要であるという判断を下しました。このため、私たちはアップデートのスケジュールをより質素にするようになりました。私たちは、開発者に安定したAPIを提供するために最善を尽くしています。API に変更があった場合は、ユーザーがアプリケーションに必要な修正を行うための時間を確保できるように、事前に十分に連絡を取り合っています。

当社では、社内で「Omnibus」と呼んでいるカスタムパッチを適用したバージョンのGethを実行しており、これには当社のクラウドネイティブアーキテクチャに合わせたパフォーマンス、安定性、監視機能の強化が含まれています。これは、バニラ版のGethを実行するのに比べて更新プロセスを複雑にしていますが、その利点は価値があり、私たちが実行しているバージョンを透明にすることを目指しています。https://forkmon.ethdevops.ioJSON-RPC API の両方で利用可能です。

curl -H content-type:application/json  https://mainnet.infura.io/v3/[YOUR_PROJECT_ID]     -X POST \

-H "Content-Type: application/json" \

-d '{"jsonrpc":"2.0","method":"web3_clientVersion","params": [],"id":1}'

{"jsonrpc":"2.0","id":1,"result":"Geth/v1.9.19-omnibus-194c4769/linux-amd64/go1.14.11"}

先に述べた安定性、下位互換性、パッチ管理の複雑さなどの懸念があるため、ノードを更新する際には非常に明確かつ慎重に行っています。コンセンサスバグが知られている場合は、もちろんすぐにアップデートします。しかし今回のケースでは、Geth v1.9.9 と v1.9.13 のコンセンサスの問題を認識していませんでした。

今回の停止で特に痛手だったのは、このような事態を回避できるクライアントバージョンへのアップデートが間近に迫っていたことです。今月初めにアップデートを予定していましたが、ユーザーがアップデートして変更に備える時間を確保し、アップグレードの安定性を保証できるようにするため、延期することになりました。

今後、どのようにして防いでいくのでしょうか。

私たちはgo-ethereumチームとのパートナーシップに心から感謝しています。Péter、Martin、Felixです。このインシデントの際の彼らの迅速な対応は、解決を大いに促進してくれました。私たちの事前の理解では、本日遭遇したようなコンセンサスバグは、リリースノートの中で目立つようにハイライトされるものとされていました。残念ながら、このようなことはありませんでしたが、その背景にある geth チームの理由は理解しています。私たちは2つのチーム間で良好なコミュニケーションラインを持っていますが、私たちは決して優遇措置や情報への内部アクセスを期待していませんでした。どの取引所も、どのマイナーも、どの API プロバイダも、単一のノードランナーとは異なる扱いを受けるべきではありません。一般的に、このような重要なバグ修正がコミュニティに伝達される方法は、議論され、改善されるべきものだと思います。具体的には、Infuraについては3つの主要なアクションアイテムがあります。

  1. コンセンサスバグが新しいリリースで静かに修正される可能性があることを承知の上で、安定性の維持と下位互換性を維持する責任のバランスを取りながら、最新のクライアントリリースに近づけるようにプロセスを調整していきます。

  2. OpenEthereum のような他のクライアントの利用を増やし、Besu クライアントをインフラに再導入することで、クライアントの多様性を高めていきます。また、NethermindやTurbo-Gethのような他のクライアントの追跡、テスト、評価も継続していきます。

  3. 当社のインシデント対応手順を見直し、復旧までの時間を短縮するために実施できる改善点があるかどうかを判断します。

今回の事件は、2016年に発生したEthereumに対するネットワーク全体の攻撃以来、私たちのチームが見たことがないような、オールハンズオンデッキのイベントでした。このインシデントを解決している間、ユーザーとコミュニティの皆様のご理解と忍耐に改めて感謝したいと思います。今後も、コミュニティフォーラム、ブログ、ニュースレターを通じて、エンジニアリングの改善に関するより多くの最新情報を共有していきます。ご質問やご不明な点がございましたら、eg@infura.io、godsey@infura.io、michael@infura.io までお問い合わせください。

まとめ

ETH2.0でなぜマルチクライアントにこだわるのか、良くわかる事例になりました。

ルノードの運営といっても影響力のあるノード、ほとんどサーバーとして機能している場合は影響力大なのでインフラエンジニアも相当なレベルが求められますね。障害発生後にここまでレポートが提出されていれば、全て無料の自己責任のシステムのため、僕らもある程度彼らの労をねぎらう気持ちも必要ではないでしょうか。ユーザ側の対策としては、フルノードまたは、試してはいませんがLightノードで対処すると良いかもしれません。

Ethereum Nodes で代替となるフルノード一覧があるので障害を確認したらこちらを参照するようにMetamask等ウォレットの設定を変えるのも良いでしょう。