Web3
スマートコントラクトの先にあるWeb3インフラ
本番稼働するブロックチェーンシステムにおいて、コントラクトはもっとも小さな部分です。エンジニアリングの大半、そして運用リスクの大半はオフチェーンにあります。ノードへのアクセス、インデックス作成、鍵のカストディ、トランザクション管理、監視です。
· 5分で読めます · Starone エンジニアリング
ブロックチェーンエンジニアリングの議論はスマートコントラクトに集中しがちです。それは理解できます。コントラクトはデプロイ後は変更できず、誰でも検査でき、ミスを許容しません。監査と形式的な検証に値するものです。しかし同時に、実際に動くプロダクトのコード全体から見ればごく一部でもあります。
Web3システムの信頼性は主にオフチェーンで決まると私たちは考えています。コントラクトが正しくても、アプリケーションが遅延したノードから古い状態を読み取ったり、後に再編成で取り消される入金を残高に反映したり、ノンスが詰まってトランザクションを送信できなかったりすれば、ほとんど役に立ちません。本稿では、コントラクトを取り巻くインフラと、それが迫る判断について説明します。
ノードとRPCアクセスは他と同じ依存先
すべての読み取りと書き込みは、セルフホストであれサービスとして提供されるものであれ、ノードのRPCインターフェースを経由します。このインターフェースは、レート制限、タイムアウト、停止といった一般的な形で失敗するだけでなく、ブロックチェーン特有の形でも失敗します。ノードは正常に稼働していながら数ブロック遅れていることがあり、同じロードバランサーの背後にある2つのノードが、連続するリクエストに対して異なるチェーンの先頭から応答することもあります。
私たちはRPCアクセスを、他の重要なアップストリームと同じ注意を払って設計します。リクエストは複数のプロバイダーを束ねる内部ゲートウェイを経由し、ゲートウェイは各プロバイダーの最新ブロック高を追跡し、遅れているものをローテーションから外し、冪等な読み取りは別のプロバイダーでリトライします。相互に整合していなければならない読み取りは、latestではなく特定のブロック番号またはブロックハッシュに固定します。自前でノードを運用すれば制御と独立性が得られますが、その代償としてストレージ、同期時間、ネットワークのフォークのたびのアップグレードが必要になります。過去の状態の問い合わせに必要なアーカイブノードは、さらにかなり重くなります。
インデックス作成:チェーンはクエリエンジンではない
ノードが答えられるのは、あるアドレスの状態や、あるブロック範囲のログといった狭い問いです。特定のユーザーが関わるすべての送金を時刻順に並べ、アセットで絞り込むといったプロダクト上の問いには答えられません。それには、ブロックとイベントログを取り込み、デコードし、クエリ向けに構築したデータベースへ書き込むインデクサーが必要です。
インデクサーは、一つだけ特殊な性質を持つデータパイプラインです。データソースが、すでに伝えた内容を撤回することがあるのです。堅実な設計では、導出したすべての行にブロック番号とブロックハッシュを保存し、新しいブロックごとに親ハッシュを保存済みのものと照らし合わせ、不一致があれば共通の祖先まで巻き戻して再処理します。ハンドラーは冪等かつ決定的でなければなりません。そうすれば、ジェネシスからの完全な再インデックスでも同じ状態が再現されます。
ファイナリティと再編成
ブロックに取り込まれたトランザクションは、まだ確定したわけではありません。プルーフ・オブ・ワークのチェーンではファイナリティは確率的で、後続のブロックが増えるごとに高まります。Ethereumのようなプルーフ・オブ・ステークのネットワークでは、safeとfinalizedのブロックタグを通じて公開される明示的なチェックポイントが加わり、それ以降の巻き戻しにはバリデーターの大きな割合がペナルティを受けることが必要になります。レイヤー2のネットワークでは、シーケンサーによる確認と基盤となるチェーンでの決済というさらなる区別が加わります。
エンジニアリング上の帰結は、コンファメーションポリシーがチェーンごと、アクションごとのリスク判断であり、コードではなく設定として持つべきだということです。
- 受信した送金は検知した時点で保留中として表示し、インターフェースの応答性を保ちます。
- 利用可能な残高への反映は、その金額に見合ったコンファメーションの深さまたはファイナリティの水準に達してからに限ります。
- 商品の引き渡しや銀行経由の出金など、取り消しのできないオフチェーンの処理は、ファイナリティの後にのみ実行します。
- すでに記録したトランザクションが再編成によって取り除かれた場合に、内部元帳のエントリを取り消せる能力を保持します。
鍵管理が影響範囲を決める
秘密鍵は、そのアドレスに許可されたあらゆる操作を承認でき、署名済みのトランザクションは取り消せません。環境変数やアプリケーションのメモリに保持された鍵は、サーバーへのあらゆる侵害を資金の喪失に変えてしまいます。私たちは署名を専用サービスの背後に置き、アプリケーションコードは署名を要求するだけで、鍵そのものを決して保持しないようにします。
- ハードウェアセキュリティモジュールやクラウドの鍵管理サービスは、鍵をエクスポート不可の状態に保ちます。必要な曲線、一般にはsecp256k1またはed25519への対応を事前に確認する必要があります。
- マルチパーティ計算や閾値署名は鍵を分割し、単一のマシンや人物だけでは署名できないようにします。
- オンチェーンのマルチシグやスマートコントラクトアカウントは、承認ルールを透明な形で強制します。その代わり、トランザクションコストが高くなり、チェーン固有の挙動を伴います。
- 署名サービスの前段に置くポリシーエンジンは、送信先の許可リスト、金額上限、レート制限を適用し、閾値を超える場合は人による承認を要求します。
ノンス管理とスタックしたトランザクション
アカウントベースのチェーンでは、あるアドレスからの各トランザクションは連番のノンスを持ち、ネットワークはそれらを厳密に順番どおりに処理します。同じ保留中の件数を読み取った2つのワーカーは同じノンスを使い、一方のトランザクションは破棄されます。現在の手数料水準に対して手数料が低すぎるトランザクションはメモリプールに滞留し、そのアドレスからの後続のトランザクションをすべてブロックします。
そのため、本番環境の送信処理ではノンスの割り当てを直列化されたリソースとして扱います。アドレスごとに1つのアロケーターを置き、永続ストアに裏付けられた形でノンスを順番に発行し、各トランザクションの状態を送信、ブロックへの取り込み、ファイナリティまで記録します。ウォッチャーは保留が長すぎるトランザクションを検知し、同じノンスとより高い手数料で置き換え、送信の失敗によって生じたギャップを埋めます。各トランザクションの背後にある業務上の操作には独自の冪等性キーが必要です。そうすれば、クラッシュ後のリトライで同じ送金が二重に行われることはありません。
チェーンが教えてくれないものを監視する
ここでは、標準的なサービスメトリクスは必要ですが十分ではありません。インシデントの予兆となるシグナルは固有のものです。各RPCプロバイダーの先頭ブロックと既知の最大ブロックとの差、ブロック数と秒数で測ったインデクサーのラグ、送信元ごとの最も古い保留中トランザクションの経過時間、手数料を支払うすべてのアドレスのネイティブトークン残高、リバートされたトランザクションの割合、そして再編成の深さです。所有権の移転、アップグレード、一時停止といったセキュリティ上の意味を持つコントラクトイベントは、直接アラートを発報すべきです。
トレードオフの所在
これらはすべて、分散型のシステムを取り巻く中央集権的なインフラであり、その緊張関係は認めておくべきです。インデクサーとRPCゲートウェイは、ユーザーが信頼せざるをえないコンポーネントです。信頼された仲介者を取り除くことを目的とするプロダクトであれば、より遅く簡素なインターフェースを代償として受け入れ、ライトクライアント、ウォレットからの直接読み取り、オンチェーンでの検証を選ぶこともあるでしょう。
オンチェーンでの決済と、従来型のアカウントやユーザー体験を組み合わせる大半のプロダクトにとって現実的な立場は、検証可能でなければならないものをオンチェーンに、高速または非公開でなければならないものをオフチェーンに置き、その境界を注意深く設計することです。そもそもブロックチェーンが必要なのかを問うことにも価値があります。単一の組織がすべての参加者を管理している場合は、追記専用の監査ログを備えたデータベースのほうがシンプルで安価であり、運用も容易です。
