Bolt 12 Contacts:闪电网络的联系人支付提案
Bolt 12 Contacts 是闪电网络的一个提案,允许用户在支付时附加联系人信息,使接收方识别付款人、建立联系人并方便退款。它引入 invreq_contact_secret、invreq_payer_offer 和 invreq_payer_bip_353_name 等字段,通过确定性推导生成联系人秘密,支持双向识别。该提案旨在提升闪电钱包的用户体验,让点对点支付更接近传统支付应用。
bLIP: 42 标题: Bolt 12 联系人 状态: 活跃 作者: Bastien Teinturier <bastien@acinq.fr> 创建日期: 2024-07-19 许可证: CC0
摘要
Bolt 12 引入了 Offer,它们是静态的闪电网络"地址"。于是,将 Bolt 12 Offer 与你的朋友和联系人关联起来就变得很自然。
向联系人发送付款时,你可能希望他们知道付款来自你。我们提出了一种方案,可以选择性地将联系人信息包含在对外付款中,使收款方能够:
- 检测到付款来自其已知联系人之一
- 否则,能够将付款方添加到其联系人列表中
- 无需额外交互即可将资金返还给付款方
此功能让联系人之间的付款与法币支付应用非常相似,为闪电钱包带来了更好的用户体验。
版权
本 bLIP 采用 CC0 许可证授权。
动机
此功能让联系人之间的付款与法币支付应用非常相似,为闪电钱包带来了更好的用户体验。
规范
发票请求 TLV
invreq_contact_secret 字段是联系人对的标识符:
- 类型: 2000001729 (
invreq_contact_secret) - 数据:
- [
32*字节:contact_secret]
- [
invreq_payer_offer 字段允许付款方透露一个 Bolt 12 Offer,联系人可用它向付款方付款:
- 类型: 2000001731 (
invreq_payer_offer) - 数据:
- [
...*字节:payer_offer]
- [
invreq_payer_bip_353_name 字段允许付款方透露其 BIP 353 名称,以便联系人能向他们付款:
- 类型: 2000001733 (
invreq_payer_bip_353_name) - 数据:
- [
u8:name_len] - [
name_len*字节:name] - [
u8:domain_len] - [
domain_len*字节:domain]
- [
invreq_payer_bip_353_signature 字段允许付款方使用与 BIP 353 名称关联的 Offer 的签名密钥之一来签署 invoice_request,从而证明对该 BIP 353 名称的所有权。
- 类型: 2000001735 (
invreq_payer_bip_353_signature) - 数据:
- [
点:offer_signing_key] - [
bip340sig:signature]
- [
要求
创建新的 支付联系人 时:
- 必须获得用户授权才能向该联系人透露支付信息。
- 如果节点之前收到过来自此联系人且包含
invreq_contact_secret字段的付款:- 必须将收到的
invreq_contact_secret、invreq_payer_offer和invreq_payer_bip_353_name与该联系人关联。 - 如果包含了
invreq_payer_bip_353_name:- 应该使用它来获取
invreq_payer_offer(如果未包含)。 - 必须验证收到的 Offer 是否与
invreq_payer_bip_353_signature中提供的offer_signing_key匹配。 - 应该使用它在 Offer 过期时刷新
invreq_payer_offer。 - 可能定期使用它来刷新
invreq_payer_offer。
- 应该使用它来获取
- 必须将收到的
- 否则:
- 必须为该联系人生成唯一的
invreq_contact_secret,使用确定性推导。 - 必须将此
invreq_contact_secret与该联系人关联。 - 每次支付此联系人时都必须包含此
invreq_contact_secret。
- 必须为该联系人生成唯一的
invoice_request 的写入方:
- 如果他们希望收款方能够识别谁付了款:
- 如果收款方不是 支付联系人:
- 必须请求用户授权,创建一个与该收款方匹配的 支付联系人。
- 必须包含与该联系人关联的
invreq_contact_secret。 - 必须包含
invreq_payer_offer或invreq_payer_bip_353_name之一。 - 如果包含
invreq_payer_bip_353_name:- 必须将
name设置为 BIP 353 HRN 中 ₿ 之后、@ 之前的部分。 - 必须将
domain设置为 BIP 353 HRN 中 @ 之后的部分。 - 必须使用其
offer_issuer_id的私钥(如果其 Offer 不包含offer_issuer_id,则使用其一个offer_paths的blinded_node_id的私钥)来包含invreq_payer_bip_353_signature。签名数据的计算应按照 Bolt 12 中的详细说明进行,签名标签为 "lightning" || "invoice_request" || "invreq_payer_bip_353_signature"。
- 必须将
- 如果包含
invreq_payer_offer:- 必须将
payer_offer编码为其各记录的 TLV 流。 - 如果编码后的 Offer 长度超过 300 字节:
- 不应该包含
invreq_payer_offer。 - 应该改为包含
invreq_payer_bip_353_name。
- 不应该包含
- 必须将
- 如果收款方不是 支付联系人:
- 否则:
- 不得包含
invreq_contact_secret、invreq_payer_offer或invreq_payer_bip_353_name。
- 不得包含
invoice_request 的读取方:
- 如果提供了
invreq_payer_bip_353_name:- 如果
invreq_payer_bip_353_signature缺失或无效,必须忽略该invoice_request。
- 如果
- 必须发回一张
invoice,其中包含发送方提供的invoice_request联系人字段,如 Bolt 12 中规定。
当发票已支付时:
- 如果
invoice_request中包含了invreq_contact_secret:- 如果它匹配一个已有的 支付联系人:
- 如果提供了
invreq_payer_note,应该显示它。 - 必须忽略
invreq_payer_offer和invreq_payer_bip_353_name。
- 如果提供了
- 否则:
- 可能请求用户授权,根据此收到的付款添加新联系人或更新现有联系人(如果用户已验证付款来自该联系人)。
- 如果它匹配一个已有的 支付联系人:
原理
contact_secret 字段用于相互识别:它由发起第一次付款的节点设置,并且收款方在反向发送付款时必须重复使用。其用法和边界情况在下面的联系人密钥部分详细说明。
节点通常不存储收到的每一个 invoice_request,因为这样会使其面临 DoS 攻击。相反,它们将想要存储的字段包含在返回的 invoice 的盲化路径的 path_id 字段中。由于此 path_id 随后会包含在支付洋葱中,而支付洋葱限制为 1300 字节,节点必须确保生成的 path_id 不会太大,以免限制付款方可用的支付路径。因此,我们建议只在 invreq_payer_offer 中包含小于 300 字节的 Offer,或者一个小的 BIP 353 HRN。
当包含 BIP 353 HRN 时,接收 invoice_request 的节点并不知道它是否真的属于发送方。这就是为什么发送方还必须包含一个签名,证明其控制了存储在 BIP 353 记录中的 Offer 的其中一个私钥。
当付款来自已知联系人时,可选包含的 payer_note 包含垃圾信息的风险较小。因此建议显示它,而通常不建议显示来自未知付款方的 payer_note。
当从现有联系人收到付款时,必须忽略 Offer 和 BIP 353 HRN:这确保如果 contact_secret 泄露,冒充我们联系人的恶意节点无法将我们未来的付款重定向到他们自己的 Offer。
联系人密钥
本提案的主要机制是交换 contact_secret。本节详细说明了可能出现的各种场景以及如何正确处理每种场景,以及推荐的用户体验。
添加联系人
当 Alice 想要支付 Bob 时,她的钱包应提供将其添加到联系人列表的选项(使用复选框或专用按钮)。如果她选择该选项,她会生成一个 contact_secret(有关生成的详细信息,请参见确定性推导部分)。对于所有未来向 Bob 且她希望透露自己是付款方的付款,Alice 将包含此 contact_secret。钱包应始终提供私密支付联系人的选项,在这种情况下,contact_secret 和付款方信息将不会包含在 invoice_request 中。
一旦 Bob 收到包含 contact_secret 的付款,他的钱包应显示一个选项,将付款方添加到自己的联系人列表中(例如,通过收款页面上的专用按钮)。如果他知道此付款来自 Alice(因为 Alice 可验证地告诉他确实来自她),他可以将 Alice 添加到他的联系人中,并使用她提供的 payer_offer 或 payer_bip_353_name 向她付款。对于所有未来向 Alice 且 Bob 希望透露自己是付款方的付款,Bob 将包含由 Alice 生成的 contact_secret。注意,在这种情况下,Bob 不会生成不同的 contact_secret,因为他已经有一个由 Alice 创建的可用密钥,并且他知道 Alice 可以用它来识别付款。
但是,如果 Bob 在未使用 Alice 的付款的情况下将 Alice 添加到联系人列表,或者他在不同于接收 Alice 付款所用的钱包上将她添加到联系人列表,Bob 将生成一个不同的 contact_secret。对于所有向 Alice 且他希望透露自己是付款方的付款,他将使用这个新的 contact_secret。当 Alice 收到这些付款时,她无法仅根据 contact_secret 自动识别出来自 Bob,因为它与她生成的不同。但如果 Alice 知道某笔特定付款来自 Bob(因为他可验证地告诉她),她的钱包应允许她将此付款归于现有联系人(例如,通过点击收款页面上的"添加到联系人"按钮,然后选择"添加到现有联系人"选项)。然后她的钱包会将那个额外的 contact_secret 添加到 Bob 可能用于向她付款的密钥列表中。此操作会自动调和来自 Bob 的过去和未来的付款。
因此,一个联系人条目包含以下信息:
primary_contact_secret:使用的第一个contact_secret,必须用于所有对此联系人的对外付款,它可能由我们创建(如果我们发起了第一次付款),也可能由我们的联系人创建(如果我们基于收到的付款将他们添加到联系人列表)。additional_remote_contact_secrets:我们的联系人可能用于向我们付款的次要contact_secret列表,通过手动将付款与现有联系人关联获得。
泄露的联系人密钥
联系人密钥不应公开分享,因为这会让他人发出看似来自你的付款。但这并不允许窃取资金:即使冒名顶替者在以你的名义发出的付款中将自己的 Offer 包含在 invreq_payer_offer 字段中,如果接收节点已经存储了你的联系信息,则会忽略它。如果他们没有存储,则没有理由基于此付款创建新联系人。
确定性推导
创建新联系人时,我们对 contact_secret 字段使用以下确定性推导:
- 对于给定的 Bolt 12 Offer,我们定义其
offer_node_id为:- 如果 Offer 包含
offer_issuer_id:offer_node_id = offer_issuer_id。
- 否则,Offer 必须包含
offer_paths:offer_node_id设置为第一个path的最后一个blinded_node_id。
- 如果 Offer 包含
offer_node_id的私钥称为offer_priv_key。- 当支付
remote_offer并且在invreq_payer_offer字段中包含我们的local_offer时:- 我们计算两个
offer_node_id的 ECDH:shared_key = local_offer.offer_priv_key * remote_offer.offer_node_id。
- 我们使用标签哈希来推导
contact_secret:contact_secret = SHA256("blip42_contact_secret" || shared_key)。
- 我们计算两个
使用这种确定性推导有多个好处。首先,它保证当两个节点使用同一组 Offer 时,它们能独立推导出相同的 contact_secret,这消除了当节点同时将对方添加到联系人列表时需要调和密钥的必要性。
对于使用单个 Offer(从用户的种子确定性创建)的钱包尤其有用:这确保 contact_secret 也可以从种子恢复。
测试向量
以下测试向量使用上一节中的确定性推导。
[
{
"comment": "当两个 Offer 都仅使用盲化路径时推导确定性 contact_secret",
"alice_offer": "lno1qgsqvgnwgcg35z6ee2h3yczraddm72xrfua9uve2rlrm9deu7xyfzrcsesp0grlulxv3jygx83h7tghy3233sqd6xlcccvpar2l8jshxrtwvtcsrejlwh4vyz70s46r62vtakl4sxztqj6gxjged0wx0ly8qtrygufcsyq5agaes6v605af5rr9ydnj9srneudvrmc73n7evp72tzpqcnd28puqr8a3wmcff9wfjwgk32650vl747m2ev4zsjagzucntctlmcpc6vhmdnxlywneg5caqz0ansr45z2faxq7unegzsnyuduzys7kzyugpwcmhdqqj0h70zy92p75pseunclwsrwhaelvsqy9zsejcytxulndppmykcznn7y5h",
"alice_offer_priv_key": "4ed1a01dae275f7b7ba503dbae23dddd774a8d5f64788ef7a768ed647dd0e1eb",
"alice_offer_node_id": "0284c9c6f04487ac22710176377680127dfcf110aa0fa8186793c7dd01bafdcfd9",
"bob_offer": "lno1qgsqvgnwgcg35z6ee2h3yczraddm72xrfua9uve2rlrm9deu7xyfzrcsesp0grlulxv3jygx83h7tghy3233sqd6xlcccvpar2l8jshxrtwvtcsz4n88s74qhussxsu0vs3c4unck4yelk67zdc29ree3sztvjn7pc9qyqlcpj54jnj67aa9rd2n5dhjlxyfmv3vgqymrks2nf7gnf5u200mn5qrxfrxh9d0ug43j5egklhwgyrfv3n84gyjd2aajhwqxa0cc7zn37sncrwptz4uhlp523l83xpjx9dw72spzecrtex3ku3h3xpepeuend5rtmurekfmnqsq6kva9yr4k3dtplku9v6qqyxr5ep6lls3hvrqyt9y7htaz9qj",
"bob_offer_priv_key": "12afb8248c7336e6aea5fe247bc4bac5dcabfb6017bd67b32c8195a6c56b8333",
"bob_offer_node_id": "035e4d1b7237898390e7999b6835ef83cd93b98200d599d29075b45ab0fedc2b34",
"contact_secret": "810641fab614f8bc1441131dc50b132fd4d1e2ccd36f84b887bbab3a6d8cc3d8"
},
{
"comment": "当一个 Offer 同时使用盲化路径和 issuer_id 时推导确定性 contact_secret",
"alice_offer": "lno1qgsqvgnwgcg35z6ee2h3yczraddm72xrfua9uve2rlrm9deu7xyfzrcsesp0grlulxv3jygx83h7tghy3233sqd6xlcccvpar2l8jshxrtwvtcsrejlwh4vyz70s46r62vtakl4sxztqj6gxjged0wx0ly8qtrygufcsyq5agaes6v605af5rr9ydnj9srneudvrmc73n7evp72tzpqcnd28puqr8a3wmcff9wfjwgk32650vl747m2ev4zsjagzucntctlmcpc6vhmdnxlywneg5caqz0ansr45z2faxq7unegzsnyuduzys7kzyugpwcmhdqqj0h70zy92p75pseunclwsrwhaelvsqy9zsejcytxulndppmykcznn7y5h",
"alice_offer_priv_key": "4ed1a01dae275f7b7ba503dbae23dddd774a8d5f64788ef7a768ed647dd0e1eb",
"alice_offer_node_id": "0284c9c6f04487ac22710176377680127dfcf110aa0fa8186793c7dd01bafdcfd9",
"bob_offer": "lno1qgsqvgnwgcg35z6ee2h3yczraddm72xrfua9uve2rlrm9deu7xyfzrcsesp0grlulxv3jygx83h7tghy3233sqd6xlcccvpar2l8jshxrtwvtcsz4n88s74qhussxsu0vs3c4unck4yelk67zdc29ree3sztvjn7pc9qyqlcpj54jnj67aa9rd2n5dhjlxyfmv3vgqymrks2nf7gnf5u200mn5qrxfrxh9d0ug43j5egklhwgyrfv3n84gyjd2aajhwqxa0cc7zn37sncrwptz4uhlp523l83xpjx9dw72spzecrtex3ku3h3xpepeuend5rtmurekfmnqsq6kva9yr4k3dtplku9v6qqyxr5ep6lls3hvrqyt9y7htaz9qjzcssy065ctv38c5h03lu0hlvq2t4p5fg6u668y6pmzcg64hmdm050jxx",
"bob_offer_priv_key": "bcaafa8ed73da11437ce58c7b3458567a870168c0da325a40292fed126b97845",
"bob_offer_node_id": "023f54c2d913e2977c7fc7dfec029750d128d735a39341d8b08d56fb6edf47c8c6",
"contact_secret": "4e0aa72cc42eae9f8dc7c6d2975bbe655683ada2e9abfdfe9f299d391ed9736c"
}
]
参考实现
- lightning-kmp: <https://github.com/ACINQ/lightning-kmp/pull/719>
- phoenix wallet: <https://github.com/ACINQ/phoenix/pull/645>
- 原文链接: github.com/lightning/bli...
- 登链社区 AI 助手,为大家转译优秀英文文章,如有翻译不通的地方,还请包涵~