Midnight の改善提案リポジトリに、日本時間 9 月 16 日の朝、MPS-0040「Cross-Contract Call Provenance in Compact Circuits」が追加されました。契約が別の契約から呼び出されたとき、呼ばれた側には「どの契約の、どの回路から呼ばれたのか」を知る手段がない。その穴を 446 行かけて正面から記述した提案です。
起案者は Hector Bulgarini 氏と Nicolas Di Prima 氏で、状態は Proposed、分類は Core、作成日は 9 月 15 日。SIPO が 9 月 10 日に取り上げた MPS-0039 と同じく、Midnight の契約言語 Compact が「回路の外にあるものを中からどう見るか」で詰まっている領域です。どこが非対称なのか、そしてなぜ既存の回避策では埋まらないのかを見ていきます。
■ 呼ぶ側の記録だけが、きちんと残る
提案の冒頭はこう書き出されます。「A Compact contract invoked as the callee of a cross-contract call cannot learn whether a contract invoked it, which contract invoked it, or which circuit of that contract made the call.」呼ばれた契約は、そもそも契約に呼ばれたのか、どの契約に呼ばれたのか、その契約のどの回路が呼んだのか、いずれも知ることができません。
呼ぶ側の作法は、対照的にきちんと決まっています。呼び出し側は kernel.claimContractCall で、呼び先のアドレス・呼び先の回路のハッシュ・通信のコミットメントを自分の記録に書き込みます。検証はこの主張に対応する呼び出しが同じ区間に存在しない取引を拒み、同じ組み合わせに対する二重の主張も拒みます。
おもしろいのは、情報そのものは存在しているという点です。台帳は呼び出し元のアドレスを呼び出しの枠ごとに導出し、仮想マシンの文脈配列の 6 番目の位置に置いています。提案は「Any observer can reconstruct the edge from the transaction alone」と書き、取引を外から見るインデクサなら誰がどの契約を呼んだか復元できる、と指摘します。読めないのは当事者である呼ばれた側だけです。
言語の側にも入口がありません。Compact のカーネル ADT が備える操作は 16 個あり、身元の読み出しは self ただ 1 つ。kernel.caller は 0.33.0-rc.2、0.34.0-rc.0、0.34.0 のいずれでも「operation caller undefined for ledger field type Kernel」として拒否されます。呼び出した回路がどれだったかに至っては、どこにも記録されていません。
■ していない呼び出しを、主張できてしまった
提案が強いのは、机上の指摘で終わっていないところです。node 2.1.0 と ledger-9.1.0.0-rc.3 の組み合わせで、著者らは実際に試しています。kernel.claimContractCall の引数は Compact の側から自由に書けるため、ある契約が「クライアントが行った呼び出し」を自分のものとして主張したところ、その取引はそのまま受理されました。提案は結果を SucceedEntirely と記しています。
そのうえで、受理されたバイト列に台帳自身の導出処理を後から適用すると、その呼び出しの呼び出し元は主張した側の契約として解決されます。三つの検証規則が働いて防げる範囲もあり、たとえば二重主張や順序違反はノードの受け入れ時点で名前つきで弾かれます。ただし著者らの整理では、他人が勝手に偽装することは防げても、自発的な「貸し出し」は残る。文脈配列の 6 番目から読める値は、正確には「この呼び出しを自分の記録で主張している契約」であって、「実際に呼んだ契約」とは限りません。
■ 代わりになりそうな手は、代わりにならない
ではアドレスを引数で渡せばよいのでは、という発想は、提案がまとめて潰しています。ContractAddress は回路の中で任意のバイト列から構成できるため、引数で渡された身元は呼び出し側が選んだ 32 バイトにすぎません。呼び出し側が自分を認証しようにも、契約の秘密は witness にしか存在せず、自分自身が呼ばれる側になった契約は実行時に witness を持てません。コールバックで確かめ直す道は、再入防止の仕組みと、呼び出しの木構造を崩す取引の拒否という二重の壁に阻まれます。
他チェーンとの比較も具体的です。Aztec の function_selector と Solidity の msg.sig はどちらも呼ばれた側自身の情報であり、Aleo の self.caller が指すのはプログラム単位。Solana の instructions sysvar は、クロスプログラム呼び出しが作る内側の命令を含みません。Move と Sui は呼び出し元の基本命令をあえて持たず、witness 型と capability オブジェクトで答えていますが、Compact の型は構造的で、実装の解決も呼ぶ側の文脈から行われるため、その答えはそのままでは移植できないと整理されています。
■ この提案が、あえて引かなかった線
MPS-0040 は目標を 9 つ挙げますが、実装の設計には踏み込みません。非目標の最後に「The design of any primitive: no mechanism, type, or namespace is chosen here.」と明記し、どんな命令をどんな名前で足すかは決めない、と宣言しています。これは著者の遠慮ではなく、文書の種類がそう決まっているからです。リポジトリの説明によれば、MPS は解決策に踏み込まない問題記述であり、何を解くべきかを MPS が定め、どう解くかは MIP が提案する、という分担になっています。
条件のほうは踏み込んでいます。呼ばれた側が契約からの呼び出しかどうかを区別できること、直前の呼び出し元のアドレスだけを知れること、どの回路が呼んだかを既存の識別子の形で知れること。そして 4 番目に、その答えが呼び出し側にも同じ意図に含まれる他の契約にも送信クライアントにも偽造できず、witness 経由では決して渡らないこと。8 番目には、Compact が持たない証明の再帰を必要としないこと、外から見える情報を増やさないことが置かれています。
扱わない範囲も明示されています。ウォレット側の身元は MPS-0029、呼び出し境界をまたぐ witness と秘密状態は MPS-0021、再入制御は「これは同一性の検査であって並行性の制御ではない」として外されました。すでに採択済みの MIP-0007 が定める「契約が所有する名前」は、名前レジストリ自身が呼ばれる側になるため、この穴が埋まるまで実装できないままだ、とも書かれています。
■ 穴を、先に書いておくということ
SIPO がこの種の提案を追うのは、機能の追加より先に「まだできないこと」が公開の文書に残るかどうかを見ているからです。MPS-0039 は呼び出しのたびに 384MB の成果物が要ることを記録し、MPS-0040 は呼ばれた側が呼び出し元を知れないことを記録しました。どちらも、使えるようになった機能の告知ではなく、composability を実際に組もうとした人が最初にぶつかる壁の座標です。
Cardano のガバナンス提案でもそうですが、こうした文書が一次資料として残るかどうかで、後から入ってくる開発者が踏む地雷の数は変わります。MPS-0040 は後続の MIP を 2 本示し、そのうち主となる 1 本を「the keystone the others hang from」、つまり他が乗る要石と位置づけました。状態はまだ Proposed で、Review・Accepted・Implemented という段階が先に控えています。それでも、穴の形と、その穴を確かめた手順は公開されました。
Midnight の契約層を触る予定がある方は、参照実装を書き始める前にこの 1 本を読むだけで、設計の前提が一つ変わるはずです。
■ ことば
- Compact — Midnight の契約を書くための言語です。処理の内容をゼロ知識証明の回路へ落とし込む前提で設計されているため、通常のプログラミング言語なら当たり前に読める情報が、回路の中からは読めないことがあります。
- 回路(circuit) — 契約の処理を、証明を作れる形に変換したものです。契約の入口ごとに回路が対応し、今回の提案は「どの入口から呼ばれたか」が呼ばれた側に伝わらない点を問題にしています。
- MPS — Midnight Problem Statements の略で、解決策を決めずに「何が足りないか」だけを書き残す文書です。どう直すかを書く MIP と対になっており、今回の提案が実装の中身に触れていないのは、この役割分担によるものです。
一次ソース / 関連リンク
- MPS-0040: Cross-Contract Call Provenance in Compact Circuits(提案本文)
https://github.com/midnightntwrk/midnight-improvement-proposals/blob/main/mps/mps-0040-cross-contract-call-provenance.md - 追加時のコミット(PR #310・2026 年 9 月 16 日)
https://github.com/midnightntwrk/midnight-improvement-proposals/commit/8a76400c561d41565f9a681eb54cb1c17435f5a4 - SIPO.TOKYO「Midnight MPS-0039、契約呼び出しに 384MB の成果物が必要と文書化」
https://sipo.tokyo/signal-20260910-b8145ba1/
