Cashu NUT-25规范:基于BOLT12的铸造与熔化

cashubtc 发布于 2025-08-29 阅读 58

本文档是 Cashu 协议中 NUT-25 规范,定义使用 BOLT12 支付方法(基于 Lightning Network 的 offer)进行 ecash 铸造(mint)和熔化(melt)的流程。它扩展了 NUT-04 和 NUT-05,详细说明了铸造报价和熔化报价的请求/响应数据结构、多次发行机制、设置选项,并提供了 curl 示例。关键点包括:铸造时需要提供唯一公钥,熔化时支持无金额 offer,以及处理找零。

可选

依赖于: NUT-04 NUT-05 NUT-20


本文档描述了使用 bolt12 支付方式铸造和熔化 ecash 的过程,该方式基于 Lightning Network offers。它是 NUT-04NUT-05 的扩展,这两份文档涵盖了任何支持的支付方式共用的铸造和熔化 ecash 的协议步骤。

铸造报价

对于 bolt12 方法,钱包包含以下特定的 PostMintQuoteBolt12Request 数据:

{
  "amount": <int|null>,
  "unit": <str_enum[UNIT]>,
  "description": <str|null>,
  "pubkey": <str>
}

注意: 尽管根据 NUT-20 的规定,公钥对于 NUT-04 是可选的,但在本 NUT 中它是必需的;如果未包含公钥,铸币厂不得发布铸造报价。

隐私: 为防止多个铸造报价被关联,钱包为每个铸造报价请求生成唯一的公钥。

铸币厂返回以下 PostMintQuoteBolt12Response

{
  "quote": <str>,
  "request": <str>,
  "amount": <int|null>,
  "unit": <str_enum[UNIT]>,
  "expiry": <int|null>,
  "pubkey": <str>,
  "amount_paid": <int>,
  "amount_issued": <int>
}

其中:

  • quote 是报价 ID
  • request 是 bolt12 offer
  • expiry 是铸造报价有效的 Unix 时间戳
  • amount_paid 是通过 bolt12 offer 支付给铸币厂的金额
  • amount_issued 是针对给定铸造报价已发行的 ecash 金额

示例

请求 使用 curl:

curl -X POST http://localhost:3338/v1/mint/quote/bolt12 -d \
'{"amount": 10, "unit": "sat", "pubkey": "03d56ce4e446a85bbdaa547b4ec2b073d40ff802831352b8272b7dd7a4de5a7cac"}' \
-H "Content-Type: application/json"

响应

{
  "quote": "DSGLX9kevM...",
  "request": "lno1qcp...",
  "amount": 10,
  "unit": "sat",
  "expiry": 1701704757,
  "pubkey": "03d56ce4e446a85bbdaa547b4ec2b073d40ff802831352b8272b7dd7a4de5a7cac",
  "amount_paid": 0,
  "amount_issued": 0
}

检查报价状态:

curl -X GET http://localhost:3338/v1/mint/quote/bolt12/DSGLX9kevM...

铸造代币:

curl -X POST https://mint.host:3338/v1/mint/bolt12 -H "Content-Type: application/json" -d \
'{
  "quote": "DSGLX9kevM...",
  "outputs": [
    {
      "amount": 8,
      "id": "009a1f293253e41e",
      "B_": "035015e6d7ade60ba8426cefaf1832bbd27257636e44a76b922d78e79b47cb689d"
    },
    {
      "amount": 2,
      "id": "009a1f293253e41e",
      "B_": "0288d7649652d0a83fc9c966c969fb217f15904431e61a44b14999fabc1b5d9ac6"
    }
  ]
}'

响应:

{
  "signatures": [
    {
      "id": "009a1f293253e41e",
      "amount": 2,
      "C_": "0224f1c4c564230ad3d96c5033efdc425582397a5a7691d600202732edc6d4b1ec"
    },
    {
      "id": "009a1f293253e41e",
      "amount": 8,
      "C_": "0277d1de806ed177007e5b94a8139343b6382e472c752a74e99949d511f7194f6c"
    }
  ]
}

多次发行

与 BOLT11 发票不同,BOLT12 offer 可以多次支付,允许钱包为一个报价多次铸造。钱包可以调用检查 bolt12 端点,铸币厂将返回包含 amount_paidamount_issuedPostMintQuoteBolt12Response。这两个值之间的差额表示钱包可以调用铸造端点铸造的金额。钱包可以铸造最多不超过该可用差额的任何金额;特别地,它们可以铸造少于可铸造金额的数量。铸币厂必须接受输出总金额小于或等于 (amount_paid - amount_issued) 的铸造请求。

