动机
去中心化运动雄心勃勃,将为新的社会和经济往来提供绝佳的机遇。同时,去中心化制衡了权力集中的大型组织偶尔发生的滥权和腐败。去中心化支持自我决策和权利分散的社区组织。当然,分散的社区组织也将会面临挑战和问题,如国际法律、公共利益和慈善如何兑现等问题。
我们非常赞赏比特币,以太坊和其他平台的巨大创新,这些平台大大推进了去中心化组织的发展,迎来了加密货币和智能合约的新时代。但是,我们也看到这些项目没有使用最好的工程技术和形式化模型以支持系统的扩展性和关键任务的正确性。目前社区正在热烈讨论的扩容问题和可靠性问题反应了这些平台底层架构的不足。例如,对于在地球上进行的所有区块链交易,坚持一个明确的序列化处理顺序是否是一个可扩展的设计?
要成为具有工业规模的区块链解决方案,RChain必须以Facebook的规模提供内容支持,并以Visa的速度支持交易。对众多区块链项目的现状进行调查之后,并与其他区块链开发团队的深入合作,了解了各自的路线图之后,我们得出结论:当前和近期的区块链架构不能满足这些要求。在2016年年中,我们决心设计一个更好的区块链架构。
与区块链行业一起,我们仍处于这种去中心化运动的曙光之中。现在是时候奠定坚实的基础。对于那些相信这个雄心勃勃的愿景的人来说,前进的道路是值得的,而且这份文件总结了这个愿景以及我们如何实现这一愿景。
途径
这个平台将实现以下最低要求:
- 动态的,响应式的,可证明正确性的智能合约。
- 同时执行独立智能合约。
- 数据分离以减少独立代币和智能合约不必要的数据复制。
- 保证节点到节点间通信的鲁棒性。
- 非计算密集型的共识算法/验证协议。
构建高质量的软件是非常具有挑战性的,相对来说构建一个看上去“聪明”的软件会容易的多。然而,由此产生的软件往往质量低劣,充满了缺陷,难以维护,难以发展。继承和开发这样的软件对于开发团队来说是残忍的,更别提他们的客户了。尤其是在构建一个用于支持重要任务的开源系统时,我们不会采用最小成功的思维模式来保证软件的质量和正确性。
为了达到上述要求,我们的设计致力于以下几个方面:
- 一个假定细粒度并行和动态网络拓扑的计算模型。
- 一个可组合以及动态的资源寻址方案。
- 使用函数式编程范式,因为它更自然地适应分布式和并行处理。
- 形式化验证,利用模型检查和定理证明来构建正确的协议。
- 遵循内涵和复合性原则。
介绍
开源的RChain项目构建了一个 去中心化的,高实用的,自治的通用计算基础设施和区块链平台。在平台上,可以创建和执行被称为“智能合约”的程序。它是可靠的、可扩展的和可并发的,并具有pos共识和内容分发功能。
通过使用智能合约,可以在此平台上构建大量可完全扩展的去中心化应用程序(dApp)。DApps可用于诸如身份,代币,时间戳,金融服务,货币化内容传输,去中心化自治组织(DAO),交易所,信用,私人社交网络和市场等诸多领域。
图:高层次的RChain架构
RChain网络实现了直接的点到点的通信,每个节点运行RChain平台并在这之上运行一系列dApp。
RChain的核心是Rho虚拟机(RhoVM),RChain可运行多个RhoVM,每个单独的RHoVM执行一个智能合约。这些虚拟机是并发执行的而且是多线程的。
所谓的并发性,是围绕移动进程演算的形式化模型而设计的,伴随可组合的命名空间应用,从而允许 每个节点实际上有多条区块链。这种多链、独立执行的虚拟机实例与“全局计算”的设计形成鲜明对比,后者限制了交易只能在单个虚拟机上顺序执行。另外,每个节点都可以配置为订阅和处理它感兴趣的命名空间(或区块链)。
像其他区块链一样,在区块链上实现跨节点的共识至关重要。RChain协议中关于副本和共识的部分被称为Casper,是一种权益证明(pos)的协议。类似于以太坊,合约在一种状态下开始,许多节点收到一个带签名的交易,然后在RhoVM实例上执行该合约并进入到下一状态。一系列节点运算符或“被绑定的验证器”将共识算法应用于加密-高效地验证RhoVM实例上状态的配置和状态转移的整个记录,并且这些都被精确的备份到分布式的数据存储中。
区块链合约(也称为智能合同,进程或程序),包括在安装中被写入的系统合约,均使用RChain通用语言“Rholang”(高阶反射语言)编写。Rholang从rho-演算的计算形式论派生而来,支持内部程序并发。它形式化地表达了以并行组合方式执行的进程之间的通信和协调。Rholang自然地适应了代码的变迁,反射/一元 APIs,并行性,异步性和行为类型等行业趋势。
由于节点是内部并发的,每个节点不需要运行所有的命名空间(或者区块链),系统也将是 可扩展的。
由于合约语言和虚拟机都是从可数学证明的形式化规范构建而来的,并且由于编译器设计和工程方法都是 正确构建*的 ,我们认为这个平台将被视为 *值得信赖 的。
区块链之间的比较
下表比较了比特币、以太坊和RChain的几个关键特性。
架构概览
RChain架构的主要构成组件如下所示:
RChain架构图
RChain的执行架构可能会依赖一些操作相关的外部组件,但是当运行在JVM上时,这些因素会被控制到最小的程度。RhoVM的执行环境运行在JVM之上,每一个RhoVM实例都拥有自己的执行环境。
RhoVM执行环境 为合约的执行提供了上下文,以及一个RhoVM实例的生命周期。
接下来自底向上地介绍架构中的每一层结构:
P2P 网络交互层 提供了节点间进行信息交互的机制。具体采用那种方案需要根据商业级别进行确定,例如采用开源项目ZeroMQ或者RabbitMQ。
存储 RChain采用一种KV数据库MongoDB进行数据的存储,而内存中的数据主要通过前缀树来进行维护。
数据抽象层 提供了对本地节点或者远端节点数据访问的一致性。数据抽象层是SpecialK技术演变得来(包括去中心化内容传递,键值数据库,节点间消息传递和数据访问模式)。这一层将使用Rholang进行实现,因此该层将依赖于RhoVM执行环境以及Rholang的外部函数接口来实现P2P节点信息交互以及本地数据的访问。
共识 (Capser 一种基于权益证明的共识协议)将确保区块链网络中所有节点状态的一致性。
在RChain网络中,所有节点都将包含最基本的使用Rholang进行开发的系统合约。系统启动过程中将使用这些系统合约来实现RhoVM实例的运行,负载均衡,dApp合约的管理,代币系统,节点信任以及其他一些功能。
代币系统合约包括了那些需要运行和本地节点之外进行交互的协议的合约,这些是协议访问代币,他们可以分为以下两类:
- 权益代币 是用来运行共识算法的,包括了 RChain Rev 代币。另外权益代币可能会通过运行的rchain节点来进行分发。权益代币是用来支付对节点的资源使用。在RChain中,燃料 用来测量系统资源的消耗(与以太坊中Gas的概念类似),这种机制是多维的,取决于计算资源(执行的指令数量),存储资源(存储数据的大小以及存储时间),网络带宽资源(网络服务质量以及吞吐量)的使用。其他可见“流量控制机制”一节。
- 应用代币,这类代币是可选的,用来运行特定的dApp。新的应用代币可以随时由dApp的开发者进行分发,这类代币与以太坊的ERC20代币类似。
Rho API 为用户提供了访问执行环境以及节点信息的能力。语言绑定 使得所有基于JVM开发的编程语言以及其他潜在的编程语言都可以用来开发智能合约。一个 REPL (读取、执行、输出、循环)开发工具将会提供给合约开发者。此外,每个节点都会有一个命令行界面。节点所有的API都会通过HTTP以及JSON RPC暴露给外部用户。
并发 VS 并行
读者理解并发执行的含义是至关重要。当我们说“并发”时,我们并不是指同时执行多个进程,这种情况应该用“并行”来描述。“并发”是一个结构化的属性,它允许多个独立的进程组成复杂的进程。当多个进程之间相互不竞争资源,我们称这些进程是独立的。
由于RChain致力于在Rholang和RhoVM中实现并发性,所以我们将获得并行性和异步性这一自由且重要的属性。 无论节点是在一个处理器还是在一百万个处理器上运行,RChain设计都是可扩展的。 话虽如此,本文档的读者将会注意到并发计算的设计模式。
节点和区块链语义
下面的UML类图描述了主要的概念类和结构之间的关系。
RChain区块链结构语义图
合约设计
RChain合约是一个具有明确规定的、表现良好的并且被形式化验证过的程序,它可以与其他合约程序交互。
在本小节中,我们通过Rholang的设计思路来介绍智能合约。首先, 我们给出了 RChain 平台上智能合约的概述。之后, 我们描述了RChain用于实现形式化验证的核心模型, 并在 RChain 系统层上对并发进行建模。然后, 我们将探讨如何将该核心模型进行扩展, 以适应业界的接口语言标准, 如反射、并行、异步、响应式数据流和编译时安全类型检查。
合约概述
从宽泛的’合约’的定义来讲, 一个智能合约是一个具有以下内容的过程:
- 持久化状态
- 相关代码
- 相关的RChain地址
要记住的一个要点是智能合约具有任意复杂度的特性。它可能是一个原子操作或是一个构成复杂协议的协议超集。
一个合约可以被外部网络代理的信息所触发,外部代理可以是一个合约或一个网络用户。
一条信息:
- 触发信息通过指定通道发布,该通道可以是公开的或私人的。
- 触发消息可能是格式范围内的任何值,从一个简单值到一个无序的字节数组、一个变量、一个数据结构、一个过程的代码以及介于该范围之间的大多数事物。
代理在指定通信信道上发送和接收消息,该信道称为“命名通道”。
一个命名通道:
- 是一个 “定位”, 独立进程将确保同步该位置。
- 用于节点之间信息发送和接受的过程。
- 是不可猜测和匿名的,除非在处理过程中被特意引入。
通道被实现为在“只读”和“只写”进程之间共享的变量。因此,通道的功能仅受限于解释一个变量的含义。由于通道代表了“定位”的抽象概念,因此可能会采用不同的形式。对于我们的早期解释,命名通道的功能可以从单个服务器的本地存储器地址(变量)到分布式系统中节点的网络地址。
与该解释一致,一个区块链地址是经过命名的通道,即一个代理可以到达的位置。
两个合约在名为“Address”的通道上发送和接收信息:
这个模型描述了两个合约,它们都可以接收和发送消息。在某个时刻,一个外部actor会提示 Contract1
在通道 address
上发送了一个值 v
,该 address
是 Contract2
的地址。同时,Contract2
监听 address
通道上的值 v
。在收到 v
后, Contract2
继续将 v
作为参数并调用后续操作。最后两个步骤是按序发生的。
注意,这个模型假设发送者拥有 Contract2
的地址。还要注意的是,在发送者发送了 v
后,Contract1
已经运行终止。因此,除非提示,它不能再发送任何信息。类似地,在它继续调用后续操作之后,Contract2
也运行到终止状态,因此它不能再监听任何其他消息。
RChain合约用有良好的内部并发性,这意味着这些流程以及任何不相互依赖的流程都可以并行组合执行。所以我们修改了我们的符号:
与其他许多进程并行执行时,外部actor会提示 Contract1
在通道 address
上发送了一个值 v
,该通道即 Contract2
的地址。如果 Contract1
没有值要发送,该通道将会阻塞。如果 Contract2
没有收到任何值,它会阻塞信道且不会触发后续调用操作。
交易
交易语义如何符合我们对合约的描述? 从过程层面上来看,一个交易就是确认一条消息已经被“见证”在一个信道上。
消息本身就是虚拟的对象,但一个合约的前置状态和后置状态指的是代理发送一条消息之前和之后的状态,这些状态被另一个代理见证,打上时间戳并被记录在存储器中,也被称为(在观念上)“区块链”。
消息传递是一个原子操作。无论一条信息是否被见证,只有成功见证的消息才有资格作为一条可验证的交易并被放入一个区块中。迄今为止,我们的例子描述了原子协议本身,但是完整的应用程序可以在运行时产生、发送和接收数以万计的信道。因此,当一些资源的价值被一个过程所改变和见证时,通过代理记录它在什么时间和地点被见证了该改变。这个实现与将数据解释为线性资源是一致的。
在发送消息之前和之后, RChain拥有在信道的任一端放置消息的能力,因此查看消息的序列化形式是RChain的特定属性。此外, 通过将成功的消息声明为交易, 所有消息 (无论是从外部用户到合约,还是在合约之间) 都将被解释。因此, 我们平衡了合约的可扩展自治性和职责。
有关此模型如何适应响应式编程的行业趋势的范例, 请观察以下两个合约, 基于 “实时” 数据源上的交互模型:
与多个其他进程组合并行执行时, Contract1
会被提示在信道 address
上发送一组值 vN
,即 Contract2
的地址。在这种情况下,读者将会注意到 Contract2
作为一个线程,它监听一组值作为单个数据流的输入,这与一个流末端的输出的一组值是双向的。当信道 address
见证了一组值 v1…vN
时,将使用 v1…vN
作为参数来调用后续一些操作。虽然 Contract1
和 Contract2
之间的交互是异步的,输入操作 address?(v1...vN)
和 Contract2
的 Continuation(v)
必然是连续的。在每个实例中 address?(v1…vN)
被称为 Continuation(v)
的前缀。
我们对 RChain 平台上的并发合约的交互进行了非常基本的描述, 包括合约、将地址识别为通信信道以及把成功地通过上述信道传递的消息看作交易。接下来, 我们将概述形式化构建了这些结构的核心系统。
形式化模型: Rho演算
形式化验证是许多关键任务技术的 实际 标准。一些最早的形式化验证方法被应用于核电机组的两级停堆系统 [1]。许多ATM软件解决方案通过从线性时序逻辑模型推导解决方案来验证性能表现。许多军事信息和决策系统调用Hoare逻辑来验证碰撞容忍度。一个不加选择的智能合约工具, 它要求托管的关键任务合约, 对其用户负有相同的可验证责任。因此,我们的接口语言和执行模型的设计方法是基于一个可证明其正确性的计算模型 [2]。
同时,能够处理其核心模型中并发进程的编程范式和语言也相对较少。相反, 他们不采用一些基于线程的并发模型, 而是通过一次做多个事情来解决可扩展性问题。相比之下,移动过程计算为“计算是什么”提出了一个完全不同的概念。在这些模型中,计算主要来自过程的交互。为了形式化验证一个执行模型,并允许该执行模型从根本上支持并发,这就是我们替RChain的计算模型选择了一个进程演算模型的原因。
具体来说,RChain执行模型源于rho演算的语法和语义。rho演算是π演算的一个变种,π演算于2004年被推出,它是第一个提出使用反射策略的并发计算模型。 “Rho”代表反射的,高阶的。
虽然对本文的目的来说,理解π演算是不必要的,但还是强烈鼓励那些不熟悉π演算的人去学习和探索π演算相关知识。 π演算是第一个成功构建网络的形式化系统,其中节点可以定期加入网络并从网络中退出。它假设细粒度的并发和进程通信,即两个进程可能由第三个进程引入。 rho-calculus扩展继承了所有这些特性,并添加了反射。
有关更多信息, 请参见 The Polyadic Pi-Calculus 和 Higher Category Models of the Pi-Calculus.
反射
目前,反射被广泛认为是实用编程语言的一个关键特性,广义上被称为“元编程”。反射是一种规范的方式,它将程序转换为程序可以操作的数据,然后将修改后的数据转换成新的程序。 Java、C#和Scala最终都将反射作为其核心特性,甚至OCaml和Haskell最后也开发了反射版本 [3]。原因很简单:在工业规模上,程序员使用程序来编写程序。如果没有这种能力,编写先进的工业规模的程序将花费太长的时间。
语法和语义
rho演算构造“名字”和“进程”。类似于π演算, 名字可能是沟通的信道或一个值。 然而,在加入“反射”的rho演算中,一个名字也可能是一个“引用”的进程,其中一个引用的进程是一个进程的代码。在接下来的章节中,名字的通用性将变得尤为重要。
从名称和过程的概念,该演算建立了一些基本的“进程”。 进程可能具有持久状态,但并不假设它一定成立。 术语“进程”是“智能合约”中较为通用的术语。 因此,每一个合约都是一个过程,但不是每一个进程都是智能合约。
Rho演算构建了以下基本术语来描述进程之间的交互:
P,Q,R ::= 0 // nil or stopped process
| for( ptrn1 <- x1; … ; ptrnN <- xN ).P // input guarded process
| x!( @Q ) // output
| *x // dereferenced or unquoted name
| P|Q // parallel composition
x,ptrn ::= @P // name or quoted process
前三个术语用于表示I/O,用于描述消息传递的动作:
0
表示惰性或停止进程的,是模型的基础- 输入项
for( ptrn1 <- x1; … ; ptrnN <- xN )P
表示是输入守护进程P
,在一组信道xN
上监听一组模式ptrnN
。在接收到这样的模式时, 调用附加部分P [4]。Scala程序员会注意到“for-comprehension”是单子化处理信道访问的语法糖 [5]。 结果就是所有的输入通道都服从于模式匹配,这就构成了各种输入守护。 - 输出项
x!( @Q )
在通道上x
发送名字@Q
。 尽管在x
上发送的名字可能是一个值、一个通道或一个被引用的进程(它本身可能包含许多信道和值),我们的符号使用@Q
来重申名字的表达能力。
下一个项是一个结构性的,描述并发:
P|Q
is the form of a process that is the parallel composition of two processes P and Q where both processes are executing and communicating asynchronously.
另外两个项被引入来提供反射:
@P
这个“反射”项引入了“引用进程”的概念,引用过程是一个进程的代码,它被序列化并且通过信道发送。x
这个“修改”项,允许引用的进程从一个信道上接收并反序列化。
这个语法给出了包含Rholang类型系统原语的基本项。 rho演算假设名称上的内部结构,在进程之间传递时被保留下来。 能够探索名字的内部结构的一个结果是,进程可以被序列化到一个通道,然后在被接收时被反序列化,这意味着进程不仅可以相互传递信号,还可以将完整形式的进程发送给其他人。 因此是更高阶的扩展。
Rho演算也给出了一个单一可归约(替代)规则来实现计算,被称为“COMM”规则。 归约是原子性的; 他们要么发生,要么不发生。 这是直接归约rho演算中项的唯一规则:
for( ptrn <- x ).P | x!(@Q) -> P{ @Q/ptrn } //Reduction Rule
COMM规则要求两个进程是位于并发执行的。它还要求两者处于同信道关系。也就是说,一个进程正在从通道 x
中读取,而另一个进程正在写入通道 x
。这两个进程被称为“同步”于 x
。输入进程并行地在 x
等待符合某模式,记为 ptrn
的数据到来。在模式被匹配到后,执行后续代码 P
。 归约之后,简化的项表示 P
,它将在某个环境下运行,其中 P
绑定到 ptrn
。也就是说,在 P
的代码体中,出现的每一个 ptrn
都会被替换为 @Q
。
COMM规则意味着通过信道可以成功地传递消息。 读者可能还记得,通过一个信道成功传递一条信息可以构成一个可证实的交易。 事实上, 一个归约是一个事务 ,正是因为它证实了一个资源已被访问和修改。 结果, 归约执行的次数对应于所执行的原子计算数量,它们基本上与提交给一个块的事务数有关。 这种对应确保所有平台计算都是可无区别可量化的。
能够探索名字的内部结构的另一个含义是,信道可能封装更多的信道。 虽然它们在原子意义上是非常轻量级的,但是当信道具有内部结构时,它们可以用作数据存储,数据结构,以及任意深度的可证明的无限队列。 实际上,在几乎所有的实现中,一个合约的持久存储由存储在一个通道 state
中的状态值组成,接受请求 set
来设置值,用 get
来后去一个新的值 newValue
。 我们将在名称空间这一节中展示内部结构对信道的广泛影响。 更多详细信息,请参阅 A Reflective Higher-Order Calculus 和 Namespace Logic - A Logic for a Reflective Higher-Order Calculus。
行为类型
一个行为类型是一个对象的一个属性,它将对象绑定到action模式的一个离散范围内。行为类型不仅限制输入和输出的结构,而且还限制在不同条件下通信和(可能地)并发进程之间允许的输入和输出的顺序。
行为类型特定于移动过程计算,特别是由于非确定性移动计算的引入和适。更具体地说,并发模型可以引入多个场景,在这些场景下可以访问数据,但并不知道这些场景发生的顺序。数据可以在协议的某个阶段被共享,但不能在后续阶段共享。从这个意义上说,资源竞争是有问题的。如果系统不遵守对象的精确共享约束,则可能导致系统改变。因此,我们要求网络资源严格按照规定使用,这些规定描述和详细说明了一系列过程(这些过程表现出了类似的“安全”行为)。
Rholang 行为类型系统将用模态逻辑运算符迭代地修饰相关条件, 它是关于这些条件行为的命题。最终属性数据信息流和资源访问将在类型系统中具体化,该系统支持编译时检查。
行为类型系统Rholang将支持根据其具体代码和行为方式来评估合约集合。因此, Rholang合约将语义提升到类型级别的优势项, 在这里我们能够确定整个协议如何安全地进行衔接。
在他们的开创性论文 Logic as a Distributive Law 中,Mike Stay和Gregory Meredith提出了一种用于从任何一元数据迭代生成空间行为逻辑的算法。
意义
这个模型在过去的十年里多次被同行评审过。证明其可靠性的原型已经有近十年的历史了。最小的rho-calculus语法表达了六个原语 - 远远低于以太坊智能合约语言Solidity中的原语,然而这个模型比Solidity能表达的更丰富。特别是,基于Solidity的智能合约不具备内部并发的特性,而基于Rholang的合约则支持内部并发。
总而言之,rho演算的形式化模型是第一个实现下面特性的计算模型:
- 通过“反射”来实现最大化的代码可移植性,这允许将完整形式的流程作为一级公民来传递给其他网络过程。
- 借用一个框架,以数学方式来验证反射、通信过程和动态拓扑网络的基本并发系统的行为。
- 表示一个完全可扩展的设计,自然适应结构化模式匹配、流程延续、响应式API、并行性,异步性和行为类型的行业趋势。
RhoLang — 一种并发语言
Rholang是一个功能全面的图灵完整的通用编程语言,它使用rho演算来进行构建。它是一个具备行为类型和反射机制的高阶进程语言(r-eflective, h-igher o-rder process language),也是 RChain官方智能合约所使用的语言。其目的是具体化细粒度的并发编程。
语言必然是面向并发的,重点是通过保护输入信道来传递消息。通道是静态类型的,可以用作单个消息管道、流或数据存储。与类型化的函数式语言类似,Rholang将支持不可变的数据结构。
为了体验Rholang,下面是一个名为`Cell’的合约,它只有一个值并允许客户端获取和设置该参数:
contract Cell( get, set, state ) = {
select {
case rtn <- get; v <- state => {
rtn!( *v ) | state!( *v ) | Cell( get, set, state )
}
case newValue <- set; v <- state => {
state!( *newValue ) | Cell( get, set, state )
}
}
}
该合约需要一个 get
请求信道,一个 set`请求信道,以及一个 :code:`state
信道,我们将在状态信道中保存一个数据资源。它等待 get
和 set
信道的客户端请求。客户端请求通过 case
类来进行模式匹配 [6]。
收到一个请求后,合约加入 ;
到一个刚达到的请求 state
信道的客户端。这个连接做了两件事。首先,它从访问中删除内部的 state
。同时,又会依次执行 get
和 set
操作,所以它们总是对一个单一的资源副本进行操作 - 同时提供一个数据资源同步机制和一个访问和在 state
更新的操作。
在 get
的情况下,一个请求带有一个 rtn
地址,state
中的值 v
将被发往该地址。由于 v
已经从 state
信道中被取回,它被放回,且 Cell
操作将被递归地调用。
在 set
的情况下,一个请求带有一个 newValue
,它被发布到 state
信道(旧值被连接操作所拿走)。同时,Cell
操作将被递归调用。
通过 select
确认,在 Cell
中只有一个线程可以响应客户端请求。这是一场竞赛,失败的线索,无论是getter还是setter操作,都会被杀死。这样,当调用 Cell
的递归调用时,失败的线程不会被挂起,而新的 Cell
进程仍然能够响应客户端的这两种请求。
有关Rholang的更完整的历史叙述,请参阅 Mobile Process Calculi for Programming the Blockchain。
[1] | Lawford, M., Wassyng, A.: Formal Verification of Nuclear Systems: Past, Present, and Future. Information & Security: An International Journal. 28, 223–235 (2012). |
[2] | In addition to selecting a formally verifiable model of computation, are investigating a few verification frameworks such as the K-Framework to achieve this. |
[3] | 查看Scala文档:Reflection |
[4] | 查看Scala文档:For-Comprehensions |
[5] | 查看Scala文档:Delimited Continuations |
[6] |
查看Scala文档:Case Classes |
命名空间逻辑
为了实现互联网规模的区块链解决方案,必须像互联网一样拥有推理出资源“位置”的逻辑。具体来说,我们如何引用资源?我们如何确定哪些代理可以在什么条件下访问该资源?与其他地址(公钥)只有一个维度的区块链相比,RChain的虚拟地址空间将被分割成多个命名空间。 在一个非常大体的解释中,命名空间是一组命名通道。 因为通道常常被实现作为数据存储,所以命名空间相当于一个有争议资源的集合。
问答:命名空间和双花问题
问:假设Alice,Bob和Carol在相互不同的命名空间中,发生了两笔付款:Alice付给Bob和Alice付给Carol。假设我是一个只关心Alice的节点,我如何确定Alice不会双重花费?
答:命名空间只是一组名称。所有的区块链地址都是名称。一个集合可以用几种方式来描述。其中之一是在外部明确地写出集合中的每个项目。另一种方式是内在性地提供一个规则或程序,或者用于生成集合,或者识别一个物品是否在集合中。第二类命名空间更有意思。
现在,下一步是将这些与用户,合约以及节点联系起来。用户和合约都通过名称相互通信。节点验证给定的命名空间中的事务,事务是跨名字(通道)间的I/O事件。任何涉及到两个独立命名空间的事务都必须由处理这些命名空间的节点集合来处理。如果处理这些事务的结点不能组合成命名空间,则该事务将不能发生。
如果有多个节点组合这些命名空间,那么一致性算法可以保证所有节点都同意该事务。更具体地说,他们同意每次竞争的获胜者。因此,永远不会有双花发生。最大的威胁是找到有多个验证者服务的复合命名空间。幸运的是,您可以看到命名空间背后的验证者的多少,由此决定是否信任该命名空间。
我们已经明确两个进程必须共享一个指定的通道进行通信,但是如果多个进程共享相同的通道呢?事务的不确定性是在两个一般条件下引入的,导致资源有争议并容易受到竞争条件的影响:
for(ptrn <- x){P1} | x!(@Q) | for(ptrn <- x){P2}
第一种竞争条件发生在多个并行组合的客户端在命名通道上竞争 接受 数据资源时。在这种情况下, P1
和 P2
在命名通道 x
等待由另一个进程中由 x
发送的资源 @Q
。当且仅当在该位置处看到正确的值时,客户端才会执行其附加部分代码。在其他情况下,许多客户处于竞争中,可能存在许多归约,但是在这种情况下,两个中只有一个可能会成立。其中之一是 P1
首先接收 @Q
,另一个是 P2
首先接收到 @Q
,这两者在 @Q
代入到他们各自的协议中都可能会返回不同的结果。
x!(@Q1) | for(ptrn <- x){P} | x!(@Q2)
第二种竞争条件发生在当两个客户端竞争一个命名通道以发送一个数据资源时。在这种情况下,两个客户端在命名通道 x
处竞争,向客户端发送数据资源 @Q
,但是以下两个事务中的只有一个可能发生,一个是接收客户端首先接收 @Q1
,另一个是先收到 @Q2
,两者在代入到 P
的协议体重可能返回不同的结果。
对于竞争资源的协议,这种水平的不确定性是不可避免的。之后,在共识那一节,我们将描述共识算法如何通过将非确定进程中发生的多个可能的交易收敛到一个可维护复制状态。 现在来看看在第一个竞争条件下重新定义名称约束规约是多么简单的事:
for(ptrn <- x){P1} | x!(@Q) | for(ptrn <- v){P2} → P1{ @Q/ptrn } | for(ptrn <- v){P2}
—第二个竞争条件
x!(@Q1) | for(ptrn <- x){P} | u!(@Q2) → P{ @Q1/ptrn } | u!(@Q2)
在这两种情况下,通道和正在传输的数据资源都不再是有争议的,因为它们现在正在通过两个不同的命名通道中进行通信。 换句话说,它们在不同的命名空间中。 另外,名字是可证明不可猜测的,所以只有当存在一个任意的外部过程给出时,其他进程才能获得。因为一个名字是不可猜测的,所以一个资源只对知道这个名字的进程/合约才可见[5]_。 因此,在非冲突的命名通道集合上执行的一组进程(比如在分开的命名空间中执行的事务集合)可以被并行执行,如下所示:
for(ptrn1 <- x1){P1} | x1!(@Q1) | ... | for(ptrnn <- xn){Pn} | xn!(@Qn) → P1{ @Q1/ptrn1} | ... | Pn{ @Qn/ptrnn }
| for(ptrn1 <- v1){P1} | v1!(@Q1) | ... | for(ptrnn <- vn){Pn} | vn!(@Q1) → P1{ @Q1/ptrn1} | ... | Pn{ @Qn/ptrnn }
在名称空间 x
中并行执行的一组事务,以及在名称空间 v
中执行的一组事务是双盲的; 除非由辅助过程引入,否则它们是相互匿名的。 两组事务都在传递相同的资源 @Q
,甚至要求 @Q
满足相同的 ptrn
,但是没有竞争条件,因为每个输出都有一个单独的输入副本,事务发生在不同的命名空间中。这种隔离进程/合约交互集的方法基本上将RChain的地址空间分割成许多独立的事务环境,每个环境都是内部并发的,并且可以相互并行执行。
在这种表示中,该性质仍然保持成立,资源对那些知道通道名称并且满足模式的进程/合约可见。 将地址空间划分为独立事务环境的一个多路复用后,我们如何进一步细化可以在类似环境中与资源进行交互的进程/合约的类型?以及在什么条件、在什么程度上可以这样做? 为此,我们开始进行定义。
命名空间定义
命名空间的一个定义是进程/合约在一个命名空间中运行所需的最低条件的形式化描述。 事实上,命名空间的一致性直接并唯一地依赖于该空间如何定义一个名称,根据命名空间定义所描述的合约的预期功能,这可能会有很大的不同。
命名和功能都可能满足或不满足定义。以下命名空间定义在交互中实现为“if条件”,它描述了一组进程将一组合约发送到一组命名地址,这些命名地址组成了一个命名空间:
- 一组合约,
contract1…contractn
被发送到一组通道(命名空间),即address1…addressn
. - 一个进程并行地监听命名空间
address
中每个通道的输入。 - 当任一通道上收到一个合约时,它被提供给
if cond.
,检查命名空间的源点、发送者的地址、合约的行为、合约的接口以及合约所携带的数据大小。 - 如果这些性质与命名空间
address
的定义相一致,P
将会被执行,并将contract
作为它的参数。
命名空间定义有效地限制了命名空间中可能发生的交互类型——空间中存在的每个契约都展示出一种常见可预测的行为。也就是说,驻留在名称空间中的合同所调用的状态更改操作都必须经过授权,定义并且对该名称空间是正确的。这种设计选择使在命名空间上的目录式快速查询非常方便有效。
命名空间定义也可以控制在空间中发生的交互,比如说,通过制定
- 接受的地址
- 接受的命名空间
- 接受的行为类型
- 数据大小的最大最小值
- I/O结构
定义可能,并且一般都会指定一组可接受的命名空间和地址,这些名称空间和地址可以与其定义的代理进行通信。
请注意上图中针对行为类型的检查。这是为了确保合约所表达的操作串与命名空间的安全规范一致。行为类型检查可以用来评估可用性,边界,有无死锁和资源同步等特性,这些特性最大程度上保证了命名空间中资源上的“安全”地改变状态。因为行为类型意味着操作排序,所以行为类型条件可以指定合约的后置条件,这又可以满足后续命名空间的先决条件。因此,名称空间架构支持事务环境的安全组合,或“链接”在一起。
可组合的命名空间 - 资源寻址
在此之前,我们已经将命名通道描述为任意宽度的扁平实体。通过命名渠道的反射和内部结构,实现深度。
一个名字空间可以被认为是一个URI(统一资源标识符),而一个资源的地址可以被认为是一个URL(统一资源定位符)。 例如,URL的路径部分 scheme://a/b/c
,可以被视为等同于一个RChain地址。 也就是说,一系列嵌套通道,每个通道都可以携带消息,其中命名通道 a
是“顶部”通道。
但是请注意,URL路径并不是都可以组成的。例如 scheme://a/b/c
和 scheme://a/b/d
, 在传统的URL方案中,这两者不能通过组合生成一条路径。然而,每一个平坦路径都自动是树路径,并且,作为树,这些*可以*组合产生新的树 scheme://a/b/c+d
。 因此,树为资源寻址提供了一个可组合的模型。
图-可组合的树路径
以上,统一可以作为一种树匹配和分解的算法,而基于统一的匹配和分解为查询提供了基础。 为了探索这个想法,让我们用这种形式重写路径/树的语法:
scheme://a/b/c+d ↦ s: a(b(c,d))
然后将语法调整为rho演算的I/O操作:
s!( a(b(c,d)) )
for( a(b(c,d)) <- s; if cond ){ P }
最上面的表达式表示输出—将资源地址 a(b(c,d)
放在命名通道 s
。下面的表达式表示输入。对于匹配形如 a(b(c,d))
的模式,输入信道为 s
,如果满足一些前提条件,执行后续部分 P
,以地址 a(b(c,d)
作为一个参数。当然,这个表达式暗示 s
是一个命名信道。所以调整的通道结构可表示为:
图:树结构中嵌套通道的URL方案
鉴于现有的地址结构和命名空间访问逻辑,客户端可以查询并发送到该地址结构中的名称。 例如,当rho-演算的I/O进程被放置在并发执行环境中时,下面的表达式表示一个将被引用的进程 (@Q,@R)
放到位置 a(b(c,d))
:
for( a(b(c,d)) <- s; if cond ){ P } | s!( a(b(@Q,@R)) )
求值过程可以被符号化地写为:
for( a(b(c,d)) <- s; if cond ){ P } | s!( a(b(@Q,@R)) ) → P{ @Q := c, @R := d }
也就是说,P
是在某个环境下执行的,其中 c
用于代替 @Q
, d
用于替换 @R
。更新后的树结构如下所示:
图:在通道中放置进程
除了有资格成为命名空间的平面通道集合外(例如 s1…sn
),每个具有内部结构的通道本身就是一个命名空间。 因此, s
, a
和 b
可以递增地施加单独的命名空间定义,类似于由平面命名空间给出的命名空间定义。 在实践中,命名通道的内部结构是一个任意深度和复杂度的n元树,其中“顶部”通道(在本例中为 s
)只是 s1...sn
中这些拥有内部结构的众多可能的名称之一。
该资源寻址框架代表了对历史上使用最广泛的互联网寻址标准的一步步适应。 RChain通过命名空间实现了私有、公共以及联盟可见性所需的组合地址空间,但显而易见的用例是解决可伸缩性。绝非偶然,也不意外,命名空间也将为RChain的分片解决方案提供了一个框架。
[5] | 命名空间逻辑-适用于高阶反射演算的逻辑 |
执行模型
概论
每一个 Rho虚拟机 (RhoVM)实例都维护着一个环境,该环境重复应用底层Rho演算归约规则于持久性键值数据库中的元素,这些规则在高级Rholang合约语言中表达 [1]。 RhoVM的“状态”类似于区块链的状态。
图 - RhoVM作为联排式键值存储和执行引擎
合约的执行会影响RhoVM实例的 环境 和 状态 。在这种情况下,所谓的“环境”不仅指的是执行环境,也包括键值结构的配置。环境和状态分别是名称到内存中的位置以及内存中的位置到值的映射。变量直接引用内存中的位置,这意味着环境相当于命名到变量的映射。程序通常在运行时修改这些关联中的一个或两个。环境的改变伴随着Rholang的词法作用域规则发生,它的值可能是简单或复杂,比如原始值或完整程序。
图 — 从名称到值的两阶段绑定
RhoVM针对键值数据库操作。RhoVM的状态变化是通过修改键映射到值关系的操作来实现的 因为像Rholang一样,RhoVM是从Rho演算计算模型中衍生的,那么操作就是底层的rho演算归约规则。卓有成效地,被称为“COMM”的归约规则是一种替代方案,它定义了如果在一个键上观察到新值,则要执行 P
。键类似于一个名字,在该名字中它引用内存中持有将被替换的值的地址。在下面的例子中,key
是一个键, val
是被替换的值:
for ( val <- key )P | key! ( @Q ) -> P { @Q/val }
除了共识之外,这就是在区块链上存储合约的并发协议的计算模型。 在某个线程上,输出进程 key!
,将合约 @Q
的代码存储在由 key
指示的位置。在另一个并发运行的线程上,输入进程 for ( val <- key )P
等待一个新的 val
出现在 key
上。 当在 key
上出现一些 val
时,在这种情况下, @Q
, P
将在一个环境中被执行,在该环境中,出现的每个 val
都将被替换为 @Q
。该操作修改了 key
所引用的值,比如说,key
先前映射到一般的 val
, 而现在映射到合约 @Q
的代码,有资格作为RhoVM的状态转换的归约。
在 key
上的输入和输出进程的同步是通过触发RhoVM的状态转换的事件来实现。乍一看,将合约 @Q
存储到由 key
所表示的位置的输出进程,本身构成一个状态转换。但是,Rho演算归约语义要求具有 可观测性。对于任何未来将发生的计算 P
,归约规则要求输入进程 for ( val <- key) P
,观察到 在 key
上的赋值。这是因为只有输入项定义未来的计算,也就是说输出项本身在计算上是不重要的。 因此,直到输入和输出项在 key
上同步之后,才会出现 可观察 的状态转换。这个可观测性要求在编译时被强制执行,以防止通过重复调用输出 key!(@Q)
的DDoS攻击。
已经证明,将rho演算归约规则应用于键值数据存储的数据元素上,构成了RhoVM实例上的状态转换。但是,其目的是为了验证和维护实例上所执行的每个合约指定的所有状态转换。这意味着键值数据存储的配置历史记录必须通过修改来维护,因此它是一个可持久化的数据结构。 所以,每个key必须映射到在该位置发生的已验证的归约历史记录:
图 - 内存中某个位置的归约/转换历史
每个键映射到一个归约列表,它实际上是某个地址的“事务历史”。 事务 { for(val1 <- keyn).P1 | keyn!(@Q1), … , for(valn <- keyn).Pn | keyn!(@Qn) } -> { P1{@Q1/val1}, … , Pn{@Qn/valn} }
的历史意味着合约 @Q
已经做了的修改,其中 @Qn
是仓库中的最近版本。认识到这个方案是RChain平台上的最顶层事务是非常重要的。传递的消息本身就是合约,这通常发生在客户系统或系统间交互中。 但是,每个合约 @Q
本身也可以在更简单的值上执行许多底层事务。
在事务/归约应用之后,就要达成共识。共识验证在 keyn
上的事务历史,{ for(val1 <- keyn).P1 | keyn!(@Q1) … for(valn <- keyn).Pn | keyn!(@Qn) }
,在所有运行RhoVM实例的节点上一致性地复制。一旦事务历史被验证,最近的事务就被添加到事务历史中。 当事务被提交到这些位置时,相同的共识协议会应用到一系列的键 { key1 -> val1 … keyn -> valn }
。
拓展一下,事务块表示已经应用到持久化键值存储中元素的归约集合,而事务历史表示一个Rho虚拟机实例的状态配置和转换的可验证快照。请注意,当且仅当节点上提出冲突的归约历史时,共识算法才会被应用。
总结:
- RhoVM虚拟机由Rholang表达的Rho演算归约规则,以及一个持久化的键值数据存储组成。
- .Rho演算归约规则将一个键的值替换为另一个值,其中一个命名频道对应于一个键,并且值可以是简单或复杂的。
- 替换就是事务,表现为存储在键中的字节码的差异。这些字节码差异在所有运行该RhoVM实例的结点上的精确复制,是通过共识算法验证的。
[1] | RhoVM“执行环境”之后将作为“Rosette VM”引入。选择使用Rosette虚拟机的原因取决于两个因素。首先,Rosette系统已经在生成环境中应用了超过20年。 其次,Rosette VM的内存模型,计算模型和运行时系统为RhoVM所需的并发提供了支持。RChain已经承诺将以Scala语言对Rosette VM进行现代化的重新实现,以作为最初的RhoVM执行环境。 |
可扩展性简介
从传统软件平台的角度来看,“并发”虚拟机实例的概念是多余的。虚拟机实例都被假设为独立运行。因而,不存在所谓的“全局”的RhoVM虚拟机。相反,在任意给定的时刻,存在多路在跨网络的结点上独立运行的RhoVM,各自在相关的分片(即我们已经提到的命名空间)上执行和验证交易。
这个设计选择形成了RChain平台上的系统级并发,而指令级的并发由Rholang提供。 因此,当本文引用RhoVM的单个实例时,都假定有多路RhoVM实例同时在不同的命名空间执行不同的合约集合。
执行环境
Rosette是什么?
Rosette是一种反射式,面向对象的语言,通过actor语义实现并发。Rosette系统(包括Rosette虚拟机)在1994年已经部署到生成环境中,应用在自动取款机上。 由于Rosette展示出来的可靠性,RChain Cooperative已经承诺会在Scala(针对JVM)完成Rosette VM彻底的重新实现。 这样做有两个主要的好处。 首先,Rosette语言满足Rholang中表达的指令级并发语义。 其次,Rosette VM被有意设计成支持在任意数量的处理器上运行的多计算机(分布式)系统。 有关更多信息,请参阅 Mobile Process Calculi for Programming the Blockchain。
模型校验与理论证明
在RhoVM和其他潜在的上游合约语言中,有许多技术和检查会被应用在编译时和运行时。这些有助于解决让诸如开发者和系统本身,能够提早知道类型良好的合约将终止的需求。 形式化验证将通过模型检查(如SLMC)和理论证明(如Pro Verif)来确保端到端的正确性。此外,这些相同的检查也可以运行时应用,当新提出组装的合约被运行的时候。
发现服务
一个先进的发现特性将最终被实现,从而能够从其他合约中搜索兼容的合约,并合并成一个新的复合合约。通过形式化验证技术,新合约的作者可以得到保证,当工作合约被合并在一起时执行时,与单独合约执行的效果相同。
编译
为了允许客户端在RhoVM上执行合约,RChain开发了一个以Rholang源代码为起点的编译器流水线。 Rholang源代码首先经过转译,变成Rosette源代码。经过分析,Rosette源代码被编译成一个Rosette中间状态(IRs),它将被优化。从Rosette IR中,Rosette字节码被合成并传递给VM用于本地执行。编译流水线中的每个转换步骤要么是可证明正确的,要么在生产系统中经过了商业测试,或者两者兼而有之。这个流水线如下图所示:
图 - RChain编译策略
-
分析:从Rholang源代码或者编译到Rholang的另一个智能合约语言,这一步包括:
- 计算复杂度分析
- 注入速率限制机制的代码
- 事务语义的形式化验证
- 解语法糖
- 等价函数的简化
-
转译 :从Rholang的源代码,编译器:
- 执行从Rholang到Rosette源代码的源代码到源代码的转换。
-
分析 :从Rosette源代码,编译器执行:
- 依照Rosette语法进行词法、语法和语义分析,构建AST; 和
- 合称为Rosette中间表示
-
优化:从Rosette 中间表示(IR),编译器:
- 通过冗余消除,子表达消除,无用代码消除,常量折叠,变量推理和抢断简化来优化IR
- 合成由Rosette VM执行的字节码
限速机制
The compilation pipeline will implement a rate-limiting mechanism that is related to some calculation of processing, memory, storage, and bandwidth resources. Because the rho-calculus reduction rule is the atomic unit of computation on the RChain platform, the calculation of computation complexity is necessarily correlated to the amount of reductions performed per contract. This mechanism is needed in order to recover costs for the hardware and related operations. Although Ethereum (Gas) has similar needs, the mechanisms are different. Specifically, the metering will not be done at the VM level, but will be injected in the contract code during the analysis phase of compilation. For more details visit the `developer wiki`_. Compiler work can be seen on GitHub.
存储与查询
总览
对存储层与网络查询层来说,每个节点都被视为一个本地节点,通过异步的方式来访问租借存储区域的数据库。不过实际上,存储与查询是完全分离的,共同服从于共识算法。根据区块链固有的微交易能力,RChain上的dApp用户使用代币来支付他们使用的区块链资源(计算,内存,存储和网络)。在RChain的设计中,所有的数据会被存储,只不过并不是所有的数据都是永远存储的。RChain的数据存储区域是可以租借的,存储资源的价格由数据的大小,复杂度,存储时长决定。消费者每次访问数据都可能被要求付费。数据的生产者以及消费者间接地向RChain节点的运营者付费。
从经济学的角度来看,租赁存储空间时用户必须付费是合理的。否则用户的数据很难被可靠地或永久地存储。我们选择直接定制经济规则。假设存储是免费的,仅通过一些不相关的措施来补贴存储的开销,这并不是一个环境友好型的想法。真实成本的其中一小部分可以在数据中心的热签名中测量出来。对数据访问需要付费的机制也有利于减少针对于存储的攻击,即存储非法的内容来抹黑相关技术。
RChain支持多种格式的数据,包括公开未加密的json格式的数据,加密的大文件或者混合的数据格式。这些数据可以是简单的指针或是存储于私有/公有/联盟链中的内容哈希。
数据语义
状态数据、本地交易记录以及相关的合约数据将会被存储在RChain区块链上。与以太坊类似,RChain也将实现加密-经济型的可验证的交易语义,以便于创建在区块链上进行运算的线性时间历史记录。请注意,区块链语义结构下的数学被称作可追溯的范畴论。如果想了解更多的细节,请参阅Masahito Hasegawa发表的该主题的论文Recursion from Cyclic Sharing:Traced Monoidal Categories and Models of Cyclic Lambda Calculi。
数据访问层以及领域专属语言
SpecialK是用于数据访问的DSL,而KVDB是DSL背后的分布式内存数据结构。SpecialK以一致的方式定义了分布式数据访问模式,如下所示:
数据项级别的读写(分布式锁定) | 数据库读写 | 消息发布与订阅 | 历史信息发布与订阅 | |
---|---|---|---|---|
数据 | 暂时的 | 持久化的 | 暂时的 | 持久化的 |
**连续(K)**[1] | 暂时的 | 暂时的 | 持久化的 | 持久化的 |
**生产者动作**[2] | 放置 | 存储 | 发布 | 携带历史发布 |
用户动作 | 获取 | 读取 | 订阅 | 订阅 |
SpecialK的数据访问模式图
从SpecialK的DSL和API角度来看,当它执行一个数据访问动作,比如Get(带有一个请求模式)请求时,对于这个数据是存储在本地还是在远程(即存储在其他网络节)是不做区分的。无论如何都有一个单一的查询机制。
在2016年之前,SpecialK的技术栈(包括代理服务,SpecialK,KV数据库,RabbitMQ以及MongoDB)提供了一个分散的内容交付网络,尽管它不是资源计量或者资源货币化的方式。SpecialK和KVDB组件基于底层数据库MongoDB以及一个优化的消息队列协议(ZeroMQ),以创建用于存储和检索内容(包括本地和远程内容)的去中心化逻辑。目前SpecialK和KVDB的1.0实现都是用Scala编写的。
查询语义根据体系结构中相关的级别而有所不同。在SpecialK级别,键值是prolog表达式,该表达式可以通过datalog表达式进行查询。在架构级别,标签的prolog表达式用于存储,标签的datalog表达式用于查询。在RChain中,SpecialK和KVDB层将用Rholang重新实现(与先前在Scala中的定制实现有界连续和代码序列化的实现有所不同)。
更多相关信息,请参阅SpecialK&KVDB - 一种Web的模式语言。
KVDB - 数据,连续访问和缓存
数据将使用SpecialK语义进行访问,存储在去中心化的键值数据库(KVDB)中。下图显示了两个节点如何协作响应一个GET请求:
SpecialK的去中心化数据访问方式
- 节点首先在内存缓存中查找所请求的数据。如果没有在缓存中命中,
- 在本地存储中进行查询,如果没有命中,则在该位置存储一个定界符
- 在网络中进行查询,如果在网络中查找到了合适的值,定界符将在范围内返回并且将检索到的数据作为参数返回。
为什么RChain没有选择分布式存储的IPFS(星际文件系统)?除了存在集中化风险之外,IPFS使用内容路径来访问内容,而SpecialK使用完整树来访问内容。IPFS有一个直观的路径模型,但是这个设计存在如何进行查询的问题。 SpecialK从寻址开始。一旦SpecialK查询语义设计完成,RChain项目还可以借鉴IPFS的成果,例如利用哈希来完成内容的路由。SpecialK还可以利用一个随机生成的数据无关的键。
P2P节点通信
SpecialK去中心化存储语义需要节点通信的基础设施。与其他去中心化实现相似,在RChain中P2P通信组件还是处理节点的发现、节点间的信任以及节点间的通信等问题。现在的实现方案采用了基于RabbitMQ的P2P架构,尽管我们同时也在考虑是否采用ZeroMQ。
[1] | 请注意,按照惯例,一个连续函数会被当作一个参数名为k的参数。 |
[2] | 这只是这个功能分解下动作的一个子集。例如fetch动作,在没有数据的情况下,在数据检索过程中不会留下任何有界连续信息。 |
Casper 共识算法
网络中的验证节点负责维护区块链,使得全网状态达成一致。 验证者节点还需要确保区块链是自洽的,没有被篡改以及防止女巫攻击。
Casper 共识协议的几个方面使得参与者达成共识,包括以利益为基础的绑定,解绑定和周期性下注。 分布式共识协议的目的是为了确保横跨多个节点的区块链或部分区块链(基于命名空间)的一致性。 在错误频发的网络条件下,为了实现这一点,任何共识协议都应该产生一个确定的结果,并且证明协议的安全性和终止属性。
RChain’s consensus protocol uses stake-based betting, similar to Ethereum’s Casper design. This is called a “proof-of-stake” protocol by the broader blockchain community, but that label leads to some misperceptions including overstated centralization risks. Validators are bonded with a stake, which is a security deposit placed in an escrow-like contract. Unlike Ethereum’s betting on a whole blocks, RChain’s betting is on logical propositions. A proposition is a set of statements about the blockchain, for example: which transactions (i.e. proposed state transitions) must be included, in which order, which transactions should not be included, or other properties. A concrete example of a proposition is: “transaction t should occur before transaction s” and “transaction r should not be included”. For more information, see the draft specification Logic for Betting – On Betting on Propositions.
在某些汇合点处,验证者计算所有提议的最大共同子集。 有些时候,这个计算可能会非常困难,需要很长时间。 正因为如此,我们会需要一个超时机制,如果达到了规定的超时时间,验证者可以提出一个比较小的提议集合。一旦验证者对最大一致的提议子集达成共识,寻找下一个块就可以通过找到提议有效的最小模型而容易地实现。 对于这种设计,由于每个名称空间的事务隔离,所以大多数块可以并行合成。
我们来看看典型的工作流程:
- 验证者是一种节点角色。 每个验证者都会建立一个类似于债券的赌注,以确保其他验证者能工作良好。 如果他们不是一个好的验证者,那么这个股份就有被没收的风险。
- 客户端将事务请求发送给验证者。
- 接收到事务的验证者会创建一个包括最近交易的提议。
注意:只有在历史事务记录不一致时才会执行共识
-
在节点之间产生一轮下注周期:
- 原始验证者准备下注,这个赌注中包括以下内容:
- 来源 =下注的来源
- 目标 =下注的目标
- 认领 =认领赌注。 认领可以是一个区块,一个提议或者是一个最大一致的子集
- 信心 =代表玩家对发起认领中的证据拥有的信心。 这是验证者所使用下注策略的一种。
- 理由。 用于证明为什么这是一个合理的赌注。
- 验证者下注。
- 验证者会评估接受到的下注。 请注意,这些接受到的“理由”可以用来确定网络的各种属性。 例如,一个算法可以检测出模棱两可的情况,或者创建一个“理由”的图表,或者在下注的时候发现接受到了太多信息。 注意需要考虑攻击向量,以及博弈论应该如何被应用于协议设计。
-
投注周期朝着证明一个结果继续进行。 注意:
- 下注周期的目标是验证者节点在最大一致的提议集上达成共识。
- 能进行证明的一个前提条件是 2/3 的验证者的行为是合理的。
- 最终下注周期会逐渐收敛。
- 收敛过程中是部分同步的。
- 通过对提议下注,能够一次性合成更大块的区块链。 如果没有冲突,周期可以快速收敛。 这种方法的关键点是可以同时生成多个块。这种方式突破了块大小的限制。 关于这一点没有任何争论,因为最大的一致提议集合可以允许一次同意数百甚至数千个块。 相比现有的区块链而言,这将带来巨大的速度优势。
- 对于每个下注周期,验证者节点可能会赢得或失去他们的下注。
6.可扩展性是通过对提议进行精细划分并通过共识协议的嵌套(递归)来实现的。如果验证者在一组提议上达成一致性,并且证明在投注周期已经收敛,那么区块就可以通过协议被合成。 当前的下注周期结束。
对于更多相关信息详见:
- “共识游戏”:分析和比较广泛共识协议的公理化框架。
- 关于RChain共识议定书的更多细节,请参阅“投注逻辑 - 投注议案”。
- 要了解更多关于以太坊的 Casper 加入“以太坊研究Gitter”和“Reddit / ethereum”进行讨论。
- 验证者的下注周期可以用一个迭代函数来表示,最终收敛对应于迭代函数中的不动点。通过这个原理,我们可以证明奖励与惩罚的收敛原则。我们可以给每一个验证者充分的自由度,唯一需要解决的是验证者所参与的下注行为是可收敛的。
应用
任何数量和种类的应用都可以构建在RChain平台的顶层,平台提供了一个分布式的公共计算工具。这些应用包括:
- 钱包
- 交易所
- Oracles & 外部适配器
- 定制协议
- 智能合约
- 智能财产
- DAOs
- 社交网络
- 市场
智能合约开发和部署
接下来的讨论是为了说明命名空间如何允许异构部署合同和合同状态。命名空间是分片的关键特征之一,我们从中得到的好处类似于侧链、私有链和联盟链以及测试与生产之间的区别,所有这些都在一个说明中。
例如,下图描述了一些可能的开发、测试、部署配置和注意事项,以及如何使用命名空间和分片来启用版本管理。
图 - 开发和部署的可能性
我们将与IDE工具提供商合作来整合Rholang平台和验证工具。
引用
引用
[MiBH] | Miller, H., Burmako, E., Haller, P.: Reflection Overview, http://docs.scala-lang.org/overviews/reflection/overview.html. |
[Scal] | Scala: Sequence Comprehensions, http://docs.scala-lang.org/tutorials/tour/sequence-comprehensions.html. |
[Scal] | Scala: Continuations, http://www.scala-lang.org/files/archive/api/2.11.8/scala-continuations-library/?_ga=1.83694417.619951421.1484537916#scala.util.continuations.package. |
[Scal] | Scala: Case Classes, http://docs.scala-lang.org/tutorials/tour/case-classes. |
[MeRa05] | Meredith, L., Radestock, M.: A Reflective Higher-order Calculus. Electronic Notes in Theoretical Computer Science. 141, 49–67, http://www.sciencedirect.com/science/article/pii/S1571066105051893?via%3Dihub |
[MeRa05] | Meredith L., Radestock M: Namespace Logic - A Logic for a Reflective Higher-Order Calculus. Trustworthy Global Computing Lecture Notes in Computer Science 353–369. doi: 10.1007/11580850_19, http://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.95.9601 |