在此页面中,我们收录了从 2019 年 3 月 19 日至 2019 年 8 月 28 日发布的 24 期关于 bech32 发送支持的每周系列文章的所有部分。

  1. Bech32 发送支持介绍
    1. 发送到传统地址
    2. 发送到 bech32 地址
  2. Bech32 使用统计
  3. 使用 bech32 参考库
  4. 查找 bech32 地址中的拼写错误
  5. 使用原生 segwit 节省费用
  6. Bitcoin Stack Exchange 精选 bech32 问题
  7. 基于 bech32 的其他地址格式
  8. 自动 bech32 支持未来的软分叉
  9. 使用 bech32 地址创建更高效的二维码
  10. Bech32 支持作为能力的代理
  11. 仅支持接收 bech32 的钱包
  12. Bech32 趣闻
  13. 采用速度
  14. 地址安全性
  15. 读取和抄录 bech32 地址
  16. 快速测试清单
  17. 消息签名支持
  18. 支持问题调查
  19. 费用节省的美元价值
  20. Bech32 采用率
  21. BRD 实地报告
  22. 相同费用下更快确认
  23. 来自 segwit 兼容性矩阵的见解
  24. 结论

Bech32 发送支持介绍

最初发布于 Newsletter #38.

Bech32 原生 segwit 地址几乎在两年前首次公开提议,并成为 BIP173 标准。随后,segwit 软分叉于 2017 年 8 月 24 日锁定。然而,在锁定后十七个月,一些流行的钱包和服务仍然不支持向 bech32 地址发送比特币。其他钱包和服务的开发者已经厌倦了等待,希望默认接收 bech32 地址的付款,以便他们可以实现额外的费用节省和隐私改进。Newsletter 希望帮助这个过程,因此从现在到 segwit 锁定的两周年纪念,每一期的 Newsletter 都将包括一个简短的部分,提供资源以帮助全面部署 bech32 发送支持。

请注意,我们仅直接提倡 bech32 发送支持。这允许你支付的对象使用 segwit,但不要求你自己实现 segwit。(如果你想使用 segwit 来节省费用或访问其他好处,那很好!我们只是鼓励你先实现 bech32 发送支持,这样你支付的对象可以立即开始利用它,而你可以在升级其余代码和基础设施以全面支持 segwit 的同时进行。)为此,本周的部分重点展示了发送到传统地址和发送到 bech32 地址之间的差异有多小。

发送到传统地址

对于你已经支持的 P2PKH 传统地址,例如 1B6FkNg199ZbPJWG5zjEiDekrCc2P7MVyC,你的 base58check 库会将其解码为一个 20 字节的承诺:

 6eafa604a503a0bb445ad1f6daa80f162b5605d6

这个承诺被插入到一个 scriptPubKey 模板中:

OP_DUP OP_HASH160 OP_PUSH20 6eafa604a503a0bb445ad1f6daa80f162b5605d6 OP_EQUALVERIFY OP_CHECKSIG

将操作码转换为十六进制,这看起来像:

76a9146eafa604a503a0bb445ad1f6daa80f162b5605d688ac

这被插入到输出的 scriptPubKey 部分,该部分还包括脚本的长度(25 字节)和支付的金额:

     amount                           scriptPubKey
|--------------|  |------------------------------------------------|
00e1f505000000001976a9146eafa604a503a0bb445ad1f6daa80f162b5605d688ac
                |
    size: 0x19 -> 25 bytes

这个输出可以添加到交易中,然后交易被签名并广播。

发送到 bech32 地址

对于等效的 bech32 P2WPKH 地址,例如 bc1qd6h6vp99qwstk3z668md42q0zc44vpwkk824zh,你可以使用其中一个参考库 将地址解码为一对值:

0 6eafa604a503a0bb445ad1f6daa80f162b5605d6

这两个值也被插入到一个 scriptPubKey 模板中。第一个值是见证脚本版本字节,用于使用 OP_0OP_16 的某个操作码将值添加到堆栈中。第二个值是也被推送到堆栈的承诺:

OP_0 OP_PUSH20 6eafa604a503a0bb445ad1f6daa80f162b5605d6

将操作码转换为十六进制,这看起来像:

00146eafa604a503a0bb445ad1f6daa80f162b5605d6

然后,就像之前一样,这被插入到输出的 scriptPubKey 部分:

     amount                        scriptPubKey
|--------------|  |------------------------------------------|
00e1f505000000001600146eafa604a503a0bb445ad1f6daa80f162b5605d6
                |
    size: 0x16 -> 22 bytes

输出被添加到交易中。交易然后被签名并广播。

对于 bech32 P2WSH(P2SH 的 segwit 等效)或未来的 segwit 见证版本,你不需要做任何特殊的事情。见证脚本版本可能是一个不同的数字,需要你使用相应的 OP_0OP_16 操作码,承诺可能是不同的长度(从 2 到 40 字节),但输出的其他部分不会改变。由于长度变化是允许的,确保你的费用估算软件考虑到 scriptPubKey 的实际大小,而不是使用以前根据 P2PKH 或 P2SH 大小计算的常数。

以上是你需要在软件后端进行的全部更改,以启用发送到 bech32 地址。对于大多数平台,这应该是一个非常简单的更改。参见 BIP173参考实现以获取一组测试向量,用于确保你的实现正常工作。

Bech32 使用统计

最初发布于 Newsletter #39.

正如上周所述,实现 segwit 仅花费应当是容易的。然而我们怀疑一些管理者可能会想知道是否有足够多人使用 segwit 来证明他们的团队值得在此方面花费开发精力。本周,我们查看了追踪各种 segwit 采用统计数据的网站,以便你可以决定它是否足够流行,以至于你的钱包或服务如果不支持它会成为异类。

Optech 在我们的仪表盘上追踪 segwit 使用的统计数据;另一个追踪相关统计数据的网站是 P2SH.info。我们看到平均每个区块大约有 200 个输出被发送到原生 segwit 地址 (bech32)。这些输出随后在大约 10% 的比特币交易中被花费。使得涉及原生 segwit 地址的支付比几乎所有的山寨币都更受欢迎。

Optech 仪表盘 segwit 使用统计数据的截图

然而,许多钱包想要使用 segwit 但仍需要处理尚未支持 bech32 发送的服务。这些钱包可以生成一个引用其 segwit 详细信息的 P2SH 地址,这比使用 bech32 效率低,但比不使用 segwit 效率高。由于这些是普通的 P2SH 地址,仅通过查看交易输出我们无法判断哪些 P2SH 地址是预 segwit 的 P2SH 输出,哪些包含嵌套 segwit 承诺,因此我们不知道支付到嵌套 segwit 地址的实际数量。然而,当这些输出之一被花费时,花费者会揭示该输出是否是 segwit。上述统计网站报告,目前大约 37% 的交易包含至少一个嵌套 segwit 输出的花费。这对应于平均每个区块大约 1,400 个输出。

任何支持 P2SH 嵌套 segwit 地址的钱包也可能支持 bech32 原生地址,因此当前由希望利用 bech32 发送支持的钱包进行的交易数量超过 45% 且在不断增加。

为了进一步评估 segwit 的流行度,你可能还想知道哪些值得注意的比特币钱包和服务支持它。为此,我们推荐社区维护的比特币 Wiki 上的 bech32 adoption 页面或由 BRD 钱包维护的 when segwit 页面。

统计数据和兼容性数据显示 segwit 已经得到了良好的支持并且经常使用,但仍有一些值得注意的迟缓者尚未提供支持。我们希望我们的活动和其他社区努力将帮助说服这些迟缓者赶上 bech32 发送支持,以便所有希望利用原生 segwit 的钱包可以在接下来的几个月中做到这一点。

使用 bech32 参考库

最初发布于 Newsletter #40.

前几周,我们讨论了创建传统地址输出与原生 segwit 地址输出之间的差异有多小。在那一部分中,我们简单地指引你参考 bech32 参考库并告诉你会得到两个值。本周,我们详细演示使用 Python 参考库的确切步骤,以便你可以看到工作量有多小。我们从导入库开始:

>>> import segwit_addr

