NUT-11:支付到公钥(P2PK)

  • cashubtc
  • 发布于 2025-08-20 11:24
  • 阅读 12

该文档(NUT-11)描述了Pay-to-Public-Key(P2PK)方案,它是一种基于NUT-10的Secret的支付条件。

NUT-11:支付到公钥(P2PK)

optional

depends on: NUT-10, NUT-08


此NUT描述了支付到公钥(Pay-to-Public-Key,P2PK),这是一种基于NUT-10的知名Secret的支付条件。使用P2PK,我们可以将ecash代币锁定到接收者的ECC公钥,并需要使用相应的私钥进行Schnorr签名来解锁ecash。支付条件由mint强制执行。

警告:如果mint不支持这种类型的支付条件,证明可能会被视为常规的任何人都可以花费的代币。应用程序需要通过检查mint的NUT-06信息端点来确保mint是否支持特定类型的支付条件。

支付到公钥

NUT-10 Secret kind: P2PK

如果对于一个ProofProof.secret是一种kind: P2PKSecret,则必须通过提供见证Proof.witness以及数组Proof.witness.signatures中的一个或多个有效签名来解锁该证明。

在基本情况下,当花费锁定的代币时,mint需要Proof.witness.signatures中关于Proof.secret的,由Proof.Secret.data中的公钥进行的有效 Schnorr 签名。

为了给出基本情况的具体示例,要mint一个锁定的代币,我们首先创建一个P2PK Secret,内容如下:

[
  "P2PK",
  {
    "nonce": "859d4935c4907062a6297cf4e663e2835d90d97ecdd510745d32f6816323a41f",
    "data": "0249098aa8b9d2fbec49ff8598feb17b592b986e62319a4fa488a3dc36387157a7",
    "tags": [["sigflag", "SIG_INPUTS"]]
  }
]

这里,Secret.data是锁定ecash的接收者的公钥。我们将此Secret序列化为Proof.secret中的字符串,并获得mint的盲签名,该签名存储在Proof.C中(参见NUT-03)。

拥有公钥Secret.data的私钥的接收者可以通过提供Proof.secret字符串的签名来花费此证明,然后将其添加到Proof.witness.signatures中:

{
  "amount": 1,
  "secret": "[\"P2PK\",{\"nonce\":\"859d4935c4907062a6297cf4e663e2835d90d97ecdd510745d32f6816323a41f\",\"data\":\"0249098aa8b9d2fbec49ff8598feb17b592b986e62319a4fa488a3dc36387157a7\",\"tags\":[[\"sigflag\",\"SIG_INPUTS\"]]}]",
  "C": "02698c4e2b5f9534cd0687d87513c759790cf829aa5739184a3e3735471fbda904",
  "id": "009a1f293253e41e",
  "witness": "{\"signatures\":[\"60f3c9b766770b46caac1d27e1ae6b77c8866ebaeba0b9489fe6a15a837eaa6fcd6eaa825499c72ac342983983fd3ba3a8a41f56677cc99ffd73da68b59e1383\"]}"
}

签名方案

要花费锁定有P2PK的代币,花费者需要在花费的证明中包含签名。我们使用libsecp256k1的序列化64字节Schnorr签名,对要签名的消息的SHA256哈希进行签名。要签名的消息是输入中的Proof.secret字段。如果输入中的Secret.tags.sigflag指示,则输出可能还需要对消息BlindedMessage.B_进行签名。

swapmelt这样的ecash花费操作可以有多个输入和输出。如果我们有多个锁定的输入,我们要么在每个输入中单独提供签名(对于SIG_INPUTS),要么仅在第一个输入中提供整个交易的签名(对于SIG_ALL)。输入是在inputs字段中提供的Proofs,输出是请求正文的outputs字段中的BlindedMessages(请参见NUT-05中的PostMeltRequestNUT-03中的PostSwapRequest)。

标签

更复杂的支出条件可以在Secret.tags中的标签中定义。所有标签都是可选的。标签是具有两个或多个字符串的数组,格式为["key", "value1", "value2", ...]。我们将证明中的特定标签表示为

