B-1 Setpeer


クロスチェーンの「相手(peer)」を後から差し替えられたら、攻撃者は偽のチェーンになりすまし、canonical な読み取りを装って “fraud / no-fraud” の判定を偽造できます。この項目は、その peer 差し替えの経路が本当に塞がれているかを、コードとオンチェーンの両面から確かめた記録です。live V1 の結論を先に言えば――配線済みの peer は、所有者であっても二度と変更できません。

やさしく言うと

チェーンとチェーンをつなぐとき、「どの相手を信用するか」という配線(peer)を設定します。もし攻撃者がこの配線を後から差し替えられたら、偽のチェーンを本物になりすませ、判定を偽造できてしまいます。

配線を一度だけ設定setPeer

ロックがかかるOnlyOnce

二度目は拒否所有者でも

この項目で確かめたのは、その配線に「一度きり」のロック(OnlyOnce)がかかっていること。電話交換機の線を一度つないだら二度と変えられない、というイメージです。しかも変えられないのは部外者だけでなく、所有者(管理鍵)自身も。だから「鍵が漏れても配線は偽物に差し替えられない」という、ふつうの管理者ロックより一段強い守りになっています。

このページは検証台帳B-1 項目の詳細記録です。所有権の所在(A-2)と密接に噛み合います。

1. 主張(検証対象)

既に配線されたクロスチェーン peer は、デプロイヤー(所有者)を含め誰にも差し替えられないこと。したがって peer のすり替えによるメッセージ偽造は成立しないこと。

なぜ peer が急所なのか――アーカイブのコメント自身がこう書いています。relay が運ぶ bytes は CawActions のストレージから読むので canonical だが、peer をすり替えれば、その canonical な読み取りを装って任意の bytes を通せてしまう。fraud / no-fraud の判定を偽造する道がここにある、と。だから peer の凍結こそが偽造を止める要になります。

2. 検証方法

1

setPeer を override する全 OApp を特定

setPeer を override している保護対象は4つ(CawProfile・CawProfileL2・CawActionsArchive・CawChallengeRelay)。そのすべてが public override onlyOnce(keccak256(abi.encode("setPeer", _eid))) で、本体は super.setPeer(_eid, _peer) を呼ぶだけ、という構造をソースで確認。

2

アクセス制御の実効を読む

override 側は onlyOnce。本体が呼ぶ基底 super.setPeer(LayerZero OAppCore)は onlyOwner。両者を合わせた実効は「新 eid を配線できるのは所有者のみ・各 eid 一度きり/一度配線したら所有者でも変更不能」。配線系のセッター(setL1Peer・setL2Peer・setMinter・setUriGenerator・setCawActions)も同様にすべて一度きりで、配線層は全体がデプロイ後に凍結します。

3

OnlyOnce 本体の不可逆性を確認

OnlyOnce は、一度使ったキーを _used[key]=true にし、以後は "OnlyOnce: already called" で revert するだけの単純な仕組み。eid ごとに鍵が分かれているので、配線済み eid は固定され、未配線 eid は開いたまま(将来チェーン用)。

4

オンチェーンで差分実証(所有者から)

所有者アドレスを --from でシミュレートし、配線済み eid と未配線 eid の両方に setPeer を撃つ。配線済みは revert、未配線だけ成功するなら、ロックが「eid ごとの一回性」で効いていることが、所有者に対してさえ証明される。

3. 判定

