Private Key Tools

私钥 → P2TR 地址(Taproot)

BIP86 key-path-only 的 Taproot bc1p… 地址,每一步都摊开给你看:点 P = k·G、x-only 内部公钥及其奇偶性规则、TapTweak tagged hash、调整后的点 Q = P + t·G、输出公钥 x(Q),以及 witness 版本 1 的 bech32m 编码。

64 位十六进制 = 32 字节 = 256 位,取值范围 [1, n−1]。允许 0x 前缀、空格与大写字母,不足 64 位会自动左补 0。本页构建的是仅 key path的形式:没有脚本树,因此 TapTweak 的输入就是那 32 字节的内部公钥。
还没有输入

请在上方输入内容后点击「转换」。不知道填什么?点「填入示例」即可载入占位符里的演示数据。

技术原理详解

本页计算了什么

输入一个 256 位私钥,输出 BIP86 key path 的 Taproot 地址,也就是以 bc1p 开头的字符串。Taproot 并不是「程序更长的 SegWit」—— 公钥要先被一个由哈希得到的标量调整过,而地址承诺的 正是调整之后的结果:

P = k·G  →  t = int( tagged_hash("TapTweak", x(P)) ) mod n  →  Q = P + t·G  →  地址 = bech32m("bc", 1, x(Q))
私钥 k→ P = k·G→ x-only 内部公钥 x(P)→ tagged hash "TapTweak"→ t(标量)→ Q = P + t·G→ 输出公钥 x(Q)→ bech32m,witness v1→ bc1p…

结果面板会打印每一个中间值,包括奇偶性的判定、tagged hash 内部那两个 32 字节哈希,以及调整后的点本身。

1. Taproot 想解决什么

Taproot 是三个提案一起落地的,于 2021 年 11 月 14 日激活:

BIP定义了什么
BIP340secp256k1 上的 Schnorr 签名,定义在 32 字节的x-only公钥之上
BIP341Taproot 输出与花费规则:输出公钥、TapTweak 调整、key path 与 script path
BIP342Tapscript —— script path 花费中所用的脚本语言
BIP350bech32m,witness 版本 1 及以上地址必须使用的校验和变体

隐私。 在 Taproot 之前,付给一份合约的钱和付给一个人的钱,在链上长得不一样:输出承诺的是 脚本哈希,观察者一眼就能看出「这里发生了复杂的事」,甚至能猜出是什么。Taproot 让两者无法区分。所有输出 都长成 OP_1 <32 字节>。如果多签或通道的参与方能够合作,他们就只用 key path 上的一个签名 把钱花掉,链上呈现出来的和一笔普通的单签名付款一模一样。只有当合作失败时,script path 才会暴露 —— 而且 即便暴露,也只暴露真正用到的那一个分支。

用 Schnorr 取代 ECDSA。 Schnorr 签名(BIP340)是固定 64 字节;ECDSA 签名用 DER 编码, 长度在 70 到 72 字节之间浮动,而且编码本身是可塑的,SegWit 当初不得不绕开这个问题。更重要的是,Schnorr 是 线性的:签名之和就是「公钥之和」的合法签名。正是这一性质,让 MuSig 式的公钥聚合、门限签名和批量 验签无需任何取巧手段就能实现。

2. x-only 公钥:BIP340 为什么要丢掉 y

secp256k1 的点有两个坐标,但对任意一个 x,候选点只有两个:P 和 −P = (x, p − y)。其中必有一个的 y 是偶数。BIP340 规定:32 字节公钥的规范解释就是「x 等于该值、 且 y 为偶数的那个点」,这个函数叫 lift_x。丢掉 y 让每个签名少一个字节,也 消除了 ECDSA 拖了很多年的那层歧义。

实践中有两个后果值得牢记:

3. TapTweak 的构造,以及为什么必须用 tagged hash

输出公钥从来不是你推导出来的那个公钥本身,而是它加上生成元的一个标量倍:

t = int( tagged_hash( "TapTweak", x(P) ‖ merkle_root ) ) mod n    Q = P + t·G

对于 key-path-only 的地址(BIP86),Merkle 根被完全省略,输入就只有 32 字节的 x-only 内部公钥。本页实现的 正是这一种。结果 Q 就是输出公钥,而地址里只放它的 x 坐标。从地址上没有任何人能看出 是否存在脚本树 —— 这个承诺已经被内部公钥吸收进去了。

偏移字节数内容
032x(P) —— x-only 内部公钥
320 或 32脚本树的 Taproot Merkle 根:BIP86 key-path-only 时不存在,有脚本树时为 32 字节

tagged hash 是 BIP340 给出的、从 SHA-256 派生专用哈希的标准套路:

tagged_hash(tag, msg) = SHA256( SHA256(tag) ‖ SHA256(tag) ‖ msg )

