コンテンツにスキップ

条件付きトークンのアトミックスワップ

条件付きトークンのアトミックスワップ

Section titled “条件付きトークンのアトミックスワップ”

本ドキュメントは、ECDH鍵合意とSchnorrアダプター署名を使用したCashu条件付きトークン(NUT-CTF)のピアツーピア取引プロトコルを規定します。このプロトコルにより、カストディアル仲介者なしにトラストレスなアトミック交換(例:YESアウトカムトークンとsatsの交換)が可能になります。

NUT-CTFは将来の結果に依存する条件付きトークンを定義しますが、ユーザーがそれらをどのように取引するかは規定していません。予測市場には2つのコンポーネントが必要です:(1) 買い手と売り手をペアリングするマッチングエンジン、(2) どちらの当事者も不正ができないようにトークンをアトミックに交換する決済メカニズム。

このプロトコルは、NUT-11 P2PK支出条件、ECDH導出共有シークレット、Schnorrアダプター署名からアトミックスワップを構築することで(2)に対処します。マッチングエンジン(1)は公開鍵交換と暗号化メッセージパッシングを促進する非カストディアルリレーです。

アダプター署名スキームは、標準的な署名スキームを4つの操作で拡張します:

  • PreSign(sk, m, T) → s’: シークレットキーskとアダプターポイントT = t·Gを使用して、メッセージmプリ署名s'を生成します。プリ署名は有効な署名ではありませんが、シークレットtにコミットします。
  • PreVerify(pk, m, s’, T) → bool: s'が公開鍵pk、メッセージm、アダプターポイントTに対して有効なプリ署名であることを検証します。
  • Adapt(s’, t) → s: アダプターシークレットtを与えて、プリ署名を有効なSchnorr署名s = s' + tに完成させます。
  • Extract(s, s’) → t: 有効な署名sと対応するプリ署名s'から、アダプターシークレットt = s - s'を復元します。

重要な特性は:プリ署名s'が公開され、後に完成した署名sが現れた場合(誰かがproofを使用したため)、s'を知っている人はtを抽出できることです。これにより、2つの独立した支出イベント間に暗号学的リンクが作成されます。

secp256k1上のSchnorr署名(NUT-11で使用)の場合、アダプター署名は簡単です:

標準Schnorr: s = r + e·x (where e = H(R, P, m))
アダプタープリ署名: s' = r + e·x (but R' = R + T, so e = H(R+T, P, m))
s = s' + t (valid sig with nonce point R = R' - T)

アダプター署名がP2PK層で動作しなければならない理由

Section titled “アダプター署名がP2PK層で動作しなければならない理由”

Cashuのブラインド署名(BDHKE)は乗法構造を使用します:C_ = k · B_。アダプター署名は加法構造を必要とします(Schnorr: s = r + e·x + t)。ミント検証を壊したりNUT-12 DLEQ proofを無効にしたりせずに、乗法ブラインド署名にアダプターシークレットを埋め込むことはできません。

NUT-11 P2PK支出条件は既にsecp256k1上のSchnorr署名を使用しており、アダプター署名と完全に互換性があります。したがって、プロトコルはブラインド署名層ではなく、完全に支出条件層で動作します。

キーペア(a, A = a·G)(b, B = b·G)を持つ2つの当事者は、どちらも秘密鍵を明かさずに共有シークレットS = a·B = b·Aを計算できます。この共有シークレットは、信頼できないマッチングエンジンを介して通信が中継される場合でも、アダプターポイントとプリ署名を交換するための暗号化チャネルを確立します。

  • Alice: YESトークンを売却(satsと交換したい条件付きトークンproofを保有)
  • Bob: YESトークンを購入(条件付きトークンと交換したいsat proofを保有)
  • マッチングエンジン(ME): 注文をペアリングし暗号化メッセージを転送する非カストディアルリレー
  • ミント(M): P2PK支出条件を強制するCashuミント
  • AliceとBobの両方が有効なDLEQ proof(NUT-12)を持つCashu proofを保有
  • ミントがNUT-07(witness取得付きトークン状態チェック)、NUT-11(P2PK)、NUT-12(DLEQ)をサポート
  • AliceのYESトークンproofとBobのsat proofが合意された取引に対して互換性のある額面を持つ

AliceとBobはそれぞれエフェメラルキーペアを生成し、マッチングエンジンに注文を登録します。

