隐身地址让你只需公开一个地址,而付给你的每一笔款项仍然会落在链上的一个新位置。本指南以 Blocknet 为例,讲解它是如何做到的、查看密钥和花费密钥各自的作用,以及钱包如何找到属于自己的款项。
什么是隐身地址?
隐身地址是一个从不出现在区块链上的公开地址。它包含两个公钥:花费密钥和查看密钥。每一笔付款时,发送方的钱包都会把它们与一个新的随机数结合,生成一个一次性地址,只有接收方能识别它并从中花费。
在公开账本上,普通地址会出现在它收到的每一笔付款中。标准的比特币地址就是这样:任何知道它的人都可以查到每一笔付款并算出总额。这就是许多比特币钱包会为每笔付款提供新地址的原因。
隐身地址替你完成了这件事。你只需分享一个地址,可以放在网站上、写进个人资料,或者用 @name 的形式,每个发送方都会从它推导出不同的一次性地址。付给你的十笔款项会变成十个输出,它们彼此之间、与你分享的地址之间都没有任何可见的联系。整个过程不需要来回沟通:发送方仅凭你的公开地址和一个随机数,就能独自算出一次性地址。
隐身地址从何而来?
Monero、Blocknet 和其他币所使用的一次性地址设计,可以追溯到 Nicolas van Saberhagen 撰写、日期为 2013 年 10 月 17 日的 CryptoNote v2.0 白皮书。白皮书把这个思路称为“unlinkable payments”(不可关联支付),并描述了发送方如何根据接收方的地址生成“one-time public key”(一次性公钥)。Monero 的文档将隐身地址描述为让发送方“代表接收方为每笔交易创建随机的一次性地址”(create random one-time addresses for every transaction on behalf of the recipient)。
这个思路已经扩展到隐私币之外。比特币的 BIP 352 “Silent Payments”(静默支付)让一个固定的比特币地址可以接收在链上无法关联的付款;以太坊的 ERC-5564 创建于 2022 年,为以太坊账户制定了隐身地址标准。两者在各自的链上都是可选功能。在 Blocknet 中,每一笔付款都使用一次性地址。
隐身地址如何运作?
发送方选取一个随机数 r,在交易中公布 R = r·G,并把 r 与接收方的公开查看密钥结合成一个共享密钥。把这个共享密钥加到公开花费密钥上,就得到一次性密钥。接收方用 R 和自己的私有查看密钥重建同一个共享密钥,所以只有接收方能找到这笔付款。
你的钱包持有两对密钥。每个私钥都是一个秘密数字(s 用于花费,v 用于查看),每个公钥是这个数字乘以曲线上一个固定的点 G。你的地址就是这两个公钥 S 和 V。整个过程如下:
Receiver publishes: S = s·G and V = v·G
Sender picks r: R = r·G (stored in the transaction)
One-time key: P = H(r·V)·G + S
Receiver checks: H(v·R)·G + S == P (true because r·V = v·R)
Key to spend it: x = H(v·R) + s (so P = x·G)
其中的关键是 Diffie-Hellman 密钥交换。发送方计算 r·V,接收方计算 v·R,两者得到的是同一个点,因为它们都等于 r·v·G。其他人既不知道 r 也不知道 v,所以无法算出这个点。对这个点做哈希(H)就得到共享密钥,而一次性密钥 P 就是把这个共享密钥转换成点后,再加上你的公开花费密钥。
扫描时,你用 R 和你的私有查看密钥重新算出共享密钥,并检查它是否能得到 P。如果能,这个输出就是你的。要花费它,钱包会把同一个共享密钥加到你的私有花费密钥上,得到这一个输出专用的私钥 x。
Blocknet 在 Curve25519 之上的 Ristretto 群上采用这一设计,哈希使用 SHA-256,并加上 Blocknet 专用的域标签。它还会把每个输出在交易中的位置混入哈希,因此即使一笔交易中有两个输出付给同一个人,它们得到的密钥也毫无关联。同一个共享密钥还会推导出隐藏输出金额的盲化因子和加密备注的密钥,接收方正是借此读取金额和备注的。
查看密钥和花费密钥分别有什么作用?
把密钥分开,你就有了两个级别的访问权限:
- 私有查看密钥用于找到款项。 有了 v 和你的公开花费密钥,钱包就能识别付给你的每一个输出,解密它的金额并读取它的备注。
- 私有花费密钥用于花费款项。 只有 s 能把找到的输出变成可以签署花费的私钥 x。
这种分离让仅查看钱包成为可能。在 Blocknet 命令行钱包中,viewkeys 会用你的查看密钥创建一个单独的钱包文件。钱包的帮助说明把它描述为一个“can monitor but cannot spend”(可以查看但不能花费)的文件,适用于“you want watch-only access on another machine”(想在另一台机器上获得仅查看权限)的情况。运行这个命令时,钱包会说明它要创建什么:
# View Keys
This will create a view-only wallet that can monitor incoming
funds but CANNOT spend. Anyone with this file can see your
balance and transaction history.
只把仅查看钱包交给你愿意向其展示交易记录的人,比如你的会计,或者把它放在你自己的第二台电脑上。无论哪种方式,它都能看到资金到账,而你的花费密钥始终留在你信任的那台机器上。
钱包如何找到自己的款项?
由于一次性密钥看起来与你的地址毫无关联,链上没有任何标记表明哪些输出属于你。你的钱包会自己检查。对于每个新区块中的每笔交易,它取出 R,用你的私有查看密钥计算共享密钥,再用上面的公式检验每个输出。这就是钱包在同步时所做的事情。
Blocknet 的扫描器让这个过程保持快速。最耗时的一步,也就是用你的查看密钥乘以 R,每笔交易只执行一次,然后用结果检查这笔交易中的每个输出。当某个输出匹配时,钱包会解密金额,并先与该输出的 Pedersen 承诺核对,再计入你的余额。
扫描也让恢复变得简单。Blocknet 的两对密钥都来自你的 12 个单词的恢复助记词,因此在新电脑上恢复助记词就能重建密钥,钱包会重新扫描区块链,找到属于你的每一个输出。Blocknet 钱包指南介绍了如何备份和恢复助记词。
Blocknet 中的隐身地址
在 Blocknet 中,隐身地址就是你的地址。它是你的公开花费密钥、公开查看密钥和 4 字节校验和的 base58 编码,长度为 92 或 93 个字符。付给你的每一笔款项都使用它,因为交易格式没有透明模式。区块奖励同样发送到一次性密钥,所以独立挖矿者的 coinbase 输出也是通过同样的扫描找到的。
隐身地址与 Blocknet 的其他隐私保护配合使用:16 成员的环隐藏一笔花费来自哪个输出,承诺隐藏金额,Dandelion++ 隐藏交易进入网络的位置。Blocknet 隐私指南逐层介绍了这些保护。
隐身地址也能配合名称使用。像 @yourname 这样的 Blocknet ID 短名称会公开映射到你的地址,方便发送方在 blocknet.id 上查询。而付款本身仍然发送到全新的一次性密钥,所以你的名称不会透露你收到了什么。
因此短名称很适合用作挖矿的收款地址。启动 seine,在提示处输入你的地址或 @yourname,并接受默认矿池 bntpool.com。挖矿指南介绍了其余步骤,而你挖到的 BNT 会由钱包通过上面介绍的同一种查看密钥扫描找到。
常见问题
隐身地址和一次性地址是一回事吗?
它们是同一套机制的两个部分。隐身地址是你公开的地址,由公开花费密钥和公开查看密钥组成。一次性地址是发送方为某一笔付款从它推导出的全新密钥。链上只出现一次性地址,你公开的地址从不出现。
别人知道我的隐身地址,能看到我的余额吗?
不能。知道你公开的地址并不能让任何人找到你收到的款项,因为每一笔都落在一个看起来与它毫无关联的一次性密钥上。要找到这些款项需要你的私有查看密钥,所以只有你的钱包能列出你收到了什么。在 Blocknet 中金额同样是隐藏的,所以连输出本身也不显示数值。
别人拿到我的私有查看密钥能做什么?
持有私有查看密钥的人可以找到付给你的款项,并读取它们的金额和备注。它无法花费任何资金,因为花费需要私有花费密钥。在 Blocknet 中,viewkeys 命令可以把你的查看密钥做成一个仅查看钱包,方便在第二台电脑上查看资金。请只把它交给你信任的人。
在 Blocknet 中需要手动开启隐身地址吗?
不需要。每一笔 Blocknet 付款都会自动发送到一次性隐身地址,因为交易格式没有透明模式。你发送时,钱包会生成一次性密钥;钱包同步时,会用你的查看密钥找到收到的款项。无论你付款给地址还是 @name,都无需任何设置。