标签的哈希被放在最前面两次。这多花一个压缩块的开销,换来的是域分离:为某个用途 算出的哈希,永远不可能与为另一个用途算出的哈希撞上。没有它,攻击者就可以尝试构造一个「既是签名随机数、 又是 TapTweak 输入」的值 —— 这是一片零成本就能关掉的跨协议攻击面。对于标签 "TapTweak", SHA256("TapTweak") 等于 e80fe1639c9ca050e3af1b39c143c63e429cbceb15d940fbb5c5a1f4af57c5e9。

tweak 既是承诺,也是保护

加上 t·G 正是让脚本树变成可选的原因:树的 Merkle 根被折进公钥里,于是公钥本身 就是承诺。它同时意味着:你公布出去的公钥(内部公钥)并不是验签用的公钥(输出公钥)—— 如果有人设法替换了 内部公钥,算出的输出公钥就会不同,地址也随之不同。如果算出的 tweak ≥ n,或者 Q 落到无穷远点, 按 BIP341 这次构造就失败、必须重试;其概率约为 2−128。

4. key path 与 script path

花费一笔 Taproot 输出有两条路。

这也是本页只做 key path 的原因:单密钥钱包(BIP86)就是这么做的,而只有在这种形式下,地址才完全只依赖内部 公钥。如果你想要 script path,tweak 就必须带上树的 Merkle 根,于是 tweak 改变、输出公钥改变、地址也跟着改变。 举个量级上的例子:把 32 字节的假根 1111…11 代进去,演示私钥的 tweak 会从真实的 bae91685…85e8fa4d 变成 254264f6410f1b3890db1ee95ea9990b28875f4b3e06cf35dfd0d0a6dda6d04b,地址自然也就不同了。

不要把两套模型混在一起

地址是对某个特定输出公钥的承诺。如果你生成了 BIP86 地址,之后却想用 script path 去花它,那些脚本根本不在 承诺里,这笔花费是无效的。反过来说,如果你带着脚本树生成了地址、却始终只用 key path,币是安全的 —— 但这个 地址不会和「普通」钱包显示的一致,而这正是 BIP86 存在的理由:它钉死一种确定性的、不含脚本的构造方式, 让所有钱包口径一致。

5. bech32m,以及为什么 witness v1+ 必须用它

Taproot 地址使用 bech32m(BIP350)—— 字母表与结构与 bech32 相同,但校验和常量不同: 0x2bc830a3(734539939)而非 1。witness 版本 0 必须用 bech32,版本 1 及以上必须用 bech32m。不强制检查这种配对关系的解码器,会接受别的软件拒绝的字符串。

需要第二个变体的原因,是原始校验和存在一个插入弱点,BIP350 原文写得很直白:只要 bech32 字符串的最后 一个字符是 p,在它前面紧邻位置插入或删除任意多个 q 字符都不会破坏校验和。 校验字符会整体错位,于是同样的六个字符可以验证长度不同的数据部分 —— 同一串字符就能对应两个「不同的地址」。 这很容易演示,本页背后的服务器也演示过:合法地址 bc1qgey9e3xtk8fpdsgchch25saytdp6nlhdt9gm0p 在末尾 p 之前插入 1、2、3 个 q 之后(…gm0qp、…gm0qqp、…gm0qqqp),校验和依然有效。在针对 bc 前缀字符串所做的 78,624 次单字符插入尝试中,凡是校验和仍然成立的插入形状全部如此,而且 全部属于 bech32 —— 没有任何一个 bech32m 字符串接受了插入。对同样以 p 结尾的 bech32m 地址重复这个实验,校验和会直接失败,这正是换用新常量的意义所在。

witness 版本 0 之所以躲过了这个问题,是因为它的程序长度被限制为恰好 20 或 32 字节,执行结构检查的解码器会 拒绝被改动的字符串;而 witness v1+ 没有这层限制,Taproot 地址也更长,所以 BIP350 为它规定了修正后的常量。

配对搞反,地址就不再是地址

把本页的 Taproot 地址用 bech32 常量重新编码,得到 bc1pmsrhxnvmj8tjxxu9wj69ntvwvhvr28qufyc26tshz3rjj777tx3szg9sln,解码会报错 「Witness v1+ must use bech32m, not bech32」。把演示用的 P2WPKH 地址用 bech32m 常量重新编码,得到 bc1qy86hkm0telzmvuvzm76279jp7z5p3yqjxpf0nc,解码会报错 「Witness v0 must use bech32, not bech32m」。两者只差最后六个字符 —— 这正是为什么校验和变体是 安全属性,而不是外观细节。

6. witness 程序与地址,逐字节拆解