Alice: (a, A = a·G)を生成、公開鍵Aで売り注文を登録
Bob: (b, B = b·G)を生成、公開鍵Bで買い注文を登録

マッチングエンジンは注文板に注文を保存します。エフェメラルキーにより取引間の非リンク性が保証されます。

鍵の衛生:個別のスワップごとに新しいキーペアを生成しなければなりません(MUST)。単一の注文が複数のカウンターパーティと約定する(部分約定)場合、実装は注文レベルのキーペアをそれらの約定間で再利用してもよい(MAY)が、これは既知のリンク可能性の弱点であり、将来的には約定ごとの鍵に移行すべきです。無関係なスワップ間での鍵の再利用はECDH共有シークレットのグラフを漏洩させるため、禁止されます。

エンジンは価格と数量に基づいてAliceの売り注文とBobの買い注文をマッチングします。送信内容:

  • AliceはBobの公開鍵Bを受信
  • BobはAliceの公開鍵Aを受信

マッチした数量は条件付きトークンの額面です。決済メッセージには、支払う通常のsats金額も別フィールドとして含めます。たとえば、価格37で1,000額面satsのYESが約定した場合、Aliceは1,000額面satsのYESをロックし、Bobは370通常satsをロックします。実装は1つの金額を両方のスワップレグに使ってはなりません。

補完マッチでは、注文板は最初に最終的な約定ではなく予約を作成します。公開注文ステータスでは、その行はstatus: matchedtradeIdとして公開されます。両当事者がアトミックスワップを完了すると予約はコミットされ、status: filledになります。先に決済タイムアウトが過ぎた場合、エンジンは取引を失敗させ、予約をstatus: releasedとして解放します。解放処理は time-in-force を考慮します。GTC/GTD の数量は、同一注文内の部分約定であればエフェメラルキーの再利用がプロトコル上許容されているため、同じ注文レベルのエフェメラルキーのまま注文板に戻ります。一方、FAK/FOK の注文は再び板に残らずキャンセルされます。タイムアウトは売り手側ロックタイムに猶予時間を加えた後に設定しなければなりません。買い手側ロックタイムでタイムアウトさせると、プロトコル上まだ有効なスワップを中断してしまう可能性があります。

補完マッチのメイカーになり得る板に残る買い注文では、クライアントは注文提出前に事前 split を行うべきです(SHOULD)。メイカーは通常 sats を選択し、完全なアウトカムセットへの CTF split を mint に送信し、生成されたアウトカム proof をローカルウォレット状態に保存し、保持側と補完ロック側の両方をその注文に予約します。mint が利用できない、またはユーザーに見える提出待ち時間内に十分な担保を予約できない場合、決済不能なメイカー注文を公開するのではなく、クライアントは注文提出を失敗させるか注文経路を取消/解放すべきです。--no-preflight-split のような明示的な opt-out を実装してもよいですが、その場合でも通常担保を予約し、マッチ到着時に利用できなければ fail-closed しなければなりません。

事前 split はウォレットローカルの安全機構であり、別のワイヤプロトコルではありません。補完マッチが到着すると、メイカーは下記の売り手ブランチで Alice として振る舞います。予約済みの補完アウトカム proof はテイカーにロックされ、メイカーが保持する側の proof は約定数量分だけ見える状態になります。部分約定では、残りの事前 split proof は未約定数量のために予約されたまま残らなければなりません。

ステップ3:ECDH共有シークレット

Section titled “ステップ3:ECDH共有シークレット”

両当事者が独立して共有シークレットを計算します:

Alice: S = a · B
Bob: S = b · A

Sから対称暗号化鍵を導出し(例:key = SHA256(S))、エンジンを介した暗号化通信に使用します。エンジンは暗号文を中継しますが、復号はできません。

ステップ4:アダプターポイント生成

Section titled “ステップ4:アダプターポイント生成”

Aliceがランダムなアダプターシークレットtを生成し、アダプターポイントTを計算します:

Alice: t ← ランダムスカラー
T = t · G

AliceはECDH暗号化チャネルを介してTをBobに送信します(エンジンが中継)。

ステップ5:AliceがYES Proofをロック

Section titled “ステップ5:AliceがYES Proofをロック”

AliceはNUT-11 P2PKを使用してBobの公開鍵にロックされたYESトークンproofを作成します:

