📘 Academy原文準拠 | Phase 3 · Unit 5 · Lesson 5.1 Capstone — Extend Your Bulletin Board 内容に忠実な日本語版です。原文(英語)・図・動画は公式 Academy(外部リンク・別タブで開きます)を正本に。
ここまでの10レッスンで、Midnight DApp 開発をひととおり学んできました。開発環境のセットアップから始まり、Compact の基礎、witness(証人)、シールドトークン、そしてけいじ板(bulletin board)のスタック全体まで。いよいよ、自分の手でつくる番です。
この卒業課題(capstone)では、けいじ板の契約に新しい機能を足して拡張します。やることはこんな流れです。
- example-bboard(外部リンク・別タブで開きます) リポジトリを fork する
- Compact の契約を書きかえる
- TypeScript の連携部分を更新する
- テストを書く
- 仕上げた成果物をレビューに提出する
動画で学ぶ(公式)
課題の内容(The assignment)
下の拡張トラックから1つを選びます。あるいは、自分で考えたものを提案してもかまいません。どのトラックも、けいじ板に意味のある機能を足しつつ、このモジュールで学んだパターンをしっかり使う設計になっています。
Track A: Multi-Post Board(複数投稿のボード)
いまのボードは、メッセージをちょうど1件しか持てません。これを、同時に複数の投稿を持てるように拡張します。
変えるところ:
- Contract(契約):いまの「単一メッセージ」と「owner」の
ledgerフィールドを、複数の投稿を持てるデータ構造に置きかえます。各投稿には、それぞれ「メッセージ・owner の公開鍵・識別子(identifier)」が必要です。投稿数の上限をどう扱うかも考えましょう(ボードが無限にふくらまないように)。新しい circuit が要ります:postは「置きかえ」ではなくコレクションに追加する形に。takeDownは「どの投稿を消すか」を指定するために投稿の識別子を受け取る形にします。 - Witnesses(証人):witness はこれまでどおり秘密鍵(secret key)を提供します。ただし
takeDownは「どの投稿を消すか」を見分ける手段が要るようになります。投稿の識別子を circuit のパラメータにするのか、それとも何か別のものから導くのか、を考えてみてください。 - Tests(テスト):シミュレータのテストを書きます。異なるユーザーから複数のメッセージを投稿する/特定の投稿を消す/他人の投稿を消そうとする/投稿数の上限に達する、といったケースを確認します。
- API/CLI:API の
state$を更新して、投稿の一覧と、そのうちどれが今のユーザーのものかを見せます。CLI のメニューも更新し、どの投稿を消すかをユーザーが選べるようにします。
ここで使う中心概念:Compact でのデータ構造の設計、複数回の disclose() 呼び出し、コレクションによる状態管理、シミュレータの拡張。
Track B: Timed Posts with Expiration(時限つき投稿・期限切れ)
いまのボードでは、投稿は手で消すまでずっと残ります。これに期限(expiration)の仕組みを足して、一定時間が過ぎたら投稿を自動的に「消せる状態」にします。
変えるところ:
- Contract(契約):各投稿に timestamp(タイムスタンプ)または blockHeight(ブロック高)の
ledgerフィールドを足し、「いつ投稿されたか」を記録します。新しい circuit(expirePost、またはtakeDownを改造)を足して、期限のしきい値(threshold)を過ぎたら誰でも投稿を消せるようにします。元の投稿者はいつでも自分の投稿を消せますが、期限切れになれば、誰でもそれを片づけられます。 - Witnesses(証人):期限チェックに witness のデータが要るのか、それともオンチェーンのデータだけでできるのか、を考えましょう。ブロック高(block height)は circuit のコンテキストから手に入るので、追加の witness は要らないかもしれません。
- Tests(テスト):しきい値の前は期限切れにできないこと、元の投稿者は早めに自分の投稿を消せること、期限切れの投稿は誰でも消せること、そしてシーケンスカウンタがこれまでどおり正しく増えることを確認します。
- API/CLI:導出した状態(derived state)に、投稿の経過時間または残り時間を表示します。CLI のメニューに「expire」オプションを足します。
ここで使う中心概念:契約内での時間ベースのロジック、条件つきアクセス制御(owner または 期限切れ)、複数の認可(authorization)経路を持つ circuit の設計。
Track C: Sealed Ownership with Selective Disclosure(封印した所有権と選択的開示)
いまのボードは、owner の導出公開鍵を誰でも見える公開台帳に置いています(といっても、そこから元の鍵を逆算はできません)。これを、所有権に sealed の ledger フィールドを使うように改造し、owner をオンチェーンから完全に見えなくします。そのうえで、任意の「reveal(明かす)」circuit を足します。
変えるところ:
- Contract(契約):owner を
export ledgerからsealed ledgerに変えます。postcircuit はこれまでどおり公開鍵を導出して保存しますが、それが観察者からは隠れるようになります。revealOwnershipcircuit を足して、「秘密鍵を明かさずに、自分が owner であることを証明する」ようにします。出力は boolean でも、proof token(証明トークン)でもよいでしょう。takeDowncircuit はそのまま動きます。sealedフィールドは circuit の中からはアクセスできるからです。 - Witnesses(証人):変更は不要です。witness はこれまでどおり秘密鍵を提供します。
- Tests(テスト):所有権が機能的にはこれまでと同じく働くこと(投稿・消去)を確認しつつ、台帳の状態が、外部からの照会に対して owner フィールドをもう見せないことを確認します。reveal circuit もテストします。
- API/CLI:owner が公開台帳の状態にもう無いので、導出状態の
isOwnerの計算を変える必要があります。pureCircuitsを使うか、別の仕組みを使うことになります。台帳の表示では、公開鍵のハッシュの代わりに「sealed」と見せます。
ここで使う中心概念:sealed キーワード、プライバシー・バイ・デフォルトの設計、選択的開示(selective disclosure)の circuit、可視性の境界(visibility boundaries)の理解。
Track D: Your Own Extension(自分で考える拡張)
自分で機能を提案してつくります。出発点になりそうなアイデアをいくつか:
- Upvote/downvote システム — ZK 証明を使って、ユーザーに投稿への投票をさせる(1人1票、しかも誰が投票したかは明かさない)
- プライベートメッセージング — 既存の witness パターンを使い、意図した受信者だけが読める暗号化メッセージを保存する
- 投稿のカテゴリやタグ — 投稿にメタデータを足し、絞り込み(フィルタ)できるようにする
- 所有権の譲渡 — 投稿者が、導出したトークンを共有することで、消去する権利を別のユーザーに引き渡せるようにする
- マルチボード factory — 独立した複数のボードをデプロイし管理する契約をつくる
このトラックを選ぶなら、つくり始める前に、提案する機能を提出物の中で説明してください。そうすれば、開発に時間をかける前に実現可能かどうかのフィードバックをもらえます。
提出の要件(Submission requirements)
どのトラックを選んでも、提出物には次のすべてが含まれていなければなりません。
書きかえた Compact 契約:コンパイルが通る、拡張した
bboard.compactファイル。コンパイルできないコードは自動的に失格です。契約のディレクトリでnpm run compactを実行し、エラーがゼロであることを提出前に確認してください。更新した TypeScript 連携:契約の変更に合わせて動く、witness・シミュレータ・API のコード。すべての TypeScript が型チェックを通ること(
npm run typecheck)。動くテスト:足した機能を確かめる、更新または新規のシミュレータテスト。新しい circuit には、最低でも2つのテストを:1つは成功ケース、もう1つは「失敗するはず」のケース。
npm run testを実行し、すべてのテストが通ることを確認してください。README または解説文:短い文書(500〜1000語)で、次を説明します。
- 何をつくったか、そしてなぜその設計判断をしたか
- モジュールのどの Compact パターンを使ったか(witness、
disclose()、sealed、アサーション など) - 拡張によって、どんなプライバシー特性を保ったか/変えたか
- 気づいている制約やトレードオフ
オリジナルの成果物であること:提出物はあなた自身のものでなければなりません。あなたが理解・テスト・適応していない AI 生成コードは見抜かれ、失格になります。私たちはこれをチェックします。解説文は、コードが「何をするか」だけでなく「なぜ動くか」についての、あなたの理解を示すものであるべきです。
提出のしかた(How to submit)
- example-bboard リポジトリを fork する
capstone/<あなたの名前>という名前のブランチを作る(例:capstone/ada-lovelace)- 変更を加える
- 確認する:
npm run compactが通り、npm run testが通り、npm run typecheckがクリーンであること - ブランチを push し、プルリクエスト(PR)を開く
- PR の説明に解説文を含め、
Ready for review(レビューの準備ができました)と記載する
評価基準(Evaluation criteria)
提出物は次の観点で評価されます。
成功のためのヒント(Tips for success)
- 契約から始める:まず Compact のコードをコンパイルできる状態にします。契約は土台です。ここで型システムが間違っていると、下流のすべてが壊れます。
- シミュレータを使う:テストが手元で通るまで、Preprod へのデプロイは試さないこと。シミュレータはミリ秒で動きます。一方、証明の生成は1トランザクションあたり30秒かかります。ロジックを書き、手元でテストし、繰り返し直し、自信が持ててから初めてデプロイしましょう。
- エラーメッセージを読む:Compact コンパイラは具体的なエラーを出します。「ある値を台帳に保存するには
disclose()が必要」と言われたら、それはまさに何をすべきかを教えてくれています。テストでアサーションが失敗したら、エラーメッセージはassert()呼び出しに書いた文字列と一致します。 - 変更を絞る:きれいに仕上げた小さな機能は、中途半端で野心的なものに勝ります。Track D を選ぶなら、完成・テスト・説明をすべてやりきれる範囲に絞りましょう。
- モジュールのレッスンを見直す:詰まったら、関連するレッスンに戻りましょう。witness が動かない? Lesson 26。
disclose()がわからない? Lesson 28 の行ごとの解説。provider の問題? Lesson 29。必要なパターンは、すでに終えたレッスンに全部書かれています。
これが準備になること(What this prepares you for)
この capstone をやり遂げるということは、Midnight DApp をゼロから組み・テストし・拡張できるということです。それは本物のスキルです。Midnight のエコシステムはまだ早期で、Compact と SDK を扱える開発者は求められています。
capstone の成果がしっかりしていたら、Midnight Content Bounty Program(外部リンク・別タブで開きます) への貢献も検討してみてください。このプログラムは、教育コンテンツ・コード例・技術ガイドに対して 300〜1,000ドルを支払います。ここで磨いたスキル — Compact 契約を書き、TypeScript 連携を組み、プライバシーパターンを説明すること — は、まさに bounty プログラムが報いるものです。
コントリビューター向けボードで、bounty タグの付いた open issue を見て、今の機会を探してみましょう。
がんばってください。自分が誇れるものをつくりましょう。
開発者として押さえる点
- 卒業課題の本体は example-bboard を fork → Compact 契約を拡張 → TS 連携を更新 → テストを書く → PR で提出。
capstone/<名前>ブランチを切る - 合否ラインは機械的:
npm run compact(エラー0)・npm run test(全通過)・npm run typecheck(クリーン)。1つでも欠けると、それ以上レビューされない - 新しい circuit には最低2テスト(成功ケース+失敗ケース)。とくに「権限のないユーザーが制限操作をできない」セキュリティ特性を確認する
- 設計の軸はプライバシー:何を
disclose()で公開し、何をsealedで隠すかを説明できること。Track C はexport ledger→sealed ledgerへの置きかえとrevealOwnershipの追加が核心 - 反復はシミュレータ(ミリ秒)で。証明生成は1トランザクション30秒かかるので、ロジックが固まるまで Preprod デプロイは後回し
- 提出は自分が理解した・テストした・適応したコードであること。未消化の AI 生成コードは見抜かれ失格になる
やさしい版・公式へ
- やさしい版:Capstone — けいじ板を拡張してみよう
- 公式:Academy Courses(外部リンク・別タブで開きます)(Phase 3 / Unit 5 / 5.1 Capstone)
- 題材リポジトリ:example-bboard(外部リンク・別タブで開きます)
- 関連 docs:Compact リファレンス(外部リンク・別タブで開きます) / Midnight 開発者ドキュメント(外部リンク・別タブで開きます)
つぎに読むページ
➡️ 原文準拠コースの入口へ戻る。このコースについて(次のレッスンは順次追加します)