摘要
本文我们提出了一种实现去中心化账本的新方法,它保留了事件的总顺序,允许无信任地传递价值、时间戳和其它功能。它既适用于公共部署,也可以私有部署,不需要做任务修改,也不需要特殊的硬件或设备。
1. 介绍
2009年1月,中本聪(Satoshi Nakamoto)介绍了第一个成功实现的无权限非信任去中心化账本技术,即区块链。它提供了一个安全、可靠和去中心化的方法来对事件标记时间戳,提供完整的顺序,而不需要第三方监管或可信实体。以这种方式对事件标记时间戳允许中本聪解决了双花问题,并实现了数字货币的概念。这就是数字货币比特币[1]
。
比特币及其巨大成功,激励其他人采用中本聪的区块链并以多种创新方式应用它。 以太坊[2]
就是个显著的例子,第一次被在2013年末被Vitalik Buterin
提出,它允许在区块链上运行任意图灵完备的逻辑,从而促进智能应用程序、合约甚至自知组织在去中心化区块链上存在和运行。
然而,区块链的实现还存在一些挑战,包括扩展性、效率和安全性。这些问题目前正阻碍其进入主流应用。替代性架构已经被开发出来,其中最值得注意的是有向无环图DAG(Directed Acyclic Graph)
,虽然DAG提供了一些改进,但是在足够的规模上他们也会像区块链那样屈服于许多相同的问题。
在去中心化账本上需要一种方法来达成无需信任的共识,该方法能够以有效的、无界的线性方式扩展。本文,我们提出了使用具有逻辑时钟的节点对等网络来生成按时间顺序排列事件的时间证明来解决双花问题。该系统是安全的,为节点提供所生成的时间证明可以参与的历史记录。
此外,通过提供执行任意脚本的能力,一个完全富有表现力的、图灵完备的状态变化系统可以建立在新的共识范式上。然而,本文没有涉及Radix上去中心化应用程序的开发人员环境,这会在后面的文章介绍。
2. Tempo
Tempo
账本由3个基本的组件组成:
- 网络节点集群
- 跨节点分布的全局账本数据库
- 一种用于生成时间上有序事件的加密安全记录的算法。
Tempo的一个实例称为一个宇宙(Universe)
,宇宙中的任何事件,如消息或交易,表示一个称为原子(Atom)
的对象。
所有的Atom包含至少一个终点目的地址,以端点地址(Endpoint address)
表示。端点地址继承自一个身份,比如用户的公钥,用于在网络上路由事件。
Atom通常采用载荷原子(Payload Atoms)
和转让原子(Transfer Atoms)
,Payload Atom
的一个例子是一种发给单方或多方的通信,就像Email或即时消息。而Transfer Atoms
用来转移一个物品的所有权给其它方,比如货币。
根据使用目的不同,Atom也可能包含其它Atom,以及其它各种数据。这种额外的数据可能包含条件性目的地址、所有方、参与方、关联方和应用程序元数据。如果需要,可以为特定的应用目的创建奇异的Atom变体。
客户端通过其连接的节点可以创建并提交Atom到网络中。提交的原子然后有网络处理,如果有效,则从该点开始为该Atom构造时间证明并与之关联。
Tempo很大程度上依赖于最终一致性来实现事件的总排序。
3. 账本架构
Tempo账本是一个在Universe中存储所有Atom的分布式数据库,它被设计为可横向扩展,支持半结构化数据并可更新条目。
一个节点上的本地账本实例可被配置为存储所有或部分全局账本,全局账本的一个子集被称为一个分片。每个Universe的总分片空间是可配置的,但是一旦部署后就不可改变。节点可以被重配置以支持分片空间中的任何子集,以便实现Universe可以处理大量负载需求而不需要节点运行在昂贵的硬件上。关键的是,这可以使性能受限的IoT设备能够作为Universe的一等公民参与其中。
分片是Radix的基本设计特征,它暗示了一种强大的方法来保证Atom处在正确的分片中,和一种有效的方法来决定哪些节点会保留哪些Atom的副本。
考虑到所有Atom必须至少有一个端点在其目的地址中,我们可以采用将目的地址与分片空间维度进行取模运算来获得一个分片ID。有些Atom,比如Transfer Atom
,可能具有多个目的地址,因此会存在与多个分片中。
这是设计使然,因为存在于多个分片中的Atom增加了Atom的冗余和可用性。另一个好处是,执行分片间转移的任何Atom都存在于先前所有者和新所有者的分片中。这部分地消除了对全局状态的需要,并减少了防止“双花”所需的任何昂贵的分片间状态验证操作。
4. 转让
Payload Atom
相对简单,包含一些任意数据、目的地址和签名,而Transfer Atom
就更负责一些。
一个拥有的对象(An owned item)
由一个消耗品(a Consumable)
表示,而所有权(Ownership)
被定义为Consumable
序列,随着时间的推移提供所有者的可审计历史。Consumable
是Atom的一个子类。
为转移Atom(α[n])
内一个Item(α)
的所有权给Bob, Alice创建一个Consumer(α[X])
,它指向以她为当前所有者并进行签名的Consumable(α[n])
。消费者Consumer
也是Atom的一个子类,识别要被“消费”的消耗品Consumable
。然后她创建一个新的消耗品Consumable(α[X])
,其包含要被转让的Item(α)
,以及新所有者的身份:Bob。消费者和消耗品被打包进一个新的Atom(α[X])
,然后提交到网络进行验证。
任何节点收到Alice的Atom(α[X])
都能轻松地验证Alice确实是Item(α)
的当前所有者。这可利用节点本地账本中保存的包含Item(α)
的最后一个消耗品中的所有者信息来验证Consumer(α[X])
中的签名。如果签名验证通过,那么Alice就是当前的所有者,转让交易就会被执行,Bob成为新的所有者。
有些转让操作可能要求Item(α)
不被整体转让,比如货币。消耗品Consumable
可以被设置为允许部分转让一个对象,如果该对象规范允许的话。这种情况,Alice会创建两个消耗品,一个转给Bob,另一个作为找零返回给她自己。类似的,多个消费者被用来指向Alice所有的很多消耗品,然后一次性转让给Bob,从而保证操作的原子性并降低网络负载。
5. 信息传递
确保将事件快速传递到分片中的所有节点,Tempo采用Gossip
八卦协议进行网络上的信息通信。已经证明,八卦协议是在对等网络中实现信息的大规模传播的有效且可靠的手段。
节点广播它们的配置信息,比如它们希望接收事件和状态信息的分片集,以及它们能提供的进一步优化信息传递的网络服务(比如中继和发现)。它们也可以广播关于它们连接的对端节点的元数据,进一步辅助信息和事件的路由。
网络中的节点采用“尽力而为”的方式,同时主动同步和八卦协议使其本地账本保持最新状态。当通过任何方式接收到一个Atom
,节点将对其本地账本执行Atom验证。如果发现可证明的差异,节点可以将此信息传递给其相邻节点,从而允许它们采取行动并解决差异。
虽然可靠,但这种方法无疑会导致丢失事件并且某些本地账本实例中对象状态可能不正确的情况。为了解决这些不一致性,节点依赖于由事件触发的可检测的因果历史异常。然后,它们查询其它节点以获取缺失的信息,并实现与网络其余部分关于事件及其后续状态的最终一致性。
例如,Node(N)
接收到一个导致不确定性验证过程的Atom(α[n])
,可能是因为Node(N)
没有Atom内指向的Consumable(α[n])
。然后,Node(N)
查询它的邻居节点来返回任何指向Consumable(α[n])
的Atom依赖并进行重验证。现在Node(N)
具有Consumable(α[n])
的一致性状态。
6. 事件可用性
要正确验证Atom,需要将它们路由到包含相关分片的节点,以便验证任何消耗品Consumable
,状态和其他信息的因果历史。端点目的地(Endpoint destination)
提供了保证Atom通过八卦协议通信层被合适节点接收到的必需路由信息。
还是以Alice转移Item(α)
给Bob为例,Alice在Atom里包含她的端点目的地,表明她正从分片(1)
转出,然后包含Bob的端点目的地,表明她正转入到分片(3)
。保存分片(1 || 3)
的节点需要知道"Alice发送;Bob接收"的事件,以及每个分片中Item(α)
的状态。发布这个事件,保存分片(1)的节点不必再关注Item(α)
未来的状态变化,除非它又被发送回分片(1)。对Item(α)
的监管责任已经被转移到存储分片(3)的任何节点。如果Bob接着又要花费Item(α)
给其它分片的一个所有者,维护Item(α)
状态的责任将再一次改变。
仅处理影响全局账本的节点子集中的状态的事件,以及状态维护的转移责任,大大降低了总状态处理开销。这就是Tempo高性能扩展的关键。
7. 逻辑钟
Tempo共识的基础是基于逻辑时钟,逻辑时钟是在分布式系统中提供事件的相对、部分排序的简单方法。[3]
在Tempo里,所有节点都有一个本地逻辑钟,一个不断增加的整数值,表示该节点见证的事件数。当见证一个新的从未见过的事件时,节点递增它们的逻辑钟。在存储一个事件时,节点同时保存与之关联的逻辑钟。然后,如果需要,此记录可用于帮助验证过去事件的时间顺序。
只有在该节点之前未见过的Atom的接收可被归类为Tempo内任何给定节点的“事件”。
8. 时间证明供应(Temporal proof provisioning)
一个宇宙(Universe)被拆分成很多分片,这样节点就不需要保存全局账本或状态的完整副本。但是,如果没有一套合适的共识算法,允许节点验证它们维护的分片之间的状态变化,“双花”会是一件很容易的事情,其中不诚实的行为者可以将同一份资金花费在不同的分片上。
时间证明为上述问题提供了一个便宜的防篡改解决方案。
在一个事件被提交到整个网络已被全球接受之前,一个节点子集会对该事件进行初始的验证,如果成功,那么一个时间证明会被构造并与Atom关联,然后将该Atom和时间证明进行全网广播。
以Alice转移Item(α)
给Bob为例,过程如下:
Alice选择一个她连接的节点
Node(N)
,提交Atom(α[n])
请求创建一个特定长度的时间证明。Node(N)
收到请求后,如果它存储了Alice或Bob的分片,则对Atom(α[n])
进行验证。如果它有Alice对应分片(1)的副本,它会保证
Item(α)
还没有被Alice花费过。如果发现任何可证明的差异,比如
Item(α)
已经被Alice花费,或Atom构造异常,对Atom的处理就会失败。否则,
Node(N)
确定与其直接相连并包含分片(1 || 3)的节点集合,随机选择一个,转发提交的请求。如果没有找到合适的节点,
Node(N)
会搜索其节点图和相关的元数据以发现可连通到维护分片(1 || 3)节点的可行中继。节点发现一个合适的代表
Node(P)
后,它会追加一个时空坐标(l, e, o, n)
和对坐标哈希Hash(l, e, o, n)
的签名到时间证明(如果还没有,则创建一个新的)。(其中l
是Node(N)
对该事件的逻辑时钟值,o
是观察节点Node(N)
的ID
,n
是Node(P)
的ID
,e
是事件哈希Hash(Atom)
)-
然后,节点
Node(N)
将Atom(α[n])
和当前的时间证明给Node(P)
。
在接收到来自
Node(N)
的提交请求后,Node(P)
同样地验证Atom(α[X])
,如果成功,也一样会选取下一个节点转发请求,同时追加其时空坐标(l, e, o, n)
和签名到时间证明中,并转发Atom(α[X])
和实践证明给下个节点。-
这个过程会一直重复,直到足够数量的节点已经参与了时间证明或这个过程中有任何节点发现一个可证明的差异。
上例中Atom(α[X])
的时间证明最后产生以下的坐标:
9. 时间证明供应的效率
时间证明的长度定义了有多少个节点需要参与供应过程。过短的长度会降低解决Atom之间冲突的效率,并且可能导致Atom未被正确验证,要求它在每个节点处经历时间顺序确定。非常长的长度会不必要地增加网络带宽负载,以及Atom最终一致的时间消耗。
一旦时间证明长度被确定,如果被传递的Atom有任何依赖或消耗品(Consumable)
,网络可以优化节点选择来提高将来验证转移的速度。这是因为如果涉及验证当前交易所依赖的先前交易的节点被包括在新的时间证明中,则可以更容易的创建可审计的因果历史。
简单来说,如果Alice发送Item(α)
给Bob,然后Bob发给Carol。如果涉及为Alice->Bob交易创建时间证明的节点之一同时也是为Bob->Carol交易提供时间证明的部分参与者,这会非常利于提高网络效率。
实现时间证明因果历史是相对简单的: 如果当参与时间证明供应,对Node(N)
可用的任何候选节点同时也是Atom(α[n])
任何依赖性的时间证明的一部分,Node(N)
会随机选择一个作为优先级,如果尚未成为Atom(α[n])
时间证明的一部分。
为了增加创建具有以上特性的时间证明的可能性,长度是一个重要的因素。对于大多数目的,log(n) ∗ 3
或Max(3, sqrt(n))
一般是足够的,其中n
是当时网络中存在节点的评估大小。
10. 向量时钟
如果两个事件之间发生冲突(双花)或异常,一个时间证明的(l, e, o, n)
时空坐标可以被用来构造向量时钟[4]
,使用每个的LogicalClock(l)
分量。一个或多个向量时钟可以被用来确定相关Atom的部分顺序。
给定2个向量时钟,分别为Atom(α[X])
和Atom(α[Y])
,以VC(α[X])
和VC(α[Y])
的形式表示。每个节点{A, B, D, F, G, L, P}
中的相应事件的逻辑时钟在下表展示:
这很容易确定VC(α[X])
关联的Atom首先在网络中出现,因为两个向量时钟都有一个共同的节点Node(P)
,它的逻辑时钟在VC(α[X])
中小于VC(α[Y])
中的。
向量时钟遵循一个简单的规则集来确定顺序:VC(α[X])
小于 VC(α[Y])
,如果VC(α[X])[Z]
小于等于 VC(α[Y])[Z]
,并且至少有一个VC(α[X])[Z]
严格小于 VC(α[Y])[Z]
。
在很多冲突的情况下,向量时钟对比提供了一个简单有效的方法来确定顺序,而不需要更多昂贵的办法。但是,如果一对向量时钟不包含一个共同的节点,则表示它们是并发的,这种场景下我们需要另一种机制。
给定2个Node(N)
接收到的并发的向量时钟:
为确定哪一个是更早的时间证明,Node(N)
有两种可用的机制来处理这种情况。
第一种机制引用节点的本地账本,它可以信任它返回有关迄今为止所见事件的诚实信息,而不颠覆真相。这个节点检索VC(α[X])
之后涉及节点{A, D, F, S}
的事件,尝试发现同时包含VC(α[Y])
中的节点{B, G, L, V}
的时间证明,然后进行逻辑钟值比对。
如果节点在其本地账本中发现一个事件具有如下的向量时钟:
VC(α[X])
和VC(α[Y])
的时间顺序就可以被解析,因为上面同时存在S和V。VC(α[Z])
中Node(S)
的逻辑时钟大于VC(α[X])
中的,因此VC(α[Z])
在VC(α[X])
之后被创建。VC(α[Z])
中的Node(V)
的逻辑时钟小于VC(α[Y])
中的,因此VC(α[X])
和VC(α[Z])
在VC(α[Y])
之前被创建。
如果初始查找没有得到任何结果,事件向量时钟可以按顺序被连接起来以增加找到解决方案的可能性。假如VC(α[Z])
只包含Node(S)
而没有VC(α[Y])
中的节点,那么节点可以扩展搜索包括VC(α[Z])
中其它节点的向量时钟。
以这种方式解决并发事件是简单,快速,有效的,并且独立于验证时所需的任何其他外部信息。只要节点是活动参与者并且存储与冲突相关的一个或多个分片,就足以解决大部分并发事件。
如果冲突没办法解决,Node(N)
会将存储Atom(α[X])
和Atom(α[Y])
同时保存在本地账本,按照接收的顺序对它们进行标记,并启用第二个事件排序机制:承诺顺序确定(Commitment Order Determination)
。
对于只存储自身相关事件的轻节点,比如IoT设备,承诺(Commitment)是它们确定顺序的唯一方法。
Commitments(承诺)
为了帮助确定事件的总顺序,节点向网络声明他们已经看到的所有事件的定期承诺。当节点参与事件的时间供应时,或者在任意时间间隔内随意产生此承诺。承诺是一个Merkle哈希[5]
,由节点在提交先前的承诺后见证的事件构成,第一个叶子是节点提交的最后一个承诺,随着时间的推移产生一个链接的承诺序列。
如果节点参与时间证明供应过程,则承诺将作为c包含在节点的时间坐标中,从而产生扩展的时空坐标(l,e,o,n,c)。 承诺是防篡改的,因为坐标由生产节点签名。
可以请求节点提供信息以便能够验证其在任何时间产生的任何承诺。 它们应该将所有相关的Atom哈希值传递给请求节点,允许它重建承诺哈希值并进行验证。 然后,请求节点可以在检测到欺诈性承诺的情况下采取适当的动作。
可以请求承诺验证的这种不确定性还可以防止节点篡改其逻辑时钟值,因为所有承诺都具有与它们相关联的逻辑时钟值,因此可以容易地检测到篡改。
例如,假设Commitment(1)
中的逻辑时钟值(l)为100,而Commitment(2)
中的逻辑时钟值(l)为200,那么Commitment(1)
应该包含100个Atom事件。如果在验证时请求节点没有返回100个哈希值,那么可能已经发生了对逻辑时钟的篡改。承诺还用于提供确定事件时间顺序的辅助机制。假设Node(N)
接收到与Atom(α[X])
相冲突的Atom(α[Y])
,Node(N)
联系其中的一个邻居节点Node(P)
,查询与Atom(α[X])
相关的任何承诺信息。Node(P)
回应:与Atom(α[X])
相关的承诺;一个同一个承诺中位于Atom(α[X])
之后的Atom(β[S])
集合;Merkle哈希的叶子。通过此信息,Node(N)
可以验证返回的Atom的逻辑时钟值,承诺的完整性以及返回的Atom是其中的一部分。
一旦从Node(P)
获得该信息,Node(N)
就可以查询传递Atom(α[Y])
的Node(Q)
。 它请求Node(Q)
返回Atom(α[Y])
和任何Atom(β[S])
的承诺和逻辑时钟信息,以及允许Node(N)
验证的Merkle哈希叶子。
从上图可以看出,
Atom(α[X])
发生在Atom(α[Y])
之前。
由于我们现在有承诺和逻辑时钟值,分辨事件事件顺序就很简单了:
-
Atom(α[X])
在Atom(α[Y])
之前,如果Node(P)
返回的任意Atom(β[S])
存在于Node(Q)
发布的一个承诺,其中Node(Q)
中的任意Atom(β[S])
的逻辑时钟小于Node(Q)
中Atom(α[Y])
的逻辑时钟值。 - 如果
Atom(α[X])
是一个承诺中的最后一个事件,Node(P)
返回后续承诺,或者Node(N)
可以联系其它节点。Atom(α[X])
是所有节点的所有承诺中的最后一个的可能性是无穷小的。 - 如果
Node(Q)
在本地账本没有任何可用的Atom(β[S])
,Node(P)
可以用相同的过程向其它邻居查询关于Atom(α[Y])
的信息。 - 如果冲突还是没法解决,
Node(N)
会在本地账本保存Atom(α[X])
和Atom(α[Y])
,按接收到的顺序进行标记,直到最终找到解决办法。
结论
本文提出了一种方法,用于确定分布式系统内事件的总顺序,而不依赖于信任,该方法具有可扩展性,高效性且与运行环境无关性。为了减少开销并提高性能,我们的解决方案定义了一种结构化的可分片的体系架构,该体系架构将状态传输信息限制在仅需要网络的成员里。对等网络通过可靠和健壮的八卦协议提供通信和信息路由的手段。在见证事件时,节点维护逻辑时钟,并定期产生代表这些事件的防篡改承诺。在见证事件时,节点相互协作创建时间证明,其包含可验证的时空坐标,用于构造向量时钟并确定何时网络首次看到事件。节点可以随意加入和离开网络,并依赖可检测的一致性异常来同步并使它们保持最新。
参考资料
[1] S.Nakamoto, Bitcoin: A Peer-to-Peer Electronic Cash System, 2008: https://bitcoin.org/bitcoin.pdf
[2] V. Buterin, 2014: https://ethereum.org/pdfs/EthereumWhitePaper.pdf.
[3] L. LamportTime, Clocks, and the Ordering of Events in a Distributed System, 1978: http://lamport.azurewebsites.net/pubs/time-clocks.pdf.
[4] C.J.Fidge, Timestamps in Message-Passing Systems that preserve the Partial Ordering, 1988: http://zoo.cs.yale.edu/classes/cs426/2012/lab/bib/fidge88timestamps.pdf.
[5] R.C. Merkle, Merkle Tree, 1979: https://en.wikipedia.org/wiki/Merkle tree.