Bech32 地址有一个可读部分 (HRP),用于指示地址所属的网络。这些是地址的前几个字符,并由分隔符 1 与地址的数据部分分开。例如,比特币测试网使用 tb,一个示例测试网地址是 tb1q3w[…]g7a。我们将在代码中设置比特币主网的 HRP bc,以便我们可以确保解析的地址属于我们期望的网络。

>>> HRP='bc'

最后,我们有几个要检查的地址——一个应该有效,两个应该无效。(参见 BIP173 以获取完整的参考测试向量。)

>>> good_address='bc1qd6h6vp99qwstk3z668md42q0zc44vpwkk824zh'
>>> typo_address='bc1qd6h6vp99qwstk3z669md42q0zc44vpwkk824zh'
>>> wrong_network_address='tb1q3wrc5yq9c300jxlfeg7ae76tk9gsx044ucyg7a'

现在我们可以尝试解码每个地址

>>> segwit_addr.decode(HRP, good_address)
(0, [110, 175, 166, 4, 165, 3, 160, 187, 68, 90, 209,
     246, 218, 168, 15, 22, 43, 86, 5, 214])

>>> segwit_addr.decode(HRP, typo_address)
(None, None)

>>> segwit_addr.decode(HRP, wrong_network_address)
(None, None)

如果我们得到的第一个值(见证版本)是 None,则该地址在我们选择的网络上无效。如果发生这种情况,你需要在堆栈上抛出异常,以便与用户交互的过程可以让他们提供正确的地址。如果你得到一个数字和一个数组,则解码成功,校验和有效,长度在允许范围内。

见证版本必须是 0 到 16 之间的数字,因此你需要检查(例如 0 <= x <= 16),然后将其转换为相应的操作码 OP_0OP_16。对于 OP_0,这是 0x00;对于 OP_1OP_16,这是 0x51 到 0x60。然后你需要根据数据的长度添加一个推字节(2 到 40 字节为 0x02 到 0x28),然后将数据作为字节序列附加。Pieter Wuille 的代码很简洁地完成了这一点:

>>> witver, witprog = segwit_addr.decode(HRP, good_address)
>>> bytes([witver + 0x50 if witver else 0, len(witprog)] + witprog).hex()
'00146eafa604a503a0bb445ad1f6daa80f162b5605d6'

这就是你的完整 scriptPubKey。你可以在交易的输出中使用它并发送它。请注意,bech32 scriptPubKeys 的大小可以从 4 到 42 vbytes 不等,因此你需要在费用估算代码中考虑 scriptPubKey 的实际大小。

你的代码不需要用 Python 编写。参考库也提供了 C、C++、Go、Haskell、JavaScript、Ruby 和 Rust 的版本。此外,BIP173 对 bech32 的描述非常详细,以至于任何优秀的程序员都可以在他们首选的语言中从头实现它,而不需要超出大多数编程语言内置和标准库提供的功能。

其他 bech32 发送支持更新: BitGo 宣布他们的 API 现在支持发送到 bech32 地址;请参阅他们的公告以获取有关 bech32 接收支持的更多详细信息。Gemini 交易所本周也显然添加了 bech32 发送支持,并且用户报告称 Gemini 默认接受存款到 bech32 地址。

查找 bech32 地址中的拼写错误

最初发布于 Newsletter #41.

上周的 Newsletter 中,我们使用 Python 参考库对 bech32 进行了演示,将一个地址解码为一个 scriptPubKey,您可以支付给该地址。然而,有时用户提供的地址会包含一个拼写错误。我们建议的代码可以检测到拼写错误,确保您不会支付到错误的地址,但 bech32 也可以帮助检测用户拼写错误的位置。本周,我们将使用 Javascript 示例代码来展示这种功能。

该代码使用 Node.js 风格的模块包含语法编写,所以第一步是将其编译为可以在浏览器中使用的代码。为此,我们需要安装一个 browserify 工具:

sudo apt install node-browserify-lite

然后将其编译为一个独立文件:

browserify-lite ./segwit_addr_ecc.js --outfile bech32-demo.js --standalone segwit_addr_ecc

接着将其包含在我们的 HTML 中:

<script src="bech32-demo.js"></script>

为了方便,我们已在该 Newsletter 的网页版 中包含了该文件,因此您只需打开 Web 浏览器中的开发者控制台,就可以按照本例的其余部分操作。让我们从检查一个有效地址开始。回想一下上周,我们在检查地址时提供了网络标识符(bc 表示 Bitcoin 主网):

>> segwit_addr_ecc.check('bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t4', 'bc')
error: null
program: Array(20) [ 117, 30, 118, … ]
version: 0

如上所示,就像上周一样,我们得到见证版本和见证程序。版本字段的存在,加上没有错误,表明该程序解码时没有任何校验和失败。

现在我们在上面的地址中替换一个字符,并尝试检查它:

>> segwit_addr_ecc.check('bc1qw508d6qejxtdg4y5r4zarvary0c5xw7kv8f3t4', 'bc')
error: "Invalid"
pos: Array [ 21 ]

这次我们得到了错误的描述(地址无效,因为它不匹配其校验和)和一个位置。如果我们将上面的地址逐个字符对齐,可以看到这个“21”标识了特定错误的位置:

                   1x        2x
         0123456789012345678901
>> good='bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t4'
>> typo='bc1qw508d6qejxtdg4y5r4zarvary0c5xw7kv8f3t4'
                              ^

如果我们对拼写错误的地址进行另一次替换并再次尝试会怎样?

>> segwit_addr_ecc.check('bc1qw508d6qejxtdg4y5r4zarvary0c5yw7kv8f3t4', 'bc')
error: "Invalid"
pos: Array [ 32, 21 ]

我们得到两个位置。同样,当我们将这些地址逐个字符对齐,可以看到它识别了两个不正确的字符:

                   1x        2x        3x
         012345678901234567890123456789012
>> good='bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t4'
>> typo='bc1qw508d6qejxtdg4y5r4zarvary0c5yw7kv8f3t4'
                              ^          ^

Pieter Wuille 的互动演示 包含几行额外的代码(在该页面查看源代码以查看该函数),该代码使用拼写错误字符的位置将其以红色突出显示:

bech32 互动演示的截图,拼写错误的地址位于上方

check() 函数能够具体识别的错误数量有限。之后,它仍然可以告诉您地址包含错误,但无法识别地址中的具体位置。在这种情况下,它仍然会返回地址无效,但不会返回位置详细信息:

>> segwit_addr_ecc.check('bc1qw508z6qejxtdg4y5r4zarvary0c5yw7kv8f3t4', 'bc')
error: "Invalid"
pos: null

如果地址存在其他问题,error 字段将设置为更具描述性的信息,这些信息可能会也可能不会包含错误的位置。例如:

>> segwit_addr_ecc.check('bc1zw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t4yolo', 'bc')
error: "Invalid character"
pos: Array [ 43 ]

您可以查看源代码以获取完整的错误列表。

尽管我们在这个迷你教程中花了很多时间研究错误,但我们希望展示在基于 Web 的平台上为用户提供 bech32 地址时提供良好互动反馈是多么容易。我们鼓励您玩转互动演示,以了解如果您使用这种 bech32 地址功能,您的用户可能会看到什么。

使用原生 segwit 节省费用

最初发布于 Newsletter #42.

你的用户和客户希望你实现 Bech32 发送支持的一个原因是它将允许那些支付接收者在重新花费这些钱时节省费用。本周,我们将看看他们能节省多少钱,并讨论他们的节省如何也能帮助你省钱。

对于在第一个版本的 Bitcoin 中实现的传统 P2PKH 地址格式,授权支出的 scriptSig 通常为 107 vbytes。对于 P2SH 包装的 segwit P2WPKH,相同的信息被移动到一个见证数据字段,该字段仅消耗四分之一的 vbytes(27 vbytes),但其 P2SH 开销增加了 23 vbytes,总共 50 vbytes。对于原生 segwit P2WPKH,没有 P2SH 开销,因此仅使用 27 vbytes。

