DeFMI: 取引の中身を読まない決済層
DeFMI は、金額を公開せずに資産の移転を検証する研究実装である。note の支出権限、数量の保存、予約残額、証券と資金の同時受渡しを検査する。専用の非 EVM Avalanche L1 上で動作するが、独立組織による運用や法的な決済 finality を確立した FMI ではない。
更新: 2026-09-12。現在の note・資産 ID 秘匿経路は下記の版固定の証跡を参照。従来の表とグラフは defmi/DEFMI.md の過去の計測で、最新経路の性能を示すものではない。元の環境: host-c、rustc 1.97.1、ed25519。較正値: スカラー乗算 38.4 ± 4.1 µs、40 ビット範囲証明 20.88 ms。
note と commitment のまま決済する仕組み
note は資産の管理単位、Pedersen commitment は数量を隠す方法である。非 PQC の note でも加法準同型性を使う。同じ生成元にそろえた数量 commitment を C(v;r) = vG + rH と書くと、C(a;r1) + C(b;r2) = C(a+b;r1+r2) である。資産 ID を隠す経路では、資産と数量の対応を示す証明も検査する。
例えば 100 の note を消費し、相手に 30、自分にお釣り 70 の note を作る。これは当事者から見た説明で、100・30・70 を公開するわけではない。検証者は公開 commitment と証明から数量の保存、非負の範囲、所有権、資産の対応を確認する。市場決済では数量と価格の積も検証し、予約を減らして証券と資金の受領権を原子的に作る。受領権の償還で、再び支出できる note を得る。
どの note かを明かさず、二重使用を防ぐ
同じ入力 note の支出には、必ず同じ nullifier が必要である。現在の Triptych 系証明は、隠された同じ入力位置に所有権・数量・serial_point を結び付ける。検証者は証明を検査し、その番号が使用済みなら拒否する。新しい番号を自由に選んで元の note を使い直すことは、正しい証明ではできない。出力として作った新しい note には、新しい支出用の番号が対応する。
公開する情報とウォレットに残す秘密
| 対象 | 公開する情報 | 秘密にする情報 |
|---|---|---|
| 数量 | commitment と整合性の証明 | 数量、資産の開封情報、秘匿用乱数 |
| 受取り | 使い捨ての受取先の公開点と暗号文 | 復号鍵と復号した内容 |
| 支出 | nullifier と支出証明 | 支出鍵と証明に使う秘密 |
公開 note のコピーだけでは支出権限を得られない。復号できることと支出できることも別である。一方、同じ取引で作られた出力、時刻、出力数、市場、予約の関係などは見える。送金者は自分が送った内容を知っている。公開用の暗号形式であることは、取引全体の追跡不能性や周辺情報からの推測耐性を保証しない。鍵保管の法的な区別は日本の自己保管と顧客資産管理を参照。
資産 ID 秘匿の native 経路: 2026-09-12
資産 ID を乱数付き commitment に置き換え、公開の候補群にある同じ資産へ対応することを証明する。候補群は 2〜64 資産で、ゼロ数量の note でも受取人は暗号化された開封情報から資産を識別できる。資産定義と候補群は公開され、同じ与信枠では予約と決済を通じて同じランダム化 ID を保持する。
単一 Linux ホスト上の 5 つの AvalancheGo 検証者で、発行、予約、部分約定、取消し・期限切れ返却、受領権償還、受領 note の再送金を実行した。再起動後のウォレット読取りでも、売り手は証券 57・通貨 600、買い手は証券 53・通貨 410、未約定返却の確認用保有者は 25・25 となり、全検証者の状態ルートが一致した。
保証するもの、しないもの
DeFMI が検査できるのは、何も開かずに算術だけで決着がつく事柄に限られる。
| 保証 | 成り立たせるもの |
|---|---|
| 価値が生成も消滅もしていない | 資産ごとの commitment の保存関係、開封知識・範囲の証明、正規の入力と発行権限の検証 |
| 残高が負にならない | 差額に対する範囲証明 |
| 両脚が一緒に動く | どちらか一方を適用する前に、両方を検査する |
| 一つの指図は一度だけ決済される | nullifier(使用済みの印)の登録 |
| 資金脚 = 数量 × 価格 | 三つの commitment にわたる積の証明 |
| 証券脚は指図どおりの数量である | 生成元をまたぐ等価証明 |
DeFMI は資産の対応や署名された指図との一致を証明で検査する。価格の経済的妥当性や法的な権利の有効性は、算術だけでは決まらない。既存の市場経路では、認可された委員会と業務ルールへの依存も残る。
過去の残高幅ベンチマーク
線形バックエンドの証明は、台帳の残高範囲をビット分解したものである。だからコストは幅に比例するはずで、実際にそうなった。構築は 0.98 ms/bit、決済は 0.80 ms/bit、wire 上(通信路に載るバイト数)は 896 B/bit。決済側の切片 2.1 ms は、zkPI 指図そのものの検証にかかる分である。40 ビットでは決済に 34.0 ms かかる。そのうち約 6% が指図の検証で、残りは台帳側の範囲証明である。
ここから導かれるのは暗号の判断ではなく、銘柄を登録するときの判断である。決済を速くしたいなら、残高の幅を見直せばよい。証券の数量と現金の金額は桁が何段も違うので、レール(資産ごとの決済の経路)ごとに別の幅を与える。証券を 24 ビット、現金を 48 ビットにすると、両方 48 ビットの場合より決済が 24% 速くなり、送るバイト数は 10,752 B 減る。暗号には何も変更がない。
Bulletproof バックエンド
最適化したバックエンドは、監査済みの bulletproofs crate を使う。証明サイズは幅に比例する形から対数の形に変わる。ただし細かい幅は選べなくなる(2 の冪だけ)。そのため幅をまたいで公平に比べられる組は、40 ビットの線形バックエンドと 64 ビットの Bulletproof バックエンドである。
| 残高の幅 | 決済(線形) | 決済(Bulletproof) | 倍率 | パッケージ(線形) | パッケージ(Bulletproof) |
|---|---|---|---|---|---|
| 8 bit | 8.5 ms | 2.09 ms | 4.1× | 29,267 B | 2,656 B |
| 16 bit | 14.8 ms | 2.78 ms | 5.3× | 36,435 B | 2,912 B |
| 32 bit | 27.6 ms | 4.26 ms | 6.5× | 50,771 B | 3,168 B |
| 40 bit 線形 vs 64 bit BP | 34.0 ms | 7.02 ms | 4.8× | 57,939 B | 3,424 B |
artifact(計測の出力ファイル): defmi.json、rust_bench.json · 最適化バックエンドは host-a(較正値 25.8 µs)、基準は host-c。採用の根拠に使う前に、両方を同じホストで再計測すること
1 コアあたりに換算すると、毎秒 29.4 件から 142.5 件の決済になる。独立したパッケージの検証は互いに何も共有しないので、完全に並列化できる。1 ノードで 8 ワーカーで毎秒 217 件を計測した(1 ワーカーの 7.40×)。決済ノードの容量は、機材をどれだけ調達するかの問題である。
従来の asset tag の構成と計測
MPC は、依頼がどの資産についてのものかを隠す。ところが決済の段で取引を銘柄ごとのレールに載せると、隠したものが元に戻ってしまう。逆に全銘柄を一本のレールに載せると、価値の保存は銘柄をまたいだ合計でしか成り立たない。資産 A を資産 B として持ち出せてしまう。
そこで使う構成が asset tag(資産ごとの生成元による印)である。資産 a を q 単位持つとは、A_a^q · h^r を持つことである。移転のたびに新しい y を選んで H = A_a · h^y を公開し、範囲証明はすべてこの H に対して作る。この隠した印を実在の資産に結び付けるのは、差額に対する範囲証明である。支払側の残高はすでに A_a の下に置かれているので、他の tag ではその残高を開けない。決済一件あたりの追加分は 32 B と、生成元をまたぐシグマ証明が一つ。時間の増分は、計測の 0.2 ms のノイズに埋もれる。one-of-many 所属証明(Groth–Kohlweiss。N 個のうち一つを持つことを、どれかは言わずに示す)が必要なのは、残高を口座に発行するときの一度だけである。検証は 4 銘柄で 0.58 ms、64 銘柄で 2.24 ms。
攻撃も計測した。別の資産として登録済みの tag で残高を持ち出す。登録されたことのない点を使う。どちらも "remainder does not equal balance minus amount" で拒否される。パッケージは 64 銘柄のどれであっても、バイト単位で同一である。
ネッティング: gross-gross、gross-net、net-net
これらは BIS の DvP モデル 1、2、3 にあたる。同時に、範囲証明を何本、どこで作るかという問題でもある。gross のレールは、注文ごとに取引後のポジションが負でないことを証明する。だから構成上、決済不履行は起こり得ない。その代わりに順序に依存する。net のレールは、期間中は準同型に累積するだけで何も証明しない。締めの時点で参加者ごとに一度、net 額が足りていることを示す。commitment は大きさだけでなく符号も隠すので、途中で負になったポジションからは何も漏れない。
| N | P | 方式 | 注文ごとの検証 | 締め | 合計 | gross-gross 比 |
|---|---|---|---|---|---|---|
| 64 | 8 | gross-gross | 6.25 ms | 0 | 400.0 ms | 1.00× |
| 64 | 8 | gross-net | 4.84 ms | 10.3 ms | 320.1 ms | 1.25× |
| 64 | 8 | net-net | 3.54 ms | 20.7 ms | 247.5 ms | 1.62× |
| 64 | 8 | net-net + attestation | 0.00 ms | 20.5 ms | 20.6 ms | 19.40× |
最初の予測は外れた。範囲証明の本数だけを数えれば、net-net の仕事は 1/8 になるはずだった。計測では 1.62× にとどまった。理由は、注文ごとの 3.5 ms が zkPI 指図そのものの検証だからである。これは取引ごとに一度は必要で、ネッティングでは取り除けない。取り除くには、指図の粒度を変える。定足数がサイクル全体にまとめて署名すれば、決済層の仕事は取引数に依存しなくなる。そのとき配分は証明ではなく、定足数の attestation(署名による保証)になる。これは清算機関が昔からやってきたことそのものであり、ここではそれを明示しただけである。それを守れる形にする場所が DeCCP である。
commitment としての日中当座貸越
実務では、net のポジションを負にする注文をすべて拒否するわけではない。差し入れた担保に対して限度を与える。これはまさに日本銀行の日中当座貸越である。ここでは限度そのものが commitment である。足りているかどうかは position + limit について証明するので、ポジションがゼロのどちら側にあったかは証明から分からない。限度のコストは実質ゼロである。なしで 9.4 ms、ありで 9.2 ms。同じ幅の範囲証明を別の commitment に対して作るだけだからである。限度の付与は構築 9.4 ms、検証 1.3 ms で、一度きりである。
PositionBook::grant と Cycle::admit は別々の呼び出しで、差し入れた担保をロックする状態もない。限度のコストは計測済みである。しかし原子性(全部まとめて成立するか、何も起きないか)は文書に書かれた要件にとどまり、実装されていない。デフォルト・ウォーターフォール
net のレールは締めの時点で不履行になり得る。その不履行をどの順序で処理するかが、この取り決めの本体である。だから順序を強制する。「トランシェ k は、トランシェ k−1 が尽きた後にしか引き出せない」という条件を draw_k × remaining_{k−1} = 0 と書き、その積の commitment を単位元に固定する。コストはトランシェ数に比例し、トランシェあたり構築 11.2 ms、検証約 1.4 ms。デフォルトごとに一度である。
清算機関を間に入れる
更改(novation)の下では、検証すべき二者間の分割は残らない。A と B の取引は、A 対清算機関と、清算機関対 B の二つになる。債務は commitment なので、一本の辺を二本に置き換えるのは乗算二回で済み、証明は要らない。清算機関の帳簿が差引ゼロであることも、同じ構成で確かめられる。参加者 8 で計測すると、net-net は取引数とともに増える(16 件で 66 ms、256 件で 794 ms)。清算済みサイクルは増えない(19 ms から 38 ms)。更改のコストは取引あたり 0.53 µs。これで得られるのは、attestation の系統が守れる形になることである。名前の分かる清算機関が反対側に立つ。その帳簿が差引ゼロであることは誰でも検査できる。その証拠金は commitment された上限である。その資本は、CPMI-IOSCO と EMIR が定める位置、つまりウォーターフォールの中にある。
やらないことが四つある。債務グラフは資産ごとなので、銘柄をまたぐ更改は拒否する。異なる提供者にあるポジションを一括の証拠金で相殺することはしない。算術上の妥当性は、法的な妥当性について何も言わない。清算機関は取引を落とすことはできる。ただし取引をでっち上げることはもうできない。check_novation が、更改前のグラフのすべての辺に両当事者の署名を要求するからである。
誰が誰に払ったかを隠す: note 台帳
現在の note 形式は、使い捨ての所有者公開点、数量 commitment、一時公開鍵、受取人向け暗号文を別々に保持する。バージョン 2 の Triptych 系証明が、所有権と数量を同じ隠された入力に結び付け、公開所有者鍵ではなく U/S を nullifier として公開する。以下の表とグラフは旧方式の計測値であり、現在の証明の性能・安全性を示すものではない。
| ring の大きさ | 証明(支払側) | 検証(ノード) | wire |
|---|---|---|---|
| 2 | 10.9 ms | 1.8 ms | 37,024 B |
| 16 | 11.7 ms | 2.6 ms | 37,696 B |
| 64 | 14.5 ms | 3.7 ms | 38,144 B |
| 512 | 40.3 ms | 9.3 ms | 38,816 B |
この非対称性が狙いである。wire 上のバイト数は ring を倍にするごとに 224 B 増えるだけで、検証は ring 512 まで 10 ms を下回る。増えるのは証明の時間で、それは支払側自身の機器で使う。つまり匿名集合の上限を決めるのは支払側であって、決済ノードではない。受取人は note あたり 0.063 ms、スカラー乗算一回でプールを走査する。プールの大きさに比例するコストはこれだけである。使い捨ての公開点だけから恒久アドレスへの対応は示されないが、周辺情報からの関連付けは残る。
Rust 移植では、二本の note レール上の同じ DvP が ring 8 で 51.9 ms で決済され、パッケージは 5,476 B になった。退役した実装では 50,827 B だった。ここでも Bulletproofs とビット分解の差が出ている。そこでの構築列には 3-of-7 FROST の署名手順全体が含まれるので、端から端までの構築コストである。
ring の実際の価値
ring が匿名集合として働くのは、おとりが本物の note と見分けられないときに限る。実際に使われる note は、直前に受け取った note である。決済とはそういうものだからである。一様に選んだおとりはたいてい古い。だから ring の中で最新の note を当てにいく観察者は、それだけで 1/R より高い確率で正解する。この観察者の戦略は、実行前に明記しておいた。
| おとり | ring | 通信量 0 | 通信量 4 | 通信量 16 | 名目 1/R |
|---|---|---|---|---|---|
| 一様 | 16 | 1.000 | 0.547 | 0.359 | 0.062 |
| 新しさを合わせる | 16 | 1.000 | 0.062 | 0.062 | 0.062 |
artifact: rings.json(host-a)·「通信量」は、受け取ってから払うまでの間に入る他の決済の件数
note レール上の匿名集合とは、他人の通信量のことである。おとりの規則は、そのうちどれだけを ring に使えるかを決めるだけである。
他の通信量がなければ、どちらの規則でも、ring がどれだけ大きくても、その価値はゼロである。16 件分の通信量があると、新しさを合わせたおとり(ring_recent、30 行)は観察者の正解率をちょうど名目値まで押し戻す。一様なおとりでは、その 5.8× にとどまる。
履歴とともに増えなくなった状態ルート
ring のベンチマークで、検証時間がプールとともに増えていくことが見つかった。しかし check_spend の中に、プールに比例する処理はない。増えていたのは状態ルート(台帳全体の要約ハッシュ)の計算だった。保持したことのある全 note を再圧縮し、使用済みの全 serial を再整列する処理を、決済一件につき四回行っていた。4,096 note では、暗号が 8 ms のところに 61.3 ms の帳簿付けが乗っていた。note は追記のみ、serial は挿入のみなので、ルートは今では変更のたびに一度だけ更新するハッシュになった。0.15 µs で一定である。全体を歩き直すと 15.3 ms だった。
二つの台帳をまたぐ PvP
状態を共有しない二つの台帳には、両脚をまとめて動かす関数がない。第三者なしの公正な交換は一般には不可能なので、何かが両者の間を渡らなければならない。ハッシュロックでは前像を渡す。しかし前像は両方の台帳に平文で残るので、両方を読める者は両脚を結び付けられる。代わりに使うのが adaptor signature である。先手側が一方の台帳で claim(受取の実行)をすると、後手側にスカラーが一つ渡る。各台帳に記録されるのはふつうの署名で、相手の台帳の記録と共通点は何もない。
1. Bob -> Alice : Y = g^y
2. Alice prepares leg A; her money leaves her account
Alice -> Bob : a pre-signature over "leg A", adapted to Y
3. Bob prepares leg B, and sends his own pre-signature
4. Bob claims on A -> he is paid, and y becomes readable
5. Alice reads A, recovers y, claims on B -> she is paid
Bob が秘密を選んで先に動くので、Bob がリスクに晒されることはない。Alice が晒されるのは手順 4 と 5 の間の一つの窓だけである。二つの期限の隙間が Alice の反応時間を覆っているとき、かつそのときに限り、Alice は安全である。この隙間が、この取り決めにおけるヘルシュタット・リスクである。32 ビットのレールで計測すると、脚の準備 1.29 ms、先手側の claim 0.05 ms、後手側の反応 0.06 ms、期限切れの脚の巻き戻し 0.55 µs。資金をリスクに晒しているのは暗号ではない。期限の隙間は二つの台帳の決済 finality(取り消せなくなるまでの時間)が決め、それは暗号の処理時間より 3 桁から 5 桁大きい。窓を短くしたい配備は、速い証明ではなく速い finality を求めるべきである。
一つのチェーン上なら、二つの DeFMI 市場を一つのトランザクションから呼ぶこともできる。追加コストなしで原子的になり、露出はゼロである。ただし、そのトランザクション自体が二つの脚を結び付ける。adaptor の系統は呼び出しが 4 倍、検証が 3.0% 増える。その代わりに連結不能性(二つの脚を結び付けられないこと)が得られるが、それも handle を市場ごとに導出しているときに限る。zkPI のページの表が示すのは次のことである。一つの名前をどこでも使うと観察者の正解率は 1.000、市場ごとの handle なら 1/k になる。暗号は変わらない。
正本台帳と一致させる
日本の振替制度の下では、この台帳は振替口座簿にはなれない。権利は、振替機関と口座管理機関が保持する記録に基づく。したがって DeFMI は正本を写した鏡であり、照合はその取り決めの代価である。照合は一行の代数で済む。commitment は乗算できるので、残高の積はその合計への commitment になる。そこから asset tag の下で正本台帳の数字を割り出すと、残るものは h の純粋な冪でなければならない。その指数を知っていることを証明すれば、合計が一致することが証明され、それ以外は何も語らない。証明は台帳の中身が何であれ 96 B で、4,096 ポジションの検査は 0.65 ms。3-of-7 の定足数が、合計の blinding(隠すための乱数)のシェアから同じ命題を 4.5 ms で組み立てる。だから誰も合計を持たない。合計を持つ者は台帳全体を開けてしまうので、これは重要である。
不一致は合否でしか出ない。どこが違うかを見つけるには小計が要る。主張した小計はすべて公開になり、最後は一ポジション、つまり一つの残高まで降りていく。これは DeFMI の中で唯一、意図して開示する操作であり、何かがおかしい日にこそ使いたくなる操作でもある。ポジションごとに数字を持つ正本台帳なら、追加コストなしに場所を特定できる。
監査人に一部分だけ見せる
note のウォレットは、閲覧鍵(view key)と支出鍵(spend key)からなる。見せる範囲(scope)を区切る仕組みは鍵ではなく、アドレスにある。scope(銘柄、四半期、mandate)ごとに、ウォレットの seed からハッシュで専用のアドレスを導出する。そこへ送られた note は、その scope の閲覧鍵でだけ見つかる。付与は発行 0.08 ms、検査 0.07 ms。scope を持つ者はプールのうち付与された 5 分の 1 にちょうど届き、serial は一つも復元できない。serial には支出鍵が要るからである。
消えない限界が三つある。第一に、付与は取り消せない。失効とは次の scope へ移ることであり、それはアドレス管理の作業である。Rolling はその予定表を、人の規律ではなくコード上の対象物にする。第二に、閲覧鍵で見えるのは入金だけで、出金は別の開示になる。第三に、scope の細かさは支払側が協力する範囲にとどまる。前四半期のアドレスを使う支払側は、note を前四半期の scope に入れてしまう。arrived_off_schedule はそれを検知するが、防ぐものはない。
handle を持てるのは誰か
handle は誰でも選べる。許可が要るのは審査(vetting)を通ることである。審査名簿が保持するのは handle ではなく、封印された封筒 C = a·G + r·h である。だから一つ追加しても一様ランダムな点が現れるだけで、加入のタイミングからは、どの項目がその人のものか分からない。所属は、固定した群(crowd)に対する one-of-many で証明する。証明ごとに新しい ring を作るほうが秘匿されるように見えるが、実際は逆である。重なり合う ring は交わりから絞り込めるからである。Schnorr 証明が handle を封筒が定める一つに固定するので、一つの審査から出る handle は一つだけで、一族にはならない。Roll::vetted() と Roll::crowd() は公開なので、匿名集合の大きさは誰でも検査できる。審査の事象を隠すことと、群の大きさを証明することは同じ情報である。この設計は後者を選ぶ。
検証時間は群とともに倍になると予測したが、実際は約 1.4× にとどまった。まとめて行うマルチスカラー乗算の効果が現れている。群 128 で検査は 3.84 ms、証明は 1,676 B なので、128 を既定値とする(vetting::CROWD)。数千の群には回路の中の Merkle 木が要る。それはここで使うシグマ・プロトコルや Bulletproofs とは別の証明系である。
Avalanche L1
配備済みの実行経路は、avalanche/defmivm/ にある専用の非 EVM カスタム VM である。AvalancheGo が RPCChainVM プロトコル 45 で起動する。ネイティブの状態遷移は、資産登録、口座開設、事前承認済みの予約、複数脚の原子的決済。完全な受入ゲート(合格条件の一式)が覆うのは、口座を持たない note、事前承認済みの予約、7 者 MPC の zkPI、複数 RFQ の原子的決済、法人単位で共有する上限、再起動からの復旧、状態ルートの一致である。
| 項目 | 観測値 |
|---|---|
| ローカルの AvalancheGo プロセス | 5 |
| 受理された高さ | 1, 2, 3, 4, 5 |
| 決済の受理 | 207.3 ms |
| 口座開設 2 件 | 424.6 ms |
| ノードの再起動と状態復旧 | 1454.7 ms |
| 再起動の前後で状態ルートが同じ | yes |
artifact: avalanche_l1_acceptance.json、avalanche_qomm_full_acceptance.json(高さ 53 で 2 件の RFQ を一つの原子的バッチに、高さ 54 で claim の確定、検証者の再起動後もルートが同一)
この実行は 1 台のホスト上の 5 プロセスで行った。証明できるのは、ネイティブ合意による受理、冪等な(繰り返しても結果が変わらない)クラッシュ復旧、状態ルートの一致である。5 つの独立した組織で動くことや、公開ネットワークで使える段階にあることは証明しない。
まだ欠けているもの
- 検証者は提出された証明一式を完全に検証する。しかし非公開の MP-SPDZ トランスクリプトを再実行することも、シェアから証明への受け渡しを証明することもしない。この委員会への信頼を取り除くには、非公開の計算全体の証明と、別途の合意ベンチマークが要る。
- 受付、担保差入、限度、支払は一つの原子的な事象になっていない。
- 現在の nullifier は送金者に支出秘密を渡さない設計である。ただし、時刻・候補群・予約などの周辺情報による関連付けは残る。
- 第二の決済通貨の追加は口座開設の判断であり、コード変更ではない。tag 付きの資金脚は、口座の建値通貨に対して開くからである。