一个 Taproot 输出脚本共 34 字节:一个版本操作码、一次 32 字节推送,以及输出公钥本身。

偏移字节数内容含义
0151OP_1 —— 推送数值 1,同时也表示 witness 版本 1
1120推送接下来的 32 字节(0x20 = 32)
232x(Q)x-only 调整后的输出公钥 —— 即 witness 程序

以及把它编码出来的地址:

部分演示值规则
hrpbc主网 bc,测试网 tb
分隔符1从最后一个 1 处切开;1 不在数据字符集里
版本字符pp = 数值 1 = witness v1(q 是数值 0,P2WPKH 用它)
程序字符msrhxnvm…777tx3s(52 个)256 位 ÷ 5 = 51.2,故为 52 个字:51 个整字再加 1 位、后面补 4 个 0
校验和h54u636 个字符,bech32m 常量 0x2bc830a3
总长度62 个字符2 + 1 + 53 + 6

对演示地址做解码,会得到 witver = 1、变体 bech32m,以及一个与结果面板里打印的 输出公钥完全一致的 32 字节程序 —— 这是真的往返验证,不是假设。

7. 实例演算 —— 真实的演示私钥

步骤取值
私钥 k0824c314772b2c2859c889471390a36bf66e53a1ca1642f96f0f2c9307a171ca
P = k·G 的 xff812e26116a9aa140abe629e7f8a38401653caf925119def59ed3758c67ee80
P = k·G 的 y8f49cde13c7ffd64f1d730bf87a743a578eda4971ddac4a08118160732b424ae
奇偶性最后一位十六进制是 e → 偶数,无需取负,内部公钥就等于 x(P)
内部公钥(x-only)ff812e26116a9aa140abe629e7f8a38401653caf925119def59ed3758c67ee80
SHA256("TapTweak")e80fe1639c9ca050e3af1b39c143c63e429cbceb15d940fbb5c5a1f4af57c5e9
tweak tbae9168523281e7f9e91d897a8d23f7ae6fe66d61854019dcb8abb5185e8fa4d
t·G 的 xbaa35582aa3a12f4c5fb2cace80671a765dfc95cee356d28b31eb757c52b94a8
t·G 的 ybe8ddfba65063b45db3fb0c2c14d909e0981d1f04cdcde251d5fb18eeda211ff
Q = P + t·G 的 x(输出公钥)dc07734d9b91d7231b8574b459ad8e65d8351c1c4930ad2e171447297bde59a3
Q = P + t·G 的 yf54d5ae5bfeade97f7148ebd308fa916f268bde1bea6c64594e5baf904831284
输出脚本5120dc07734d9b91d7231b8574b459ad8e65d8351c1c4930ad2e171447297bde59a3
数据部分pmsrhxnvmj8tjxxu9wj69ntvwvhvr28qufyc26tshz3rjj777tx3s
校验和h54u63(对应值 23 20 21 28 26 17)
P2TR 地址bc1pmsrhxnvmj8tjxxu9wj69ntvwvhvr28qufyc26tshz3rjj777tx3sh54u63
同一私钥的测试网地址tb1pmsrhxnvmj8tjxxu9wj69ntvwvhvr28qufyc26tshz3rjj777tx3squrnq7

8. Taproot 与 P2WPKH 的对照

P2WPKH(witness v0)P2TR key path(witness v1)
地址bc1q…,42 个字符bc1p…,62 个字符
编码bech32,常量 1bech32m,常量 0x2bc830a3
witness 程序20 字节:hash160(压缩公钥)32 字节:x-only 调整后的输出公钥
输出脚本0014…(22 字节)5120…(34 字节)
签名ECDSA,DER 编码,约 71–72 字节Schnorr(BIP340),64 字节
单输入权重272 WU(68 vB)230 WU(57.5 vB)—— 轻 15.4%
花费时暴露什么每次都暴露公钥走 key path 时什么也不暴露
脚本灵活度固定:一个公钥、一个签名key path 或任意 tapscript 叶子,未使用前完全不可见
激活时间2017 年 8 月 24 日2021 年 11 月 14 日

9. 它在推导链中的位置

10. 安全须知

11. 常见错误

12. 速查表

项目取值
地址类型P2TR,witness 版本 1,key-path-only(BIP86 风格)
tweakt = int(tagged_hash("TapTweak", x(P))) mod n,不含 Merkle 根
tagged hashSHA256(SHA256(tag) ‖ SHA256(tag) ‖ msg)
输出公钥Q = P + t·G,对外只公布 x(Q)(32 字节)
地址长度主网 62 个字符
校验和bech32m,常量 0x2bc830a3 = 734539939
输出脚本OP_1 0x20 ‖ 32 字节公钥(共 34 字节)
key path 花费的 witness一个 64 字节的 Schnorr 签名