铸造设置

设置一个 description 选项,以指示 bolt12 支付方式后端是否支持提供 offer 描述。

示例 MintMethodSetting

{
  "method": "bolt12",
  "unit": <str>,
  "min_amount": <int|null>,
  "max_amount": <int|null>,
  "options": {
    "description": true
  }
}

熔化报价

对于 bolt12 方法,钱包包含以下特定的 PostMeltQuoteBolt12Request 数据:

{
  "request": <str>,
  "unit": <str_enum[UNIT]>,
  "options": { // 可选
    "amountless": {
      "amount_msat": <int>
    }
  }
}

其中,request 是要支付的 bolt12 Offer,unit 是钱包希望支付的单位。对于无金额的 offer,可以使用 options.amountless.amount_msat 字段指定要支付给该 offer 的毫聪金额。如果定义了 options.amountless.amount_msat 且 offer 本身有金额,则它们必须相等。

铸币厂返回以下 PostMeltQuoteBolt12Response

{
  "quote": <str>,
  "request": <str>,
  "amount": <int>,
  "unit": <str_enum[UNIT]>,
  "fee_reserve": <int>,
  "state": <str_enum[STATE]>,
  "expiry": <int>,
  "payment_preimage": <str|null>
}

其中 fee_reserve 是闪电支付所需的额外费用储备。铸币厂期望钱包包含至少 total_amount = amount + fee_reserve + feeProofs,其中 fee 根据 keyset 的 input_fee_ppk 计算,如 NUT-02 所述。

state 是一个枚举字符串字段,可能的值为 "UNPAID""PENDING""PAID"

  • "UNPAID" 表示请求尚未支付。
  • "PENDING" 表示请求正在支付。
  • "PAID" 表示请求已成功支付。

熔化代币

对于 bolt12 方法,钱包可以在熔化请求中包含一个可选的 outputs 字段,以接收超额支付的闪电费用的找零(参见 NUT-08):

{
  "quote": <str>,
  "inputs": <Array[Proof]>,
  "outputs": <Array[BlindedMessage]> // 可选
}

如果包含了 outputs 字段且 fee_reserve 有剩余,铸币厂将在响应中包含一个 change 字段,其中包含超额支付金额的盲签名:

{
  "quote": <str>,
  "request": <str>,
  "amount": <int>,
  "unit": <str_enum[UNIT]>,
  "fee_reserve": <int>,
  "state": <str_enum[STATE]>,
  "expiry": <int>,
  "payment_preimage": <str>,
  "change": <Array[BlindSignature]> // 如果包含 outputs 且有找零,则存在
}

示例

熔化报价请求

curl -X POST https://mint.host:3338/v1/melt/quote/bolt12 -d \
'{"request": "lno1qcp4256ypqpq86q69t5wv5629arxqurn8cxg9p5qmmqy2e5xq...", "unit": "sat"}'

熔化报价响应

{
  "quote": "TRmjduhIsPxd...",
  "request": "lno1qcp4256ypqpq86q69t5wv5629arxqurn8cxg9p5qmmqy2e5xq...",
  "amount": 10,
  "unit": "sat",
  "fee_reserve": 2,
  "state": "UNPAID",
  "expiry": 1701704757
}

检查报价状态:

curl -X GET http://localhost:3338/v1/melt/quote/bolt12/TRmjduhIsPxd...

熔化请求

curl -X POST https://mint.host:3338/v1/melt/bolt12 -d \
'{"quote": "TRmjduhIsPxd...", "inputs": [...]}'

成功的熔化响应

{
  "quote": "TRmjduhIsPxd...",
  "request": "lno1qcp4256ypqpq86q69t5wv5629arxqurn8cxg9p5qmmqy2e5xq...",
  "amount": 10,
  "unit": "sat",
  "fee_reserve": 2,
  "state": "PAID",
  "expiry": 1701704757,
  "payment_preimage": "c5a1ae1f639e1f4a3872e81500fd028bece7bedc1152f740cba5c3417b748c1b"
}

示例 MeltMethodSetting

{
  "method": "bolt12",
  "unit": <str>,
  "min_amount": <int|null>,
  "max_amount": <int|null>
}
  • 原文链接: github.com/cashubtc/nuts...
  • 登链社区 AI 助手,为大家转译优秀英文文章,如有翻译不通的地方,还请包涵~

相关文章

0 条评论