NIP-57
Lightning Zaps
kind 9735はzapレシートで、zapリクエストに応じて発行されたインボイスへの支払いが受取人のライトニングウォレットによって確認されたことを表す。 Nostr上にライトニング支払いのレシートを残すことで、クライアントはネットワーク上の実体からのライトニング支払いを表示できる。これは楽しみのために、またはスパムの抑止力として利用できる。
プロトコルの流れ
1. クライアントは、zap対象のイベントに含まれるzapタグ(別表G参照)より、または受取人のプロフィールのlud06, lud16フィールドをlnurl仕様に従ってデコードすることにより、受取人のlnurl-payリクエストURLを割り出す。クライアントはそのURLにGETリクエストを送り、レスポンスをパースしなければならない(MUST)。(レスポンスに)allowNostrが存在してtrueであり、さらにnostrPubkeyが存在して値がhex形式の有効な公開鍵であれば、クライアントはこの情報をレスポンスのcallback, minSendable, maxSendableの値とともにユーザに関連付けるべきである。 2. クライアントは各投稿上またはユーザのプロフィール上にライトニングzapボタンを表示してもよい。もしユーザのlnurl-payリクエストエンドポイントがNostrをサポートしているなら、クライアントは通常のlnurlインボイスの代わりにzapレシートをリクエストするためにこのNIPを使うべきである(SHOULD)。
3. あるユーザ(「支払人」)が他のユーザ(「受取人」)にzapを送りたいという意思を示したら、クライアントは別表Aで説明された通りにzapリクエストイベントを生成し、署名を行うべきである。
4. zapリクエストは(リレーに)送信するのではなく、受取人のlnurl-payエンドポイントからGETリクエストで取得したcallbackURLに送信されるべきものである。
5. 受取人のlnurlサーバはこのzapリクエストを受け取り、検証する。zapをサポートするためにlnurlサーバを正しく設定する方法については別表Cを参照。nostrクエリパラメータ(の内容)を検証する方法の詳細については別表Dを参照。
6. zapリクエストが有効なら、(lnurl)サーバはdescriptionとしてそのzapリクエストnoteのみを含むdescription hash invoiceを取得する。それ以外のlnurlメタデータは含めない。これはlud06に従うレスポンスとして(クライアントに)返される。 7. インボイスを受け取ったら、クライアントはそれに対して(自分で)支払いを行うか、それを支払い機能を持つアプリに渡してよい(MAY)。
8. インボイスに対し支払いが行われたら、受取人のlnurlサーバは別表Eに記載のとおりにzapレシートを生成し、zapリクエストに指定されているリレー(relays)に送信しなければならない(MUST)。
9. クライアントは投稿・プロフィール上のzapレシートを取得してもよい(MAY)が、このとき別表Fに記載の方法で正当性を確認しなければならない(MUST)。zapリクエストが空でないcontentを含むならば、zapのコメントとして表示してよい。一般にクライアントはユーザのzapリクエストを表示すべきで、zapレシートは「...によって承認されたzap」という表示のために使えるが、これは任意。
参考情報と例
※JSONやコードの例については 原文 を参照してください 別表A: Zapリクエストイベント
zapリクエストはkind9734のイベントで、リレーにではなく(zap)受取人のlnurl-payのcallbackURLに送信する。
このイベントのcontentには、支払いにあわせて送信するメッセージを含めてもよい(MAY)。
このイベントには次のタグを含めなければならない(MUST):
relays: 受取人のウォレットがzapレシートを送るべきリレーのリスト
amount: 支払人が送信しようとしている金額(millisats(satsの1/1000)単位)。推奨されているが必須ではない
lnurl: 受取人のlnurl-pay URL。lnurlから始まるbech32形式で指定。推奨されているが必須ではない
p: 受取人のhex形式の公開鍵
さらに、イベントには以下のタグを含めてもよい(MAY):
e: hex形式のイベントIDの。個人ではなくイベントに対してzapする場合、これを含めなければならない(MUST)。
(訳注) ["a", "<kind>:<pubkey>:<dタグ>", "<relay url>"] という形式
(イベント例: 原文参照)
別表B: ZapリクエストのHTTPリクエスト
署名されたzapリクエストイベントは(リレーに)送信するのではなく、受取人の(lnurl-payの)callback URLにHTTP GETリクエストによって送信する。callback URLは受取人のlnurl-payエンドポイントから提供される。
このリクエストは次のクエリパラメータを含むべきである:
amount: 支払人が送信しようとしている金額(millisats単位)
nostr: zapリクエストイベントをJSONエンコード・URIエンコードしたもの
lnurl: 受取人のlnurl-pay URLをlnurlから始まるbech32形式にエンコードしたもの
このリクエストに対し、(受取人のLNURLサーバは)prフィールドを含むJSONレスポンスを返すべきである。prフィールドはzapを完了するために支払いを行うべきインボイス。
(リクエスト処理の例: 原文参照)
別表C: LNURLサーバの設定
lnurlサーバがzapインボイスをサポートしていることをクライアントに知らせるため、追加設定が必要:
1. lnurl-pay静的エンドポイント/.well-known/lnurlp/<user>(のレスポンス)にnostrPubkeyを追加する。nostrPubkeyは、lnurlサーバがzapレシートイベントに署名する際に使う秘密鍵に対応する公開鍵。クライアントはこれをzapレシートの検証に用いる
2. allowsNostrフィールドを追加し、値をtrueに設定する
別表D: LNURLサーバによるZapリクエスト検証
クライアントがzapリクエストイベントをサーバのlnurl-payコールバックURLに送信する際、そのイベントをJSON・URIエンコードした結果を値とするnostrクエリパラメータが含まれているはず。もしnostrクエリパラメータが存在するならば、以下の方法でzapリクエストを検証しなければならない。
正当な署名を持たなければならない(MUST)
ただ1つのpタグと、0個か1個のeタグを持たなければならない(MUST)
zapレシートの送信先リレーを指定するrelaysタグを持つべき
(訳注) 別表AではMUSTとされているが、どちらが正しい?
amountタグを持つ場合、その値はamountクエリパラメータの値と一致していなければならない(MUST)
aタグを持つ場合、それは妥当なNIP-33イベント座標でなければならない(MUST) このイベントは後でインボイスに支払いが行われた際に使うため、保存しておかなければならない(MUST)。
別表E: Zapレシートイベント
zapレシートは、zapリクエストから生成されたインボイスに対し支払いが行われた際に、ライトニングノードが作成する。zapレシートは、インボイスのdescriptionがzapリクエストを含む場合に限り作成する。
支払いを受けたら、次のステップを実行する:
1. インボイスのdescriptionを取得する。これはdescription hash invoiceを生成するタイミングでどこかに保存しておく必要がある。参照実装であるCLN (core lightning) では自動的に保存される 2. bolt11 descriptionをJSON形式のNostrイベントとしてパースする。これを別表Dに示した仕様に基づいて検証すべきである(SHOULD)。
3. 以下に示す仕様を満たす、kind9735のNostrイベントを作成し、zapリクエストのrelaysタグで指定されたリレーに送信する
zapレシートイベントは以下を満たすべきである:
contentは空であるべき(SHOULD)
created_atには、冪等性のためインボイスのpaid_atの値を設定すべき(SHOULD)
tagsにはzapリクエストと同じpタグとeタグ(任意)を含めなければならない(MUST))
description hash bolt11インボイスを含むbolt11タグを持たなければならない(MUST)
JSONエンコードされたインボイスのdescriptionを含むdescriptionタグを持たなければならない(MUST)
descriptionのSHA256ハッシュ値はbolt11インボイスのdescription hashと一致していなければならない(MUST)
bolt11インボイスのpayment hashと突合するためのpreimageタグを含めてもよい(MAY)。これは支払証明ではなく、インボイスが本物かどうかあるいは支払い済みかどうかを証明する方法はない。決済の正当性に関しては、zapレシートの作成者を信用することになる
zapレシートは支払証明ではなく、あるNostrユーザがインボイスを取得したことの証明でしかない。zapレシートの存在はそのインボイスに支払いが行われたことを示唆するが、悪意ある実装のもとでは正しいとは限らない。
zapをサポートするlnurlサーバの参照実装はこちら (zapレシートの例: 原文参照)
別表F: Zapレシートの検証
クライアントはNIP-01のフィルタを使い、イベントと公開鍵に関連付けられたzapレシートを取得できる(例: {"kinds": [9735], "#e": [...]})。Zapは次の手順にしたがって検証されなければならない(MUST)。
zapレシートイベントの公開鍵は、受取人のLNURLプロバイダの(プロトコルフローの手順1で取得した)nostrPubkeyと同じでなければならない(MUST)。
zapレシートのbolt11タグに含まれるinvoiceAmountは(存在する場合には)zapリクエストのamountタグと等しくなければならない(MUST)。
zapレシートのlnurlタグは(存在する場合には)受取人のlnurlと等しいべきである(SHOULD)。
訳注
「存在する場合には」がどこに掛かっているのかについて
Appendix Aにおいてzap リクエストamount, lnurlタグが「This is recommended, but optional.」とあるので存在しない場合がある。 tanakei.icon
zapリクエストはzapレシートのdescriptionタグに含まれる syusui_s.icon
zapレシートのdescriptionタグには、インボイスのdescriptionが含まれる
zapレシートは、インボイスのdescriptionがzapリクエストを含む場合に限り作成する
プロトコルの流れの(6)で作ったインボイスには、descriptionとしてzapレシートが含まれている
(special thanks: syusui_s.icon)
別表G: "zap"タグ (zap分配)
イベントが1つ以上のzapタグを含む場合、クライアントはプロフィールのフィールドの代わりにそのタグの値に基づいてlnurl-payリクエストを導出するべきである(SHOULD)。
このタグの2番めの引数は受取人の公開鍵の16進数文字列で、3番目の引数(任意)は受取人のメタデータ(kind 0)をダウンロードするためのリレーとする。
4番目の引数(任意)は各受取人に割り当てる重みを指定する。クライアントはすべての重みを取得して合計を求め、受取人ごとの(重みの)割合を計算すべきである。重みが指定されていなければ、クライアントはzapをすべての受取人に均等に分配すべき。重みが一部のみ指定されている場合、重みが指定されていない受取人にはzapを届けるべきでない(重み=0とみなす)。
(zapタグの例: 原文参照)
クライアントは投稿の中にzap分配設定を表示してもよい(MAY)。
今後の展望
対象のユーザへのzapリクエストを暗号化することによって、zapをよりプライベートな方向に拡張できるが、簡単のためこの初期草案では対象外としている。
/icons/hr.icon
以下、訳注
シーケンス図 (by tanakei.icon)
https://scrapbox.io/files/6460776d517d7b001b4995e0.png
関連項目: