Libra区块链
白皮书作者:Zachary Amsden, Ramnik Arora, Shehar Bano, Mathieu Baudet, Sam Blackshear, Abhay Bothra, George Cabrera,Christian Catalini, Konstantinos Chalkias, Evan Cheng, Avery Ching, Andrey Chursin, George Danezis,Gerardo Di Giacomo, David L. Dill, Hui Ding, Nick Doudchenko, Victor Gao, Zhenhuan Gao, François Garillot,Michael Gorven, Philip Hayes, J. Mark Hou,Yuxuan Hu, Kevin Hurley, Kevin Lewi, Chunqi Li, Zekun Li, Dahlia Malkhi,Sonia Margulis, Ben Maurer, Payman Mohassel, Ladi de Naurois, Valeria Nikolaenko, Todd Nowacki, Oleksandr Orlov,Dmitri Perelman, Alistair Pott, Brett Proctor, Shaz Qadeer, Rain, Dario Russi, Bryan Schwab,Stephane Sezer,Alberto Sonnino, Herman Venter, Lei Wei, Nils Wernerfelt, Brandon Williams, Qinfan Wu, Xifan Yan, Tim Zakian,Runtian Zhou*
翻译:巴比特资讯 (洒脱喜,Kyle)
摘要
Libra区块链是一个去中心化、可编程的数据库,其旨在支持一个低波动性的加密货币,能够作为服务全世界数十亿人的有效交易媒介。我们提出了一个关于Libra协议的提议,它会实现Libra区块链,旨在创建一个可促进创新、降低进入壁垒,并改善访问金融服务机会的金融基础设施。为了验证Libra协议的设计,我们已构建了一个开源原型实现 —— Libra Core ,并在全球范围内共同推进这一新生态系统。
Libra协议允许来自不同实体的一组副本(称为验证者)共同维护一个可编程资源的数据库。这些资源由经过公钥加密验证的不同用户帐户拥有,并遵守这些资源开发者指定的自定义规则。验证者处理事务(transaction)并相互作用,就数据库的状态达成共识。事务是基于预定义的,在未来的版本中,用户自定义的智能合约会使用一种称为Move的新编程语言编写。
我们使用Move语言定义区块链的核心机制,例如货币和验证者会员。这些核心机制能够创建一个独特的治理机制,其建立在现有项目早期已建立的稳定基础之上,但Libra系统会随着时间的推移,完全开放。
(图片来自: Libra.org)
一、引言
互联网的普及以及由此产生的产品和服务的数字化,提高了效率,降低了进入壁垒,降低了大多数行业的成本。这种连通性已通过让更多的人进入金融生态系统,而驱动了经济赋权。尽管如此,对于那些最需要金融服务的人来说,获得金融服务的机会仍然有限,这是由成本、可靠性以及无缝发送资金的能力影响的。
这篇技术论文为Libra协议提出了一个提议,旨在支持这个新生的 Libra生态系统,并解决这些挑战,扩大资本准入,并作为创新金融服务的一个平台。这个生态系统将提供一种新的全球货币 —— Libra币,它将由优质的中央银行一篮子银行存款和国债充分支撑。
这些基准货币都经历了相对较低的通货膨胀,因此,Libra稳定币继承了这一属性,且其还拥有在地理上呈现多样化的组合优势。Libra协议必须扩展,以支持其货币成长为一个全球金融基础设施,提供以实施经济和治理的灵活性,支持其操作策略。Libra协议的设计从一开始就以整体解决这些需求,它建立在现有项目以及研究的基础之上。Libra结合了新颖的方法以及已被广泛理解的技术。
金融服务业健康竞争和创新的一个关键前提,是依靠处理事务、维护账户以及确保跨服务和组织的互操作性公共基础设施。通过降低进入壁垒和转换成本,Libra协议将使初创企业和现有大型企业能够在公平竞争的环境中竞争,并试验新的商业模式和金融应用。区块链技术能够很好地解决这些问题,因为它可以用于确保没有单个实体能够控制生态系统或可单方塑造其进化优势[2]。
Libra区块链将是去中心化的,它由一组共同工作的验证者组成,用于处理事务并维护区块链的状态。这些验证者也构成了Libra协会的成员,这将为网络的管理以及支持货币的储备提供一个框架。最初,该协会(验证者)将由一组地理分布广泛的创始成员组成。这些成员是根据客观参与标准选择出来的,包括它们在引导Libra生态系统和为其成功而投入的权益。随着时间的推移,成员资格将转变为完全开放,只基于Libra的持有量。Libra协会已发表了报告,概述了其愿景[1]、提议的结构[3]、货币的经济学[4]以及向无许可系统转变的路线图[5]。
而本文,则是构建支持Libra生态系统的技术基础设施的第一步。
我们发布这份早期报告,是为了征求社区对Libra的初步设计、系统的发展计划以及当前未解决的研究挑战的反馈意见。因此,协会建立了一个开放源码社区[6]以供大家讨论以及支持项目的开发。
Libra协议。Libra区块链是使用Libra协议进行维护的密码学证明数据库 [7, 8, 9]。该数据库存储了一个可编程资源(例如Libra币)的账本。资源(resource)遵循其声明模块指定的自定义规则,该模块还存储在数据库中。资源由使用密码学公钥进行验证的帐户拥有。帐户可代表系统的直接最终用户以及实体(例如托管钱包),代表他们的用户。帐户的所有者可签署对帐户内的资源进行操作的事务(transaction)。
图1显示了使用Libra协议进行交互的两种类型的实体:(1)验证者,其负责维护数据库,(2)客户端,它们对数据库执行查询,并提交事务以修改它。
图1:Libra协议的纵览图
验证者维护这个数据库,并处理由包含在数据库:
(1)的客户端提交的事务(transaction)。验证者使用一个分布式共识协议来商定不断增长的,已提交到数据库的事务列表,以及执行这些事务的结果。即使少数验证者存在恶意或错误,此共识协议也必须是可靠的。验证者轮流驱动接受事务的过程。当验证者充当领导者时,它会向其他验证者
(2)提出客户端直接提交给它的事务,以及通过其他验证者间接提交的事务。所有验证者执行事
(3)并形成一个经验证的数据结构,其中包含新账本历史记录。作为共识协议
(4)的一部分,验证者对该数据结构的确认者进行投票。作为在版本i提交一个事务ti的一部分,共识协议在版本i输出数据库完整状态的签名(包括整个历史),以确认对来自客户端
(5)查询的响应。
客户端可以向验证者发出查询,以从数据库中读取数据。由于数据库是经验证的,因此可保证客户端对其查询的响应的准确性。作为对读取查询的响应的一部分,验证者返回验证者已知数据库最新版本i的签名验证器。
此外,客户端还可以选择通过与验证者同步事务历史,来创建整个数据库的副本。在创建副本时,客户端可检查验证者是否正确执行了事务,从而改善了系统的问责制和透明度。其他客户端可以从保存副本的客户端读取副本,这和从验证者处读取副本以验证响应真实性的方式相同。
为了简单起见,本文的其余部分假设客户端直接查询一个验证者,而不是副本。
组成(Organization)。本文讨论了Libra协议的组成部分:
-
逻辑数据模型 :(第2节)描述了逻辑数据模型,该模型组织了对验证者和客户端可见的去中心化数据库。
-
执行事务:(第3节)描述了Move [10]编程语言的使用,这是一种用于定义和执行数据库事务的新编程语言。
-
经验证的数据结构和存储:(第4节)描述了基于Merkle树的逻辑模型到经验证数据结构的映射[11]。
-
拜占庭容错共识:(第5节)描述了HotStuff协议[13]的变体LibraBFT [12],允许具有潜在恶意验证者的网络,通过执行Move语言事务,并在使用经验证的数据结构执行事务时达成一致,来维护一个单一的、一致的数据库。
-
网络:(第6节)描述了使验证者能根据协商一致的需要,安全地相互通信的协议。
而在之后,我们展示了开源的Libra Core原型[6]。第7节内容则讨论了Libra Core如何结合了Libra协议的组件来处理事务。
第8节内容则探讨了性能考虑。
最后,我们将解释如何使用该协议来支持Libra生态系统政策的经济稳定和治理。
第9节内容展示了我们如何使用Move语言来实现低波动性、储备支撑的Libra货币,以及反映 Libra协会治理的验证者管理系统。
第10节内容,讨论了有关Libra生态系统的未来计划,及其当前正面临的挑战,这也将是这篇文章的结尾。
二、逻辑数据模型
Libra区块链中的所有数据都存储在单一版本的数据库当中[14, 15]。版本号是与系统已执行的事务数相对应的无符号64位整数。在每个版本i中,数据库都包含一个元组 (Ti, Oi, Si) ,它们分别表示事务 (Ti),事务输出(Oi)以及账本状态 (Si)。给定一个确定性执行函数Apply,这个元组的含义是:对账本状态Si−1执行事务Ti会产生输出Oi以及新账本状态Si,即Apply(Si−1, Ti) → 〈Oi, Si〉。
Libra协议使用Move语言来实现确定性执行函数(请参见第3节内容)。在本节内容中,我们将重点介绍版本化数据库,它允许验证者:
-
根据最新版本的账本状态执行事务;
-
响应客户端有关当前和以前版本的账本历史记录的查询;
我们首先解释存储在单个版本中的账本状态的结构,然后讨论版本化账本历史视图(History view)的目的。
2.1 账本状态
账本状态代表了有关Libra生态系统的基本事实,包括每个用户在给定版本中持有的Libra币的数量。每个验证者必须知道最新版本的账本状态,以便执行新的事务。
Libra协议使用基于帐户的数据模型[16]对账本状态进行编码。状态被构造为一个键值存储,它将帐户地址键映射到帐户值。账本状态下的账户值是已发布Move资源和模块的集合。这个 Move资源存储数据值,而模块则存储代码。初始帐户集及其状态在创始账本状态中指定(见第3.1节)。
帐户地址:帐户地址是一个256位的值。要创建新帐户,用户首先为签名方案生成一个新的验证/签名密钥对 (vk, sk),并使用公共验证密钥vk的加密哈希作为帐户地址a = H(vk)。当从现有账户发送的事务调用这个create_account(a) Move指令时,则这个新账户就会在账本状态中创建。这通常发生在一笔事务尝试将Libra发送到尚未创建的地址a的帐户时。
在地址a中创建新帐户后,用户可使用私人签名密钥sk,签署要从该帐户发送的事务。该用户还可以在不更改其地址的情况下,更改用于签署交易的密钥(例如,主动更改密钥或响应密钥可能被盗的情况)。
Libra协议不会将帐户链接到用户的真实身份(即没有KYC)。用户可通过生成多个密钥对来自由创建多个帐户。由同一用户控制的帐户没有固有的相互联系。该方案参照了比特币和以太坊,为用户提供了假名性 (又称半匿名)[19]。
资源值:所谓资源值或资源,是将命名字段绑定到简单值(例如整数或复杂值)的记录。
(脚注1: 具体地说,我们使用SHA3-256 [17]实例化哈希函数,并使用edwards25519曲线[18]实现EdDSA数字签名)
图2:具有四个帐户的账本状态示例。
在这个图中,椭圆表示资源,矩形表示模块。从资源到模块的定向边,意味着该模块声明了资源的类型。地址为0x12的帐户,包含由货币模块声明的Currency.T资源。该货币模块的代码存储在地址0x56。地址0x34处的帐户,同时包含了一个Currency.T资源以及StateChannel.T资源,这是由存储在地址0x78的模块声明的。
每个资源都有一个由模块声明的类型。资源类型是名义类型[20] ,其由类型的名称和资源的声明模块的名称和地址组成。例如,图2中的 Currency.T资源的类型是0x56.Currency.T.这里,0x56是存储货币模块的地址,Currency是模块的名称,而Currency.T则是资源的名称。
要在帐户地址0x12取回资源0x56.Currency.T,客户端将请求0x12/resources/0x56.Currency.T.
这种设计的目的,是让模块为顶级帐户值定义一个可预测的模式——也就是说,每个帐户都将其0x56.Currency.T资源存储在同一路径下。因此,每个帐户最多可存储一个给定类型的资源。但是,这一限制是没有约束的,因为程序员可定义包装器(wrapper)资源,以自定义的方式组织资源。(例如,resource TwoCoin { c1: 0x56.Currency.T, c2: 0x56.Currency.T })。
用于改变、删除和发布资源的规则,编码在创建资源并声明其类型的模块当中。
Move的安全和验证规则,会阻止其他代码对资源进行修改。
模块值:所谓模块值或模块,包含声明资源类型和程序的Move字节码(更多细节见第3.4节)。与资源类型一样,模块由声明模块的帐户地址识别。例如,图2中Currency模块的标识符是0x56.Currency。
模块必须在一个帐户中唯一命名,每个帐户最多只能声明一个具有给定名称的模块。例如,图2中地址0x56处的帐户无法声明另一个名为Currency的模块。另一方面,地址为0x34的帐户可以声明Currency的模块。此模块的标识符将为0x34.Currency。注意,0x56.Currency.T和0x34.Currency.T 是不同的类型,它们彼此不能互换使用。
在当前版本的Libra协议中,模块是不可变的。一旦在帐户地址下声明模块,就不能修改或删除它,除非通过硬分叉的方式。我们正研究在未来的版本中启用安全模块更新的方案选项。
2、2事务(Transactions)
Libra区块链的客户端是通过提交事务来更新账本状态的。从高维度来讲,事务由事务脚本(以Move字节码编写)和事务脚本的参数(例如,接收方帐户地址或要发送的Libra币数量)组成。验证者通过运行脚本及其参数和当前账本状态作为输入来执行事务,以生成完全确定的事务输出。在共识(第5节)期间,通过同意对事务输出的约束性承诺(第4节),在事务被承诺之前,账本状态不会改变。
我们将在第3节中更详细地讨论事务的结构和执行。
事务输出:执行一个事务Ti将生成新的账本状态Si以及执行状态代码、gas使用量以及事件列表(汇总在输出Oi中)。执行状态代码记录了执行一个事务的结果(例如,成功、因错误e退出、gas耗尽等)。
gas使用,记录了执行事务时使用的gas单位数量(有关如何使用gas,来管理与事务处理相关费用的详细信息,请参见第3.1节)。
事件(Events):事件列表,是由执行事务时产生的一组副作用(2)。Move代码可通过一个事件结构触发事件。每个事件都与一个唯一的key相关联,它标识事件发出的结构,以及提供活动详细信息的有效载荷。一旦按照共识协议提交了事务,事务生成的事件将添加到商定的账本历史记录中,并提供成功执行事务导致特定效果的证据。例如,一笔付款交易会发出一个事件,允许接收方确认已收到付款并确认付款金额。
乍一看,事件似乎是多余的。相比通过一笔事务Ti查询事件,客户端可询问事务Ti是否包含在区块链中。但是,这很容易出错,因为包含Ti并不意味着成功执行(例如,gas用完后可能会中断)。在事务可能失败的系统中,事件不仅提供了特定事务已执行的证据,而且还提供了它已以预期效果成功完成的证据。
事务只能生成事件-它们不能读取事件。这种设计允许事务执行仅为当前状态的函数,而不是历史信息(例如以前生成的事件)。
2、3 账本历史
账本历史记录存储已提交和已执行事务的序列,以及它们发出的关联事件。账本历史的目的,是为了记录最近的账本状态是如何被计算的。
账本历史中不存在一组事务的概念(3)。共识协议将事务批处理至区块,作为优化并驱动这一共识协议(有关共识的详细信息,请参阅第5节内容)。然而,在逻辑数据模型中,事务按顺序发生,而不会区分哪个区块包含每个事务。
(脚注2: 事件的作用,类似于以太坊中的日志和事件概念 [16],然而,Libra协议中的事件机制是完全不同的。
脚注3:这与比特币和以太坊相反,在比特币和以太坊中,区块和区块最大值的概念扮演着重要的角色。 )
尽管验证者不需要知道账本历史就可以执行新的事务,但是客户端可以对账本历史记录执行经验证的查询,并使用账本历史来审核事务执行。
响应客户端查询:验证者可使用账本历史来回答客户端关于以前账本状态、事务和输出的查询。例如,客户端可能会询问有关特定版本的账本状态(例如,在第30个版本的地址x的帐户余额是多少?),或者特定类型事件的历史(例如,Y地址的账户在最后20分钟收到了哪些付款?)。
审核事务执行:客户端可通过重新执行历史记录中的每个事务Ti,并将计算的账本状态与版本数据库中相应的账本状态Si和事务输出Oi进行比较,来检查账本状态是否正确。
此机制允许客户端审计验证者,以确保正确执行事务。
三、执行事务
在Libra区块链中,更改区块链状态的唯一方法是执行一个事务。
本节内容概述了事务执行的要求,定义了事务的结构,解释Move虚拟机(VM)如何执行一个事务,以及描述Move语言的关键概念。
在最初版本的Libra协议中,只有有限的Move功能子集可提供给客户端。虽然Move是用于定义核心系统概念,如 Libra货币,但用户无法发布声明自己资源类型的自定义模块。这种方法允许Move语言和工具链在暴露给用户之前能够变成熟。这种方法还推迟了事务执行和数据存储中的可扩展性挑战,而它们正是通用智能合约平台所固有的问题。
正如我们在第10节中所讨论的,我们致力于通过Move语言来展示全面的可编程性。
3、1 执行要求
已知初始状态:所有验证者必须同意系统的初始或创始账本状态。
因为Libra区块链的核心组件(比如账户逻辑、事务验证,验证者选择以及Libra币)是由Move模块定义的,创始状态必须定义这些模块。创始状态还必须具有这些核心组件的足够实例化,以便事务可被处理(例如,至少有一个帐户必须能支付第一个事务;必须定义验证器集,以便集的法定数量的团体可对致力于第一个事务的确认者进行签名)。
为了简化系统的设计,系统的初始状态表示为空状态。然后通过定义特定模块的特殊事务T0创建创始状态,该事务定义要创建的特定模块和资源,而不是通过正常事务过程。客户端和验证者被配置为只接受以特定T0开头的账本历史记录,该T0是由其加密哈希标识的。
这些特殊事务不能通过共识协议添加到账本历史中,只能通过配置。(4)
确定性:事务执行必须具有确定性和密封性。这意味着事务执行的输出是完全可预测的,并且仅基于事务和当前账本状态中包含的信息。事务执行并不具有外部效应(例如与网络交互)。确定性和封闭性执行可确保多个验证者可以就同一事务序列产生的状态达成一致,即使事务是由每个验证者独立执行的。
这也意味着区块链的事务历史可从创始区块重新执行,以生成当前的账本状态(见第2.3节)。
计量:为了管理对计算能力的需求,Libra协议收取以 Libra币计价的交易费用(5)。这参照了以太坊普及的gas模型 [16]。
我们采用的方法,是选择具有足够容量的验证者来满足Libra生态系统的需求(见第8节内容)。这项费用的唯一目的,是在系统负载高于系统所提供承载能力时(例如,在遭到拒绝服务攻击)减少需求,系统的设计是,当其处于正常运行期间,当有足够的容量可用时,费用会是较低的。
这种方法不同于一些现有的区块链,这些区块链有时处理事务的需求会大于吞吐量。在这些系统中,费用会在出现高需求时激增,这是验证者的收入来源,但给用户造成了成本方面的问题。
费用的大小由两个因素决定:gas价格和gas消耗。每个事务都规定了提交者愿意支付的每单位gas的价格(以Libra为单位)。事务的执行动态地说明了它以gas消耗的计算能力。当系统发生堵塞时,验证者会优先执行gas价格更高的事务,并可能放弃价格较低的事务。这就减少了在系统高负荷状态下,事务的发起需求。
此外,一笔事务还包含了一个最大gas量,它指定了提交者愿意以规定价格支付的最大gas单位数量。在执行期间,虚拟机会跟踪使用的gas单位数量。如果在执行完成前达到最大gas限制,虚拟机将立即停止。这种事务所导致的部分更改都不会提交给状态。但是,事务仍会出现在事务历史记录中,而发送者的账户仍然是要为使用的gas付费的。
正如我们在本节中讨论的,Libra区块链核心逻辑的很多部分,是使用Move定义的,这包括gas费用的扣除。为了避免循环,虚拟机在执行这些核心组件期间禁用gas计量。这些核心组件必须在创始状态下定义。必须以防御性的方式编写,以防止恶意事务触发执行计算代价很高的代码。执行此逻辑的成本,可包括在为事务收取的基本费用中。
资产语义:正如我们在第2.1节中所解释的,账本状态直接对具有实际价值的数字资产进行编码。事务执行必须确保,在未经授权的情况下,不得复制、丢失或转让Libra币等资产。Libra协议使用Move虚拟机(第3.4节)安全地执行事务以及具有这些属性的自定义资产。
(脚注4:我们正在探索使用这种形式的特殊事务,来清晰地定义硬分叉的想法。客户端可指定 一种首选配置,声明类似的特殊事务(它对标准交易处理规则之外的模块和资源进行了特定的更改),应在第i版附加到账本历史当中。由于Libra协议的很多部分是使用Move语言表示的,所以这个类型的更新是可以表示出来的,但只需要对客户端软件进行配置更改。
脚注5:在第4.4节中,我们讨论了管理存储容量需求的方法。 )
3、2 事务结构
所谓一笔事务,是指包含以下数据的签名消息:
-
发送方地址:即事务发送者的账户地址。虚拟机会读取序列编号、身份验证密钥以及存储在地址的LibraAccount.T 资源的余额;
-
发送方公钥:与用于签署事务的私钥相对应的公钥。此公钥的哈希必须与发送方的LibraAccount.T资源存储的身份验证密钥匹配。
-
程序:要执行的Move字节码事务脚本、脚本输入的可选列表以及要发布的Move字节码模块的可选列表。
-
Gas价格:发送方愿意支付每单位gas的数量,以便执行此事务。
-
最大Gas量:事务中止前允许消耗的最大Gas单位数量。
-
序列号:一个无符号整数,必须等于发送方LibraAccount.T资源中的序列号。执行此事务后,序列号会增加1。由于对于给定的序列号只能提交一个事务,因此无法重放事务。
3、3 执行事务
执行事务,是通过在虚拟机内执行六个步骤来实现的。执行独立于账本状态的更新。首先,执行一个事务,是试图就其顺序达成共识尝试的一部分。由于执行是封闭的,因此它不会造成外部副作用。随后,如果达成共识,则将其输出写入账本历史。
执行一个事务,会执行以下这6个步骤:
1、核对签名:事务上的签名必须与发送方的公钥和事务数据匹配。此步骤只是事务本身的一个作用,它不需要读取来自发送方帐户的任何数据。
2、运行prologue:prologue对事务发送方进行身份验证,确保发送方有足够的Libra币支付交易中规定的最大数量的gas单位,并确保该事务不是前一个事务的重播。所有这些检查,都是通过LibraAccount模块的prologue过程在Move语言中实现的。在prologue执行期间,Gas计量是被禁用的。具体来说,这个prologue会做如下这些工作:
(1)检查发送方公钥的哈希是否等于发送方帐户下存储的身份验证密钥:如果没有这个检查,虚拟机将错误地接受具有密码学有效签名的事务,即使没有与帐户相关联密钥的通信。
(2)检查gas_price * max_gas_amount <= sender_account_balance :如果不进行此检查,虚拟机将执行可能在epilogue中失败的事务,因为它们将无法支付gas费用。
(3)确保事务序列号等于存储在用户帐户下的序列号:如果没有这项检查,恶意方可重放旧的事务(例如,Bob可以重放Alice支付给他的10个Libra币的这笔交易)。
3、验证事务脚本和模块:一旦事务prologue成功完成任务,虚拟机将使用Move字节码验证器对事务脚本和模块执行良构的检查。在实际运行或发布任何Move代码之前,这个字节码验证器检查诸如类型安全(type-safety)、引用安全(reference-safety,即没有悬空引用)和资源安全(resource-safety,即资源不会被复制、重复使用或意外销毁)等关键属性。
4、发布模块:事务的程序字段中的每个模块,都在事务发送方的帐户下发布。重复的模块名称是被禁止的,例如,如果事务尝试将名为M的模块发布到已包含模块名为M的账户中,则该步骤将会失败;
5、运行事务脚本:虚拟机将事务参数绑定到形式参数并执行事务脚本。如果此脚本执行成功完成,则脚本执行的写入操作和脚本发出的事件将提交给全局状态。如果脚本执行失败(例如,由于gas耗尽或runtime执行失败),则脚本中的任何更改都不会提交到全局状态。
6、运行epilogue:最后,虚拟机运行事务epilogue,向用户收取所用gas的费用,并增加发送方帐号序列号。就像prologue一样,这个事务epilogue 是Move LibraAccount模块的一个过程,且其是在禁用gas计量的情况下运行的。如果执行跃了步骤(2),包括步骤(3)、(4)或(5)失败,这一epilogue步骤也会始终运行。这个prologue步骤和epilogue步骤会一起工作,以确保在账本历史中的所有事务都是通过付费gas完成的。而不超过步骤(2) 的事务,不会被附加到账本历史。如果一笔事务超过了步骤(2),这个prologue步骤会确保账户拥有足够的Libra币以支付事务允许的最大gas单位数量。 即使事务用光了gas,epilogue步骤也可以按这个最大金额收取费用。
3、4 Move编程语言
正如我们所看到的,一笔事务是围绕一个Move字节码程序,经证实的包装器(wrapper)。
Move [10]是我们在设计Libra协议期间创建的一种新的编程语言,它在系统中扮演了三个重要的角色:
-
通过事务脚本启用灵活的事务;
-
允许用户定义的代码和数据类型,包括通过模块的“智能合约”;
-
支持Libra协议的配置和可扩展性(见第9节);
Move语言的关键特点是能够定义自定义资源类型,这些类型是受线性逻辑[21]启发的。用于编码可编程资产的资源类型,其表现类似于普通程序值:
资源可以存储在数据结构中,作为参数传递给过程,等等。然而,这个Move类型系统为资源提供了特殊的安全保障。资源无法被复制,只能移动。此外,资源类型只能由声明该类型的模块创建或销毁。这些保证,都是通过Move虚拟机静态地强制执行的。这使我们能够在Move语言中将Libra币表示为一种资源类型(这与以太币和比特币是不同的,它们在各自的语言中具有特殊的地位)。
我们已发布了一份关于Move [10]设计的更为详细的文章。在本节的其余部分内容中,我们简要地概述了用于事务执行的关键Move概念:开发Move事务脚本和模块,并使用虚拟机执行Move字节码。
编写Move程序。Move程序有三种不同的表示形式:源代码、优化中间代码 (IR)以及字节码。我们目前正在设计Move源语言,这将是一种符合人体工程学的语言,旨在使编写和验证安全代码变得容易。同时,程序员可以在Move IR中开发模块和事务脚本。Move IR足以用于编写人类可读的代码,它也可直接转换为Move字节码。未来的Move源语言和Move IR都可以编译成Move字节码,而后者是Libra协议所使用的格式。
我们使用可验证的字节码作为Move的可执行表示,原因有两个:
-
上述安全保证必须适用于所有Move程序。执行这些编译器中的保证是不够的。恶意方总是可选择通过直接使用字节码编写恶意代码来绕过编译器(作为事务执行的一部分,运行编译器会使执行速度变慢和更复杂,并且需要验证者信任完整编译器代码库的正确性),因此,该协议通过字节码验证(类型安全、引用安全和资源安全)来强制Move的所有安全保证,从而避免了对编译器的信任。
-
与高级源语言相比, Move基于栈的字节码指令更少。此外,每个指令都有简单的语义,可以通过更小的原子步骤来表示。这减少了Libra协议的规范足迹,并使得开发者更容易发现实现错误。
事务脚本:事务脚本是Libra协议的主要程序。事务脚本负责启用灵活的事务,因为脚本是一个任意的Move字节码程序。脚本可使用条件逻辑,以及执行本地计算,调用在账本状态下发布的模块的多个程序,这意味着脚本可执行富有表现力的一次性操作,例如为特定的一组接收者支付。
我们期望大多数事务脚本将执行一个包含通用函数的程序调用。例如,LibraAccount.pay_from_sender(recipient_address, amount) 程序封装了执行一笔对等式支付的逻辑。以recipient_address(接收方地址)和amount(金额)为参数并调用此过程的事务脚本与以太坊中的以太币传输事务相同[16]。
模块:模块是发布在账本状态中的代码单元。模块负责声明结构类型和程序。结构值包含可保存基元值的数据字段,例如整数或其他结构值。
每个结构必须标记为资源或不受限制(即非资源)。不受限制结构不受上述复制和销毁限制。但是,不受限制结构不能包含资源结构(直接或传递),并且不能在账本状态下的账户发布。
从高维度来说,模块/结构/程序的关系,类似于面向对象编程中的类/对象/方法的关系。但是,这里有一些重要的区别。一个模块可以声明多个结构类型(或零个结构类型)。在其声明模块之外不能访问任何数据字段(即,没有公共字段)。模块的过程是静态过程,而不是实例方法;过程中没有this或self的概念。Move模块类似于没有高阶函数的有限版本的ML样式模块 [22]。Move模块与以太坊和其它区块链平台的“智能合约”概念相关,但也有不同。一个以太坊智能合约包含了账本状态下发布的代码以及数据。而在Libra,模块包含代码值,资源包含数据值。在面向对象术语中,以太坊智能合约就像是在单个帐户地址下发布的单例对象。模块是创建资源的方法,它可以创建任意数量的资源,这些资源可以在不同的帐户地址下发布。
Move虚拟机:Move虚拟机为Move字节码实现验证程序和解释程序任务。字节码以基于栈的虚拟机为目标,使用过程本地操作数栈和寄存器。非结构化控制流通过goto和标签(label)进行编码。
开发人员在Move IR中编写事务脚本或模块,然后将其编译成Move字节码。编译将结构化控制流构造(例如,条件、循环)转换为非结构化控制流,并将复杂表达式转换为少量的字节码指令,以控制一个操作数栈。这个Move虚拟机通过验证然后运行这个字节码来执行一笔事务。
这个Move虚拟机支持少量类型和值:布尔运算(booleans)、无符号64位整数,256位地址、固定大小字节数组、结构(包括资源)和引用。结构字段不能是引用类型,这将阻止在账本状态中存储引用。
这个Move虚拟机并没有堆(heap):本地数据在栈(stack)上分配,并在分配程序返回时释放。所有持久性数据必须存储在账本状态中。
四、经验证的数据结构和存储
在执行事务之后,验证者将对逻辑数据模型的更改,转换为用于表示数据库的已验证数据结构[7、8、9]的新版本。此数据结构的简短验证器是对账本历史的约束性承诺,其中包括新执行的事务。与事务执行一样,此数据结构的生成也是确定性的。
共识协议使用这个验证器来同意事务的顺序及其结果的执行(我们将在第5节内容详细讨论共识)。作为提交事务区块的一部分,验证者将短验证器集体签名至结果数据库的新版本。
使用这种集体签名,客户端可信任数据库版本代表数据库账本历史的完整、有效和不可逆状态。
客户端可查询任意验证者(或数据库的第三方副本)来读取特定的数据库值,并使用验证器以及简短的证明来验证结果。
因此,客户端不需要信任执行查询的一方,以确保结果读取的正确性。
Libra协议中的数据结构基于Merkle树,并受到其他区块链的启发;但是,在某些情况下,我们做出了一些稍微不同的决定,下面我们会进行强调。首先,我们简要讨论创建验证器的Merkle树方法。然后我们描述经验证的数据结构,从数据结构的根开始,然后我们讨论其中的子结构。图3描述了数据结构,并提供了本节内容的可视化指南。
图片3: (1)账本历史结构的根哈希是系统完整状态的验证器,即(2)由法定人数验证者签署。当事务添加到数据库时,提交到账本历史的验证器(第4.2节)将增长(用虚线箭头表示)。账本历史的每个子叶都提交至一个(3)TransactionInfo结构。此结构承诺(4)签署的事务(Ti),(5)该事务(Ei,第4.5节)期间产生的事件列表,以及(6)该事务 (Si, 第4.3节)执行后的状态。该状态是一颗稀疏Merkle二叉树,其中每个子叶上都有一个 (7)帐户斑点。
4、1 认证数据结构的背景
认证数据结构允许验证者V控制一个简短的验证器a,它对更大的数据结构D形成了约束承诺。一个不受信任的证明者P,其控制了数据结构D,计算f(D) → r并向验证者返回r (数据结构D上某些函数f的计算结果)以及π(正确计算结果的证明)。验证者V可以运行Verify(a, f, r, π),当且仅当 f(D) = r时返回真(true)值。在Libra区块链的背景中,证明者通常是验证者,而验证者是执行读取查询的客户端。但是,客户端(即使只有部分数据库副本的客户端)也可以充当验证者,并为其他客户端执行经验证的读取查询。
图片4:一颗存储D = {0 : s0, . . .}的 Merkle树。如果f是获取第三项(用虚线显示)的函数,则 r = s2 ,π = [h3, h4] (这些节点用虚线显示)。Verify(a, f, r, π) 会验证 a ? = H(h4∥H(H(2∥r)∥h3)).
Merkle树[11]是一种常见的认证数据结构形式,其用于存储整数和字符串之间的映射。在一颗大小为2^k的Merkle树中,结构D将每一个整数键i ∈ [0, 2^k)映射到一个字符串值si。
验证器由字符串创建的完整二叉树的根组成,将子叶标记为 H(i||si),将内部节点标记为H(left||right),其中H是一个加密哈希函数(我们称之为哈希)(6)。证明者希望验证的函数f,是一个包含键值对 (k, v)在映射D中的证明。
P通过返回由节点i的每个祖先的兄弟姐妹的标签组成的证明π,来验证对D中项目i的查找。图4显示了对大小为4的Merkle树中项目3的查找。项目3的节点用虚线表示,π中包含的节点用虚线表示。
(脚注6: 安全Merkle树必须使用不同的哈希函数,哈希子叶和内部节点,以避免两种类型的节点混淆。为了简单起见,我们在示例中省略了这个细节,而Libra协议使用了一种独特的哈希函数,用于区分不同的哈希函数类型,以避免基于类型混淆的攻击[23]。)
4、2 账本历史
从比特币 [24]开始的大多数区块链,用一个包含单个祖先哈希的区块,维护一个共识一致的每个交易区块的链接列表。
这一结构就导致了客户端效率低下。例如,信任某区块B并希望验证祖先块B'中的信息的客户端,需要获取和处理所有的中间祖先区块。
Libra协议使用单个Merkle树,为账本历史提供一个经验证的数据结构。图3解释了支持Libra数据库的完整数据结构,包括包含事务的账本历史,这些记录依次包含有关数据库状态、事件和帐户的信息。账本历史表示为一颗Merkle树,它将顺序数据库版本号i映射到一个TransactionInfoi结构。每个TransactionInfoi结构都包含一个已签名的事务 (Ti), 执行Ti(Si,在第4.3节中讨论)后的状态验证器,以及 Ti(Ei,在第4.5节中讨论)生成的事件验证器。
与其他Merkle树一样,这个账本历史支持O(log n)大小的证明(其中n是已处理的事务总数),以验证一个特定TransactionInfoi的查询。 当客户端希望查询版本I的状态,或查找在版本I中生成的事件时,它将使用包含的状态或事件列表验证器对TransactionInfoi 执行已验证的查找。
具体来说,账本历史使用Merkle树累加器[25,26]方法来形成Merkle树,其还提供有效的附加操作。此操作很有用,因为可以通过将新事务附加到旧账本历史来进行递增计算。Merkle树累加器还可以生成一个有效的证明,证明在给定验证器a提交到事务I之前的账本信息时,验证器a′提交到账本信息最多 j > i具有与事务i相同的历史。换句话说,这证明了a是a′的前缀。这些证明可有效地验证一个账本历史承诺是另一个承诺的延续。
修剪存储:账本历史Merkle累加器的根哈希,是系统完整状态的验证器。它在系统的每个现有版本、发送的每个事务以及生成的每个事件上提交状态。虽然第4.3节中描述的状态存储允许有效地存储账本状态的多个版本,但验证者可能希望减少以前版本占用的空间。验证者可自由地删除旧状态,因为它们不是处理新事务所必需的。Merkle树累加器支持修剪历史数据,其只需要O(log n)大小的存储来附加新记录[25]。
系统的任何副本都可以自由保留整个状态,并可以将其数据的真实性跟踪到由共识协议签名的根哈希。副本比验证者更易于扩展。副本不需要得到保护,因为它们不参与共识,可创建多个副本来并行处理读取查询操作。
4、3 账本状态
一个账本状态Si表示版本i中所有账户的状态,作为键值对的映射。密钥基于256位帐户地址,它们的对应值是账户确认者(在第4.4节中讨论)(7)。使用类似于图4的表示法,将映射表示为大小为2^256 的一棵Merkle树,虽然一棵大小为2^256的树是一种难以处理的表示,但可以应用优化来形成一棵稀疏Merkle树。图5显示了两种用于将简单实现(1)转换为有效实现的优化方法。首先,完全由空节点组成的子叶将替换为证书透明度系统(certificate transparency system) [27]中使用的占位符值 ( 2 ) 。
这种优化创建了一个可跟踪大小的表示,而没有实质性地改变Merkle树的证明生成。然而,子叶总是存储在二叉树的底层,这意味着结构需要在每次子叶修改时计算256个哈希。第二种优化,用单个节点( 3 )替换由一个子叶组成的子树。预期中,任何给定项的深度都是O(log n),其中n是二叉树中项目数。这种优化减少了在映射上执行操作时要计算的哈希数。
(脚注7: 地址的哈希作为密钥。即使地址是作为公钥的哈希,恶意方也可以在与真实地址几乎冲突的地址创建一个新帐户。虽然无法从此帐户发送任何事务,因为与该帐户地址对应的私钥未知,但树中存在此近似冲突,会增加邻近地址的证明长度。在将地址用作密钥之前对其进行哈希处理,可减少此类攻击的影响。)
图5 : 一棵稀疏Merkle树存储D = {01002 : A, 10002 : B, 10112 : C}的三个版本 有效实现,如Libra Core,可以优化它们的磁盘布局和节点的批处理层,以避免在查找过程中的请求。(8)
(脚注8: 这种优化使磁盘性能与以太坊中使用的16元帕特里夏Merkle树方法相似,但其证明时间更短。)
当稀疏Merkle树在执行事务后更新时,新的树将重用上一版本的未更改部分,形成一个持久的数据结构[28,29]。如果一个事务修改了系统中n个帐户中的m个帐户,则在与以前版本不同的新账本状态树中平均创建了O(m · log n) 个新分支和子叶。这种方法允许验证者有效地存储账本状态的多个版本。此功能还允许在处理事务后高效地重计算账本状态验证器。
我们考虑过基于AVL树结构,它比稀疏Merkle二叉树结构提供了更优的最坏情况证明长度[30]。然而,稀疏Merkle树方法允许实现者进行优化,例如跨服务器共享存储以及并行根哈希计算。
4、4 账户
从逻辑层来讲,帐户是存储在帐户地址下的资源与模块的集合。从物理层来讲,帐户被视为字节数组值访问路径的有序映射。访问路径是一个分隔字符串,类似于文件系统中的路径。
在协议的第一次迭代中,我们将一个帐户序列化(serialize)为按访问路径排序的访问路径和值列表。帐户的验证器是此序列化表示的哈希。注意,此表示要求在对帐户进行任何修改之后,对整个帐户重新计算验证器。这个操作的开销是O(n),其中n是完整帐户的字节表示长度。此外,从客户端读取,需要完整的帐户信息来验证其中的任何特定值。
我们在账户中存储数据的策略与其他系统(如以太坊)不同(9)。我们的方法允许Move语言通过将帐户表示为访问值路径的有序映射,以提供更好的编程抽象。这种表示允许Move通过虚拟机有效地管理资源的保护。Move鼓励每个用户在他们自己的账户当中持有资源。在最初发布的Libra当中,我们针对小客户进行了优化。而在未来的版本中,我们可能会考虑支持更高效的结构来代表更大的账户,比如说Merkle AVL二叉树结构 [30]。
(脚注9: 相反,以太坊[16]使用Merkle树将数据存储为稀疏memory映射,该二叉树表示256位key到256位值的无序映射。这种方法使以太坊能够有效地处理大的账户。)
账户收回和重加工:我们预计,随着系统的使用,最终与帐户相关的存储增长可能会成为一个问题。正如gas鼓励负责任地使用计算资源(见第3.1节),我们预计存储可能需要一种租赁机制。我们正在评估最适合生态系统的租赁机制。我们讨论了一个可应用于任何确定过期时间的策略选项,在时间到期之后,数据就可以被收回。
在每个影响账户的事务执行之后,虚拟机计算逻辑过期时间,在该时间内,为帐户分配的存储可被释放。虚拟机可自由地应用任何确定性方法来确定到期时间。这种政策的一个例子是收取以Libra币计价的费用,该费用是基于账户的存储时间及其大小确定的。
帐户的验证器表示为H(H(AccountBlob)||(ExpirationTime),它对帐户的过期时间进行编码。到期后,虚拟机拒绝访问帐户,从而引发一个错误。由于AccountBlob是不可访问的,因此在ExpirationTime之后,验证者可以自由删除此数据。然而,验证者允许事务在过期后通过重新计算其内容来重新激活帐户。该事务必须包含AccountBlob的预映射(preimage),并具有包含进一步存储成本的关联交易费用。通过重新计算和检查与帐户关联的哈希值(不删除)来确保重新获取内容的真实性。这种租赁实现方法是对现有帐户收回方案的改进,而现有方法要求第三方发送一个事务来删除帐户的存储。
4、5 事件
Ei 是Ti执行期间发出的事件列表,每一个事件都以一个子叶存储在Merkle树中,该二叉树Ei形成一个验证器。事件被序列化为 form (A, p, c)的一个元组,它表示发出事件(A)、数据有效负载(p)以及计数器值(c)的事件结构访问路径。这些元组按Ti执行期间发出的事件顺序j进行索引。根据惯例,我们把事件j→(a,p,c)包含在Ei中,表示为 (j,(A, p, c)) ∈ Ei。Ei的验证器包含在TransactionInfoi中,因此,验证者可构造一个包含证明,证明在第i个事务中,第j个事件是 (A, p, c)。
包含在每个事件中计数器c,在允许客户端检索给定访问路径A的事件列表方面发挥了特殊作用。客户端也可以确保列表是完整的。在Move语言中,表示具有访问路径A的事件的事件结构,为其已发出的事件总数维护一个计数器C。
此计数器存储在账本状态中,并在每次事务执行,在访问路径A上发出事件后进行递增。
客户端可获取最近账本状态的验证器,查询与事件结构关联的计数器以获取访问路径A,并检索事件总数C。
然后,客户端可以查询不受信任的服务,以获取访问路径A上的事件列表。查询响应由一组元组(i, j, A, p, c)组成,每个元组对应一个事件,其中i是发出事件的事务序列号,j是此事务中事件发出的顺序。可以为每个事件提供 (j,(A, p, c)) ∈ Ei的关联包含证明。由于客户端知道访问路径A发出的事件总数,因此它们可确保不受信任的服务提供了此数量的不同事件,并按其序列号0 ≤ c < C对其进行排序。这使客户端确信为A返回的事件列表是完整的。
此方案允许客户端保存对访问路径A上事件的已验证订阅。客户端可定期为事件访问路径A触发总计数器C,以确定订阅是否是最新的。例如,客户端可使用它在监视的地址上维护对传入付款事务的订阅。不受信任的第三方服务可为按访问路径索引的事件提供源,而客户端可有效地验证返回的结果。
五、拜占庭容错共识
拜占庭容错共识协议允许一组验证者创建单个数据库的逻辑层次。这种共识协议在验证者之间复制提交的事务,对当前数据库执行潜在的事务,然后就事务顺序和结果执行的绑定承诺达成一致。因此,所有验证者都可以按照状态机器复制范式[31]为给定的版本号维护相同的数据库。
Libra 区块链使用了一种HotStuff [13]共识协议的变体,叫做LibraBFT协议。在传统的DLS[32]和PBF[33]以及更新的Casper[34]和Tendermint[35]的部分同步模型中,它提供了安全性和活力。本节概述LibraBFT共识机制中的关键决策。LibraBFT的完整报告[12]中包含了更详细的分析,包括安全性和活性的证明。
即使存在拜占庭错误[36],验证者之间也必须就数据库状态达成一致。拜占庭错误模型允许一些验证者不受约束地任意偏离协议,除了在计算上的界限(因此不能破坏密码学假设)。拜占庭错误是最坏的情况下的错误,在这种情况下,验证者串通一气,恶意破坏系统。一种可以容忍由恶意或被黑客攻击的验证者引起的拜占庭错误的共识协议,也可以减轻任意的硬件和软件故障。
LibraBFT假设一组3f + 1的投票分布在一组验证者中,这些验证者可能是诚实的,也可能是拜占庭式的。当最多f票由拜占庭验证者控制时,LibraBFT仍然是安全的,它可以防止双重支出和分叉等攻击。LibraBFT保持活性——向客户端提交事务,只要存在一个全球稳定时间(GST)之后,诚实的验证者之间的所有消息将在一个最大的网络延迟δ(这是引入的部分同步模型[32]发送到其他诚实的验证者。
除了传统的保证之外,LibraBFT还在验证者崩溃和重启时维护安全性——即使所有验证者同时重启。
LibraBFT的概述:验证者接收来自客户端的交易,并通过一个共享的mempool协议来彼此共享这些事务。然后LibraBFT协议按顺序进行。在每轮中,都有一个验证者会扮演领导者(leader)的角色,并提出一个事务区块来扩展一个包含完整的以前事务历史的经认证的区块序列(请参阅下面的法定人数证书quorum certificate)。
验证者接收提议的区块并检查其投票规则,以确定是否应投票支持对该区块进行验证。这些简单的规则确保LibraBFT的安全性——它们的实现可以被清晰地分离和审计。如果验证者打算为这个区块投票,它将执行该区块的事务,而不受外部影响。这将导致验证器对数据库的计算,从而促成了区块的执行。
这个验证者然后发送一个为这个区块和数据库验证人进行的投票给这个leader。leader收集这些投票,形成一个法定人数证书(quorum certificate(QC)),它为这个区块提供2f +1票的证据,并将该QC广播给所有验证者。
当满足连续的3-chain提交规则时,区块就会被提交。如果在第k轮有QC,并且在第k + 1轮和第k + 2轮有两个区块和QC确认,则区块被提交。提交规则最终允许诚实的验证者提交一个区块。LibraBFT保证所有诚实的验证者最终都将提交该区块(以及从它链接的区块的序列)。一旦提交了一个区块序列,执行事务所产生的状态就可以持续下去并形成一个复制的数据库。
HotStuff范例的优点:我们针对验证者的性能、可靠性、安全性、健壮实现的易用性和运营开销等方面评估了几个基于BFT的协议。我们的目标是选择一种协议,该协议最初将支持至少100个验证者,并且能够随着时间的推移发展到支持500 - 1000个验证者。选择HotStuff协议作为LibraBFT的基础有三个原因:(1)安全性参数的简单性和模块化;(2)容易将共识与执行相结合;(3)早期实验中的性能表现良好。
这个HotStuff协议分解为安全模块(投票和提交规则)以及活性模块(pacemaker)。这种解耦提供了独立开发和并行测试不同模块的能力。由于投票和提交规则简单,协议安全性易于实现和验证。将执行作为共识的一部分进行集成是很简单的,以避免在基于领导者的协议中由非确定性执行引起的分叉问题。
最后,我们的早期原型证实了HotStuff中独立测量的高吞吐量和低事务延迟[13]。我们没有考虑基于工作量证明(PoW)的协议,因为它们的性能差,能源(和环境)成本高[24]。
HotStuff扩展及修改:在LibraBFT中,为了更好地支持Libra生态系统的目标,我们扩展和调整了核心HotStuff协议,并以多种方式进行实现。
重要的是,我们重新制定了安全条件,并提供安全、活力和乐观反应的扩展证明。我们还实现了一些附加功能。首先,我们让验证者集体签署区块的结果状态,而不仅仅是事务序列,从而使协议更能抵抗不确定性错误。这还允许客户端使用QC来验证从数据库中读取的内容。第二,我们设计了一个传出明确超时的pacemaker资源管理器,验证者依赖于这些pacemaker中的法定数量来进入下一轮,而不需要同步时钟。第三,我们设计了一个不可预测的leader选举机制,其中一轮的leader由最新提交区块的提议者使用可验证随机函数(VRF) [37]确定。
这种机制限制了恶意方对leader发起有效拒绝服务攻击的时间窗口。第四,我们使用聚合签名[38]来保留签署QC的身份验证者。这使我们能够向有助于QC的验证者提供激励(详见9.3节内容)。它也不需要复杂的阈值密钥设置[39]。
验证者管理:Libra协议使用了一个Move模块管理一组验证者。这在共识系统和定义可信验证者集的密码学经济系统之间创建了一个清晰的分离。我们将在第9.2节中讨论Libra区块链对该合约的实施。以这种方式抽象验证者管理,是通过定义Move中的核心区块链原语而获得的灵活性的一个例子。
对验证者组的每次更改,都定义了一个新的epoch期。如果一个事务导致验证者管理模块更改验证者集,该事务将是当前epoch期提交的最后一个事务。该区块或该epoch期以后的区块中的任何后续事务,都将被忽略。一旦事务被提交,新的验证者集就可开启共识协议的下一个epoch期。
在一个epoch期内,客户端不需要同步每个QC。由于已提交的QC包含对所有以前状态的绑定承诺,客户端只需要同步到当前epoch期的最新可用QC。如果此QC是其epoch期的最后一个,则客户端可以看到新的一组验证者,更新其epoch,然后再次同步到最新的QC。如果验证者选择按照第4.2节的描述删减历史记录,那么它需要保留至少足够的数据,以向客户端提供验证者集更改的证明。
验证者管理合约,必须提供满足由共识协议要求的安全属性的验证者集。拜占庭验证者不能控制超过 f的投票权。投票权必须在epoch期间以及epoch之后的一段时间内保持诚实,以允许客户端与新配置同步。离线时间超过此时间段的客户端需要使用一些外部真实性源重新同步,,以获取他们信任的检查点(例如,从其用于接收更新软件的源重新同步)。此外,验证者集不能过于频繁地转动,以致于破坏系统的性能,或者导致客户端为过多数量的epoch期下载QC。我们计划研究最佳的epoch期长度,预计这个时间会小于1天。
六、网络设计
Libra协议,与其他去中心化系统一样,需要一个网络基层来支持其成员之间的通信。验证者之间的共识和共享的mempool协议都需要通过互联网进行通信,如第5节和第7节所述。
这个网络层在设计上是通用的,灵感来自libp2p [40]项目。它目前提供了两个主要接口:(1)远程过程调用(RPC)和(2)DirectSend,实现了向单个接收方发送“即发即弃”(fire-and-forget)类型消息。
验证者之间的网络实现为一种点对点系统,使用了Multiaddr [41]方案进行对等(peer)寻址,使用TCP用于可靠传输,Noise[42]用于身份验证和完整的端到端加密,Yamux[43]用于在单个连接上的多路复用子流,push式gossip用于对等节点发现。每个新的子流都被分配了一个由发送方和接收方都支持的协议。每个RPC和DirectSend类型都对应一个这样的协议。
这个网络系统使用与共识系统相同的验证者集管理智能合约,作为当前验证者集的一个真实性来源。这种合约持有每个验证者的网络公钥和共识公钥。一个验证者通过监视这个智能合约中的更改来检测验证者集中的更改。
要加入这个验证者间网络,一个验证者必须使用这个合约定义的最新验证者集中的网络公钥进行身份验证。启动一个验证者则需要一组种子对等节点(peers),首先对要加入的验证者进行身份验证,使其成为验证者间网络的合格成员,然后与这个新对等节点共享它们的状态。
Libra协议中的每个验证者都维护着系统的完全成员关系视图,并直接连接到需要与之通信的任何验证者。不能直接连接的验证者则被假定为属于系统拜占庭错误的范围。
图6:写入事务通过Libra Core内部组件的流程
每个验证者的健康信息,使用周期性活性试样来确定,不会在各验证者之间进行共享;相反,每个验证者直接监视它的对等节点验证者的活动。我们期望这种方法在需要部分成员视图、复杂的故障检测器或通信中继之前,可以扩展到几百个验证者。
七、Libra Core实现
为了验证Libra协议,我们构建了一个开源的原型实现 Libra Core[6]。这个实现是用Rust语言编写的, 我们选择Rust是因为这种语言专注于实现安全的编码实践,及其对系统编程的支持和高性能属性。我们将系统的内部组件拆分为gRPC [44]服务。模块化允许更好的安全性;例如,共识安全组件可以在单独的进程中运行,甚至可以在不同的机器上运行。
Libra 区块链的安全性取决于验证者、Move程序和Move 虚拟机的正确实现。目前,我们正在解决Libra Core存在的这些问题。这包括隔离代码中有助于一个验证者在共识期间签署一个事务区块的部分,并应用措施来增强这些组件正确性的保证(例如,广泛的测试、正式规范和正式验证)。高保证性方面的工作,还包括确保代码依赖项的安全性(例如,代码评审过程、源代码控制、开源库依赖项、构建系统和版本发布管理)。
7.1 写入请求生命周期
图6展示了Libra Core中支持对去中心化数据库进行写入操作的事务的生命周期。本节深入研究事务如何通过验证者的内部组件进行流动。
当客户端希望向数据库提交事务时,就会发起一个请求(request)。我们目前假设客户端有一个带外机制来查找要提交事务的验证者地址——这一机制的最终版本尚未设计出来。
准入控制(Admission control)。在接收一笔事务时,一个验证者的准入控制组件会执行初始语法检查(1)抛弃那些永远不会被执行的畸形事务。尽早进行这些检查可避免把资源用在垃圾事务上。准入控制可能访问VM(2),VM使用存储组件执行检查,如确保账户有足够的余额支付事务所需的gas。接纳控制组件设计,组件的验证器可以运行多个实例。这种设计允许验证者处理规模的事务和减轻拒绝服务攻击。这个准入控制组件的设计,可以使一个验证者运行多个组件实例。这种设计允许验证者扩展传入事务的处理,并减少拒绝服务攻击。
Mempool。通过准入控制组件的检查的事务将被送入验证者的Mempool,Mempool容纳着等待在内存缓冲区被执行的事务(3)。Mempool中可能容纳着来自同一个地址的多笔事务。因为Mempool可以一次处理多笔事务,它能执行检查,比如验证同一地址上的一系列操作是否都能够支付准入控制系统无法支付的gas。使用这个共享Mempool协议(4),一个验证者可以在自己的Mempool中与其他验证者共享事务,将从其他验证者接收的事务放到自己的Mempool中。
共识。验证者通过从其Mempool中选择一系列事务来创建区块。当验证者充当共识协议的领导者(leader)时,它从其Mempool(5)中组成一个事务区块。它将此事务区块作为一个提议发送给其他验证者(6)。共识组件负责协调所有验证器之间关于这些事务区块的顺序及其使用LibraBFT协议的执行结果的协议(第5节)。
事务执行。作为达成协议的一部分,事务区块被传递给执行器组件(7),负责管理VM(8)中事务(第3节)的执行。这种执行发生在事务达成协议之前。这种早期执行是安全的,因为事务是具有确定性的,并且没有外部影响。在执行区块中事务之后,执行组件会构建一个这些事务的账本历史(第4.2节)。这个账本历史验证者将返回到共识组件(9)。
这个leader然后将尝试组成一个法定数量证书链(第5节)来就这个账本验证者达成共识,其中每一个证书都由一组验证者来进行签名,至少获得2f+1个投票。
提交一个区块。一旦共识算法达成一致,任何诚实的验证者都能确保所有其他诚实的验证者最终将提交一个一致的账本历史。验证者可以从执行组件的缓存中读取区块执行的结果并更新其本地数据库存储(10)。
7.2 读取请求生命周期
客户端还可以提交读取请求,为去中心化数据库中帐户的内容查询验证者。读取请求不会改变状态,可以在本地处理,而不需要经过共识。读取请求被提交到验证者的准入控制组件。准入控制组件执行初步检查,从存储中读取数据,然后将结果发送回客户端。这个响应附带一个法定数量证书(如第5节中描述),其中包含一个根哈希(如第4节中描述)。
QC允许客户端对查询的响应进行身份验证。这个客户端使用与虚拟机相同的技术,使用逻辑数据模型(第2节)来解释帐户的原始字节。例如,要读取地址a 的账户余额,客户端需要解码嵌入到该帐户的原始字节中的LibraCoin.T资源。
八、 性能
Libra协议的使命是支持一个全球金融基础设施。性能是实现这一目标的一个组成部分。我们将讨论区块链性能的三个组成部分:
-
吞吐量:区块链每秒可处理的事务数;
-
延迟:客户端向区块链提交事务与另一方看到这笔事务之间的时间;
-
容量:区块链存储大量帐户数的能力;
Libra协议还处于原型阶段,因此我们还没有具体的性能指标,但我们预计Libra协议在最初发布时可支持每秒1000笔支付事务,事务提交到确认的最终时间为10秒。随着时间的推移,我们希望能够增加系统的吞吐量,以满足网络的需求。我们预计,许多支付事务将发生在链下进行,例如,在托管钱包内或通过使用支付通道[45]。因此,我们认为链上每秒支持1000笔事务,将满足生态系统的初始需求。Libra协议旨在通过以下几种方式来实现这些目标:
协议设计:Libra协议的很多元素都是基于性能挑选的。例如,LibraBFT算法在三轮网络通信中获得共识结果,不需要任何实时延迟就区块进行提议或投票。这使得提交延迟只受验证者之间的网络延迟的限制。
我们还考虑采用并行化和分片(sharding)来选择协议的元素。计算验证者的稀疏Merkle树方法允许跨多台机器对数据库进行分片((这增加了容量)或并行处理更新(这增加了吞吐量)。初始事务验证(包括计算昂贵的签名验证)也可以实现并行化。
验证者挑选:与大多数服务一样,Libra区块链的性能取决于操作它的底层验证器的性能。去中心化与性能之间存在着一种妥协。对资源非常充足的验证者需要,限制了可执行该角色实体的数量。然而,资源极度不足的验证者的存在,将限制整个系统的性能。
我们倾向于采取一种平衡的方法,将目标锁定在可以在商用硬件上运行的节点,许多实体都能买到这些硬件。但是,我们假设了这些节点运行在服务器级别硬件上,并且与数据中心能够连接良好。
根据我们进行的一次近似分析显示,该系统很可能能满足每秒1000笔事务的需求。
带宽:如果我们假设每笔事务需要5 KB的流量——包括通过mempool接收事务、重播事务、接收来自leader的区块以及复制到客户端的成本——那么验证者需要40 Mbps的网络连接来支持每秒1000笔事务。这种级别的带宽还是能够广泛获得的。
CPU:签名验证是与支付转换操作相关的重要计算成本。我们设计了允许并行验证事务签名的协议。采用Modern签名方案,一个普通商用CPU就可以支持每秒1000多个事务验证。
磁盘:从大型服务器供应商那里,我们可以获得16TB SSD存储的服务器。由于当前状态是验证者处理事务所需的惟一信息,所以我们估计,如果帐户大小约为4 KB(包括所有形式的开销),那么这将使得验证者可存储40亿个帐户。我们预计,磁盘存储的发展、验证器向多个分片的扩展,以及经济激励措施将允许商用系统保持使用这个系统。
历史数据的增长可能会超出单个服务器所能处理的数量。验证者可随意丢弃那些在处理新事务的过程中所不需要的历史数据(参见第4.2节);但是,那些希望查询来自过去事务事件的客户端可能对这些数据感兴趣。由于验证者签署了对该数据的绑定承诺,客户端可自由地使用任何系统访问数据,而不必信任交付数据的系统。我们希望这种类型的读取流量能够很容易地通过并行进行扩展。
九、 用Move来实施Libra的生态系统政策
Libra 区块链是一个独特的系统,它平衡了传统金融网络的稳定性和由加密经济手段管理的系统所提供的开放性。正如我们在第1节中讨论的,Libra协议旨在支持Libra生态系统实现新的经济[4]和治理[3]策略。该协议指定了一个灵活的框架,该框架在关键系统组件(如本地货币、验证者管理和事务验证)中是参数化的。在本节中,我们将讨论Libra 区块链如何使用Move编程语言定制这些组件。我们的讨论集中在协调网络参与者和验证者激励的挑战,以及支持Libra生态系统的运行、治理和演化的挑战。
9.1 Libra币
许多加密货币没有真实世界资产的支持。因此,投资和投机一直是这些加密货币主要用例。投资者购买这些货币时,往往认为它们会大幅升值,然后可以以更高的价格卖出。对这些货币长期价值的信念波动,导致了相应的价格波动,有时会导致价值的大幅波动。
为了推动广泛采用,Libra被设计成一种货币,任何用户都会知道,Libra今天的价值与明天甚至更远未来的价值都是接近的。储备是实现价值保值的关键机制。通过储备,每枚币都有一套稳定的流动资产作为后盾。有了与储备挂钩的一群具有竞争力的流动性提供者的存在,用户就可以放心,他们所持有的任何一枚币都可以以高于或低于底层资产价值的狭窄价差,以法定货币出售。这从一开始就赋予了Libra内在价值,并有助于防范现有加密货币所经历的投机性波动。
Libra的储备资产是一个低波动性资产的集合,包括来自稳定且声誉良好的国家央行发行的法币和政府证券。由于Libra的价值实际上与一篮子法定货币挂钩,从任何特定货币的角度来看,Libra的价值都会有波动。储备金的组成旨在减轻这些波动的可能性和严重性,特别是在负面方向(例如,经济危机中)。为此,该货币篮子的结构考虑到了资本保值和流动性。
该储备由Libra协会管理(见第9.2节),该协会发布了一份关于该储备运行的详细报告[4]。用户不直接与储备接口。相反,为了支持更高的效率,会有一些授权转售商,他们是由Libra协会唯一授权的实体,来处理大量的法定货币和Libra在储备中的流入和流出。这些授权转售商与事务所和其他买卖加密货币的机构相结合,为那些希望将现金与Libra进行来回兑换的用户提供流动性。
为了实施这一计划,Libra的合约允许Libra 协会在需求增加时铸造新的Libra币,以及在需求减少时销毁它们。该协会不制定货币政策。它只能根据授权经销商的要求铸造和销毁币。用户不必担心这种关联会导致通胀或货币贬值:要铸造新硬币,必须有相应的法定存款准备金。
使用Move自定义Libra合约的能力,允许在不修改底层协议或实现该协议的软件的情况下定义此计划。此外,还可以创建其他功能,比如需要多个签名来铸造货币和创建有限数量的密钥来提高安全性。
9.2 验证者管理和治理
共识算法依赖于验证者集管理Move模块来维护当前的验证者集,并管理验证者之间的投票分配。这种合约负责维护一个验证者集,其中3f + 1的总投票中最多有f票由拜占庭验证者控制。
最初,Libra区块链只向创始成员(Founding Members)授予投票权,这些实体:(1)满足一组预定义的创始成员资格标准[46],(2)拥有Libra投资代币(Libra Investment Tokens)。这些规则有助于确保对拥有一个安全且活动的验证者集的安全性需求。使用创始成员资格标准确保了创始成员都是已建立起声誉的组织,使其不太可能出现恶意行为,并暗示他们将勤奋地捍卫自己的验证者来抵御外部攻击。Libra投资代币代表对准备金利息回报的预期,进一步激励验证者去维持系统的运行。由于储备中的资产风险低、收益低,只有当网络成功、储备规模大幅增长时,早期投资者才能获得超额回报。
虽然这种评估验证者资格的方法是对传统许可区块链(通常形成一组封闭的业务关系)的改进,但我们希望Libra 区块链完全没有许可。为此,我们计划逐步过渡到权益证明(PoS)[47]系统,在该系统中,验证者的投票权与他们所持有的Libra币数量成正比。这将把生态系统的治理移交给它的用户,同时通过要求验证者持有稀缺资源并将它们的动机与健康系统操作结合起来,以防止Sybil攻击[48]。这种过渡需要(1)生态系统足够大,以防止单个坏行为者造成破坏;(2)为不想成为验证者的用户提供一个具有竞争性和可靠的委托市场;(3)解决Libra在Staking过程中面临的技术和可用性挑战。
Libra生态系统治理的另一个独特点是验证者形成了一个真实世界的实体,即非盈利的Libra协会,由一个验证者委员会管理。该协会的每一个理事会成员代表着每个验证者。理事会中成员的投票权重与共识协议中验证者的投票权重相同。协会执行的任务包括管理储备、评估验证者资格标准以及指导Libra协议的开源开发。
Move使将验证器管理和治理规则编码为一个模块成为可能。Libra投资代币可以使用Move资源来实现。Move通过将投资代币或Libra币包装在资源中,从而防止获得底层资产,从而允许对它们进行抵押。已抵押的资源可用于计算验证者的投票权。这个合约可以配置变更生效的时间间隔,以减少验证者集的变动。
Libra 协会的运作也得到了Move的帮助。由于该协会是储备的运营商,它可以创建Move模块,将Libra的铸造和销毁权力委托给与授权经销商进行交互的操作部门。该业务部门还可以评估潜在创始成员是否符合资格标准。Move允许灵活的治理机制,比如允许理事会行使其权力,通过投票收回其授权。
该协会发布了一份详细的文件,概述了其拟议的结构[3]。协会中的所有治理都源于验证者理事会——该理事会拥有最终的权利来维护提供给协会的任何授权。因此,随着验证者集从最初的创始成员集更改为基于PoS的集,整个Libra生态系统的治理也在不断发展。
9.3 验证者的安全与激励
在初始设置中,使用创始成员作为验证者,我们认为每个验证者的机构声誉和经济激励足以确保拜占庭验证器控制的投票不超过f。然而,在未来,一个以币所有权为代表的开放系统将需要一个完全不同的市场设计。我们已开始了解基于利益相关者和消费者对钱包和其他代表的信心的区块链系统的治理和均衡结构。在这个过程中,我们在Libra方法和更成熟的方法(如工作量证明(PoW))之间确定了新的市场设计妥协。
然而,要确定如何在确保网络安全和效率的同时,还能最好地保持生态系统中的长期竞争,这需要更多的研究。此外,由于基于份额(stake)的治理引入了对影响力的路径依赖,因此有必要探索保护较小利益相关者和服务提供者的机制。
Move允许灵活界定相关激励计划,如gas定价或staking。例如,通常讨论的stake slashing机制可以在Move中实现,方法是锁定stake一段时间,如果验证者违反LibraBFT算法的规则,影响安全性,则自动惩罚验证器。
类似地,当验证者在LibraBFT算法中投票时,这些投票可以记录在数据库中。该记录允许Move模块基于算法中的参与度来分发奖励,从而激励验证者保持活动。Libra储备的利息和gas支付也可以作为激励的来源。这两个来源都由Move模块管理,这增加了它们分配的灵活性。虽然需要更多的研究来设计一种方法来支持Libra生态系统的进化,但Move的灵活性确保了所需的方法可以在对Libra协议进行很少(如果有的话)更改的情况下实现。
十、 Libra接下来要做什么?
我们为Libra协议提出了一个提案,允许一组验证者提供一个去中心化数据库来跟踪可编程资源。我们讨论了Libra协议的一个开源原型——Libra Core,并展示了为智能合约引入的Move编程语言如何允许Libra协议实现Libra生态系统的独特设计。
本文描述的协议和实现目前都处于原型阶段。我们希望从新成立的Libra协会和更广泛的社区收集反馈,把这些想法变成一个开放的金融基础设施。我们目前正在运行一个测试网络,以允许社区对这个系统进行试验。
我们正致力于这个系统的首次启动,为了将其保持在可管理的范围内,我们计划在第一个版本中进行几个简化。在系统的早期,使用一组外部认可的创始成员减少了对共识激励系统的需求,并允许更快的更新速度。我们预计只对系统定义的模块使用Move语言,而不是让用户定义自己的模块,这将使Libra生态系统能够在完全形成Move语言之前启动。这还允许在不损害使用Move定义核心系统行为所带来的灵活性的情况下中断所做的更改。然而,我们打算在Libra协议的未来版本中提供对Move语言的开放使用。
最后,我们目前正在Libra协会的框架内工作,以启动这个新生态系统背后的技术基础设施。我们发布了一个路线图[50]的工作,我们计划为支持这个启动做出贡献。Libra协会的首要目标之一是将Libra的生态系统迁移到一个没有许可的系统中。我们已经记录了[5]在进行此迁移时所面临的技术挑战。该协会的开源社区[6]提供了有关如何开始使用Libra测试网、试用Move语言和为Libra生态系统做出贡献的信息。
致谢
感谢以下人士对本文的讨论和反馈:
Adrien Auclert,Morgan Beller,Tarun Chitra,James Everingham,Maurice Herlihy,Ravi Jagadeesan,Archana Jayaswal,Scott Duke Kominers,John Mitchell,Rajesh Nishtala, Roberto Rigobon,Jared Saia,Christina Smedley,Catherine Tucker, Kevin Weil,David Wong,Howard Wu,Nina Yiamsamatha,和Kevin Zhang。