Skip to main content

5.2 比特币集成

概述

比特币是一种基于开源P2P协议的去中心化数字货币。比特币使用未花费交易输出(UTXO:unspent transaction output)会计模型,即交易创建输出,这个输出在其他交易中被当作输入,从而创建新的交易输出。一个交易消耗一个UTXO。

比特币是一个不支持智能合约的支付网络。通过互联网计算机,智能合约对比特币的支持增加了巨大的价值:它充分利用了比特币网络和互联网计算机的综合优势,前者是世界数字黄金储备,而后者是可以安全、有效地执行智能合约平台。这个结合使去中心化金融(DeFi)应用程序成为可能,这种应用程序目前只能通过包装比特币来实现,这种包装版比特币需要其他可信实体的介入。此外,比特币可用于支付互联网计算机上的任何服务,这开启了无穷无尽的应用场景。

互联网计算机与比特币区块链集成,目标是通过两个区块链的直接的、“去信任的”集成,为比特币提供强大的智能合约功能。在这种情况下,“去信任”意味着除了信任比特币网络和互联网计算机的正确运行之外,不需要任何信任假设。换句话说,不能有额外的参与方(比如桥接或其他类型的中介),从而实现更遵守规则、更安全的集成。在比特币和互联网计算机的这种无需信任的结合中,容器可以在比特币区块链上直接持有比特币。

这种直接集成基于以下特性:

  • 容器可以有比特币地址(因此可以直接在比特币区块链上接收和持有比特币)。
  • 容器可以访问其控制的地址的UTXO集。
  • 容器可以安全地签署比特币交易。
  • 容器可以向比特币网络提交比特币交易。

集成的去信任依赖于阈值ECDSA协议,该协议使子网能够在容器请求下,根据秘密共享的私钥来计算ECDSA签名。通过该协议,每个容器都可以“控制”大量可衍生的ECDSA 密钥并为其获取签名,这从而使容器能够安全直接地在比特币区块链上接收、持有和转移比特币。当然容器必须能够检索与其比特币地址关联的UTXO。为此,互联网计算机直接从比特币网络中提取区块。下面将更详细地探讨这种集成的底层架构。

架构

在本节中,我们将在较高的层次上介绍与比特币区块链直接集成的架构。这个整合在IC协议栈的每一层都改变和增加了组件。

A high-level overview of the Bitcoin integration architecture.

比特币容器作为副本的一部分在执行层上实施,并通过虚拟管理容器公开比特币合成API。该API提供了查询比特币状态(utxo、余额和当前交易费用)和提交交易的功能。

为了提供最新的UTXO,比特币区块必须从比特币网络不断地拉入互联网计算机,所有UTXO的集合(称为“UTXO集合”)根据区块中包含的交易输入和输出进行更新。这种机制需要一个跨越IC协议栈所有层的体系结构,且包含副本外的一个新组件。

执行层中的比特币组件维护一组最近的比特币区块、整个UTXO集和一组由容器提交的传出交易。UTXO集和最近的区块用于响应UTXO和容器的余额查询。

比特币适配器,即在副本外部的沙箱操作系统级进程,连接到随机选择的、使用比特币的对等发现协议的多个比特币节点。比特币适配器维护整个区块头的一个本地副本,也维护少量区块缓存,并在收到请求时提供给副本。

在每一轮IC中,区块生成器,也是共识层的核心组件,从比特币组件向比特币适配器发送一个请求并获得响应,然后将该响应包含在IC区块的有效载荷中。更准确地说,比特币组件将请求存储在复制状态中,在那里,它被共识层的有效载荷构建器获取。payload builder使用进程间通信将请求转发给外部比特币适配器,比特币适配器处理请求并将结果返回给payload builder。然后payload builder将结果合并到ic区块块中。一个请求包含最近区块头的哈希值,比特币组件拥有交易的区块以及传出交易。比特币适配器将接收到的区块哈希与比特币网络的视图进行比较:如果它的缓存包含一个或多个比特币组件的后续区块,它会返回这些区块。此外,它将在请求中接收到的交易添加到一个队列中,以便向比特币网络异步提交传出交易。