这意味着你可以说 P2SH-P2WPKH 比 P2PKH 节省了超过 50%,而 P2WPKH 比 P2SH-P2WPKH 再节省几乎 50%,或者比 P2PKH 单独节省 75%。然而,支出交易不仅包含 scriptSigs 和见证数据,所以我们通常比较节省的方法是看原型交易。例如,我们想象一个典型的交易,包含一个输入和两个输出(一个给接收者;一个作为找零返回给支出者)。在这种情况下:

  • 支出 P2PKH 的总交易大小为 220 vbytes
  • 支出 P2SH-P2WPKH 的大小为 167 vbytes(节省 24%)
  • 支出 P2WPKH 输出的大小为 141 vbytes(比 P2SH-P2WPKH 节省 16% 或比 P2PKH 节省 35%)

要比较简单的 multisig 交易(那些只使用单个 OP_CHECKMULTSIG 操作码的交易),事情会变得更复杂,因为 k-of-n multisig 输入的大小取决于签名的数量(k)和公钥的数量(n)。所以,为了简单起见,我们只会绘制传统 P2SH-multisig 与包装 P2SH-P2WSH multisig(最多支持 15-of-15 的传统 P2SH)的大小。我们可以看到,切换到 P2SH-P2WSH 可以节省大约 40%(1-of-2 multisig)到大约 70%(15-of-15)。

多重签名交易大小图(P2SH 和 P2SH-P2WSH)

然后我们可以将 P2SH-P2WSH 与原生 P2WSH 比较,看到每个交易额外节省大约 35 字节或大约 5% 到 15%。

多重签名交易大小图(P2SH-P2WSH 和 P2WSH)

上述脚本描述了几乎所有未使用原生 segwit 地址的脚本。(使用更复杂脚本的用户,例如在闪电网络中使用的脚本,今天大多使用原生 segwit。)这些效率较低的脚本类型目前消耗了区块容量(总区块权重)的主要部分。切换到原生 segwit 以减少交易的权重,可以在不改变确认时间的情况下,以相同比例降低费用——其他所有条件相同。

但是其他所有条件并不相同。由于交易使用较少的区块权重,其他交易有更多的权重可用。如果可用区块权重的供应增加,而需求保持不变,我们预计价格会下降(除非它们已经达到默认的最低中继费用)。这意味着更多使用原生 segwit 输入的人不仅降低了那些支出者的费用,也降低了所有创建交易的人的费用——包括支持发送到 Bech32 地址的钱包和服务。

Bitcoin Stack Exchange 精选 bech32 问题

最初发布于 Newsletter #43.

本周我们来看一些来自 Bitcoin Stack Exchange 的最高票数 bech32 问题和答案。这些问题和答案涵盖了自 bech32 大约两年前首次发布以来的所有内容。

  • Schnorr 软分叉是否会引入新的地址格式? 尽管升级到 bech32 发送支持应该很容易,但你可能不希望为 Bitcoin 的下一个升级或之后的升级重复这项工作。Pieter Wuille 通过解释如何在使用基于 Schnorr 的公钥和签名时仍然使用 bech32 地址来回答这个问题。(Optech 将在未来的部分中更详细地讨论这个问题。)

  • 将 bech32 P2WPKH 地址转换为传统 P2PKH 地址是否安全? 如果你阅读了 Newsletter #38,你会注意到对于相同的底层公钥,P2WPKH 和 P2PKH 地址之间的区别仅在于 scriptPubKey 中的几个字符,使得可以自动将一个转换为另一个。Andrew Chow 和其附带评论的答案解释了为什么这是一个糟糕的主意,可能会导致用户丢失资金。

  • 为什么 bech32 解码函数需要指定地址的可读部分 (HRP) 而不是自动提取? HRP 由一个 1 与地址的其他部分分开,所以解码器似乎可以自己忽略该部分。Pieter Wuille 解释说,使用预期的 HRP 调用解码器可以确保你不会意外地将 bitcoin 支付到一个用于测试网、莱特币或其他网络的地址。Gregory Maxwell 还纠正了提问者的另一个假设。

  • 哪些区块浏览器识别 bech32 地址? 在 bech32 首次提出两年多后以及该问题首次被提出一年后,几个流行的区块浏览器仍然不支持 bech32 地址的搜索或显示。这个问题的答案建议任何想了解各种区块浏览器 bech32 支持状态的人应该查看 bech32 采用 Bitcoin Wiki 页面。

基于 bech32 的其他地址格式

最初发布于 Newsletter #44.

