在《元组空间》一文中解释了RhoMachine的可组合性原理。
一个name必定属于某个元组空间,Rholang的运算基于name间的消息机制。
而即使这些name来自不同的元组空间,对于运算逻辑没有任何影响,这是Rho演算可无限扩展的原因。
假设有两台服务器(A和B),它们都提供了接口允许其它应用部署Rholang代码,且它们各自分别维护两个元组空间。
其中 a
是A服务器维护的元组空间中的一个name; b
是B服务器维护的元组空间中的一个name。
如上图所示:
for(x <- a) { x!(Q) }
for(_ <- b) { P }
根据上一篇中对<-
的解释,可以形象地认为x!(Q)
这个continuation部署到了a
中等待消息触发; 同样, P
这个continuation部署到了b
中等待消息触发。
在此状态下,假设服务器A又收到了一个请求执行:a!(*b)
,将*b
发送到a
中而后触发x!(Q)
这个continuation。x!(Q)
其中的x
替换为b
因此化简结果为b!(Q)
。
而b
位于服务器B的元组空间中。因此服务器A将b!(Q)
这个process发送给服务器B并在得到确认后删除服务器A中的b!(Q)
。
这就是Rho演算移动性(Mobility)的一个例子,x!(Q)
process必须在x
所属的元组空间执行,如果当前元组空间并不是x
所在的元组空间,则需要与x
的元组空间协调将此process迁移过去执行。
这也是Rho演算的别名移动进程演算(Mobile Process Calculus)的由来。
需要注意的是,process跨元组空间移动过程对于Rholang代码是透明的,Rholang代码执行的结果不会因为元组空间的差异导致不同。
在上面的例子中,一台服务器负责一个元组空间,如果服务器宕机会导致元组空间停止工作。
为了确保元组空间的高可用性,每个元组空间需要多台服务器一起维护,互为备份。
每个元组空间由一组服务器共同维护,在RChain中这些服务器被称为验证者(validator)。维护同一个元组空间的验证者就组成了一个分区(shard),这个分区内的name都由这些验证者维护,因此分区也叫做名字空间(namespace),它是name的集合。分区内的所有验证者可同时处理来自外部的请求,各自独立执行化简rholang,对同一个元组空间进行修改。这体现了Rholang的并行性(Concurrency)的优势。
具体到细节上,每个分区内有N个验证者,任何一个验证者对元组空间的修改都同步到其它N-1个验证者中。当然在异步环境中不可能保证这些验证者各自的元组空间完全一致,或者不同的验证者同时对一个name修改导致冲突。但这都无妨,通过CASPER共识算法,这些验证者能够就元组空间的状态变迁达成一致。在后面的几篇中中会详细介绍CASPER共识如何解决元组空间一致性问题。这里只需明晓:
转账在RChain中如何工作?首先考虑最简单的情况,在同一个名字空间中。
假设Alice和Bob各自有钱包,分别对应同一个名字空间中的两个name(a和b)。它们都是不可伪造的名字(unforgeable name),即使知道地址也无法伪造。而且,它们只能被特定的合约操作。name中保存的数字即为金额(token的数量)。现假设a中的数字是100,b中的数字为50。在RChain中这种保存金额的name称为钱包(purse)。
例1:现在假设Alice转账20给Bob,伪代码如下:
p = a.sprout(20, signature)
bob!(*p) // 将p发送给Bob
for( x <= bob ){ // Bob接收到name
bob.gather(x)// 将name中的金额合并到自己钱包中
}
a
分离(sprout)一个新的purse p
。此过程需要验证alice私钥生成的签名。此刻p
中的金额为20, 而a
中的金额从100
变成了80
。p
发送给Bob。p
后,首先会验证此purse的签名等evidence,将purse p
中的金额合并到purse b
后,purse p
被销毁。上面就是转账过程的基本过程,不过省略了很多细节。
整个过程看上去和UTXO模型有点类似,但本质上它还是账户模型。至于为什么这么做的原因后文会说明。
上面的例子中假设Alice和Bob的purse位于同一个名字空间内。但如果Alice和Bob的purse分别来自两个不同的名字空间会如何?
由于前面说到的process跨元组空间的移动对于程序代码是透明的,因此跨名字空间转账理应没有任何不同。这在可信计算环境中是成立的,但是在区块链环境中,却不能如此。
共识是以名字空间为单位,名字空间内的验证者只对自己名字空间的状态变化进行共识。具体的共识过程在后续的文章中会介绍,这里需要注意的是:验证者不参与其它名字空间的共识。假设某公司或者团体独自运行一个名字空间,此名字空间是私有链或者联盟链,验证者数量很少(比如21个)。此名字空间中REV总量为1000,但是由于验证者数量很少且集中,它们合谋作弊,向另一个名字空间发送999999个REV的purse试图不当获利。从接收方名字空间的视角看来,接收到的purse合法有效,无法判断该名字空间是否真有足量的REV进行转账。
因此,Rho演算允许name直接跨名字空间转移,但在区块链环境中,必须禁止purse跨名字空间移动。所以RChain需要一种分区组织方式来算解决分区间的信任问题。
RChain在2017年1月的架构设计文档整理出了树形(tree)方案;而后在2018年年初又提出了晶格(lattice)方案。它们都是名字空间之间信任关系的解决方案。这两种方案的讨论一直存在,Mercury版本将会先实现树形方案,但不排除在以后的版本中集成晶格方案的可能。
树形方案的基本规则是:
Depository
(储备金)和Mint
(铸币),如下图所示:Depository
和Mint
合约可以将purse在父子名字空间中转换。
例2:从父名字空间A的purse a
转账20到子名字空间B的purse b
。
a
sprout出含有20的purse p
。Depository
和Mint
合约,将父名字空间的purse p
兑换成子名字空间的purse q
。q
合并到purse b
并同时销毁purse q
。比较此例与例1,第一步和第三步没有区别,唯一的区别在第二步。通过合约调用将父名字空间的purse p
兑换成了子名字空间的purse q
。
特别要注意的是,purse p
在销毁前合并到了purse d
,purse d
是由由Depository
合约控制的储备金账户,表示子名字空间内所有金额的总和。
例3:从子名字空间B的purse b
转账20到父名字空间A的purse a
。
b
sprout出含有20的purse p
。Depository
和Mint
合约,将子名字空间的purse p
兑换成子名字空间的purse q
。q
合并到purse a
并同时销毁purse a
。可以看到在第二步兑换时,父名字空间内的purse q
并不是凭空生成的,而是从储备金purse d
中sprout的,从而保障了子名字空间无法转出超出储备金的金额。
例2和例3演示了父子名字空间内进行purse转移时储备金账户的原理,实际情况会复杂些:
Depository
和Mint
合约的调用通过多重签名(multisig)机制,这样子名字空间与父名字空间的验证者不需要加入到同一个共识协议中。Mint
合约可以设置不同的兑换比例,例子中是最常见的1:1。这部分细节很多,需要详细了解的可以查阅此文档。
依据上文所述,子名字空间是父名字空间的客户端并信任父名字空间。依照这样的逻辑就能够构造出如下所示的树形结构。
例3:假设Alice的钱包位于/a/b
名字空间,Bob的钱包位于/d
名字空间。Alice向Bob的转账按照上面的逻辑在父子名字空间移动,它们之间的转账最终必定会经过通往最近共同祖先(least common ancestor)上的所有名字空间。
在这个例子中可以看到,如果purse所在的名字空间与目标名字空间需要经过很长的步骤移动完成,开销和耗时都比较大。
但实际情况会乐观很多:
例4:假设Alice共有100 REV,且REV在所有名字空间内的兑换比例都是1:1并有如下分布:
假如Alice需要转账1 REV到/a/b
名字空间中的Bob的purse,而Alice在该名字空间内有15 REV,因此并不需要跨名字空间移动purse。purse跨名字空间移动的情况很少,而RChain的钱包将会把此过程透明化,Alice只知道自己拥有100 REV而不关心自己的purse在哪个名字空间。
众所周知,区块链的不可能三角–去中心化、高效低能、安全性三者不能同时满足。
根据目前获得的验证者协议草案,可以看到验证者保证金逐级增加。
根名字空间(Root Shard)的保证金大概是 1000到 1000 到 2000, 鼓励大量结点参加。向下逐级增加验证者加入所需的保证金,这也就意味着越往下名字空间的验证者数量越少,但是效率可以做到更高。
以一个名字空间为单位无法覆盖到不可能三角, 但RChain整个树形结构却覆盖到了不可能三角的方方面面,提供了最大的灵活性。同时,在引入名字空间准入机制后,私有链、联盟链也可组建自己的名字空间,提供最大的包容性。最后,RChain的树形结构名字空间整体上是无限可扩展的。
转载须注明出处
本文地址:https://blog.csdn.net/wangjia184/article/details/80277924
下一篇《区块结构与DAG》