每个IC块都被传输到子网节点,并且需要经过公证和结束过程。比特币整合的公证流程进行了扩展:每个副本对IC区块中包含的比特币有效载荷进行确定性有效性的检查。至关重要的是,区块制造者只有在保证该区块将被所有真正的副本成功验证的情况下才提出一个区块,否则子网可能无法达成共识并导致整个IC区块被丢弃。

一旦带有比特币有效载荷的IC块被成功验证,结束过程将继续进行而无需任何更改。当区块最后敲定完,需要在消息路由层提取比特币有效负载,并将其发布到正确的子网队列中进行执行。当比特币有效负载到执行层时,会由执行层的比特币组件执行: 验证有效负载并相应地更新比特币组件的状态。

创建比特币交易需要计算一个UTXO当作交易输出的ECDSA签名。容器可以通过一个阈值ECDSA API请求ECDSA签名,该API是作为专用ECDSA签名子网的一部分实现的。目前已经部署了一个这样的子网,如果需求增加,将来可能会提供多个签名子网。图1以简化的方式显示了阈值ECDSA功能,作为启用比特币(Bitcoin-enabled )的子网的一部分,而不是位于一个单独的子网。ECDSA API阈值使容器可以请求ECDSA签名,该签名由ECDSA子网的副本联合计算,基于一个秘密共享的私钥。

技术细节

如上一节所述,容器使用通过管理容器公开的比特币集成API与比特币组件进行交互。比特币组件反过来又依赖于比特币适配器,比特币适配器是与比特币网络交互的组件。本节将提供关于各个组件的技术细节,自底向上地从比特币适配器开始。

请注意,开发者预览版中有一些技术细节没有实现,这将在下一节中讨论。例如,一些有效性检查没有执行。这些遗漏对于本地测试环境来说并不重要。这些遗漏对于本地测试环境来说并不重要。

比特币适配器

比特币适配器与比特币网络交互来获取区块头和区块,并由容器发布比特币交易。

连接比特币节点

默认情况下,比特币适配器连接到8个随机选择的比特币节点,但连接的数量是可配置的。为了确保每个副本的比特币适配器以高概率连接到一个不同的随机设置,比特币适配器为比特币节点查询地址,直到它收到2000个地址并从这些地址中随机选择节点,直到建立完所需数量的连接。实验表明,这个过程会使每个副本的比特币适配器连接到大多数不同的地址。

状态

比特币适配器希望维护以下状态:

  • 所有比特币区块头。
  • 比特币区块头的缓存,预计比特币组件下一个请求。
  • 已经发布但还没有传输到比特币节点的传出交易的缓存。

最初,适配器只有硬编码的初始区块头,缓存是空的。

运行中的比特币适应器

当比特币适配器启动时,它连接到比特币节点,并开始拉入区块头,直到完全同步。对于每个下载的区块头,比特币适配器执行以下检查:

  • 区块头是符合语法规则的,也就是说,它可以被解析为一个正确的区块头。
  • 前面的区块字段指向一个本地可用的区块头。
  • 基于难度目标,区块头中的哈嘻工作是足够的。
  • 区块头中的时间戳大于前面11个块的中值。

在收到来自比特币组件的请求前,缓存一直是空的。比特币组件周期性地发送一个最近的区块头哈希的列表,其中包含了它已经拥有的完整区块(包含所有交易)。比特币组件一节中提供了哪些块头哈希会被发送的详细信息。

比特币适配器检查它是否有任何比特币组件丢失的区块,并响应包含丢失区块的消息,对高度较低的区块进行优先排序。在一条消息中可以返回多个区块,软上限高达2MB,确保即使比特币区块的大小超过2MB也至少可以返回一个比特币区块。这个上限意味着对于最近大多数大小超过1mb的区块,只有一个区块(和区块头)会被返回。在一条消息中发送多个区块的能力对于更快地接收旧区块非常有用,因为它们要小得多。

