什么是 CometBFT
CometBFT 是一种软件,用于在多台机器上安全且一致地复制应用程序。这里的“安全”是指,只要以任意方式失效的机器少于 1/3,CometBFT 就能够正常工作。这里的“一致”是指,每一台没有故障的机器都会看到相同的交易日志,并计算出相同的状态。安全且一致的复制是分布式系统中的一个基础问题;它在广泛应用的容错能力中都起着关键作用,从货币系统、选举系统到基础设施编排等,皆是如此。 能够容忍机器以任意方式失效,甚至变得恶意,这种能力被称为拜占庭容错(BFT)。BFT 的理论已经存在数十年,但相关软件实现直到最近才真正流行起来,这在很大程度上要归功于 Bitcoin 和 Ethereum 等“区块链技术”的成功。所谓区块链技术,本质上是在更现代的环境下对 BFT 的重新表述,重点强调点对点网络与密码学认证。这个名字来源于交易被分批打包进区块的方式,其中每个区块都包含前一个区块的密码学哈希,从而形成一条链。 CometBFT 主要由两个核心技术组件构成:区块链共识引擎和通用应用接口。共识引擎基于 Tendermint 共识算法,确保每台机器都以相同顺序记录相同的交易。应用接口称为 Application BlockChain Interface(ABCI),负责将交易交付给应用处理。与其他自带内置状态机的区块链和共识方案不同,这类内置状态机可能是一个增强版键值存储,或者某种特定脚本语言;开发者可以使用 CometBFT 对任意应用进行 BFT 状态机复制,并自由选择最适合自己的编程语言和开发环境。 CometBFT 的设计目标是易于使用、易于理解、性能优异,并能适用于各种分布式应用。CometBFT 与其他方案
CometBFT 与两类软件大体相似。第一类是分布式键值存储,例如 Zookeeper、etcd 和 Consul,它们使用的是非 BFT 共识。第二类通常被称为“区块链技术”,既包括 Bitcoin 和 Ethereum 这样的加密货币,也包括 Hyperledger Burrow 这类替代性的分布式账本设计。Zookeeper、etcd、Consul
Zookeeper、etcd 和 Consul 都是在传统的非 BFT 共识算法之上实现的键值存储。Zookeeper 使用一种名为 Zookeeper Atomic Broadcast 的算法,而 etcd 和 Consul 使用 Raft 日志复制算法。一个典型集群通常包含 3 到 5 台机器,并能容忍少于 1/2 机器发生崩溃故障,例如 3 台中的 1 台或 5 台中的 2 台,但哪怕只有 1 个拜占庭故障,也可能危及整个系统。 这些方案都提供了功能丰富但实现略有不同的键值存储,不过总体目标都集中在为分布式系统提供基础服务,例如动态配置、服务发现、锁、领导者选举等。 CometBFT 本质上是类似的软件,但有两个关键区别:- 它具备拜占庭容错能力,这意味着它只能容忍少于 1/3 的机器失效,但这些失效可以包括任意行为,包括被入侵和恶意攻击。
- 它不规定某一种具体应用,例如某个增强版键值存储。相反,它专注于任意状态机复制,因此开发者可以构建适合自己的应用逻辑,从键值存储到加密货币,再到电子投票平台等各种场景。
Bitcoin、Ethereum 等
CometBFT 采用的 Tendermint 共识算法,起源于 Bitcoin、Ethereum 等加密货币的发展脉络,其目标是提供一种比 Bitcoin 工作量证明更高效、更安全的共识算法。在早期,基于 Tendermint 共识的区块链通常内置一种简单货币,用户若要参与共识,必须将一定数量的货币“绑定”为安全保证金;如果行为不当,这笔保证金可能被收回。这也是 Tendermint 共识之所以成为权益证明算法的原因。 此后,CometBFT 已演化为一个通用型区块链共识引擎,能够承载任意应用状态。这意味着它可以作为其他区块链软件共识引擎的即插即用替代方案。因此,你可以直接使用现有的 Ethereum 代码库,无论其采用 Rust、Go 还是 Haskell 编写,并通过 CometBFT 将其作为一个 ABCI 应用运行。事实上,我们已经在 Ethereum 上这样做过。我们也计划对 Bitcoin、ZCash 以及其他各种确定性应用采用同样的方式。 另一个基于 CometBFT 构建的加密货币应用示例是 Cosmos 网络。其他区块链项目
Fabric 与 CometBFT 的思路相似,但它对状态管理方式有更多预设,并要求所有应用行为都运行在可能很多的 Docker 容器中,它将这些模块称为 chaincode。它使用了 IBM 团队实现的 PBFT,并在此基础上增强了对潜在非确定性 chaincode 的处理能力。这种基于 Docker 的行为模式也可以作为 ABCI 应用在 CometBFT 中实现,不过对 CometBFT 进行扩展以处理非确定性问题,仍然留待未来完成。 Burrow 实现了 Ethereum Virtual Machine 和 Ethereum 的交易机制,并额外提供了名称注册、权限控制、本地合约以及另一套区块链 API。它使用 CometBFT 作为共识引擎,并提供了一种特定的应用状态。ABCI 概览
应用区块链接口(ABCI) 允许使用任意编程语言编写的应用实现拜占庭容错复制。背景
到目前为止,所有区块链“技术栈”(例如 Bitcoin)基本都采用单体式设计。也就是说,每一个区块链技术栈都是一个单独的程序,负责处理去中心化账本的所有问题;这包括 P2P 连接、交易的 mempool 广播、最新区块共识、账户余额、图灵完备合约、用户级权限等。 在计算机科学中,采用单体架构通常不是好的实践。它会让代码组件的复用变得困难,而一旦尝试复用,就往往需要为代码库的分叉维护复杂的维护流程。当代码库本身缺乏模块化设计并陷入“意大利面条式代码”时,这个问题会更加明显。 单体式设计的另一个问题在于,它把你限制在该区块链技术栈所使用的语言之中,或者反过来受制于语言本身。以 Ethereum 为例,它支持一个图灵完备的字节码虚拟机,因此你只能使用那些可以编译成该字节码的语言;虽然这个列表正在不断增长,但仍然非常有限。 与之相对,我们的方法是将共识引擎和 P2P 层与特定区块链应用状态的细节解耦。我们通过把应用细节抽象成一个接口来实现这一点,而这个接口被实现为一种 socket 协议。ABCI 简介
CometBFT 作为“共识引擎”,通过满足 ABCI 的 socket 协议与应用通信,这个协议也就是 CometBFT Socket Protocol。 为了便于理解,我们以广为人知的加密货币 Bitcoin 为例。Bitcoin 是一种加密货币区块链,其中每个节点都维护一个经过完整审计的未花费交易输出(UTXO)数据库。如果你想基于 ABCI 构建一个类似 Bitcoin 的系统,那么 CometBFT 将负责:- 在节点之间共享区块和交易
- 建立交易的规范化、不可变顺序 (即区块链)
- 维护 UTXO 数据库
- 验证交易的密码学签名
- 防止交易花费不存在的交易输出
- 允许客户端查询 UTXO 数据库
关于确定性
区块链交易处理逻辑必须是确定性的。如果应用逻辑不是确定性的,CometBFT 副本节点之间就无法达成共识。 以 Ethereum 上的 Solidity 为例,它之所以是区块链应用的一个很好的选择,其中一个重要原因就是它是一门完全确定性的编程语言。不过,通过避免以下非确定性来源,也同样可以使用 Java、C++、Python 或 Go 等现有流行语言来创建确定性应用:- 随机数生成器(在没有确定性种子的情况下)
- 线程上的竞态条件(或者干脆完全避免线程)
- 系统时钟
- 未初始化内存(在 C 或 C++ 这类不安全编程语言中)
- 浮点运算
- 具有随机性的语言特性(例如 Go 中的 map 迭代)
共识概览
CometBFT 采用 Tendermint 共识,这是一种易于理解、大体上异步的 BFT 共识算法。该算法遵循一个简单的状态机,其形式如下:
参与该算法的角色被称为验证者;他们轮流提出交易区块并对其投票。区块会按链式结构提交,每个高度对应一个区块。某个区块可能提交失败,这时算法会进入下一个轮次,并由新的验证者为该高度提出区块。一个区块要成功提交,需要经历两个投票阶段;我们称之为预投票和预提交。
图中有一对人在跳波尔卡舞,因为验证者的行为有点像在跳波尔卡。当超过三分之二的验证者对同一个区块进行预投票时,我们称之为一次波尔卡。每一次预提交都必须由同一轮中的一次波尔卡作为依据。当超过 2/3 的验证者在同一轮中对同一个区块进行预提交时,该区块就会被提交。
验证者可能因多种原因无法提交区块;当前提议者可能离线,或者网络可能较慢。Tendermint 共识允许他们判断某个验证者应被跳过。验证者会等待一小段时间,以便在投票进入下一轮之前从提议者那里接收完整的提案区块。正是对超时机制的这种依赖,使 Tendermint 共识成为一种弱同步算法,而不是纯异步算法。不过,算法的其余部分是异步的,验证者只有在听到超过三分之二验证者集合的消息后才会继续推进。Tendermint 共识的一个简化之处在于,它使用同一套机制既可以提交区块,也可以跳转到下一轮。
在少于三分之一的验证者属于拜占庭行为的前提下,Tendermint 共识算法保证安全性永远不会被破坏,也就是说,验证者绝不会在同一高度提交互相冲突的区块。为此,它引入了一些锁定规则,用来约束流程图中哪些路径可以被采取。一旦验证者对某个区块进行了预提交,它就会被锁定在该区块上。随后:
- 它必须为自己锁定的区块进行预投票
- 只有在后续轮次中,该新区块出现一次波尔卡时,它才能解锁并对新区块进行预提交
权益
在许多系统中,并不是所有验证者在共识协议中都拥有相同的“权重”。因此,我们关注的并不是验证者数量上的三分之一或三分之二,而是总投票权重中的这些比例;这些权重在各个验证者之间未必均匀分布。 由于 CometBFT 可以复制任意应用,因此完全可以定义一种货币,并用这种货币来计量投票权重。当投票权重以某种原生货币计价时,这个系统通常被称为权益证明。应用中的逻辑可以强制验证者将其持有的货币“绑定”为一笔安全保证金;如果它们在共识协议中被发现行为不当,这笔保证金就可以被销毁。这为协议安全性增加了经济层面的约束,使人们能够量化违反“少于三分之一投票权重为拜占庭行为”这一假设所需付出的成本。 Cosmos 网络 的设计目标之一,就是在一系列以 ABCI 应用形式实现的加密货币之间使用这种权益证明机制。What is CometBFT
CometBFT is software for securely and consistently replicating an application on many machines. By securely, we mean that CometBFT works as long as fewer than 1/3 of machines fail in arbitrary ways. By consistently, we mean that every non-faulty machine sees the same transaction log and computes the same state. Secure and consistent replication is a fundamental problem in distributed systems; it plays a critical role in the fault tolerance of a broad range of applications, from currencies to elections to infrastructure orchestration and beyond. The ability to tolerate machines failing in arbitrary ways, including becoming malicious, is known as Byzantine fault tolerance (BFT). The theory of BFT is decades old, but software implementations have only become popular recently, due largely to the success of “blockchain technology” like Bitcoin and Ethereum. Blockchain technology is just a reformalization of BFT in a more modern setting, with emphasis on peer-to-peer networking and cryptographic authentication. The name derives from the way transactions are batched in blocks, where each block contains a cryptographic hash of the previous one, forming a chain. CometBFT consists of two chief technical components: a blockchain consensus engine and a generic application interface. The consensus engine, which is based on the Tendermint consensus algorithm, ensures that the same transactions are recorded on every machine in the same order. The application interface, called the Application BlockChain Interface (ABCI), delivers the transactions to applications for processing. Unlike other blockchain and consensus solutions, which come pre-packaged with built-in state machines (like a fancy key-value store or a quirky scripting language), developers can use CometBFT for BFT state machine replication of applications written in whatever programming language and development environment is right for them. CometBFT is designed to be easy to use, simple to understand, highly performant, and useful for a wide variety of distributed applications.CometBFT vs. X
CometBFT is broadly similar to two classes of software. The first class consists of distributed key-value stores, like Zookeeper, etcd, and Consul, which use non-BFT consensus. The second class is known as “blockchain technology” and consists of both cryptocurrencies like Bitcoin and Ethereum, and alternative distributed ledger designs like Hyperledger’s Burrow.Zookeeper, etcd, Consul
Zookeeper, etcd, and Consul are all implementations of key-value stores atop a classical, non-BFT consensus algorithm. Zookeeper uses an algorithm called Zookeeper Atomic Broadcast, while etcd and Consul use the Raft log replication algorithm. A typical cluster contains 3-5 machines and can tolerate crash failures in fewer than 1/2 of the machines (e.g., 1 out of 3 or 2 out of 5), but even a single Byzantine fault can jeopardize the whole system. Each offering provides a slightly different implementation of a feature-rich key-value store, but all are generally focused on providing basic services to distributed systems, such as dynamic configuration, service discovery, locking, leader election, and so on. CometBFT is in essence similar software, but with two key differences:- It is Byzantine Fault Tolerant, meaning it can only tolerate fewer than 1/3 of machines failing, but those failures can include arbitrary behavior— including hacking and malicious attacks.
- It does not specify a particular application, like a fancy key-value store. Instead, it focuses on arbitrary state machine replication, so developers can build the application logic that’s right for them, from key-value stores to cryptocurrency to e-voting platforms and beyond.
Bitcoin, Ethereum, etc.
The Tendermint consensus algorithm, adopted by CometBFT, emerged in the tradition of cryptocurrencies like Bitcoin, Ethereum, etc., with the goal of providing a more efficient and secure consensus algorithm than Bitcoin’s Proof of Work. In the early days, Tendermint consensus-based blockchains had a simple currency built in, and to participate in consensus, users had to “bond” units of the currency into a security deposit which could be revoked if they misbehaved—this is what made Tendermint consensus a Proof-of-Stake algorithm. Since then, CometBFT has evolved to be a general-purpose blockchain consensus engine that can host arbitrary application states. That means it can be used as a plug-and-play replacement for the consensus engines of other blockchain software. So one can take the current Ethereum code base, whether in Rust, Go, or Haskell, and run it as an ABCI application using CometBFT. Indeed, we did that with Ethereum. And we plan to do the same for Bitcoin, ZCash, and various other deterministic applications as well. Another example of a cryptocurrency application built on CometBFT is the Cosmos network.Other Blockchain Projects
Fabric takes a similar approach to CometBFT, but is more opinionated about how the state is managed and requires that all application behavior runs in potentially many Docker containers, modules it calls “chaincode”. It uses an implementation of PBFT from a team at IBM that is augmented to handle potentially non-deterministic chaincode. It is possible to implement this Docker-based behavior as an ABCI app in CometBFT, though extending CometBFT to handle non-determinism remains for future work. Burrow is an implementation of the Ethereum Virtual Machine and Ethereum transaction mechanics, with additional features for a name registry, permissions, and native contracts, and an alternative blockchain API. It uses CometBFT as its consensus engine and provides a particular application state.ABCI Overview
The Application BlockChain Interface (ABCI) allows for Byzantine Fault Tolerant replication of applications written in any programming language.Motivation
Thus far, all blockchain “stacks” (such as Bitcoin) have had a monolithic design. That is, each blockchain stack is a single program that handles all the concerns of a decentralized ledger; this includes P2P connectivity, the “mempool” broadcasting of transactions, consensus on the most recent block, account balances, Turing-complete contracts, user-level permissions, etc. Using a monolithic architecture is typically bad practice in computer science. It makes it difficult to reuse components of the code, and attempts to do so result in complex maintenance procedures for forks of the codebase. This is especially true when the codebase is not modular in design and suffers from “spaghetti code”. Another problem with monolithic design is that it limits you to the language of the blockchain stack (or vice versa). In the case of Ethereum, which supports a Turing-complete bytecode virtual machine, it limits you to languages that compile down to that bytecode; while the list is growing, it is still very limited. In contrast, our approach is to decouple the consensus engine and P2P layers from the details of the state of the particular blockchain application. We do this by abstracting away the details of the application to an interface, which is implemented as a socket protocol.Intro to ABCI
CometBFT, the “consensus engine”, communicates with the application via a socket protocol that satisfies the ABCI, the CometBFT Socket Protocol. To draw an analogy, let’s talk about a well-known cryptocurrency, Bitcoin. Bitcoin is a cryptocurrency blockchain where each node maintains a fully audited Unspent Transaction Output (UTXO) database. If one wanted to create a Bitcoin-like system on top of ABCI, CometBFT would be responsible for- Sharing blocks and transactions between nodes
- Establishing a canonical/immutable order of transactions (the blockchain)
- Maintaining the UTXO database
- Validating cryptographic signatures of transactions
- Preventing transactions from spending non-existent transactions
- Allowing clients to query the UTXO database
A Note on Determinism
The logic for blockchain transaction processing must be deterministic. If the application logic weren’t deterministic, consensus would not be reached among the CometBFT replica nodes. Solidity on Ethereum is a great language of choice for blockchain applications because, among other reasons, it is a completely deterministic programming language. However, it’s also possible to create deterministic applications using existing popular languages like Java, C++, Python, or Go by avoiding sources of non-determinism such as:- random number generators (without deterministic seeding)
- race conditions on threads (or avoiding threads altogether)
- the system clock
- uninitialized memory (in unsafe programming languages like C or C++)
- floating point arithmetic
- language features that are random (e.g., map iteration in Go)
Consensus Overview
CometBFT adopts the Tendermint consensus, an easy-to-understand, mostly asynchronous, BFT consensus algorithm. The algorithm follows a simple state machine that looks like this:
Participants in the algorithm are called validators; they take turns
proposing blocks of transactions and voting on them. Blocks are
committed in a chain, with one block at each height. A block may
fail to be committed, in which case the algorithm moves to the next
round, and a new validator gets to propose a block for that height.
Two stages of voting are required to successfully commit a block; we
call them pre-vote and pre-commit.
There is a picture of a couple doing the polka because validators are
doing something like a polka dance. When more than two-thirds of the
validators pre-vote for the same block, we call that a polka. Every
pre-commit must be justified by a polka in the same round.
A block is committed when
more than 2/3 of validators pre-commit for the same block in the same
round.
Validators may fail to commit a block for a number of reasons; the
current proposer may be offline, or the network may be slow. Tendermint consensus
allows them to establish that a validator should be skipped. Validators
wait a small amount of time to receive a complete proposal block from
the proposer before voting to move to the next round. This reliance on a
timeout is what makes Tendermint consensus a weakly synchronous algorithm, rather
than an asynchronous one. However, the rest of the algorithm is
asynchronous, and validators only make progress after hearing from more
than two-thirds of the validator set. A simplifying element of
Tendermint consensus is that it uses the same mechanism to commit a block as it
does to skip to the next round.
Assuming fewer than one-third of the validators are Byzantine, the Tendermint consensus algorithm
guarantees that safety will never be violated—that is, validators will
never commit conflicting blocks at the same height. To do this, it
introduces a few locking rules which modulate which paths can be
followed in the flow diagram. Once a validator precommits a block, it is
locked on that block. Then,
- it must prevote for the block it is locked on
- it can only unlock and precommit for a new block if there is a polka for that block in a later round