原文
Gateway-to-gateway coordination for EIP-3668 / Proposing a mesh sync protocol — TMerlini (2026-06-02)
カテゴリ: EIPs / ERC ディスカッション タグ: eip-3668, ccip-read, gateways, attestation 共同執筆者: Tiago Merlini (@TMerlini), Damon Zwicker (OCP) @Damonzwicker, Vincent Wu (ERC-8263 / Composition Note) @VincentWu, Jimmy Shi (ERC-8274) @JimmyShi22
ギャップ
EIP-3668は、クライアントがCCIP-Readゲートウェイとどのように通信するかを正確に指定しています。リクエスト形式、レスポンスエンベロープ、リバート規約などです。しかし、ゲートウェイ同士がどのように通信するかについては何も言及していません。
これは、最も単純なケース(1つのゲートウェイ、1つのオペレーター)では問題ありません。しかし、CCIP-Readの採用が進むにつれて、単一のゲートウェイでは解決できない3つの問題が浮上します。
-
冗長性 (Redundancy)。ゲートウェイがダウンすると、ENS名(または任意のCCIP-Readリゾルバー)が機能しなくなります。2番目のゲートウェイが同じネームスペース (namespace) を提供するための標準的な方法がありません。これは単純な稼働時間以上の問題です。IPFS CIDとしてページがピン留めされているCCIP-Readのdapp(分散型アプリケーション)は、停止させるフロントエンドサーバーを持ちませんが、それでも単一のゲートウェイが故障する可能性があります。メッシュ同期プロトコルは、その最後の集中型依存関係を取り除きます。ネームスペースを同期した任意のノードが応答できるようになります。CIDでピン留めされたページと組み合わせることで、結果として、どのレイヤーでも単一障害点のないdappが実現します。
-
監査可能性 (Auditability)。ゲートウェイは実際に何を受信し、何を返したのでしょうか?各呼び出しの署名され、複製可能な記録がなければ、ゲートウェイが応答を改ざんしなかったことを検証する方法はありません。
-
帰属 (Attribution)。マルチパーティシステム(複数のエージェント、複数のノード)において、誰が共有記録セットに何を提供したのでしょうか?
これらのいずれもEIP-3668自体では対処されておらず、その後の連携標準もありません。
提案されたプロトコル
私たちは数ヶ月間リファレンス実装を運用しており、議論のために最小限のゲートウェイ間同期プロトコルを提案したいと考えています。
コアとなるプリミティブ (primitive) は、任意のEIP-3668ゲートウェイが公開できる単一のエンドポイントです。
GET /records?namespace=<str>&since=<unix>&limit=<n>&cursor=<str>
応答:
{
"protocol": 1,
"node_version": "0.3.0",
"namespace": "agent-attestations",
"records": [
{
"inputHash": "0x...",
"namespace": "agent-attestations",
"key": "0x...",
"value": "0x...",
"timestamp": 1234567890,
"signature": "0x...",
"sourcePeer": null
}
],
"cursor": "1234567890|0xabc..."
}
いくつかの意図的な設計上の選択肢があります。
オフセットではなくカーソルページネーション (Cursor pagination over offset)。timestamp|inputHash複合カーソルを使用することで、同時書き込み下でもタイムスタンプ境界でレコードがスキップされることはありません。純粋なオフセットページネーションでは、ページ間に新しいレコードが挿入されるとレコードが失われます。
INSERT OR IGNOREによる重複排除 (deduplication)。複合プライマリキーは(inputHash, namespace)です。同じレコードが2つのピアから到着しても、一度だけ保存されます。ゴシップは自由に実行しても安全です。
プロトコルバージョンフィールド (Protocol version field)。異なるプロトコルバージョンのノードは、不正なデータを黙って受け入れるのではなく、警告とともに互いをスキップします。
署名者の固定 (Signer pinning)。ピアからの最初の同期時に、復元された署名者アドレスが保存されます。異なる署名者からの後続のレコードは拒否されます。これにより、侵害されたピアが他のノードに代わってレコードを挿入することはできません。
ネームスペースのスコープ化 (Namespace scoping)。各プルはネームスペース文字列にスコープされます。token-metadataを提供するノードとagent-attestationsを提供するノードは、互いにピアリングしていても、レコードレベルで完全に分離されます。
アテステーション (Attestation) をその上に重ねる
上記の同期プロトコルはトランスポート層のものであり、署名されたレコードを移動させるだけです。私たちはその上に何が来るかについても取り組んできました。それは、ゲートウェイが何を受信し、何を返したかを証明する方法であり、単にレコードを書き込んだという事実だけではありません。
このアプローチは、任意のリゾルバー関数を以下のパイプラインでラップすることです。
- 生の
calldataをハッシュ化します:rawInputHash = keccak256(calldata) - オプションでサニタイゼーションパイプラインハッシュを適用します:
inputHash = keccak256(abi.encode(rawInputHash, pipelineHash))— またはサニタイゼーションが適用されなかった場合はkeccak256("IDENTITY_SENTINEL")を使用します。 - 応答をハッシュ化します:
outputHash = keccak256(response) - コミットメント (commitment) を計算します:
commitmentHash = keccak256(agentId · modelHash · inputHash · outputHash · timestamp) - 上記すべてを含むEIP-712
WyriweAttestation構造体に署名します。
これらのアテステーションは、通常のレコードと同じ/recordsプロトコルを使用して、別の{namespace}:wyriweネームスペースで同期されます。任意のピアはEIP-712署名を検証し、コミットメントハッシュを独立して再構築できます。
コミットメントハッシュはその後、オンチェーンにアンカー (anchor) することができます。これは単一の32バイトの書き込みであり、任意の一つのゲートウェイを信頼することなく、アテステーションを不変かつクエリ可能にします。
Sepoliaにデプロイされたコントラクト(Etherscanで検証済み):
| コントラクト | 役割 | アドレス |
|---|---|---|
| AttestationIndex | ccip-router OCP互換コミットメントストア | 0x107D706112225aC57eCf6692FBbDC283fb6E3698 |
| NodeRegistry | ノード登録 | 0x6be4966596A9CBaa7260ab6EbbFFA69bBC9a42b7 |
| WyriweProofVerifier | ERC-8274 IProofVerifier | 0x001eFFa0fD1D171b164808644678F3301d8EDC96 |
| TruthAnchorV1 (ERC-8263カノニカル — Vincent Wu) | ERC-8263リファレンスコントラクト・メインネット | 0xe95d6a15966984c209a62a2c188828555eb5ec3d |
AttestationIndexはccip-router独自のコミットメントストアです — signerOf[commitmentHash] + commitmentOf[inputHash]。これはOCP (Observation Commitment Protocol)コミットメント不変条件を満たし、ERC-8263カノニカルコントラクトとは異なる、有効なOCP (Observation Commitment Protocol)互換アンカーです。TruthAnchorV1はカノニカルなAnchorProof(uint8 agentIdScheme, bytes32 agentId, bytes32 proofHash, address operator, bytes aux)イベントを発行します。AttestationIndexは、ゲートウェイがアテステーションされた実行後に書き込むトランスポート層のアンカーです。これら2つは設計上、別々のプリミティブです。
ccip-routerがERC-8263に接続する方法: ccip-routerは、そのcommitmentHashをTruthAnchorV1のproofHashとしてアンカーします。ERC-8263のproofHashは意図的に不透明です。同じアンカー層がOCP (Observation Commitment Protocol)、WYRIWE (What You Read Is What You Execute)、ゼロ知識機械学習 (zkML)に均一にサービスを提供します。ccip-routerのcommitmentHash = keccak256(abi.encode(agentId, modelHash, inputHash, outputHash, timestamp))は、定義ではなく、1つのカノニカルなインスタンス化です。完全なチェーン: 推論が実行される → ゲートウェイがWyriweAttestationに署名しcommitmentHashを生成 → TruthAnchorV1でanchor(commitmentHash)がproofHashとして呼び出される → AnchorProofイベントが発行される。L3アンカリングを検証するには、eth_getLogsを介してproofHashトピック(=あなたのcommitmentHash)でAnchorProofをフィルタリングし、アンカリングブロックのタイムスタンプと実行時間を比較します。V1は設計上イベントのみです。アンカーごとのストレージコストはありません。同期的なオンチェーンビュー(IAnchorReader)はERC-8263 v0.3で提案されています。
WyriweProofVerifierはERC-8274 IProofVerifierを実装しています。これはL4のみのアテステーションチェックです。「このagentIdの認可されたゲートウェイは、inputHashがoutputHashを生成したことをアテステーションしましたか?」パラメータ:
inputHash、outputHash— 明示的な推論の入出力コミットメントmetadata=abi.encode(agentId, registry)— 認可された署名者のIDproof=abi.encode(modelHash, rawInputHash, sanitizationPipelineHash, commitmentHash, timestamp, sig)— L4暗号学的マテリアル
verify()は(agentId, modelHash, inputHash, outputHash, timestamp)からcommitmentHashを再計算し、完全な構造体からEIP-712ダイジェストを再構築し、署名者を回復します。外部呼び出しはありません。ゲートウェイが構造体に対して以前に行った署名は、rawInputHash → sanitizationPipelineHash → inputHashという来歴チェーンを保証します。検証者は再検証することなくこれを信頼します。これによりループが閉じられます。メッシュ全体で連携されたコミットメントは、任意のERC-8274互換コントラクトによってオンチェーンで決済可能です。
ENSワイルドカード解決
このスタックの実用的なアプリケーションの1つは、ENSオフチェーンリゾルバーゲートウェイです。リファレンス実装には、resolve(bytes name, bytes data) calldata(EIP-137ワイルドカード解決パターン)をデコードし、クリーンなハンドラーにディスパッチするwithEns()ラッパーが付属しています。
import { CcipRouter, withEns } from 'ccip-router'
const ccip = new CcipRouter({
resolver: withEns(async (name, record) => {
// name → "vitalik.eth"
// record → { type: 'addr' } | { type: 'addr', coinType: 60n }
// { type: 'text', key: 'avatar' } | { type: 'contenthash' }
return db.lookup(name, record) // return value string or null
}),
})
withEns()は、DNSワイヤーフォーマットのデコード、セレクターディスパッチ(addr、addr(uint256)、text、contenthash)、および応答のABIエンコーディングを処理します。nullはレコードタイプごとに正しいゼロ値にマッピングされます。不明なセレクターは例外をスローするのではなく0xを返します。
スタンドアロンノードには、デフォルトでDBバックアップされたENSリゾルバーが付属しています。レコードは管理パネルから管理され、コードは不要です。オンチェーンのCCIP-Readリゾルバーを介してこのゲートウェイを指す任意の名前は、自動的に提供されます。
アテステーションとの組み合わせ。 withEnsをwithWyriweの中に配置することで、すべてのENS解決が完全なWyriweAttestation(生のcalldataハッシュ、モデルハッシュ、入出力コミットメント、EIP-712署名)を保持するようにします。
resolver: withWyriwe(withEns(myResolver), attestationOpts)
SIWEによるオペレーターID。 管理ダッシュボードは、Sign-In With Ethereum(EIP-4361)を介して認証されるようになりました。認可された署名者は、ノード自身のゲートウェイキーであり、すべてのレコードに署名するのと同じキーです。秘密鍵を保持していることが、ノードを操作していることの証明となり、別途パスワードは不要です。
決済レイヤー
オンチェーンにOCP (Observation Commitment Protocol)アンカーされるcommitmentHashは、資金を解放する前に任意の決済コントラクトが必要とするプリミティブです。ERC-8274は、このL4ステップのための最小限のIProofVerifierインターフェースを提案しています。これは、基盤となる証明システムを知らなくても、任意のコンシューマーがverify(inputHash, outputHash, metadata, proof)を1回呼び出すだけで済むものです。このインターフェースは「認可された当事者がこの推論結果をアテステーションしましたか?」という狭い範囲にスコープされています。L3アンカリング(ゲートウェイの責任)や入力の来歴チェーン(ゲートウェイのEIP-712署名によって既に保証されている)は検証しません。これは、オラクル/マルチシグパターンに自然にマッピングされます。ゲートウェイが認可されたアテスターであり、metadataがそのIDを運び、proofが暗号学的証拠を運びます。
WyriweProofVerifierは、このスタックのための具体的なERC-8274実装であり、Sepoliaの0x001eFFa0fD1D171b164808644678F3301d8EDC96にデプロイされています。inputHashは、生のcalldataからサニタイゼーションパイプライン、コミットメント、オンチェーンアンカー、決済まで、すべてのレイヤーを結びつける共有の暗号学的アンカーです。
フィードバックを求めている点
/recordsプロトコル自体について。 カーソル設計は健全でしょうか?プロトコルバージョンのネゴシエーションはより正式なもの(例:GET /protocolエンドポイント)であるべきでしょうか?考慮していない障害モードはありますか?- ネームスペース設計について。 現在、ネームスペースは任意の文字列です。オペレーターは帯域外で調整する必要があります。レジストリが必要でしょうか、それともそれは不必要な複雑さでしょうか?
- 署名者固定のトレードオフについて。 最初の同期で固定するのはシンプルですが、キーをローテーションするノードはピアから見ると別のノードのように見えます。ここにはより良いIDプリミティブがあるでしょうか?(私たちはVNI —
nodeId = keccak256(signerAddress)となる署名付き{ nodeId, signerAddress, url }ドキュメントで、オペレーターが再署名すればキーローテーション後も安定する — を実験しています。) - これがフォローアップのEIPを必要とするかどうかについて。
/recordsプロトコルは十分にシンプルなので、EIP-3668の補完として短い情報提供EIPになる可能性があります。あるいは、実装が収束するデファクトスタンダードとして残しておく方が良いかもしれません。ご意見を歓迎します。 - 実行ごとのコミットメントと設定ごとのコミットメントについて。 ここで生成される
commitmentHashは厳密に実行ごとのものです。特定の呼び出しに対してinputHash、outputHash、agentId、modelHash、timestampをバインドします。エージェント設定(モデル、パイプライン)を記述する別の安定したコミットメントを一度アンカーし、複数の実行で参照すべきかどうかは未解決の疑問です。トランスポート層に位置するため、ここで表面化する必要があるため、ここで取り上げます。
リファレンス実装
ccip-router v0.3.0 — npmパッケージ + セットアップウィザード、管理ダッシュボード、および上記すべてが組み込まれたスタンドアロンノード:
- npm: https://www.npmjs.com/package/ccip-router
- GitHub + 統合ガイド: https://github.com/Echo-Merlini/ccip-router
v0.3.0に含まれるもの: WyriweProofVerifier(ERC-8274 IProofVerifier)用のWYRIWE_PROOF_VERIFIER_ABIエクスポート、withEns() ENSワイルドカードリゾルバーラッパー、DBバックアップされたENSレコード(管理パネルから管理、コード不要)、SIWE管理認証(EIP-4361、ゲートウェイキー = 管理者ID)、多目的リゾルバー用のisEnsCalldata()ガード。
この作業は、WYRIWE (What You Read Is What You Execute)、OCP (Observation Commitment Protocol) (Damon)、ERC-8263 (Vincent / Composition Note)、ERC-8004 (エージェントIDレジストリ)、ERC-8274、およびERC-8275に関連しています。これらの仕様に関する以前のスレッドは、アテステーション層と決済層自体を議論するのに適切な場所です。このスレッドは特にゲートウェイ連携層に関するものです。
Damon Zwicker (OCP)、Vincent Wu (ERC-8263 / Composition Note)、Jimmy Shi (ERC-8274) との共同執筆。
2件の投稿 - 2名の参加者