[
"P2PK",
{
"nonce": "<random>",
"data": "<Bobの公開鍵B>",
"tags": [
["sigflag", "SIG_INPUTS"],
["locktime", "<unix_timestamp>"],
["refund", "<Aliceの公開鍵A>"]
]
}
]

locktimerefundタグにより、Bobがスワップを完了しない場合にAliceがトークンを回収できます。

次に、各proofのsecretに対するアダプタープリ署名を作成します:

各proofのsecret x_iに対して:
s'_A_i = PreSign(a, x_i, T)

注記: プリ署名はBobが検証に使用するエフェメラルキーで作成され、アダプターポイントTを使用します。tで適応されるまで有効なSchnorr署名ではありません

Aliceは暗号化チャネルを介して{proofs, s'_A, T}をBobに送信します。

ステップ6:BobがSat Proofをロック

Section titled “ステップ6:BobがSat Proofをロック”

BobはAliceのロックされたproofを受信し、検証します:

  1. DLEQ検証(NUT-12):各proofが有効なミント署名を持つことを確認
  2. P2PK検証:proofが適切なlocktimeとAへの返金で公開鍵Bにロックされていることを確認
  3. PreVerify:各s'_A_iがアダプターポイントTを持つ有効なアダプタープリ署名であることを確認

すべてのチェックに合格した場合、BobはAliceの公開鍵Aにロックされたsat proofを作成します:

[
"P2PK",
{
"nonce": "<random>",
"data": "<Aliceの公開鍵A>",
"tags": [
["sigflag", "SIG_INPUTS"],
["locktime", "<unix_timestamp>"],
["refund", "<Bobの公開鍵B>"]
]
}
]

Bobは同じアダプターポイントTを使用してproofのアダプタープリ署名を作成します:

各proofのsecret y_jに対して:
s'_B_j = PreSign(b, y_j, T)

Bobは暗号化チャネルを介して{proofs, s'_B}をAliceに送信します。

ステップ7:Aliceが請求(Adapt + Swap)

Section titled “ステップ7:Aliceが請求(Adapt + Swap)”

AliceはBobのproofを検証します(DLEQ、AへのP2PKロック、s'_BのPreVerify)。

Aliceはtを知っているため、Bobのプリ署名を有効なSchnorr署名に適応できます:

Bobの各proofに対して:
s_B_j = Adapt(s'_B_j, t) = s'_B_j + t

Aliceはs_B_jをNUT-11 witness署名として使用し、ミントにスワップリクエストを送信します:

POST /v1/swap
{
"inputs": [<witness s_B_j付きBobのsat proof>],
"outputs": [<新しいトークン用Aliceのブラインドメッセージ>]
}

ミントはP2PK署名を検証し、スワップを処理します。Aliceは新しいsatトークンを保有しています。

ステップ8:Bobがアダプターシークレットを抽出

Section titled “ステップ8:Bobがアダプターシークレットを抽出”

BobはミントのNUT-07トークン状態チェックエンドポイントで使用済みsat proofを照会します。Bobはステップ6で構築したproofを保持しているため、各proofについてY_j = hash_to_curve(secret_j)をローカルで計算できます(hash_to_curveについてはNUT-00を参照):

POST /v1/checkstate
{
"Ys": ["<BobがAliceにロックした各sat proofのY_j = hash_to_curve(secret_j)>"]
}

ミントはAliceが使用した有効なSchnorr署名s_B_jを含むwitnessデータを返します:

{
"states": [
{
"Y": "...",
"state": "SPENT",
"witness": "{\"signatures\": [\"<s_B_j>\"]}"
}
]
}

Bobは任意の署名ペアからアダプターシークレットを抽出します:

t = Extract(s_B_j, s'_B_j) = s_B_j - s'_B_j

ステップ9:Bobが請求(Adapt + Swap)

Section titled “ステップ9:Bobが請求(Adapt + Swap)”

tを復元したBobは、Aliceのプリ署名を有効なSchnorr署名に適応します:

Aliceの各proofに対して:
s_A_i = Adapt(s'_A_i, t) = s'_A_i + t

BobはAliceのYES proofに対するスワップリクエストを送信します:

POST /v1/swap
{
"inputs": [<witness s_A_i付きAliceのYES proof>],
"outputs": [<新しい条件付きトークン用Bobのブラインドメッセージ>]
}

ミントが検証してスワップを処理します。Bobは新しいYES条件付きトークンを保有しています。

マーケット終了とポジション請求

Section titled “マーケット終了とポジション請求”

上記の取引スワップは、マーケットがオープンな間に条件付きトークンをユーザー間で移動します。オラクルがマーケットを解決した後、勝ち側の条件付きトークンはマッチングエンジンではなくミントで償還します。

  1. 署名済みDLCオラクルattestationによりマーケットが終了します。リレーへの公開は任意であり、ユーザーは署名済みattestationを直接エンジンに送信できます。
  2. エンジンはattestationをマーケットに登録されたオラクルに対して検証し、その条件の検証済みオラクルwitnessをキャッシュします。
  3. ポートフォリオ表示は、ウォレットに見えているCTF proofをエンジンのマーケット状態と最終結果に結合して、ActiveまたはClosedに分類します。
  4. 勝ちポジションの請求では、ローカルのCTF proofとオラクルwitnessをミントのPOST /v1/redeem_outcomeエンドポイントに送信します。ミントはwitnessを検証し、通常のecash proofを返します。負けアウトカムのproofはゼロ価値として扱われ、Closedのままです。

カテゴリカルマーケットでは、保有しているアウトカム集合がattested outcomeを含む場合に勝ちです。たとえばアウトカムがABCで、オラクルがBをattestした場合、BB|Cは勝ち集合であり、AA|Cは負け集合です。

エンジンのキャッシュされたwitnessは支払い権限ではありません。バグのある、または悪意あるエンジン応答が負けトークンを償還可能にすることはできません。ミントがredeem_outcome中にオラクル署名と条件付きkeysetを再検証するためです。

原子性には両方の要素が必要です — アダプター署名だけでは不十分です。アダプター署名はHTLCのハッシュプリイメージをECスカラーに置き換えますが、NUT-11のlocktime + refundタグは、カウンターパーティが消えた場合の安全フォールバックを引き続き提供します。どちらか一方を除くとプロトコルが壊れます:

  • アダプター署名はAliceが使用すればBobも使用できることを保証します(ライブネス/リンク可能性)。
  • Locktime返金はAliceが決して使用しない場合でも、Bobが資金を回収できることを保証します(安全性)。

両方を前提とすると、3つのケースがあります:

  • Aliceが請求した場合(ステップ7):適応された署名s_B = s'_B + tがミントに公開されます。BobはNUT-07を介してそれを取得しtを抽出でき、Aliceのproofを請求できます。両当事者がスワップを完了します。
  • Aliceが請求しない場合:どちらの当事者のトークンも使用されません。各proofのlocktime期限後、両当事者は返金パスを介して自分のトークンを回収します。
  • Bobが先に請求することはできない:Bobはtを知らないため、Aliceのプリ署名を適応できません。Aliceが使用してtを明らかにするまで待つ必要があります。

locktimeは慎重に選択する必要があります:

  • 短すぎる場合:Aliceの返金ウィンドウが開く前にBobがNUT-07に問い合わせてスワップを送信する時間がなく、競合状態が発生する可能性があります。
  • 長すぎる場合:カウンターパーティが消えた場合、トークンが長期間ロックされます。

このプロトコルはecashスワップのみを含むため(Lightningルーティング遅延なし)、locktimeは非常に短く(分ではなく秒のオーダー)設定できます。両当事者は注文マッチングフェーズ中にlocktimeに合意する必要があります。

重要 — locktime順序:AliceのYES proof locktime(T_YES)はBobのsat proof locktime(T_sat)より後でなければなりません(MUST):

T_YES > T_sat + Δ

ここでΔはBobがNUT-07を照会し、tを抽出し、自身のスワップを送信するのに必要な時間です。

Aliceが先にBobのsat proofを使用し(ステップ7)、T_satの直前まで待つ可能性があります。Bobはその使用を観察し、tを抽出し、T_YESが期限切れになる前にAliceのYES proofを使用しなければなりません。順序が逆(T_YES < T_sat)の場合、Aliceは自分のYES返金ウィンドウが開くまで待ち、YES proofを返金し、T_satが期限切れになる前にBobのsatsを請求することで、取引の両側を盗むことができます。これはP03(アトミックスワップ原子性)の中核的な不変条件であり、実装はカウンターパーティのプリ署名を受け入れる前にこの順序を検証しなければなりません(MUST)。

このプロトコルはミントがNUT-07 witness取得をサポートすることを必要とします。いくつかの考慮事項があります:

  • 可用性:ステップ7と8の間にミントがオフラインになった場合、Bobはwitnessを取得できません。locktime返金によりBobの元のsat proofは保護されますが、YES proofを請求する機会は失われます。
  • プライバシー:NUT-07への問い合わせにより、Bobが特定の使用済みproofに関心を持っていることがミントに明らかになり、取引当事者の相関に役立つ可能性があります。
  • 検閲:悪意のあるミントがwitnessデータの返却を拒否し、Bobがtを抽出できなくする可能性があります。locktime返金によりBobの資金は保護されますが、原子性が壊れます。
  • 競合状態:Aliceの使用(ステップ7)とBobの問い合わせ(ステップ8)の間に、ミントがオフラインまたは応答不能になる可能性のあるウィンドウがあります。

これらの制限はP2PK + NUT-07アプローチに固有のものです。より強い原子性保証のためには、アトミック決済エンドポイントバリアント(代替案を参照)を検討してください。

エンティティ信頼要件
マッチングエンジン非カストディアル。暗号化メッセージを中継。復号不可(ECDH)。資金窃取不可。サービス拒否は可能(中継拒否)。
ミントP2PK条件を正直に実行し、NUT-07 witnessデータを返す必要がある。トークン発行で既に信頼済み。
カウンターパーティトラストレス。窃取不可 — 完了しないことによるグリーフのみ可能(locktime返金で保護)。

NUT-14はCashu用のHash Time-Locked Contracts(HTLC)を定義しています。HTLCは理論的にアトミックスワップを可能にしますが、条件付きトークン取引には重大なデメリットがあります:

プロパティHTLC(NUT-14)アダプター署名(本プロトコル)
ロックタイプハッシュプリイメージH(x)ECポイントT = t·G
プリイメージ伝播NUT-07が必要(同じ依存性)NUT-07が必要(同じ依存性)
ミントが知る情報プリイメージ値(両スワップレグを相関可能)通常のP2PK署名(2つのスワップをリンク不可)
プライバシー同じハッシュH(x)が両レグに現れる — 自明にリンク可能異なる署名、オンチェーンリンクなし
追加NUTNUT-14(HTLCサポート)NUT-11(P2PK)以外不要
暗号学的複雑性低い(ハッシュ + プリイメージ)高い(アダプター署名数学)

プライバシーの利点が主要な差別化要因です。HTLCでは、ミントは両スワップレグで同じハッシュロックを見るため、同じ取引の一部であることを自明に判断できます。アダプター署名では、ミントは無関係な署名を持つ2つの独立したP2PK使用を見ます。

Cashuは整数sats金額と2のべき乗のproof額面を使用します。bitCasterの初回リリースでは、指値注文の額面数量を100 satsで割り切れる値に制限します。これにより、整数セントの価格は常に正確な支払金額に変換できます。たとえば、価格37で1,000額面satsなら370通常satsです。小数satsの支払いが必要になる注文は、黙って丸めるのではなく拒否すべきです。

マッチングエンジンは注文フローを見ることができ、ユーザーより先に取引する可能性があります。緩和策:頻繁なバッチオークションを使用し、時間ウィンドウ内のすべての注文を単一のクリアリング価格でマッチングし、フロントランニングの優位性を排除します。

攻撃者が注文を出し、マッチングされ、完了しないことで、カウンターパーティのトークンをlocktime期限までロックする可能性があります。緩和策:

  • 短いlocktimeと、エンジンが所有する決済タイムアウトリマインダー。タイムアウトした予約は解放され、消費された注文はエフェメラルキーを再利用したまま注文板へ戻されずキャンセルされます。
  • マッチングエンジンによるレピュテーション追跡
  • 少額のアンチスパムデポジット(例:エンジンへのLightningインボイス支払い)

このプロトコルは各マッチングペアの完全約定を要求します。部分約定は複数のアトミックスワップへの分割が必要です。ミント運営の注文板は部分約定をより自然に処理でき、このプロトコルを補完する可能性があります。

異なるミントからの条件付きトークンはファンジブルではありません。取引は同じミントのトークンに限定されます。