📘 Academy原文準拠 | Phase 2 · Unit 3 · Lesson 3.1 Transaction Lifecycle and Proof Generation 内容に忠実な日本語版です。原文(英語)・図・動画は公式 Academy(外部リンク・別タブで開きます)を正本に。
ここまでは、Midnight のしくみを部品ごとにバラバラに見てきました。このレッスンでは、それらが実際にどうつながって動くのかを1本の流れとして見ていきます。
DApp で「送信」を押した瞬間、裏では何が起きているのでしょう。これまで学んだ部品が、ここで全部つながります。
3つのフェーズ

Midnight のすべての取引は、大きく3つのフェーズを通ります。
- 準備(Preparation):あなたのアプリが取引を組み立て、証明を生成する
- 検証(Validation):ネットワークがすべてをチェックし、ブロックに取り込む
- 確定(Confirmation):状態が更新され、ウォレットが結果を同期する
🖼️ 原文の図:取引が「準備 → 検証 → 確定」の3フェーズを左から右へ進んでいく流れ図。図そのものは公式 Academy(外部リンク・別タブで開きます) を参照。
ひとつずつ歩いて見ていきましょう。
フェーズ1:取引を組み立てる

DApp を使ってトークンを送りたい、としましょう。裏側では次のことが起きています。
まず DApp は、契約の遷移関数(transition function)をローカルで・オフチェーンに実行します。ここが大事なところです。契約コードはブロックチェーン上ではなく、あなたの端末で実行されます。この実行の間、システムは次の2つを記録します。
- public transcript(公開トランスクリプト):オンチェーンに見えることになる部分(オラクル呼び出し、公開状態の変更)
- private transcript(プライベートトランスクリプト):秘密の部分(あなたの private input、witness データ)
実際の状態変更はロールバック(巻き戻し)されます。まだ何も永続化されません。DApp は、トランスクリプトと、証明に必要なデータだけを手元に持っておきます。
次に来るのが、いちばん重い処理――証明生成(proof generation)です。
なぜ証明生成が必要なのか
Lesson 17 で学んだとおり、Kachina のおかげで、秘密データそのものを明かさずに「自分の private state が、この public state の変更を正当化している」と証明できます。それが実際に起きるのが、まさにここです。
DApp は、circuit(コンパイル済みの契約ロジック)と witness(あなたの秘密データ)を Proof Server(証明サーバー)に送ります。これは多くの場合あなたの端末でローカルに動いている、別プロセスで、重い暗号計算を一手に引き受けます。
なぜ別サーバーなのか
ZK 証明の生成は計算がとても重い処理です。ミリ秒ではなく、数十秒かかる世界です。これをブラウザの中で動かすと、つらいほど遅くなります。でも証明サーバーでネイティブコードとして動かせば、実用的な速さになります。
証明サーバーは数字を計算しきって、あなたの契約呼び出しに対する ZK 証明を返してきます。
取引のバランス調整(Balancing)
提出する前に、もうひとつ段階があります。取引は “balanced(バランスが取れている)” でなければなりません。つまり、入力(input)と出力(output)が正しく合計で釣り合い、かつ手数料を支払っている必要があります。
ここで登場するのが、あなたのウォレットです。DApp はウォレットを呼び出して、支出(spend)側を任せます。ウォレットがやること:
- 入力に使うコイン(UTXO)を選ぶ
- おつり用の出力(output)を追加する
- DUST の手数料を計算してまかなう
- 支出操作のための ZK 証明を生成する
ウォレットはあなたのコインを追跡し、どれが未使用かを把握し、それらのコインが存在することを示す Merkle 証明(Merkle proof)を計算できます。UTXO の帳簿づけを全部やってくれるので、あなたが考える必要はありません。
🖼️ 原文の図:DApp・ウォレット・Proof Server がやり取りして、証明つきの取引を組み立てていく様子。図そのものは公式 Academy(外部リンク・別タブで開きます) を参照。
この時点で、あなたの手元には完全に組み立てられ、証明済みの取引ができています。すべての ZK 証明が添付された状態です。
フェーズ2:ネットワークによる検証
取引を提出すると、まず transaction pool(トランザクションプール)に届きます。ノードは well-formedness(正しい形をしているか)のチェックを行います。
- すべての ZK 証明がそろっていて、有効か?
- 入力と出力は正しく釣り合っているか?
- 手数料の計算は正しいか?
- 構造は正しいか?
このチェックは state-independent(状態に依存しない)ものです。ノードはまだ何も実行しておらず、取引がきちんとした形になっているかを確かめているだけです。
すべて問題なければ、取引はブロックへの取り込みを待ちます。
ブロックへの取り込みと実行
block producer(ブロック生成者)があなたの取引を拾うと、本当の検証が始まります。埋め込まれた ZK 証明が、現在の台帳(ledger)状態に対してフルに検証されます。
shielded(秘匿)取引では、ノードは次をチェックします。
- spend proof:使おうとしているコインを、あなたが本当に所有していることの証明
- contract call proof:あなたの実行が正当だったことの証明
- nullifier が以前に使われていないこと(二重支払い=double-spend の防止)
unshielded(非秘匿)取引では、詳細が見えているのでもっとシンプルです。
すべての証明が検証できたら、状態遷移(state transition)が実行されます。
- Zswap(トークン移転)の場合:coin-commitment の Merkle ツリーが更新され、nullifier が記録され、新しいコインが作られる
- contract 操作の場合:証明された実行に従って契約の状態が更新される
guaranteed フェーズ と fallible フェーズ
ここに Midnight ならではの特徴があります。実は各取引には、2つの実行フェーズがあります。
- guaranteed フェーズ(保証フェーズ):手数料の支払いや、必ず成功しなければならない操作を扱う。ここが失敗すると、取引はそもそもブロックに取り込まれません。
- fallible フェーズ(失敗しうるフェーズ):契約ロジック次第で失敗するかもしれない操作を扱う。このフェーズが失敗しても、guaranteed フェーズの効果は残ります。つまり手数料はすでに払っており、それは返ってきません。
この設計は、よくある攻撃――わざと失敗すると分かっている取引を提出してネットワークの資源を浪費させる――を防ぎます。Midnight では、取引の一部が失敗したとしても、使った分は支払うのです。
フェーズ3:確定の後で