常言道:“模仿是最真诚的恭维。” 在本周的部分,我们快速了解了一些使用 bech32 变体的其他系统。如果你已经需要为另一个项目实现类似 bech32 的东西,那么为 Bitcoin 实现它可能值得你花时间。

  • LN 发票 使用带有扩展的可读部分 (HRP) 的 bech32 格式,并且没有 bech32 通常的 90 字符限制。详见 BOLT11 的完整规范。示例: lnbc2500u1pvjluezpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqdq5xysxxatsyp3k7enxv4jsxqzpuaztrnwngzn3kdzw5hydlzf03qdgm2hdq27cqv3agm2awhz5se903vruatfhq77w3ls4evs3ch9zw97j25emudupq63nyw24cg27h2rspfj9srp

  • Bitcoin Cash 新型地址 使用 HRP 为 bitcoincash 和分隔符 : 的 bech32 格式。与 Bitcoin 中的版本字节编码 segwit 见证版本不同,它表示地址编码的哈希应该用于 P2PKH 还是 P2SH。详见 spec-cashaddr 的完整规范。示例: bitcoincash:qpm2qsznhks23z7629mms6s4cwef74vcwvy22gdx6a

  • 备份种子: 2018 年 6 月,Jonas Schnelli 提出了 Bech32X,一种使用 bech32 进行错误校正来编码 Bitcoin 私钥、扩展私钥 (xprivs) 和扩展公钥 (xpubs) 的方案。详见完整的草案规范。示例: pk1lmll7u25wppjn5ghyhgm7kndgjwgphae8lez0gra436mj7ygaptggl447a4xh7

  • 基于 Elements 的侧链: 基于 ElementsProject.org 的侧链,如 Blockstream Liquid,使用 bech32 地址和一种称为 “blech32” 地址的变体。Blech32 地址旨在与该平台的保密资产一起使用,并将很快由 Liquid 侧链的 Esplora 块浏览器支持。我们不知道 blech32 的规范文档,但此代码被标记为参考实现,并在项目其他地方被引用为 “参见 liquid_addr.py 获取与 bech32 的紧凑差异。” blech32 地址示例: lq1qqf8er278e6nyvuwtgf39e6ewvdcnjupn9a86rzpx655y5lhkt0walu3djf9cklkxd3ryld97hu8h3xepw7sh2rlu7q45dcew5

  • 输出脚本描述符: 尽管与 bech32 的关系不那么直接,但基于 bech32 使用的 Bose-Chaudhuri-Hocquenghem (BCH) 码的校验和已被添加到 Bitcoin Core 支持的输出脚本描述符 中。详见 Pieter Wuille 的详细评论。示例: wpkh([f6bb4c63/0'/0'/28']02bf9d38386db60191f2f785cbf7ba90d01bed5958efb7b449a552b89da7550177)#efkksxw6

自动 bech32 支持未来的软分叉

最初发布于 Newsletter #45.

bip-taproot 和 bip-tapscript 对已经实现 Bech32 发送支持或计划实现的人员意味着什么?特别是,如果您尚未实现 Segwit 发送支持,是否应等待新功能激活后再实现?在本周的章节中,我们将展示为何您不应等待,以及现在实现发送支持将来不会增加额外工作量的原因。

Segwit 和 Bech32 的设计者对未来协议改进有一个大致的想法,因此他们设计了 Segwit scriptPubKeys 和 Bech32 地址格式,以便与这些预期的改进向前兼容。例如,支持 Taproot 的地址可能如下所示:

bc1pqzkqvpm76ewe20lcacq740p054at9sv7vxs0jn2u0r90af0k63332hva8pt

您会注意到它看起来就像您见过的其他 Bech32 地址——因为它确实如此。您可以使用我们在 Newsletter #40 中提供的相同代码(使用 Bech32 参考库的 Python)来解码它。

>> import segwit_addr
>> address='bc1pqzkqvpm76ewe20lcacq740p054at9sv7vxs0jn2u0r90af0k63332hva8pt'
>> witver, witprog = segwit_addr.decode('bc', address)
>> witver
1
>> bytes(witprog).hex()
'00ac06077ed65d953ff8ee01eabc2fa57ab2c19e61a0f94d5c78cafea5f6d46315'

与我们在以前的 Newsletters 中显示的解码 Bech32 地址的区别在于,这个假设的 Taproot 地址使用 1 的见证版本,而不是 0(意味着 scriptPubKey 将以 OP_1 开头,而不是 OP_0),并且见证程序比 P2WSH 见证程序长一个字节。然而,如果您只是支出,这些对您的软件来说并不重要。我们可以使用 Newsletter #40 中的相同示例代码来创建适合您支付的 scriptPubKey:

>> bytes([witver + 0x50 if witver else 0, len(witprog)] + witprog).hex()
'512100ac06077ed65d953ff8ee01eabc2fa57ab2c19e61a0f94d5c78cafea5f6d46315'

这意味着任何以 Newsletter #40 中描述的通用方式实现 Bech32 支持的人都不需要做任何特殊的事情来支持未来的脚本升级。简而言之,您现在提供 Bech32 发送支持的工作将来在比特币协议的预期变化部署时无需重复。

使用 bech32 地址创建更高效的二维码

最初发布于 Newsletter #46.

BIP173 禁止 bech32 地址使用大小写混合。书写 bech32 地址的首选方式是全小写,但在某种情况下,全大写是有意义的:二维码。看看以下两个二维码,虽然它们的地址相同,但一个是小写,另一个是大写:

bech32 全大写

这是 bech32 的一个故意设计特性。二维码可以用多种模式创建,这些模式支持不同的字符集。二进制模式字符集用于传统地址,因为它们需要混合大小写。然而,比特币的 Bech32 地址1 只使用数字和大写字母表示,因此它们可以使用较小的大写字母和数字字符集。因为这个字符集较小,它在二维码中对每个字符编码所使用的位更少,使得生成的二维码不那么复杂。

比特币地址通常在 BIP21 URI 中使用。BIP21 技术上要求 base58check 格式化,但至少一些支持原生 segwit 地址的钱包(如 Bitcoin Core)允许它们与 bech32 一起使用。虽然 BIP21 的方案标识符 bitcoin: 的首选形式是小写,但根据 BIP21 和 RFC3986 的规定,它也可以大写。这也产生了一个不那么复杂的图像(尽管在这种情况下,我们的二维码编码库给了两个图像相同的尺寸)。

bech32 全大写

不幸的是,BIP21 URI 中传递附加参数所需的 ?& 不属于二维码的大写字符集,因此只能为这些字符使用二进制模式。此外,BIP21 规定查询参数名称如 amountlabel 是区分大小写的,所以它们的大写版本预计也不能正常工作。

然而,二维码可以支持混合字符集,当使用一个以全大写子字符串(包含 bech32 地址)开头或结尾的字符串时,这样做总是至少稍微更有效率。这是因为 bech32 地址的最小允许大小(14 个字符)与使用大写模式的效率增益(31.25%)相结合,超过了切换模式的最坏情况开销(20 个额外的比特)。我们知道的至少有两个二维码编码器 libqrencode(C 语言)和 node-qrcode(JS 语言),默认情况下会根据需要自动混合字符集,以生成最不复杂的二维码:

BIP21/bech32 混合字符模式

总之,当在二维码中使用 bech32 地址时,考虑将它们和任何其他可以大写的相邻字符大写,以生成更小且不太复杂的二维码。(然而,出于其他所有目的,bech32 地址应使用全小写字符。)

更正:本节的早期版本声称包含 BIP21 查询参数的二维码需要使用二进制模式。Nadav Ivgi 友好地通知我们,可以混合字符模式,因此我们已相应更新了本节的最后两段。

Bech32 支持作为能力的代理

最初发布于 Newsletter #47.

到目前为止,在我们鼓励钱包和服务支持发送到 Bech32 原生 Segwit 地址的系列文章中,我们几乎完全专注于技术信息。今天,这部分内容表达了一个观点:你延迟实现 Bech32 发送支持的时间越长,一些用户和潜在用户对你的软件或服务的评价就会越差。

“他们只能支付到传统地址。”
“哦。那我们找一个支持当前技术的服务吧。”

仅支持传统地址的服务很可能会向用户传达出一个信号,即在维护其比特币集成方面付出的开发努力最少。我们预计,这会向用户传递出与2019年一个充满 Shockwave/Adobe Flash 元素并声称在 Internet Explorer 7 中最佳浏览的网站相同的信息(或参考 Gregory Maxwell 写的更具想象力的比较)。

Bech32 发送并不是一些仍需测试的实验性新技术——原生 Segwit 未花费输出目前持有超过 200,000 个比特币. Bech32 发送也是易于实现的(参见 Newsletter #38#40)。最重要的是,随着越来越多的钱包和服务默认升级为 Bech32 接收,不提供发送支持的服务将显得落后。

如果你还没有实现 Bech32 发送支持,我们建议你在 2019 年 8 月 24 日之前尝试实现(Segwit 激活的两周年)。不久之后,Bitcoin Core 的下一个版本预计将在其 GUI 和可能的 API 方法中默认使用 Bech32 接收地址(参见 Newsletter #40#42)。我们预计其他钱包也会这样做——除了那些已经默认 Bech32(甚至是唯一支持的地址格式)的钱包。

仅支持接收 bech32 的钱包

最初发布于 Newsletter #48.

上周在 Newsletter #47 中,我们描述了不升级支持 bech32 发送功能的一个成本——用户可能会认为你的服务已经过时,从而寻找替代服务。本周,我们将探讨这一论点的更强形式:那些已经只能接收 bech32 地址的钱包。如果这些钱包的用户想要接收付款或从你的服务中提现,而你的服务尚未支持发送到 bech32 地址,他们要么不得不使用第二个钱包,要么不得不使用你的竞争对手。

  • Trust wallet 是一个相对较新的专有钱包,由币安加密货币交易所拥有,兼容 Android 和 iOS。作为一个新钱包,他们不需要实现旧地址接收支持,所以他们只实现了 segwit。这使得 bech32 成为发送比特币到该钱包的唯一支持方式。
  • Electrum 是一个流行的桌面和移动钱包。在创建新钱包种子时,你可以选择传统钱包和 segwit 钱包,segwit 是当前默认选项。选择 segwit 钱包种子的用户将只能生成 bech32 地址以接收付款。Electrum 警告用户,这可能会导致与尚未升级到 bech32 发送支持的软件和服务的兼容性问题:

    Electrum 中允许用户选择地址类型并警告他们某些服务可能不支持 bech32 地址的对话框

    请注意,钱包作者既不需要也不推荐为了支持新的地址格式而创建新种子。其他钱包,如 Bitcoin Core 0.16.0 及以上版本,可以从同一个种子生成传统、p2sh-segwit 和 bech32 地址——用户只需要指定他们想要的地址类型(如果他们不想要默认的)。

随着时间的推移,我们预计会有更多的新钱包只实现接收当前最佳地址格式。今天,这意味着使用 bech32 的 P2WPKH 和 P2WSH 的 v0 segwit 地址,但如果 Taproot 被采用,它将使用同样使用 bech32 的 v1 segwit 地址。news45 bech32。你的服务延迟实现 bech32 发送支持的时间越长,你就越有可能因为客户无法使用他们喜欢的钱包请求付款而失去客户。

Bech32 趣闻

最初发布于 Newsletter #49.

这个部分标志着 Bech32 系列已过半,因此我们决定在本周介绍一些与 Bech32 相关的趣闻,这些趣闻有趣但不足以独立成段。

  • Bech32 的发音是什么? 提案的共同作者 Pieter Wuille 使用轻声的“ch”,因此这个词听起来像 “besh thirty two”。这个名字是一个混合词,将地址的纠错编码(BCH)的字母与其数值基(base32)的名称结合起来。将其与轻声“ch”发音,使得 bech32 的第一个音节与比特币的传统地址格式 base58 相似。我们承认,这种详细解释破坏了这个笑话,但这是一个巧妙而有趣的文字游戏。

  • BCH 与 Bitcoin Cash 的代码无关: Bech32 所基于的 BCH 代码名称是 Bose-Chaudhuri-Hocquenghem 的缩写,Hocquenghem 于 1959 年发明了这种类型的循环码,随后 Bose 和 Ray-Chaudhuri 在 1960 年独立重新发现了它们。此外,Bech32 地址格式在 2017 年 3 月宣布,比后来被称为 Bitcoin Cash 的计划(最初计划使用代码 BCC)的首次计划早了三个月。

  • 消耗超过十年的 CPU 计算时间: 使用现有的关于 BCH 代码的信息,Bech32 的作者能够找到为比特币地址提供他们所需的最低错误检测能力的代码集。然而,这个集合中有近 16 万个符合条件的代码,作者预计其中有些代码会比其他的更好。为了在其中找到最优的代码,使用了超过 200 个 CPU 核心和超过 10 年的计算时间

采用速度

最初发布于 Newsletter #50.

Bech32 地址并不是比特币用户第一次改变地址格式。在 2012 年 4 月,P2SH 地址(以 3 开头)被引入,并最终在大约 25% 的交易输出中得到使用。本周,我们将查看这两种不同地址格式的相对采用速度。由于我们稍后将描述的原因,这不能算作一个完全公平的比较,但它可以为我们提供一个大致的指导,看看我们在 Bech32 采用方面迄今为止的表现如何。

我们首先来看一下从每个提案在主网激活的那一天起,发送到 P2SH 或原生 Segwit(Bech32)地址的每个区块的输出百分比。本节中的所有图表均以 30 天的简单移动平均值计算。我们还将 P2SH 图表中的数据点限制在 Segwit 激活前约两个月,以便几乎没有 P2SH 包裹的 Segwit 输出被误算为传统 P2SH。

P2SH 采用速度与原生 Segwit 的比较。Segwit 线是 P2WPKH 和 P2WSH 的总和

上图中有一个特别不公平的方面是,P2SH 主要对高级脚本(如多重签名)有用。对于使用单签名地址(以 1 开头)的人来说,没有必要也没有好处升级到 P2SH。相比之下,原生 Segwit 地址既有针对单签名用户的(P2WPKH),也有针对高级脚本用户的(P2WSH)。为了使这种比较更加公平,下图在较小的日期范围内将原生 Segwit 的两种用途分开,这样您可以将 Bech32 P2WSH 的使用与其大致相当的 P2SH 进行比较。

P2SH 采用速度与原生 Segwit 的比较。分别展示 P2WPKH 和 P2WSH 的数据

值得注意的是,到目前为止,几乎所有原生 Segwit 地址的使用都集中在单签名的 P2WPKH 上。Segwit 激活前的 P2SH 活动曾达到所有输出的 25% 的峰值,但这些活动并没有迁移到原生 P2WSH 输出。事实上,当我们考虑到所有闪电网络(LN)的存款交易(以及至少一些其他链上 LN 交易)都在使用原生 P2WSH 输出时,似乎几乎没有 2017 年末的 P2SH 活动转移到 P2WSH。

这也指出了使不同地址数据难以比较的另一个方面:使用传统 P2SH 所能实现的所有功能,也都可以使用 P2SH 包裹的 Segwit 地址或原生 P2WSH 地址来实现。P2SH 包裹的 Segwit 地址具有向后兼容性,并且可以显著减少交易费用,而 Bech32 地址与旧钱包不兼容,与 P2SH 包裹的 Segwit 相比,仅能节省一小部分固定的额外费用。这可能使高级脚本用户在短期内没有足够的动力从 P2SH 包裹的 Segwit 地址切换到原生 Segwit 地址。

总体来看,图表似乎表明 P2SH 地址花了大约三年的时间才真正开始普及,而 Bech32 地址在 Segwit 软分叉激活后仅几个月内就已经取得了成功。随着一些钱包已经默认采用 Bech32,更多钱包计划在未来几个月内也将如此,我们预计在 2019 年底前会看到采用率的进一步提高。

地址安全性

最初发布于 Newsletter #51.

有一类多重签名用户不仅通过使用 bech32 地址来节省费用,还因提高了对一种称为哈希碰撞的潜在攻击的安全性而受益。这类用户包括许多交易所和其他企业用户。

为了提供一些背景信息,目前比特币上的所有常见单签名地址都是通过将公钥转换为 160 位的 RIPEMD160 哈希摘要而生成的。理论上,攻击者可以生成第二个他们控制的公钥,对其进行哈希并生成相同的地址。然而,如果我们假设哈希函数产生的输出是完全不可预测的,那么使用像 RIPEMD160 这样的 160 位哈希函数时,每次攻击者尝试生成哈希碰撞的成功概率为 1/2160

作为比较,当前比特币矿工大约每 5 小时执行 280 次哈希操作。虽然矿工执行的 SHA256d 哈希操作与此 RIPEMD160 碰撞攻击使用的哈希操作不同,因此他们的设备无法被重新利用来进行这种攻击,但我们可以用此作为一个参考,以了解当前真实世界系统可以执行的暴力破解操作数量(虽然代价昂贵)。按此速度,执行 2159 次操作以获得 50% 成功率的攻击将需要大约 2500 万倍于宇宙至今的估计年龄的时间。

然而,当使用多重签名地址时,攻击者可能是参与生成地址的一方,因此可能有能力操纵最终选择的地址。例如,Bob 将他的公钥发送给 Mallory,期望 Mallory 也将她的公钥返回给他。然后他们各自将公钥放入一个多重签名脚本模板中,哈希成一个地址,并有人将资金存入该地址。

然而,Mallory 采用了脚本模板和 Bob 的公钥,在没有告知 Bob 的情况下插入了她的一个公钥,并将其哈希成一个地址。这样,Mallory 可以在她承诺使用该公钥之前查看 Bob 将接受的地址。然后,Mallory 可以将该地址与她的数据库中仅支付给她的脚本生成的地址进行比较。如果两个地址匹配(碰撞),她就将公钥返回给 Bob,等待资金存入该地址,然后使用她数据库中的脚本来窃取资金。如果没有匹配,Mallory 可以一次又一次地尝试不同的公钥,直到她成功(假设她有无限的时间和资源)。

虽然这似乎与之前描述的具有每次尝试 1/2160 成功概率的暴力攻击类似,但我们必须考虑 Mallory 数据库的大小。如果我们假设该数据库有 100 个地址,那么她每次尝试不同的公钥时的成功概率为 100/2160,因为只要它与 Mallory 数据库中的任何一个地址匹配就可以成功。

这种攻击类型称为碰撞攻击。碰撞攻击有几种在 CPU/内存方面进行权衡的算法,但安全研究人员遵循的通用规则是,针对完美哈希函数的碰撞攻击将其安全性降低到其组合数的平方根,即将其位数减半。这意味着我们可以大致认为 RIPEMD160 的安全性降低到 80 位——这与我们之前提到的当前技术下比特币矿工每 5 小时执行的操作次数相同。再一次,比特币挖矿设备无法用于此攻击,并且攻击者设计和构建足够的定制设备以在五小时内找到碰撞可能需要花费数十亿美元——但这是一个理论上可能的攻击,应该引起那些在 P2SH 中存储大量价值的人的关注,尤其是在定制硬件变得更快和更便宜的时候。此外,RIPEMD160 函数中可能存在的弱点也可能导致有更简单和更便宜的碰撞攻击变体。

可以设计多重签名设置协议,使其没有这个问题,从而保持 160 位的碰撞抵抗性。然而,segwit 的开发者认为,对于 segwit 的 P2SH 类似物——P2WSH——使用稍长的哈希函数更为合适,这样用户就不需要担心这些密码学细节。因此,segwit P2WSH 使用了比特币中其他地方使用的相同的 SHA256d 函数,该函数为单方情况下提供 256 位安全性,为多方情况下提供 128 位的最差安全性。为了继续我们的粗略比较,如果我们认为 80 位相当于五小时的比特币挖矿,那么 128 位相当于 1600 亿年的挖矿。

在我们总结这一部分之前,我们想确保几件事情是清楚的:

  1. 我们认为目前没有人能够执行所描述的攻击(但我们不能排除这种风险)。

  2. 该攻击只能在创建地址时使用(尽管实际盗窃可能会在很长时间后发生)。

  3. 该攻击仅适用于多方多重签名地址。如果你是使用仅自己信任设备的 P2SH 多重签名单方用户,或者你使用的是 P2SH-P2WPKH(单签名地址),则你不会受到此攻击的威胁。

  4. 该攻击适用于 P2SH 包装的 segwit P2WSH 地址以及常规 P2SH 地址。要消除风险,你必须使用原生 segwit(bech32)地址或安全的密钥交换协议。

总结来说,希望获得最高安全性的多方多重签名用户应该迁移到 bech32 P2WSH 地址,以利用其额外的碰撞抵抗性。随着用户进行这种迁移,各服务确保他们实现 bech32 发送支持以便能够向这些注重安全的用户发送支付将变得更加重要。

读取和抄录 bech32 地址

最初发布于 Newsletter #52.

在 Segwit 激活之前,开发者讨论了使用何种格式来表示原生 Segwit 地址,有些开发者建议这是一个让地址更易于阅读和转录的机会。开发者 Gregory Maxwell 有效地表达了这一观点,他询问其他开发者打电话给他,并尝试通过电话成功传达一个混合大小写的传统 base58check 地址。如果在传达过程中仅有一个字符出错——甚至只是那个字符是大写还是小写——双方都需要回过头来仔细查找错误。

BIP173 的 Bech32 地址能够解决这两个问题。它们仅使用单一大小写(通常优先使用小写字母,但在使用二维码时可以使用大写字母以提高效率),并且它们使用带有校验码的错误校正码,可以帮助用户定位错误,同时确保在绝大多数情况下能够捕捉到输入错误。

然而,随着钱包和服务考虑升级以支持 Bech32 地址的发送和接收,我们认为值得提醒任何犹豫的实施者注意 Bech32 地址的这一关键用户收益功能——因此我们自动化了 Maxwell 旧电话测试的一部分,以便您私下评估转录传统地址和原生 Segwit 地址的相对难度。

如果您点击以下链接(在新标签页中打开),您会发现两个支付相同哈希值的地址的录音。您可以将地址输入到下面相应的框中,如果输入了错误的字符(区分大小写),框会立即变红。注意:为了提高准确性并消除特定语言环境的字母发音问题,我们在文件中使用音标字母读取每个字母,例如 Alfa 代表 ABravo 代表 B,等等。

Base58check 和 Bech32 地址的朗读(1 分 33 秒)

传统 base58check 地址:

原生 Segwit Bech32 地址:

如果您发现 Bech32 地址更容易准确转录,这意味着 Bech32 设计者在实现新地址格式的一个目标上取得了成功。发现 Bech32 优势的用户更可能希望在需要读取或转录地址的情况下使用 Bech32 地址,因此如果您的软件或服务支持发送到 Bech32 地址,他们将更倾向于使用它。

快速测试清单

最初发布于 Newsletter #53.

最近,一位 Optech 贡献者对许多流行的钱包和比特币交易所进行了调查,以了解它们支持哪些技术功能。对于其中一家交易所,他最初记录其支持发送到 bech32 地址,但后来发现其支持并不完全。

问题在于,该交易所支持 P2WPKH bech32 地址(单签名地址),但不支持 P2WSH bech32 地址(多重签名和复杂脚本地址)。另一个问题是,该交易所接受全小写的 bech32 地址,但不接受全大写的 bech32 地址。而另一家交易所则限制了地址表单字段的长度,以至于无法容纳所有有效的 bech32 地址。

考虑到这些问题,我们创建了一个简短的检查清单,用于测试基本的 bech32 发送支持。请仅使用少量比特币执行这些测试,以防万一出现问题时不会造成大的损失。

  1. 生成两个自己的地址,一个用于 P2WPKH,另一个用于 P2WSH。例如,使用 Bitcoin Core、jq JSON 解析器和 Bash shell,你可以运行以下命令:

      $ p2wpkh=$( bitcoin-cli getnewaddress "bech32 test" bech32 )
      $ p2wsh=$(
        bitcoin-cli addmultisigaddress 1 \[$(
          bitcoin-cli getaddressinfo $p2wpkh | jq .pubkey
        )\] "bech32 test" bech32 | jq -r .address
      )
      $ echo $p2wpkh $p2wsh
      $ echo $p2wpkh $p2wsh | tr '[a-z]' '[A-Z]'
    
  2. 使用你的软件或服务的常规支出或提现表单,测试向每个小写地址发送比特币。

  3. 使用每个地址的大写形式再次测试(这些对于二维码很有用)。

  4. 通过检查你用于创建地址的钱包或区块浏览器,确保你收到了资金。如果一切正常,你的软件完全支持当前的 bech32 支付地址。

    如果你使用了临时的 Bitcoin Core 钱包创建了地址,可以等待交易确认后,使用以下命令将所有资金发送到你的常规钱包:bitcoin-cli sendtoaddress YOUR_ADDRESS $( bitcoin-cli getbalance ) '' '' true

对于不实际尝试发送资金的单元测试,或在 testnet 或回归测试模式下发送资金的集成测试,BIP173 提供了更全面的测试向量

消息签名支持

最初发布于 Newsletter #54.

正如我们在本系列早前部分中展示的那样,Bech32 地址在几乎所有方面都优于传统地址——它们允许用户节省费用更容易转录可以定位地址输入错误,并且在 QR 码中更高效。然而,有一个特性是传统 P2PKH 地址支持但原生 Segwit 钱包尚未广泛支持的——消息签名功能。为了全面披露信息,并希望能激励钱包开发者采取行动,我们将探讨 Bech32 地址支持中缺失的这一部分。

作为背景,许多钱包允许用户使用传统的 P2PKH 地址,通过与该地址相关联的私钥签署任意文本消息:

$ bitcoin-cli getnewaddress "" legacy
125DTdGU5koq3YfAnA5GNqGfC8r1AZR2eh

$ bitcoin-cli signmessage 125DTdGU5koq3YfAnA5GNqGfC8r1AZR2eh Test
IJPKKyC/eFmYsUxaJx9yYfnZkm8aTjoN3iv19iZuWx7PUToF53pnQFP4CrMm0HtW1Nn0Jcm95Le/yJeTrxJwgxU=

不幸的是,目前没有广泛实施的方式可以为传统 P2SH、P2SH 封装的 Segwit 或原生 Segwit 地址创建签名消息。在 Bitcoin Core 和许多其他钱包中,尝试使用除传统 P2PKH 地址以外的任何地址都会失败:

$ bitcoin-cli getnewaddress "" bech32
bc1qmhtn8x34yq9t7rvw9x6kqx73vutqq2wrxawjc8

$ bitcoin-cli signmessage bc1qmhtn8x34yq9t7rvw9x6kqx73vutqq2wrxawjc8 Test
error code: -3
error message:
Address does not refer to key

一些钱包确实支持 Segwit 地址的消息签名——但采用了非标准化的方法。例如,TrezorElectrum 钱包分别提供了对 P2WPKH 和 P2SH 封装的 P2WPKH 地址的消息签名支持。然而,这两个实现是独立完成的,并且使用了略有不同的协议,因此它们无法验证由另一个系统生成的签名。此外,我们所知的所有钱包所使用的算法都无法轻松适配用于多签名和其他高级限制的 P2SH 和 P2WSH 脚本。这意味着目前的消息签名功能普遍仅限于单签名地址的用户。

有一个提议的标准应允许任何地址类型或脚本用于创建签名消息,BIP322. 该协议甚至应与未来的 Segwit 版本向前兼容,例如 bip-taprootbip-tapscript(但存在一些与时间锁相关的未解决限制)。不幸的是,尽管该提议在一年多前首次提出(参见 Newsletter #13),但至今仍没有任何实现——甚至没有一个正在审查中的提议实现。

这使得 Segwit 用户无法获得与传统地址用户相同级别的消息签名支持,这可能是某些用户不愿意迁移到 Segwit 地址的原因之一。除了钱包放弃消息签名支持外,唯一的解决方案是钱包开发者就一个标准达成一致并广泛实施该标准。

支持问题调查

最初发布于 Newsletter #55.

我们从钱包提供商那里听说,他们对默认接收 bech32 地址的犹豫原因之一是担心会显著增加客户支持请求。尽管如此,一些钱包已经默认使用 bech32 地址,其他一些钱包也计划很快开始使用,例如 Bitcoin Core

我们向包括 BitGo、BRD、Conio、Electrum 和 Gemini 在内的多家服务商征求了关于使用 bech32 地址对客户支持负担的意见。大多数服务商报告称问题很少(“没有支持请求”和“没有太多困惑”)。

有一家服务商表示:“与比特币地址相关的客户支持票增加了 50%,但票数绝对数量如此之小,可能无法给出太多意义。在 Bech32 出现之前或之后,这个话题的票数从来都不多,所以不确定这是否是让交易所转换的一个重要论点。相反,我可能建议关注费用问题,如果你使用的是旧的钱包实现,费用确实可能会累积。”

Electrum 也确实看到了一些公开的报告,例如[“奇怪的地址”]和[“Localbitcoins 不支持发送到 bech32”]。

尽管结论尚不明确,但令人欣慰的是,选择支持接收 bech32 地址的服务并没有对其客户支持团队产生负面影响。上面关于考虑费用节省的建议可能远远超过了这些担忧,并且与 Bitcoin Optech 的指导一致。鉴于少数负面报告和支持接收 bech32 地址的钱包和服务可以显著节省费用,可能是时候让更多的钱包开始将 bech32 作为默认地址格式。如果这种情况发生,那么其他钱包和服务支持发送到 bech32 地址将变得更加重要。

费用节省的美元价值

最初发布于 Newsletter #56.

截至本文撰写时,比特币的价格在过去几个月中已迅速上涨。比特币价格的显著变化在 Bech32 的上下文中是值得注意的,因为交易费用是以比特币计价,而非美元计价。这意味着,即使费率保持不变,发送交易的实际成本也会随着比特币价格的上涨而增加。

我们之前讨论过用户和服务通过切换到原生 Segwit (Bech32) 地址可以节省多少,但我们仅从虚拟字节(vbyte)和百分比节省的角度进行描述。在本节 Bech32 发送支持中,我们将以实际价值来探讨节省的情况。

最低的实用费用为 0.00000001 BTC/vbyte。到目前为止,最高的典型费用是在 2017 年 12 月和 2018 年 1 月达到的 0.00001000 BTC/vbyte。对于该范围,以下图表显示了两个常见交易模板(单签名和 2-of-3 多重签名)的用户可能节省的金额:

单签名传统 P2PKH 与 Segwit P2WPKH 的对比

2-of-3 多重签名传统 P2SH 与 Segwit P2WSH 的对比

对于使用其他交易模板的传统交易用户,您可以通过将您发送的典型交易的 txid 粘贴到诸如 Esplora 区块浏览器(如 Blockstream.info)之类的信息网站中,粗略了解您将节省的百分比。您可以将该百分比乘以交易的虚拟字节大小,查看您将节省多少虚拟字节。请注意,使用第三方服务会暴露您对该交易的兴趣,可能会显著降低您的隐私。您可以通过检查您的一笔典型交易来私下获取大致的虚拟字节节省量。2 当您知道将节省多少虚拟字节时,您可以通过将节省的虚拟字节数乘以您的预期费率(以 BTC/vbyte 计)和您预期的比特币价格,计算出以另一种货币计的节省金额,例如 saved_vbytes * feerate * price

如果原生 Segwit 的用户每笔交易开始节省几十到几百美元,我们预计将对高频支出者(如交易所)产生更大的竞争压力,促使他们迁移到只接受使用 Bech32 地址的存款。鉴于每日比特币交易中有很大比例是存款到交易所,我们预计不提供 Bech32 发送支持的钱包和服务将很快失去用户的青睐。

  1. 使用工具解析交易。Bitcoin Core 附带了一个名为 bitcoin-tx 的工具可以为您完成此操作。运行 bitcoin-tx -json <hex_serialized_tx>

  2. 汇总所有 scriptSig 的总大小。bitcoin-tx 输出每个 scriptSig 的十六进制表示,将此十六进制字符串长度减半即可得出 scriptSig 的大小。

  3. 将 scriptSig 的大小乘以 0.75 即可得到节省的虚拟字节数。

Bech32 采用率

最初发布于 Newsletter #57.

在过去的一年中(大约 50,000 个区块),以百分比计算,原生 segwit 输出(即支付到 bech32 地址的交易数量)略有下降。一个明显的解释是,人们尝试了 bech32 地址,但不喜欢,随后又回到使用传统地址或 P2SH 包裹的 segwit 地址。如果情况确实如此,我们是否应该放弃 bech32 地址?

支付给原生 segwit(bech32)输出的所有交易的百分比

注意:本节中的所有图表都使用简单移动平均值计算,范围为 10,000 个区块。

或许我们应该首先调查是否存在其他解释。百分比是合成结果——通过结合多个其他来源的信息得出——所以我们首先要看的是原始数据。下图显示了过去两年中支付给各种类型脚本的输出总数:

支付给 P2PKH、P2SH、bech32 和 nulldata 的所有交易的总数

与百分比数据相反,我们看到 P2WPKH 输出的数量在缓慢(但稳定)增加。我们还看到其他输出的数量也在增加。如果将总数堆叠在一起,这可能会变得更加清晰:

支付给 P2PKH、P2SH、bech32 和 nulldata 的所有交易总数的堆叠图

现在我们可以看到,今天的平均输出数量略高于 2017 年底和 2018 年初比特币活动高峰时的数量。这是有道理的——使用 segwit 的交易越多,留给其他交易的区块空间就越多。然而,尽管整体增加,但支付输出(P2PKH、P2SH 和原生 segwit)的数量略有减少。剩余的输出显示出 OP_RETURN(nulldata)脚本数量的显著增加。

输出总数和 nulldata 输出数量的增长解释了为什么尽管绝对数量在增加,但 bech32 输出的百分比却在下降。这意味着我们不应该放弃 bech32。事实上,近期输出总数的激增(以及其他可在其他地方获取的数据)可能是即将到来的费率上涨的信号,这将鼓励更多用户和组织通过切换到 bech32 来降低成本。

BRD 实地报告

最初发布于 Newsletter #58.

以下案例研究由 Optech 成员公司 BRD 提供,描述了他们在为其钱包实现 bech32 和其他 segwit 技术时的经验。

我们于 2018 年 1 月开始在 BRD 钱包中实现 bech32 支持,在 breadwallet-core添加了 bech32 解码和编码支持。breadwallet-core 是一个 MIT 许可的跨平台 C 库,无需外部依赖。我们所有的软件都尽可能避免第三方库的依赖,目前只使用 Pieter Wuille 的 libsecp256k1. 最小化依赖是高安全性加密项目的典型做法。对于 bech32 的实现,我们发现 BIP173 文档非常完善,因此没有遇到复杂的具体问题。

2018 年 3 月,breadwallet-core 更新以自动解析作为比特币地址提供的任何内容,判断它是传统 P2PKH、传统 P2SH 还是 segwit bech32,并自动为每种情况生成相应的 scriptPubKey。这使得 BRD 开始支持向 bech32 地址发送比特币。最终在 2018 年 10 月,我们在整个库的后端和移动应用前端实现了完整的 segwit 支持,允许用户开始接收 bech32 地址,同时将所有找零地址默认设置为 bech32。

我们从未实现对 P2SH 封装的 segwit 地址的接收支持,而是直接使用 bech32。这是为了更好地优化 BRD 用于扫描影响用户钱包交易的布隆过滤器机制。为了允许用户跟踪他们何时收到付款,布隆过滤器会与 scriptPubKey 中的每个数据元素进行匹配。对于给定的公钥,scriptPubKey 中的数据元素在传统 P2PKH 和原生 segwit (bech32) P2WPKH 中是相同的。以下是 Optech 之前使用的一个示例:

  • 地址 1B6FkNg199ZbPJWG5zjEiDekrCc2P7MVyC 的传统 P2PKH scriptPubKey:

    OP_DUP OP_HASH160 OP_PUSH20 6eafa604a503a0bb445ad1f6daa80f162b5605d6 OP_EQUALVERIFY OP_CHECKSIG
  • 地址 bc1qd6h6vp99qwstk3z668md42q0zc44vpwkk824zh 的原生 segwit (bech32) P2WPKH scriptPubKey:

    OP_0 OP_PUSH20 6eafa604a503a0bb445ad1f6daa80f162b5605d6

由于针对给定元素的布隆过滤器将匹配同一公钥的 P2PKH 和 P2WPKH 地址,BRD 能够以零额外开销的方式扫描这两种支付类型。这也使得实现更加简洁,不会增加提供布隆过滤器服务的公共节点的资源使用。这对于使用其他类型扫描的钱包和服务来说,可能也是一种有价值的优化,因为这可能比 BIP84 推荐的单独 HD 派生路径产生更少的开销。

由 bech32 地址生成的 scriptPubKey 长度各不相同,这会影响需要支付的交易费金额。比特币的手续费计算非常复杂——有时费率在 24 小时内会猛增几个数量级——但这在 segwit 之前已经是事实,所以我们以前花了很多时间在手续费计算上,并使其尽可能灵活。这意味着由 bech32 地址生成的 scriptPubKey 大小的变化不会对 BRD 造成影响。

我们希望今天的应用程序能够适应未来,因此代码支持发送到未来的 segwit 版本(请参阅 Optech 的描述)。这意味着,例如,如果比特币用户选择通过软分叉更改共识规则,BRD 将自动支持支付到 taproot 地址。

一旦真正的势头建立起来,且大多数其他钱包和服务支持发送到 bech32 地址,BRD 的 bech32 接收支持将作为默认设置推广给我们的所有用户。为准备这一过渡,尽可能多的公司和服务自愿支持 bech32 发送能力非常重要。为了推动采用,我们创建了 WhenSegwit 网站并成为 Optech 成员公司。我们希望其他钱包和服务在手续费相对较低时尽快实现完整的 segwit 支持。

相同费用下更快确认

最初发布于 Newsletter #59.

我们经常提到使用 segwit 输入可以节省手续费,但从未提到您不必利用这些节省。如果您愿意,您可以支付与未使用 segwit 时相同的费用,从而在其他条件相同的情况下让您的交易更快得到确认。对于某些用户,例如试图在交易所间进行套利的交易员来说,省钱可能没有以相同费用更快确认交易来得重要。

为了研究这一想法,让我们首先生成一个使用 Bitcoin Core 手续费估算工具的图表,展示创建典型单签名、单输入、双输出交易时的潜在手续费节省:

按预估手续费以美元计费,Y=手续费,X=确认目标

我们看到,通常来说,支付的费用越少,交易的确认时间就越长——但 segwit 用户可以常常为相同的等待时间支付更少的手续费。现在让我们简单地重新排列这些数据的坐标轴:

按预估手续费以美元计费,Y=确认目标,X=手续费

对于相同的手续费,预计 segwit 用户有时会比传统用户等待更短的确认时间,其中原生 segwit 用户获得的优势最大。在这些估算中,为相同总手续费支付的不同交易类型的确认速度差异可能超过 6 个区块(平均大约一个小时)。

对于那些每笔交易愿意支付的手续费有固定上限的用户和组织来说,使用 segwit 可以显著减少其交易在高活动期间的确认时间。尽管这一优势仅惠及使用 bech32 和其他 segwit 地址支付的人,但这是另一个预计用户和组织将在不久的将来越来越多地要求您的软件和服务支付 bech32 地址的理由。

来自 segwit 兼容性矩阵的见解

最初发布于 Newsletter #60.

截至撰写本文时,我们认为从创建和审核兼容性矩阵中获得了一些关于 Bech32 的重要见解。

  • 大多数工具支持支付到 Bech32 地址:调查的 74% 的钱包和服务支持支付到 Segwit 地址。虽然这还不是我们希望看到的近乎普遍的支持,但这已经足够多,可能很快我们会看到更多钱包默认切换为 Bech32 接收地址。

  • 支持 P2WPKH 但不支持 P2WSH:当我们开始测试各种应用时,假设“Bech32 发送支持”是一个二元问题——要么工具支持,要么不支持。然而,我们调查的一个服务支持向本地 Segwit (Bech32) P2WPKH 地址支付资金,但不支持 Bech32 P2WSH 地址。这促使我们分别追踪这两种不同的 Segwit 版本 0 地址。(如果你是开发者,请同时支持这两种地址类型。)

  • 地址输入字段长度限制:某些服务可能支持发送到 Bech32 地址,但当我们尝试输入 Bech32 地址时,要么被拒绝为长度过长,要么字段根本无法接受所有字符。(提醒一下,BIP173 对比特币主网 Bech32 地址长度的说明是:它们“总是介于 14 到 74 个字符之间 [含 14 和 74],并且其长度模 8 不能是 0、3 或 5。”)

  • 输入字段的截图:我们记录了尝试发送到 Bech32 地址的步骤,收集了大量的截图,供 UI 设计人员在实施他们自己的 Bech32 发送支持(或其他功能,如 RBF 支持)时参考最佳实践。

  • 缺乏 Bech32 找零地址支持:由于支付到 Bech32 地址的支持仍然不普遍,Segwit 兼容钱包默认生成 P2SH 包裹的 Segwit 接收地址是合理的。然而,这些钱包中的许多也使用 P2SH 包裹的 Segwit 地址来接收从自己发给自己的找零。在某些情况下,这可能是出于隐私的考虑(例如,Bitcoin Core 当前尝试使找零输出类型与支付输出类型匹配),但在大多数情况下,这似乎是钱包没有充分利用将找零发送到其自己的 Bech32 地址以节省费用的机会。

结论

最初发布于 Newsletter #61.

在经过六个月、超过 10,000 字的发布之后,这是我们最后一篇关于 bech32 发送支持的章节。我们的目标是尽可能温和地说服尽可能多的开发者在他们的应用程序中增加对支付 bech32 地址的支持。我们希望这能使支持 segwit 的钱包更容易从默认使用 P2SH 包裹的 segwit 地址切换到更高效的原生 segwit bech32 地址。

通过我们的努力以及许多其他比特币爱好者的努力,我们认为成功已经近在咫尺:我们评估的 23 个热门钱包和服务中,有 19 个已经准备好支持支付 bech32 地址,其中 4 个已经默认生成 bech32 接收地址。

每周,钱包切换到 bech32 的合理性看起来越来越明显,我们预计将会听到越来越多的开发者表示他们的下一个主要版本将默认使用 bech32 接收地址。这将降低这些软件用户的交易费用,并为所有比特币用户提供更多的区块空间,帮助所有人的费用保持更低的水平,哪怕只是稍微长一点时间。

即便这一系列文章已经结束,我们仍将继续更新兼容性矩阵中的 segwit 部分,并在每周的 Newsletter 的其他部分报告关于 bech32 的值得注意的进展。感谢所有阅读此系列的你们,并感谢你们通过一个钱包和服务的改进为比特币的扩展性做出贡献。

脚注

  1. Bech32 地址有三个部分,可读前缀 (HRP),例如 bc,分隔符(始终为 1)和数据部分。分隔符和数据部分保证是二维码的大写字母和数字字符集的一部分,但根据 BIP173 允许在 HRP 中使用的字符范围包括不属于该大写字母和数字字符集的标点符号。具体来说,以下字符在 bech32 HRP 中是允许的,但不属于二维码大写字母和数字字符集:

    !"#&'()';<=>?@[\]^_`{|}~
    

    我们所知比特币中使用的 bech32 HRP(bc、tb、bcrt)都不使用这些字符,其他应用程序也没有使用。然而,在代码中,你可能不想假设非比特币 bech32 地址的二维码总是可以使用大写字母生成较小的二维码。