如果比特币适配器的缓存中没有丢失的区块,它会立即返回一条空消息以避免共识延迟。有了对比特币组件状态的准确视图后,比特币适配器下载下一个丢失的区块,以便它可以将它们打包到比特币组件未来请求的返回消息中。由于比特币适配器不跟踪交易,它不能验证接收区块中的交易的有效性。然而,为了防止比特币组件用无效区块发送垃圾信息,它会执行一些基本检查:

  • 该区块是符合语法规则的,也就是说,它可以被解析为一个正确的比特币块。
  • Merkle树根哈希对应于相应区块头中的哈希。

一旦接收到的区块头哈希集合表示比特币组件已经接收到该区块,该区块就会从缓存中删除。

当比特币适配器接收到出站交易时,它们被放置在交易缓存中并发布到比特币网络。一旦交易被传输到连接的比特币节点,交易就会从缓存中移除。

通过共识

技术上来说,当副本被选择为下一个(IC)区块制造者时,副本的比特币适配器会被查询比特币区块。在将比特币适配器放入IC区块之前,共识调用一个函数来检查其响应的有效性。要确保有效性检查能通向子网中所有副本的一致结果,否则整个IC块可能会被删除,正如前面提到的。因此,有效性检查必须是一个确定性函数,即不能考虑诸如当地时间和比特币适配器的本地可用状态等因素。以下有效性检查会被执行:

  • 区块头是符合语法规则的,即他们可以被解析为一个正确的区块头。
  • 区块是符合语法规则的,即他们可以被解析味正确的比特币区块。
  • 对于每个区块,Merkle树根哈希对应了相应的区块头中的哈希。

当副本接收到一个被提的IC区块时,它对IC区块中的比特币适配器的响应执行相同的验证检查。一旦IC区块通过了共识(以及消息路由),比特币区块(和相应的区块头)会被提供给比特币组件以供吸收。

比特币组件

比特币组建是副本的一部分。比特币集成API是通过管理容器提供的。

状态

比特币组件存储以下状态:

  • 从创始到某区块高度h的完整UTXO集。
  • 区块,包括从高度h开始的区块头。
  • 所有接收到的块头的完整历史记录。
  • 外出交易的集合。

由于比特币组件不存储完整的交易历史,它必须决定何时可以安全地删除一个区块,只依赖于它维护的UTXO集中的信息。

由于长时间分叉的风险,比特币组件不使用确认数来确定什么时候删除一个区块。相反,它使用了一个稍微复杂一点的概念,我们称之为稳定性。简单地说,当有一定数量的后续区块时,我们会认为一个区块是稳定的,减少了区块在未来被丢弃的风险,并且任何分叉的前端也至少与包含区块的链前端的后面的区块数量相同。更正式地说,如果h(b)表示区块b自创始以来的原有区块的数量,并且d(b)表示从区块b开始的最长的后继路径的长度,稳定性定义如下:

Definition (𝜹-stability): Let B denote the set of locally available blocks.
For a parameter 𝜹>0, we say that a block b is 𝜹-stable if the following conditions hold:
* d(b) ≥ 𝜹
* ∀ b’ ∈ B \ {b}, h(b’) = h(b): d(b) - d(b’) ≥ 𝜹

比特币组件配置了144的稳定阈值𝜹,也就是说,如果没有分叉,比特币组件将保持所有区块大约一天(平均每10分钟一个区块)。一旦一个区块变成𝜹-稳定,交易将应用到UTXO集并且该区块将被丢弃。因此,稳定性被用于定义上述状态描述的截止高度为h的区块。所有稳定性低于阈值的区块都被认为是不稳定的

开发者预览

当向比特币适配器请求更新时,比特币组件向比特币适配器发送包含所有不稳定区块的哈希值的消息,该适配器使用哈希值来确定比特币组件丢失了哪些区块(如果有的话)。当从比特币适配器接收区块和区块头时,每个区块/区块头对会被执行以下有效性检查:

  • 该区块头是符合语法规则的,即它可以被解析为一个正确的区块头。
  • 基于难度目标,区块头中的哈希工作是足够的。
  • 区块头指向一个已知的原身。
  • 该区块是符合语法规则的,即它可以被解析为一个正确的比特币区块。
  • Merkle树根哈希对应于相应块头中的哈希。
  • 所有交易都是有效的,也就是说,签名是正确的且只有未使用输出才会被消耗。
  • 区块头的时间戳大于前面11个区块的中值。