取引が検証され、ブロックに取り込まれると、いくつかのことが起こります。
indexer(インデクサー:チェーンを監視するサービス)が、確定した取引を見つけます。あなたのウォレットは indexer を購読(subscribe)しており、自分のアドレスに関係する取引について通知を受け取ります。
そしてウォレットは次を行います。
- 自分の coin set を更新する(使ったコインを「消えた」と印付けし、新しいコインを追加する)
- 追跡している Merkle ツリーのデータを更新する
- あなたの残高を計算し直す
もし DApp を使っていたなら、DApp は公開状態についての見え方をリフレッシュし、先ほど保存しておいた private transcript を取り出します。これで、オンチェーンの公開結果と、ローカルにとどまった private データの両方を含む、完全な結果をあなたに見せられます。
🖼️ 原文の図:確定後に indexer → ウォレット → DApp へと通知が伝わり、コインセット・Merkle ツリー・残高が更新される様子。図そのものは公式 Academy(外部リンク・別タブで開きます) を参照。
証明サーバー(Proof Server)をもう少し
証明サーバーはすべての中心なので、もう少し詳しく見ましょう。
Midnight の開発環境をインストールしたり、ウォレットを動かしたりするとき、ふつうはローカルで証明サーバーを動かします。これは JavaScript ではなくネイティブのバイナリです。証明生成には生の性能(raw performance)が必要だからです。
証明サーバーは2種類の証明を扱います。
- contract call proof:あなたの契約実行が正当だったことの証明
- spend proof:使おうとしているコインを所有していることの証明
どちらも同じパターンに従います。circuit(何を証明すべきか)と witness(それを真にする秘密データ)を送ると、サーバーは誰でも素早く検証できる簡潔な証明(succinct proof)を返します。生成にはかなりの計算がかかったとしても、検証は速いのです。
この非対称性(asymmetry)が鍵です。証明(proving)は重い。検証(verification)は軽い。
これこそがシステム全体を実用的にしています。重い作業は、取引を作るときに一度だけやる。ほかのみんなは、ただ速い検証をするだけでいいのです。
Ethereum との違い
Ethereum を使ったことがあるなら、別のモデルに慣れているはずです。
Ethereum では:
- 署名つきの取引を提出する
- すべてのノードが契約コードを再実行する
- 結果がオンチェーンに保存される
Midnight では:
- 契約をローカルで実行する
- その実行が正しかったという証明を生成する
- (実行ではなく)証明を提出する
- ノードは(遅い再実行ではなく)証明を検証する(速い)
これが、Midnight が private state を扱える理由です。実際の計算は、あなたの秘密データを使ってあなたの端末で起きます。オンチェーンに乗るのは証明だけで、その証明は private input について何も明かしません。
取引のタイプ
すべての取引がスマートコントラクトを伴うわけではありません。ただトークンを送りたいだけのこともあります。ウォレット API はこれを簡単にしてくれます。
- Transfer transaction(送金取引):宛先・金額・トークン種別を指定する。ウォレットが適切な UTXO を選び、おつりを計算して取引を組み立てる。
- Balance transaction(バランス取引):DApp とやり取りする場合、DApp が取引の一部(contract call)を作り、あなたのウォレットが、手数料や価値の流れをまかなう入力・出力を足してバランスを取る。
- Prove transaction(証明取引):未証明の取引を受け取り、すべての ZK 証明を生成する。
- Submit transaction(提出取引):証明済みの取引をネットワークに送る。
実際には、ウォレットがこれらの手順をまとめて行うことがよくあります。balanceAndProveTransaction 関数が、バランス調整と証明を1回の呼び出しで扱ってくれます。ほとんどの DApp はこれを使います。
ぜんぶをつないでみる
完全な例を1つ追ってみましょう。DAO でプライベートな投票を投じたい、とします。
- DApp で「Vote Yes(賛成に投票)」をクリックする
- DApp が、あなたの投票内容(private)で投票契約をローカルに実行する
- public transcript(投票が行われた)と private transcript(どちらに投票したか)を記録する
- circuit + witness を証明サーバーに送る
- 証明サーバーが、選択を明かさずに「正当に投票した」という証明を返す
- DApp がウォレットに取引のバランス調整を頼む(DUST の手数料を払う)
- ウォレットが、使う DUST のための spend proof を生成する
- 完全な、証明済みの取引が提出される
- ネットワークがすべての証明を検証し、ブロックに取り込む
- 投票数がオンチェーンで更新される(public state)
- あなたの投票内容はあなたの端末にとどまる(private state)
- ウォレットが同期し、取引が完了したと表示する
公開の記録が示すのは:「正当な投票が1票行われた」だけ。
秘密のまま残るのは:あなたが誰で、どちらに投票したか。
これが、動いている Midnight の取引ライフサイクルです。
開発者として押さえる点
- 取引は 準備 → 検証 → 確定 の3フェーズ。契約はローカル実行し、証明だけをチェーンに乗せる(Ethereum のように全ノードが再実行しない)
- 実行時に public transcript と private transcript を記録し、状態変更は一旦ロールバック。証明生成は Proof Server(ローカルのネイティブバイナリ)が担う=数十秒かかる重い処理
- ウォレットがバランス調整を担当:UTXO 選択・おつり・DUST 手数料・spend proof 生成。実装では
balanceAndProveTransactionが balancing と proving を1回でこなす - 検証では spend proof / contract call proof / nullifier 未使用(二重支払い防止)をチェック。確定後は Zswap の coin-commitment Merkle ツリーと nullifier が更新される
- guaranteed フェーズ(手数料など必須)と fallible フェーズ(失敗しうる操作)。fallible が失敗しても guaranteed の効果と手数料は残る=資源浪費攻撃への対策
- proving は重く、verification は軽いという非対称性が、システム全体を実用的にしている
やさしい版・公式へ
- やさしい版:取引の流れ
- 公式:Academy Courses(外部リンク・別タブで開きます)(Phase 2 / Unit 3 / 3.1)
- 関連 docs:Proof Server について(外部リンク・別タブで開きます) / ウォレットと取引の概要(外部リンク・別タブで開きます)
つぎに読むページ
➡️ 原文準拠コースの入口へ戻る。このコースについて(次のレッスンは順次追加します)