配線済み peer は所有者でも変更不能
setPeer は eid ごと一度きり(OnlyOnce)。配線できるのは所有者のみだが、一度配線すると所有者を含め誰も差し替えられない。dev コメント自身が脅威(rogue/compromised owner)を名指しし、所有者から張替権を外す設計。標準 OApp の owner 限定より強い。

  • 不変 配線済み peer は 所有者を含め誰も変更できない(onlyOnce)。ソースの dev コメント自身が「owner でも変更不能」と明記。鍵漏洩時のクロスチェーン偽造をコード構造で封じています。
  • 開放 開いているのは 未配線 eid(将来チェーン)だけ。配線できるのは所有者のみ・各 eid 一度きり(基底 super.setPeer の onlyOwner による)。これは文書化された設計トレードオフです。
  • 実証 所有者から setPeer を撃っても、配線済み5本(L2:40161/40231/40231、L1:40245/40231)は revert、未配線 40267 のみ成功。差分が「eid ごとの一回性ロック」を所有者に対してさえ証明します。

peer すり替えによる偽造ベクタは閉じています。鍵を増やして守るのではなく、二度と開かない構造で閉じている、というのがこの項目の結論です。

4. 正直な但し書き ── setDelegate と所有権

setPeer の凍結だけを取り上げて「完了」とは書きません。同じ setPeer のすぐ上に、開発者自身のセキュリティノートが書かれています。隠さず引き継ぎます。

継承元の setDelegate(OAppCore)と transferOwnership(Ownable)はあえて override していません。ソースのコメントは、これらを「pre-mainnet チェックリスト(multisig への移管、または renounce)で扱う」と明記しています。つまり所有者鍵が生きている間は、LZ delegate のローテーションや所有権移転が依然として可能です。

ただし delegate が触れるのは LayerZero の side-channel 設定(DVN・Executor・nonce 等)で、プロトコルのメッセージを直接偽造することはできません。さらに上記の通り、delegate を替えても配線済み peer は動かせない(B-1 の本体)。

そして現状、この所有者鍵は A-2 で確認したデプロイヤー EOA(単一鍵・mainnet 前)です。これは A-2 が記録している残存の所有者前提とまったく同じもので、ソース自身が pre-mainnet チェックリストで解消すると宣言している箇所。B-1 固有の隠れたリスクではなく、A-2 と地続きの「mainnet 前の単一鍵」状態として、ここに正直に併記します。

5. receipt(公開証明)

所有者から setPeer を撃った差分テストの結果です(配線済み=revert、未配線=openable)。

OApp(チェーン) eid peer 状態 setPeer(所有者から)
CawProfileL2(Base Sepolia) 40161 配線済 → CawProfile@L1 revert(ロック済)
CawChallengeRelay(Base Sepolia) 40231 配線済 → Archive@L2b revert(ロック済)
CawActionsArchive(Base Sepolia) 40231 配線済 → Relay@L2b revert(ロック済)
CawProfile(Sepolia) 40245 配線済 → ProfileL2@L2 revert(ロック済)
CawProfile(Sepolia) 40231 配線済 → ProfileL2@L2b revert(ロック済)
CawActionsArchive(Base Sepolia) 40267 未配線 成功(openable・設計どおり)

public ノードの RPC は revert 理由文字列を返さず、配線済み eid は一律 execution reverted と表示されます。理由文字列 "OnlyOnce: already called" はソース(OnlyOnce.sol)由来。「配線済みは revert/未配線だけ成功」という差分こそが、per-eid の一回性ロックの動かぬ証拠です。再現は次の一行で(所有者アドレスを --from に置くだけ。送金は発生しません)。

所有者を from に置いて setPeer を eth_call(読み取りのみ・送金なし)で叩く再現は、自分のノードと同じ ethers v6(node) 版を、下のツールの「コマンドで確認」にコピー安全な形で用意しています(配線済みは revert、未配線のみ成功)。

▶ ブラウザでこの検証を再現する(別窓)

押すと、所有者を from に置いて setPeer をシミュレートします(eth_call=読み取りのみ・送金なし)。配線済み5本が revert・未配線が成功という差分を一括確認。from 付き POST のため、ウォレット拡張のある Chrome では弾かれることがあります(その場合はツール内「コマンドで確認」か Edge/シークレットで)。 さらに from を編集でき(所有者でないアドレスにすると未配線も revert に変わり、差分が from 依存だと分かります)、「わざと壊す」で実際に赤くなる(=常に合格ではない)ことや、叩いたリクエスト、開発者ツールの Network タブで実通信を確認できます。