支持的标签有:

  • sigflag: <str_enum[SIG_FLAG]> 设置签名标志
  • pubkeys: <hex_str> 是其他的公钥(和secret里data字段里的公钥一起),可以提供签名(允许多个条目
  • n_sigs: <int> 指定提供有效签名的公钥的最小数量
  • locktime: <int> 是锁过期的 Unix 时间戳
  • refund: <hex_str> 是可选的退款公钥,只能在 locktime 之后花费(允许多个条目
  • n_sigs_refund: <int> 指定提供有效签名的退款公钥的最小数量

[!NOTE]

标签序列化类型是[<str>, <str>, ...],但某些标签值是int。钱包和mint必须为反/序列化适当地转换类型。

签名标志

签名标志在Secret.tags['sigflag']标签中定义。当前,有两个签名标志。

  • SIG_INPUTS 要求所有输入都具有独立的有效签名。它是默认的签名标志,如果缺少sigflag标签,则将应用该标志。
  • SIG_ALL 要求对交易的所有输入和所有输出都进行有效签名。

如果任何一个输入具有签名标志SIG_ALL,则要求所有输入都具有相同的类型,标志SIG_ALL和相同的Secret.dataSecret.tags,否则将返回错误。

仅当没有输入为SIG_ALL时,才强制执行SIG_INPUTS

签名标志 SIG_INPUTS

SIG_INPUTS表示每个Proof(输入)都需要自己的签名。该签名在每个输入的Proof.witness字段中单独提供。见证的格式稍后定义。

签名的输入

secret上具有签名P2PKWitness.signaturesProof(输入)是JSON(请参见NUT-00):

{
  "amount": <int>,
  "secret": <str>,
  "C": <hex_str>,
  "id": <str>,
  "witness": <P2PKWitness | str> // "secret"上的签名
}

secret字段被签名成一个字符串

见证格式

签名存储在P2PKWitness对象中,并且分别在所有输入的每个Proof.witness中(对于SIG_INPUTS)或仅在交易的第一个输入中(对于SIG_ALL)中提供。P2PKWitness是以下形式的序列化JSON字符串

{
  "signatures": <Array[<hex_str>]>
}

signatures是十六进制签名数组,对应于一个或多个签名公钥的签名。

签名标志 SIG_ALL

仅当满足以下条件时,才强制执行SIG_ALL

  • 如果一个输入具有签名标志SIG_ALL,则所有其他输入必须具有相同的Secret.dataSecret.tags,并且扩展到也必须是SIG_ALL
  • 如果一个或多个输入与此不同,则返回错误。

如果满足此条件,则强制执行SIG_ALL标志,并且只有交易的第一个输入需要一个见证,该见证涵盖该交易所有其他的输入和输出。签名公钥的所有签名必须在交易的第一个输入的Proof.witness中提供。

SIG_ALL的消息聚合

要签名的消息取决于包含具有签名标志SIG_ALL的输入的交易类型。

swap的聚合

交换包含inputsoutputs(请参见NUT-03)。为了提供有效的签名,签名公钥的所有者必须将所有Proofs(输入)的secret字段和所有BlindedMessages(输出,请参见NUT-00)的B_字段连接到单个消息字符串中,按它们在交易出现的顺序进行连接。然后,将此字符串连接起来进行哈希和签名(请参见签名方案)。

如果交换交易具有n个输入和m个输出,则要签名的消息变为:

msg = secret_0 || ... || secret_n || B_0 || ... || B_m

这里,||表示字符串连接。每个输出的B_一个十六进制字符串

melt的聚合

对于熔化交易(melt transaction),要签名的消息由所有输入,要支付的报价ID和NUT-08空白outputs组成。

如果熔化交易(melt transaction)具有n个输入,m个空白输出和一个报价ID quote_id,则要签名的消息变为:

msg = secret_0 || ... || secret_n || B_0 || ... || B_m || quote_id

这里,||表示字符串连接。每个输出的B_一个十六进制字符串

多重签名

Cashu 提供两种级别的多重签名保护:Locktime MultiSigRefund MultiSig,它们根据证明的 locktime 标签的状态激活。

Locktime 多重签名

[!NOTE] 只有当 locktime 标签不存在,或者是一个未来的时间戳时,Locktime 多重签名条件才适用。

如果存在 pubkeys 标签,则仅当 Secret.data 字段或 pubkeys 标签中包含的至少一个公钥给出有效签名时,该 Proof 才可以花费。

如果 n_sigs 标签是一个正整数,则 mint 将要求至少 n_sigs 个公钥提供有效签名。

如果具有有效签名的公钥数量大于或等于 n_sigs 中指定的数量,则交易有效。签名在 P2PKWitness 对象中的字符串数组中提供。

表示为“n-of-m”方案,n = n_sigs 是所需签名的数量,m = 1 (data field) + len(pubkeys tag) 是可以签名的公钥的数量。

[!CAUTION]

由于 Schnorr 签名是不确定的,因此我们期望最少数量的具有有效签名的唯一公钥,而不是期望最少数量的签名。

Locktime

如果标签 locktime 是 unix 时间,并且 mint 的本地时钟大于 locktime,则除了以下条件也为真以外,该 Proof 可以被任何人花费。

[!NOTE] 如果一个 Proof 只需要一个 secret 和一个有效的签名 C 就可以花费(这是默认情况),那么它被认为是任何人都可以花费的。

退款多重签名

如果 locktime 标签在过去,并且存在 refund 标签,则只有当至少一个 refund 公钥给出有效签名时,该 Proof 才可以花费。

如果存在 n_sigs_refund 标签,则 mint 将要求至少 n_sigs_refundrefund 公钥提供有效签名。

[!CAUTION]

由于 Schnorr 签名是不确定的,因此我们期望最少数量的具有有效签名的唯一公钥,而不是期望最少数量的签名。

复杂例子

这是一个 Secret 示例,它使用 Pay-to-Pubkey (P2PK) 条件锁定 Proof,该条件要求来自 data 字段和 pubkeys 标签中的公钥的 2-of-3 签名。如果 timelock 过去,则 Proof 可以仅使用来自 refund 标签中的两个公钥之一的签名来花费。签名标志 sigflag 指示签名对于此 Proof 花费的交易的 inputsoutputs 都是必要的。

[
  "P2PK",
  {
    "nonce": "da62796403af76c80cd6ce9153ed3746",
    "data": "033281c37677ea273eb7183b783067f5244933ef78d8c3f15b1a77cb246099c26e",
    "tags": [
      ["sigflag", "SIG_ALL"],
      ["n_sigs", "2"],
      ["locktime", "1689418329"],
      [
        "refund",
        "033281c37677ea273eb7183b783067f5244933ef78d8c3f15b1a77cb246099c26e",
        "02e2aeb97f47690e3c418592a5bcda77282d1339a3017f5558928c2441b7731d50"
      ],
      [
        "pubkeys",
        "02698c4e2b5f9534cd0687d87513c759790cf829aa5739184a3e3735471fbda904",
        "023192200a0cfd3867e48eb63b03ff599c7e46c8f4e41146b2d281173ca6c50c54"
      ]
    ]
  }
]

用例

使用P2PK可以解锁以下用例:

  • 公开张贴锁定的ecash,该ecash只能由预期的接收者兑换
  • 最终的离线接收者付款,当与诸如DLEQ证明的离线签名检查机制结合使用时,不能进行双重花费
  • 锁定ecash的接收者可以延迟和批量进行多次mint往返以接收证明(需要DLEQ)
  • 通过多重签名功能由多人拥有的Ecash
  • 结合使用时间锁定功能的原子交换

Mint信息设置

NUT-06 MintMethodSetting指示对此功能的支持:

{
  "11": {
    "supported": true
  }
}
  • 原文链接: github.com/cashubtc/nuts...
  • 登链社区 AI 助手,为大家转译优秀英文文章,如有翻译不通的地方,还请包涵~
点赞 0
收藏 0
分享
本文参与登链社区写作激励计划 ,好文好收益,欢迎正在阅读的你也加入。

0 条评论

请先 登录 后评论
cashubtc
cashubtc
江湖只有他的大名,没有他的介绍。