如果校验成功,则该区块会被添加到不稳定区块列表中。此外,如果一个区块因此变成𝜹-稳定, UTXO集将根据该区块中的交易更新,并且该区块将被丢弃,只保留相应的区块头。

比特币组件公开了以下API:

  • bitcoin_get_utxos: 该函数将未使用交易输出(utxo)返回至给定比特币地址。
  • bitcoin_get_balance: 该函数返回给定比特币地址的余额。
  • bitcoin_send_transaction: 该函数将给定的交易发送到比特币网络。
  • bitcoin_get_current_fees:该函数为最近10,000个交易返回费用百分比(以中本聪/字节为单位)。

支持以下地址格式:

  • 支付给公钥哈希(P2PKH)
  • 支付给脚本哈希(P2SH)
  • 支付给见证公钥哈希(P2WPKH)
  • 支付给见证脚本哈希(P2WSH)

关于API的详细信息可以在GitHub页面找到。当回应get_utxos和get_balance调用时,它提供了一个可选的min: _confirations参数,比特币组件考虑UTXO状态以及至少有请求的最小确认次数的不稳定区块中所有交易。换句话说,在从UTXO集中提取相关的未使用输出之后,不稳定区块将被解析去找到其他未使用的输出。此外,如果有交易消耗了任何收集的输出,这些输出将会被视为已使用并被删除。The function span style="font-family: 'Courier New';">bitcoin_get_utxos 进一步提供了分页API,这对于有大量utxo的地址是很有用的。

为了减少分叉导致的不一致的风险,确认的计数是保守的,使用的还是𝜹-稳定性。区块的稳定性计数被定义为最大的𝜹,因此区块是𝜹-稳定的。确定交易确认数量的过程如下:最大的区块高度被认定是在这个高度上唯一的区块。不稳定区块的链到第一步中被认定的区块会被考虑,并且一个区块中确认交易的数量被定义为区块的稳定性计数加1(+1是因为当一个交易出现在区块中时,它就已经有了一个确认)。

虽然这个规则可能看起来很复杂,但它有几个不错的属性:如果没有分叉,每个交易的确认次数就和人们预期的一样:当一个交易在一个区块中,它就有了一个确认,如果还有后续的区块,它有两个确认,如此等等。然而,如果有多个高度相似的竞争分叉,确认的数量将保持在较低水平直到其中一个链占居上风,此时确认数量开始增加。简而言之,该规则旨在确保基于稳定性计数的大量确认,表明交易很可能不会被取消,即使存在竞争分叉。

下图显示了一个有分叉的链的例子。区块内的数字为稳定性计数,区块右下角圆圈内的数字表示最长链上所有区块,对应区块内交易确认的数量。 figure2

当比特币组件接收到一个传出交易并且该交易通过验证检查时,它将该交易转发给比特币适配器。比特币组件缓存交易,如果交易没有出现在一个区块中,比特币会定期重新提交交易。当交易出现在区块中就会被删除或者在24小时后到期。

开发者预览

2022年2月3日发布的开发者预览版是比特币集成功能的早期版本。它旨在帮助开发人员开始执行针对API的智能合约并提供反馈,以便在最终发布前可以通过社区反馈来改进API。开发者预览版仅作为本地开发环境(Canister SDK)的一部分可用,不与比特币主网或测试网集成。相反,它以回归测试(“regtest”)模式中运行本地比特币节点(bitcoind),以此来模拟真实的比特币网络,同时完全由开发人员控制,这有利于调试。开发者预览版的发布通过上文所述的比特币集成API来提供与比特币相关的功能逻辑。然而,与IC协议栈的集成被简化了:它没有使用与IC协议层的自定义集成,尤其是共识,但添加了一个适配器分隔片作为外部组件,它通过查询调用和入口消息将适配器连接到常规IC协议栈。使用开发者预览版的用户需要在他们本地的Canister SDK环境上安装这个版本并启动它。此外,比特币组件是作为Wasm容器公开的,而比特币集成API是通过主网上的管理容器公开的。