6. 注記・限界

この項目(B-1)が扱うのは「既存 peer の不変性」と、それに隣接する「delegate/所有権の正直な但し書き」です。配線された peer が正しい相手を指しているか(整合性)は別問題で、B-2 で扱います。B-1 は「動かせないこと」を、B-2 は「正しく繋がっていること」を担保します。

なりすましの扉は、鍵を増やして守るのではない。
二度と開かない構造で閉じる。それを、自分の手で確かめる。

If a cross-chain “peer” could be swapped after the fact, an attacker could impersonate a fake chain and, disguised as a canonical read, forge a “fraud / no-fraud” verdict. This item is the record of checking — in code and on-chain — whether that peer-swap path is truly closed. The live V1 bottom line, up front: a wired peer can never be changed again, not even by the owner.

In plain terms

When you connect one chain to another, you set the wiring (the peer) — “which counterpart do we trust?” If an attacker could swap that wiring after the fact, they could pass off a fake chain as the real one and forge the verdict.

wire oncesetPeer

lock engagesOnlyOnce

second try rejectedeven the owner

What we checked is that this wiring has a “once-only” lock (OnlyOnce). Picture a telephone switchboard line that, once connected, can never be changed. And the one who can’t change it is not just an outsider — it’s the owner (the admin key) too. So “even if the key leaks, the wiring can’t be swapped for a fake” — a guard one notch stronger than an ordinary admin lock.

This page is the detailed record for item B-1 of the Verification Ledger. It meshes closely with where ownership sits (A-2).

1. Claim (what is verified)

That an already-wired cross-chain peer cannot be swapped by anyone, the deployer (owner) included. Therefore message forgery by peer substitution cannot occur.

Why is the peer the crux? The archive’s own comment says it: the bytes a relay carries are canonical because they are read from CawActions storage — but swap the peer and you can push arbitrary bytes while disguised as that canonical read. That is the road to forging the fraud / no-fraud verdict. So freezing the peer is the linchpin that stops forgery.

2. Method

1

Identify every OApp that overrides setPeer

Four contracts override setPeer (CawProfile, CawProfileL2, CawActionsArchive, CawChallengeRelay). We confirmed in source that all of them are public override onlyOnce(keccak256(abi.encode("setPeer", _eid))), with a body that just calls super.setPeer(_eid, _peer).

2

Read the effective access control

The override carries onlyOnce. The base it calls, super.setPeer (LayerZero OAppCore), carries onlyOwner. Together the effect is: “only the owner can wire a new eid, once per eid; once wired, not even the owner can change it.” The other wiring setters (setL1Peer, setL2Peer, setMinter, setUriGenerator, setCawActions) are likewise one-shot, so the whole wiring layer freezes after deploy.

3

Confirm OnlyOnce is irreversible

OnlyOnce simply marks a used key _used[key]=true and thereafter reverts with "OnlyOnce: already called". The key is per-eid, so wired eids are fixed while unset eids stay open (for future chains).

4

Demonstrate the differential on-chain (from the owner)

Simulate the owner address with --from and fire setPeer at both a wired eid and an unset eid. If the wired one reverts and only the unset one succeeds, the lock is proven to hold “per-eid, once-only” — even against the owner.

3. Verdict

Wired peers are immutable, even for the owner
setPeer is once-per-eid (OnlyOnce). Only the owner can wire, but once wired no one — owner included — can swap it. The dev comment itself names the threat (a rogue/compromised owner) and deliberately strips the owner of the re-point power. Stronger than vanilla OApp owner-gating.

  • immutable A wired peer cannot be changed by anyone, the owner included (onlyOnce). The dev comment in source itself states “never changeable even by the owner.” Cross-chain forgery on key leak is sealed by code structure.
  • open What stays open is only unset eids (future chains). Wiring is owner-only and once per eid (via the base super.setPeer’s onlyOwner). This is a documented design trade-off.
  • demonstrated Firing setPeer from the owner, the five wired eids (L2: 40161/40231/40231, L1: 40245/40231) revert, while only the unset 40267 succeeds. The differential proves the per-eid once-only lock — even against the owner.

The peer-swap forgery vector is closed. The conclusion of this item: it is closed not by adding more keys, but by a structure that never opens twice.

4. An honest caveat — setDelegate and ownership

We won’t write “done” off the peer freeze alone. Right above that very setPeer, the developer left a security note of their own. We carry it forward without hiding it.

The inherited setDelegate (OAppCore) and transferOwnership (Ownable) are deliberately not overridden. The source comment states these are “handled by the pre-mainnet checklist (multisig handoff or renounce).” So while the owner key is live, rotating the LZ delegate or transferring ownership is still possible.

That said, what a delegate can touch is LayerZero’s side-channel config (DVN, Executor, nonce, etc.); it cannot directly forge a protocol message. And as above, even changing the delegate cannot move a wired peer (the core of B-1).

Currently this owner key is the deployer EOA (a single key, pre-mainnet) confirmed in A-2. This is the exact same residual owner assumption A-2 records — the very thing the source declares it will resolve at the pre-mainnet checklist. It is not a hidden risk specific to B-1; we note it honestly here as the same “single key, pre-mainnet” state that is continuous with A-2.

5. Receipt (public proof)

Results of the differential test, firing setPeer from the owner (wired = revert, unset = openable).

OApp (chain) eid peer status setPeer (from owner)
CawProfileL2 (Base Sepolia) 40161 wired → CawProfile@L1 revert (locked)
CawChallengeRelay (Base Sepolia) 40231 wired → Archive@L2b revert (locked)
CawActionsArchive (Base Sepolia) 40231 wired → Relay@L2b revert (locked)
CawProfile (Sepolia) 40245 wired → ProfileL2@L2 revert (locked)
CawProfile (Sepolia) 40231 wired → ProfileL2@L2b revert (locked)
CawActionsArchive (Base Sepolia) 40267 unset succeeds (openable, by design)

Public-node RPCs do not return the revert reason string; a wired eid shows simply as execution reverted. The reason string "OnlyOnce: already called" comes from source (OnlyOnce.sol). It is the differential — “wired reverts / only unset succeeds” that is the hard proof of the per-eid once-only lock. Reproduce with one line (just put the owner address in --from; no funds move).

Reproducing the from-bearing setPeer eth_call (read-only, no funds) — the same ethers v6 (node) you would run on your node — is provided copy-safe under “CLI” in the tool below (wired revert, only the unset one succeeds).

▶ Reproduce this check in your browser (new tab)

Simulates setPeer with the owner as from (read-only eth_call — no tx, no funds) and checks the differential: 5 wired revert, the unset one succeeds. It uses a from-bearing POST, so a Chrome wallet extension may block it — if so use “CLI commands” inside the tool (or Edge / an incognito window). You can also edit the from address (a non-owner makes the unset edge flip to revert, showing the differential depends on from), hit “Sanity-break” to watch it turn red (not always-pass), and inspect the real traffic in the DevTools Network tab.

6. Notes & limits

What this item (B-1) covers is the immutability of existing peers, plus the adjacent honest caveat on delegate/ownership. Whether a wired peer points at the correct counterpart (integrity) is a separate question, handled in B-2. B-1 secures “it can’t be moved”; B-2 secures “it is connected correctly.”

The door to impersonation is not guarded by adding more keys.
It is closed by a structure that never opens twice — and we verify that with our own hands.
Copyright ©  CAW JAPAN.com All Rights Reserved.
PAGE TOP
夜の薔薇園へ