查看原文
其他

Sub Dev 分享 | 波卡跨链核心技术最强合集

徐杨 一块Plus社区 2020-11-11

一块链习是首家区块链技术学习社区,提供最系统的区块链技术课程学习,定期出品有深度的技术观察 + 评论。


《从0到1学会Substrate区块链应用开发》是由Parity 和一块+ 联合出品的全球首个Parity 官方合作课程。


每周日晚8点,作为课程内容知识拓展——助教技术分享会,由各位第一期的助教们自发轮流在线上进行分享,为学员们详细解读一个 Substrate 技术相关内容。


上周日晚,由拓链科技的CTO ——徐杨在直播间为大家带来第六讲「波卡跨链核心技术介绍」。


内容复盘如下

 

首先介绍一下我自己。我叫徐杨,我是拓链科技的CTO。我们公司主要是做数字货币托管平台,现在我们也和HashKey一起在做一个产品叫HashKey Custody。从去年四五月份,我就开始接触波卡和substrate,也参与了一块社区去年第一次的培训,是由陈锡亮老师讲解的。


学好了substrate之后,我自己也写了一个 substrate-dex,就是一个去中化交易所的demo项目,大家可以点击这个链接 http://github.com/alexxuyang/substrate-dex 看到这个项目。在后面的一些技术分享的过程当中,我也做了一个关于使用rust语言去实现RSA算法的分享,大家也可以从这个链接http://github.com/alexxuyang/RSA-rust找到。

 

关于本次的直播,最后大家如果有任何的问题或者想沟通的话,也欢迎添加我的微信与我联系。


| 版本说明

本次分享大概是从6月25号开始准备的,相关的几个代码库的版本号这边也写出来了。所以如果大家要详细的去查看一些代码,可以按照这样的版本,可以保持一致性。 

 

| 为什么要了解波卡跨链机制

第一,对跨链协议本身非常感兴趣,驱使我们去直接了解topic。


第二,它能够帮助我们更好的将我们做的parachain,通过cumulus这样的代码库接入到我的relay chain里面。


第三个,因为波卡也是使用substrate做的最好的一个参考,所以当我们在使用substrate去做一些parachain开发的时候,去学习整个波卡的代码库是一个最好的学习案例。

 

| 理解波卡跨链的常见问题: 

第一,收集人他到底在干什么,他和parachain,relaychain之间的关系是什么?


第二,跨链到底是什么东西在跨链,是什么东西从parachain上跨到relaychain上?


第三,平均每个parachain会分配10个左右的见证人,为什么这样能够保证安全性呢?


第四,我们经常会看到一个叫做pooledsecurity或者是叫 shared security的概念,到底是在讲什么?


第五,与跨链相关的各种各样的数据,最终在relay chain上面是哪一些上链了,哪一些没有上链,中间的划分以及设计的依据是什么? 


第六,我们知道波卡其实是基于 substrate建立的,那么哪一些模块是放到了波卡上面,哪一些模块又放到了substrate上面,这中间划分到底又有怎么样的一个设计和思考? 


第七,我们为什么要去使用一些像纠删码或者是VRF这样的一些技术?


第八,就是整个波卡生态,它的tps能够达到多少?TPS又是怎样计算出来的?



这张图是从波卡的官网上面下载下来的,我第一次看到这张图的时候,感觉像是一个在太空里面的飞行器的感觉,同时他也具有了很多艺术性。所以当我们越来越多的去了解波卡之后,其实会发现整个波卡的设计,不管是它的理念设计,还包括它的代码里面的一种软件工程上的一些设计,其实都会带给我一种艺术品的感觉。 

 


.01
波卡架构

第一个就是 relay chain, 就是图片中红色标出来的这几个小点。relay chain主要的工作是负责整个波卡的安全性,共识和跨链的一些操作。

 

第二个就是parachain,parachain其实也是叫做平行链,它是一个主权的区块链,它在不加入波卡的时候也可以存在,那么它同时也可以加入到波卡里面来,享受波卡带给他的一个安全性。 

 

第三个是Parathreads。Parathreads讲的是对于某一些场景下面,其实它可能不需要长时间占用一个parachain的插槽,那么在这个场景下面,它就可以临时的去租用一个插槽,就是按照我使用了多少就去付多少费这样的一种方式来计算。


第四个就是bridge,bridge是主要考虑是,波卡希望能够将不是通过substrate构建的一些区块链也连接到波卡生态里面,那么就需要这样的一个桥接的 bridge来进行一个连接,目前主要的是连接以太坊和比特币这种非常大的外部的区块链。 


 

.02
共识参与者


我们再来看一下共识的参与者,共识的参与者总共有4个。

 

第一个是nominators,就是提名人,他可以把自己的 dot stake给我们的见证人,然后再让validator去进行整个波卡生态的安全保护。

 

第二个是validators,就是见证人,他通过抵押dot,去验证collators的证据,然后与其它所有的见证人一起共同保护relay chain。

 

第三个是collator,就是收集人,这个也是我们在理解整个跨链过程当中会比较谜的一个环节。它主要的作用其实收集在parachain上面的这些区块,然后它通过P2P的网络再散播给我们的 relay chain。

 

最后一个是fisherman,它其实是一个独立的第三方的监控的角色,它会监控在整个的跨链和共识的过程当中,是否有作弊或者是有错误的一些场景,如果有的话fisherman可以将这样的一些场景报告出来,然后让更多的见证人去验证他说的到底是对的还是错的。

 

从这个图上面其实大家可以看到fisherman的角色其实是可以通过一个 collator来做,也可以通过一个validator来做。


| Polkadot与Substrate的关系


我们知道波卡是基于substrate构建出来的,那么为了让整个生态里面其他的一些项目能够快速的搭建它的parachain的区块链,parity是将波卡中的非常统一的一些逻辑抽象出来,然后将这些逻辑形成了一个substrate,parity再在基于 substrate去构建整个波卡的一个代码库。 

 

substrate之上会有一些仅仅和波卡相关的定制化的代码,这部分的代码是分成了两大块,第一个是和共识相关的,那么这个共识相关的这边我也罗列了一下,包括collation,collator,candidate receipt,executor,纠删码,一些定制化的网络的协议,以及 availability store。所有的这些内容其实都是和共识相关的一些内容。 

 

第二块就是我们常说的业务逻辑,也就是大家学 substrate的时候会去写runtime的一些逻辑。Bizlogic这个业务逻辑主要都是存在于runtime里面的一些业务逻辑,包括Parachain的生命周期管理,Parachain的执行管理,包括最近正在进行的募资管理,以及众筹等等这样的一些逻辑。

 

所以大家可以理解成基于substrate之上,在构建了一些定制化的共识和业务逻辑,这三部分加在一起,就形成了我们的波卡的代码库。 

 


.03
跨链协议流程


我们来看一下跨链协议的整个的流程,在parity的文档里面叫做availability and validity。就是可用性和有效性,总共是6个阶段,

第一个阶段是平行链的阶段,就是平行链会出块,然后通过收集人将这个区块传送给我们的relay chain。

 

第二块就是relay chain收到区块之后,会进行非常多的验证,最后形成 candidate receipt,就是候选收据,最终会把候选收据会提交到区块里面。 

 

第三部分是可用性的一个子协议,它主要涉及到的是纠删码这一部分的一个设计。

 

第四到第六块其实都是这个区块已经出块了之后,我们会执行非常多的二次验证,以及一些独立审计人像fisherman这样的一些验证,以及最终通过我们的grandpa去让整个区块链进行最终性的一个确定,所以4~6我后面会统一的放在一起,做一个统一的讲解。 



.04
平行链阶段

 

| 平行链阶段-网络架构

 

第一块我们来看一下平行链的阶段,首先我们来看一下网络架构,收集人它其实是一个独立的程序,我们可以看到在图例中是放在中间的,它其实是同时在两个网络当中跑的一个节点,左边其实就是和Parachain跑在了一个P2P网络里面,右面就和relay chain跑在了一个P2P网络里面。

 

所以collator处在这样的一个角色当中,他就可以把 parachain的一些数据传导给relay chain,同时也可以把relay chain发生的一些事情最终也传回 parachain。我们可以看到collator它相当于是一个信息的摆渡人。

 

所以基于这样一个特性的定义,整个波卡生态其实是不需要collator进行任何的dot的抵押。因为collator其实仅仅是把信息做一个摆渡,他没有参与更多的共识上面的一些验证的工作,所以他是不需要抵押dot同时相应的他也不会受到任何的惩罚。

 

| 平行链阶段-整体流程

我们看一下平行链阶段的整体流程。

我们就看一下上面的这张图,这边其实是有三个parachain,每一个parachain中间都会有一个collator,就是收集人,收集人会在parchain的网络当中去收集一个区块,这边就是我们叫做一个区块candidate,这个就是区块的数据,这个我们叫做有效性证明validity proof。收集人收集好的这一整块的数据之后,它会把整个数据通过P2P网络传送给最右侧的relay chain,让relay chain去做一个验证的工作。

 

我们可以看到,其实对于collator来讲,它收集的整个区块和有效性证明,它可以是有效的,也可能是无效的,因为最终的验证其实是需要通过relay chain上面的validator去进行的。那么collator仅仅是把这边发生的一些事情给传倒过来。 

 

| 平行链阶段 - collator激励

我们看一下对collator的激励,其实前面也讲了collator是没有任何收益的,同时我们也不需要collator做任何的这样的一个抵押。

 

从一些官方文档看,每一个parachain其实只需要一个诚实的收集人来提交区块就可以了,但其实从波卡的代码里面去看的时候,它是可以连接更多的 collector来传递信息给到 relay chain的。那么其中有一个是主的收集人,其他的是一些候选收集人,这样的一个架构就可以允许万一主的收集人出了问题,可以马上会有一些预备队standby的收集人马上可以来进行工作,把这个区块从parachain传送到relay chain。

 

如果说对collator来说需要有一些经济奖励的场合,也是需要实现在parachain上, 同时collator它其实除了可以做collect这样的工作,就是信息摆渡这样的工作,因为它处于这样的一个中间桥梁的位置,其实他同时也可以做另外一个角色,就是做fisherman这样的一个角色。所以如果把这两个角色都整合到了一个程序里面,这样就可以对这样的一个程序进行有相应的收益,因为fisherman如果发现了一些真正的问题,最终波卡是会给他一些奖励的。 

 

| 平行链阶段 - 测试方法

我们看一下在平行链阶段是怎么去测试,假如说我的一个parachain和relay chain之间这样的一个交互的大概会有两种方法。


 这一部分涉及后面结合代码库的讲解,建议看直播回放



.05
中继链阶段

 

| 中继链阶段 - slot

我们看一下第二个阶段就是中继链的阶段,这个阶段首先我们的collator生成了候选区块之后,它会通过 P2P网络将区块传送给relay chain的见证人,我们都知道在substrate或者说波卡里面,有个概念叫 slot。

slot就是每6秒钟去分配一个slot,然后会在这样的一个slot或者说插槽当中去做很多的事情,包括去选出一个见证人,然后去对整个全网进行一个出块。同时我们和这里相关就是在这样的一个slot当中,会对每一个parachain分配在这个思路当中负责,对他的区块进行验证的这些validity。 

 

Validator_per_parachain = (validator _ count-1)/ parachain _ count这一行

其实是每一个parachain它的 validator的数量的一个公式,它其实是等于总共的validator的数量减一再除以Parachain的 count。那么假设我们总共有101个见证人,然后我们有10个parachain,这样的话相当于101-1就是100,然后每一个parachain就会分到10个validator会在这样的一个周期当中为他进行工作,那么还剩余了一个 validator,就不会分配在任何的一个Parachain上面。 

 

| 中继链 - duty_roaster

我们再看一下中继链阶段的有一个概念叫做duty roaster。duty roster讲的是说在每一个slot当中,我们会去计算负责所有parachain的所对应的validator是哪一些?所以这样的一个 validator和他负责的工作所对应的这样的一个 hush map的称呼就叫做duty roster。

 

大家可以理解成是一个工作任务表,然后做这样的一个分配的时候,他是从代码上面看,它其实是基于我们的有一个随机数,这个随机数就是基于之前81个区块的数据所生成的1个随机数,然后也会按照右边的 shuffle的一个算法进行随机的分配,

       

就会把乱序的某一个validator的工作和另外一个validator工作进行一个随机的调换,然后最终产生的一个效果就是所有的validator他要负责的工作,无论是某一个parachain的工作,或者是他不负责parachain的工作,只负责relay chain的工作,这样的一个结果其实都是随机分配,这个是我们从代码里面看到的是这样的一个效果。


parachain 有一位工程师Joe写了一篇有关 parachain block整个的生命周期的介绍,文章是说validator它其实是使用的 v r f的这样的一个随机输出,就是被使用的这样的一个结果然后去进行分配的,在这个文章当中是这样去说的。但是我们从算法的里面看,其实是使用了我们链上的一个随机数。

 

| 中继链阶段 - VRF

那么说到了VRF,我们就来简单的介绍一下VRF。

 

虽然我们在去计算duty roster的时候,其实并没有使用到VRF。但是我们在后面会讲解到一些关于争议的一些解决的时候,文档里面也会指出是需要使用vIf所以我们这边来看一下这个vrf它简单的一个概念是怎么样的? 

 

首先 v r f是我们在 substrate里面babe模块去确定每一个slot当中出块的见证人的一个算法。他基于的一个思路是这样,首先我们知道一个哈希函数,我把一个info这样的数据进行了哈希,然后我最终可以得到一个结果。

 

如果说我希望把一个密钥就是我们的一个private key也放到整个的哈希函数的过程当中,我也可以生成一个结果。但是这个过程其实我们很难够去验证,因为我如果要去验证这个result,它是根据哪个哈希的输入算出来的话,我是需要知道 private key的,这个其实是不符合我们整个公私钥的这样的一个架构的设计。 

 

所以在这样的一个前提下,其实我们就设计了VRF的一套算法,他大概的逻辑是说我们首先根据私钥和info,我可以算出一个result,同时我根据私钥和info,我也可以算出一个proof一个证明。那么任何拿到这4个数据的人,哪4个数据呢,就是与私钥相对应的公钥。第二个是info,第三个是算出的result。第4个是proof。任何拿到这4个数据的任何一个人都可以通过一个函数去验证它是真的还是假的。 

 

整个这样的一个设计,它的好处是什么?

 

它的好处就是说我首先能够验证 result是真的还是假的。第二个他其实是能够知道说我是哪一个private key所对应的那一个见证人所算出来的。所以这两个是validator的key pair。 

 

| 中继链阶段 - VRF在波卡中的应用

那么看一下VRF波卡里面的这样的一个应用,它大概的过程是什么样子?

 

这一部分涉及后面结合代码库的讲解,建议看直播回放


| 中继链阶段-整体逻辑 

第一步,我们会在relay chain上面会收到收集人发来的 candidate block。


第二步,分配给这个 parachain相关的见证人,他们会使用 s t f (State Transition Function) 验证状态转移去确认它整个的状态转移是否是正确的。 


第三步,如果他是正确的时候,见证人就会把这个结果和一些相关数据发给其他的validator。


第四步,如果分配给某一个parachain相关的所有的validator超过半数以上的这些validator都认可了这一个区块的时候,就可以准备一个candidate receipt,我们叫做候选收据,最终在relay chain上面上链的一个数据其实是candidate receipt。


第五步,再将 candidate receipt发到交易池之前,还需要使用纠删码的技术去保存所有的 block的相关的一个数据。因为这些数据是需要保存下来,为后面的见证人或者fisherman去做验证的时候去使用的一些数据。这些数据其实是不会上链的,它只会保存在我们的见证人的本地的一个数据库里。


第六步,我们可以把 candidate receipt发到节点的交易池里面,最后就是打包的节点会将 candidate receipt打包进入到了一个区块里。那么这7步做完整个中继链阶段就执行好了。 

 

我们来看一下一些里面具体的场景,我后面会详细的介绍一下第二、第三、第四还有第七这4步的里面的一些细化的逻辑。 

 

| STF-默克尔树

首先我们看一下这个状态转移函数,是基于默克尔树这样的一个概念来做的。默克尔树其实是在比特币和以太坊的区块链里面都会使用的一个技术。它大概的逻辑是说我们所有的状态,这边每一个叶子节点都是一个状态,举例子这边是Ben的一个余额是200,这边是Louise的余额是700,然后这里是有一个公投,它的技术现在是有三票。

 

那么所有叶子节点其实都是我们的状态,然后我们会把当前链的所有的状态放在了叶子节点上面,然后再往上一层,就基于每一个状态的值去生成一个哈希,这样上一层全部是哈希了。然后我们再去根据两两哈希合在一起再做一个哈希,就会生成上一层的哈希,依此类推,最后我们就会在根上得到了一个根哈希,我们也叫做状态根,在这里这个状态根就是 b37e。

 

然后默克尔树它实际上是有一个很好的属性或者说一个特性。就是如果某些值发生改变的时候,我们从验证的角度,其实会需要更少的一些数据量就可以进行一些验证,我们这边来看一些具体的例子。

 

假设这里A1到A8是这边的8个值,然后我们在第8个“7”这个值上面假如说发生了一个变化,就是从A8变到了B8。变到了B8之后,我们就会发现b37e是之前的状态根,那么因为叶子节点它的值发生了变化之后,其实整个的默克尔树的状态根一定会发生改变,在发生改变的时候,我们会怎么去计算。

 

首先它是已经改变了,所以这边是第一个节点,我是需要知道的,就是这一块,那么它需要跟它再做一次哈希,所以我就需要这个节点,同时我又需要另一个节点,所以我需要这3个节点,然后再配合这个值的哈希总共4个节点,我就可以算出这个状态根来。

 

所以并不是我们之前说的,我需要8个这样的数据才能够算出新的状态根,所以我其实需要的是3个,这里面它有一个算法,大概就是log (n)这样的一个逻辑。基于这个属性,我们在做验证的时候可以非常方便。 

 

我们再看第二个例子,就是我其实是改变了三个状态,就是ben的余额从200变成了250,是因为Louise 转了50给他,然后这边公投又多投了一票,那么这三个数据其实都发生了改变,然后我在计算新的状态根的时候,其实我就需要4个绿框里面的哈希和3个新的值所对应的哈希总共合在一起,我就可以把新的状态根给计算出来。 

 

| STF - 验证过程

我们来看一下relay chain在做整个 s t f的验证的过程当中,大概的逻辑是什么样的?

 

首先对于collator来讲,它会提供三大块的数据。


第一个就是区块,区块里面是包含了很多的交易列表,或者说我们叫做状态转换的一个列表,这些状况状态转换列表可能是转账,也可能是公投的数量上的提升。


第二个,数据就是这个区块从collator的角度,他认为所修改的平行链的数据库里面的值,他要把这些之前的值提供出来,也就是这边我们标成黄颜色的之前的值。 


第三个,collator同时需要提供在整个默克尔树里面没有改变或者说未受影响的那些数据根的哈希。那么这些值是哪些?其实就是这些绿色的这些值,绿色的这些框,它其实全都是没有改变的,那么没有改变的话,对于新的计算来说,整个这一块其实只要这个哈希就可以了。那么对这里来说也是,我就需要这一个哈希就行了,这两个也是类似的一个道理。那么collator就把这三块的数据就提供给了我们的 validator。


那么我们可以看到其中这第一个就是 candidate block, 2和3就是validity proof有效性证明,一般来讲我们看代码的时候,这是通过这两个名字去称呼的。那么 relay chain的validator首先可以基于2和3算出在老的默克尔树里面的状态根S1。


所谓2和3,首先2就是这些黄颜色的地方,然后3就是这些绿颜色的这些格子,那么把黄颜色的和绿颜色的做一个叠加,我们可以把老的状态根计算出来,然后同时见证人使用我们的一些执行器去执行1区块里面的所有的这些交易。 

 

最终这些交易其实会让这些节点的值发生了改变,然后validator需要保证几点,首先它需要保证的是说修改的状态数据一定是这三个, 不能多也不能少,同时它可以得到一些新的值,就是这些红颜色里面的值。然后validator它可以基于新的值,就是4这一块的值以及3的值,3的值就是未受影响的这些绿颜色的这些哈希。 

 

基于3和4,计算出执行了新区块之后的一个状态跟S2,见证人需要比较的是,把S1和S2合在一起,与collator传过来的数据进行比较,如果相同就表示这个验证就成功了。

 

| trust free shared security

我们来看一下一些关于状态转换验证的一些概念。 

波卡或者说relay chain的验证程序其实是不会去检查平行链当中的所有的值的,因为它只会去关心那些变化的值,以及未变化值的根哈希,然后它会通过执行区块去检查已经修改的值确保它的修改有效。所以我们可以得到一个结论,这个结论是什么呢?

 

就是如果一条区块链在加入波卡网络之前,他的状态就是有效的话,那么波卡是可以保护从这个时间点之后所有的状态转换是有效的。所以我们有一句话就叫做,波卡其实不是保证了有效的状态,而是保证了有效的状态转换。


这一点其实对parathreads来说就是更加适用了,因为对于parathreads来说,他其实会经常的加入或者说退出整个的 relay chain。所以他在退出的时候,他的状态转换其实就只能由他自己去保证了。

 

所以这边我们就提到了两个概念。


第一个是无信任,因为整个的状态转换的验证,其实都是在P2P和区块链的模型下去执行的,所以我们可以称其为无信任的。 


第二个就是共享安全,因为分配给每一个Parachain的见证节点,其实都是随机挑选的,所以虽然我们看上去是10个validator给1个parachain去认证它的整个的一个状态转换,但其实它是由整个波卡的所有的见证人来进行的一个保护,所以就叫做共享安全这样的一个概念。

 

| STF - executor

这一部分涉及后面结合代码库的讲解,建议看直播回放

 

| 广播验证结果 - network设计

见证人验证好了之后,其实会把相关的一些结果通过我们的network模块发送给其他的一些见证人。所以这一块我们就简单的来看一下,在波卡这边整个的 network模块是怎么构建的?

波卡其实是在整个 substrate之上,构建了一些定制化的网络层。在定制化的网络层里面,它其实主要的有两个模块,一个是叫做 protocol,一个叫做gossip。 

 

然后我们就来看一下这张图,这里的protocol包括三个组件。


第一个组件是Handler,它其实是做一些比较实际的工作的。


第二个组件是Worker,它其实是一个路由层, worker是把所有的消息会路由给Handler或者路由给后面的gossip模块。 


第三个组件是service,其实也是封装了整个worker和handler,然后对外提供了一些trait的实现。这个是我们的 protocol。 

 

然后右侧这边就是gossip,gossip其实它会使用 substrate里面的 gossip engine引擎来发送一些 p 2p网络里面的消息。Gossip其实主要实现了两个体系,一个就是我们说的 attestation,就是和我们共识相关的一些消息的转发。


第二个就是, 因为 parachain和parachain之间是可以通过XCMP就可以把消息进行一些直接通讯,所以对于gossip这个模块来说,它也要支持这样的消息转发和路由。我们其实主要看的是这一块,然后我们也可以来看一下这里面的具体的代码,我们找到 network。 

 

我们首先来看一下Protocol部分的逻辑。 


第一,我们来看一下Handler, handler就是具体去做一些事情的组件。我们可以看一下这边他会去执行,假如说有新的节点连上的时候的一些逻辑,然后有节点断线的逻辑,然后有消息过来之后的对消息截包,然后再去转发给其他的一些组件的逻辑。像这里它会转发给 on_status,就是调用到这里。然后on_collator_role就是见有一个validator告诉我,其实他也是一个collator, 就会发这个消息给我,on_collator_role其实就是在这边。所以这个就是 on_raw_messages其实做了很多消息的解构,以及再把它分发到具体的执行逻辑里面。这里我们就不讲。 

 

第二,我们看一下 worker。这个worker其实是包括了我们刚刚说的handler,同时它也包括了我们的一个gossip这个模块,那么worker作为路由转发的一个中心,它其实负责的一些像创建共识网络逻辑,以及对所有的消息进行一些转发。这三个刚刚我们看到过了,包括去拿到一个 parachain block,包括这边我们可以拿到纠删码的数据,把纠删码的数据在网络上做分发,这边很多我们就不做具体的介绍了。 

 

最后,我们可以看一下 service。Service在下面我们其实可以看到它其实实现了很多trait,他其实是封装了我们前面讲的worker和handler,然后对外提供不同行为的一些trait,包括他可以去创建一个这样的路由表,包括去为collator上去进行一个collate的操作,去拿到一个collation。对纠正删码的模块,我也可以提供一个获取纠删码数据的操作等等。这三个就是我们说的Protocol这一块的一些具体的代码。 

 

| 候选回执(candidate receipt)

我们看一下2.4候选回执,候选回执讲的是说最终当所有的 validator对某一个 Parachain过来的区块进行验证了之后,如果超过一半以上的validator都认可它是正确的,这个时候就会去生成这样的一个候选回执。

候选回执的数据大概是在右边,大家可以看到其实所有的数据基本上都是ID签名或者是哈希。所以整个的候选会址它的大小是比较恒定的数据,不会说根据因为 parachain传过来的区块的大小发生一些变化,因为全都是一些固定长度的数据。这样的一个设计其实就是可以让我们在parachain数量从少变多做扩展的时候,让整个过程是一个线性的过程。 

 

我们可以看一下这里,最后其实有两个状态根。也是前面我们讲的 s t f状态转换的时候,就是在做stf之前的状态跟和做 s t f之后的一个状态根。

 

这边其实还有一个模块,在波卡里面叫做availability store,它就是在每一个见证人本地去储存区块数据,状态数据,有效性证明的数据以及纠删码数据,储存对所有的一些数据的本地的key_value的数据库。

 

下面这个图其实就表示的是说我们在collator传过来的其实是一个candidate block 和一个validity proof,然后见证人会对他进行一个认证,认证成功了之后其实会发一个声明 statement,我们就会在多个见证人之间看是不是有超过半数的信任,达到了一个共识。如果有的话我们会生成 candidate receipt。最后一步就是我们会把 candidate receipt放到交易池里面,最终把它打包上链。


 

| 打包区块

这边我们来看一下打包区块的大概流程。

这个流程其实是在代码里面的一个调用的流程。我们前面在substrate里面也讲了,其实我们会有一个slot的概念,所以babe这个模块它其实:


第一步,会回调一个叫做on_slot的函数。


第二步,这个函数最终其实是会调用到了我们在波卡的共识组件里面的有一个 block production模块,就调用到了这个模块里面的 propose函数。

 

第三步,其实 propose函数会调用到我们的 ShareTable.proposed set函数。


第四步,是会调用到了 StatementTable. proposed candidate函数。

 

这边其实我会针对3和4详细去讲一下什么叫做share table,什么叫做 statement table。


Share table和statement table其实都是在波卡里面,它每一轮的共识里面的一些过程当中的数据。statement table保存的数据都是一些哈希签名和ID,是一些最终的数据。share table会去持有 statement table的一个索引,它同时也会去持有 availability store的 reference,所以share table其实就是数据总体都是放在这里的,但其实它分成了两块,第一块就是本轮的一些数据都是放在了statement table里面,是通过这些形式去放的。

 

在availability store里面其实放的数据就是这些非常大的数据:区块数据,状态数据,还有纠正删码的数据,前面我们也讲过都是放在这里的,所以理解了这三个数据之间的关系大概就能够搞清楚了整个的调用逻辑。 

 

然后我们最后来看一下,对于babe当中赢得所有的见证人来讲,它的交易池里面其实会有很多的candidate receipts,因为每一个 Parachain都是有可能会把一个区块发到 relay chain上,然后最终生成candidate receipt。

 

所以对于见证人来说需要保证两点,第一他需要保证的是当前的candidate receipt的父亲的candidate 所对应的 receipt也已经收录在了之前的区块里面。第二个是在见证人A的他本地的 availability_store里面,已经保存了 candidate所对应的纠删码的分片数据,所以首先这一点就保证了它是在一条正确的路径上,往前推进 parachain的区块进度,下面这个其实保证了,我如果有纠正删码的数据分片,那么在可用性上面都得到了一些提升。 

 


.06
可用性子协议-纠删码


我们来看一下整个的第三部分,就是纠删码的这一部分。

 

前面说了我们的candidate receipt生成以后,它在进入交易池之前,其实我们是需要去确保可用性的,也就是把他的所有的区块数据和有效性证明的数据,通过纠删码的这样的方式保存在所有的validator上面。

我们这边举了个例子,纠删码它是把一个数据分成10份,假如超过某一个阈值4的时候。分成10份了之后,其实是会发送给10个validator。那么超过4个 validator他们的分片组合在一起的时候,其实就是可以还原出原始的 A数据。


纠删码底层的算法是多项式差值的算法,在波卡里面其实我们是在最终的一个架构里,其实是会有1000个见证人,所以纠删码其实最终也会发送到这1000个节点上,最终是会需要从超过334个节点上面拿到分片,我们就可以把纠删码恢复出来。 

 

.06
更多的安全保障


我们来看一下更多的安全保障。流程走到了这里,就相当于已经有一个区块送到了relay chain,见证节点也对它进行了验证,也生成了candidate receipt。同时candidate receipt也已经打包到了区块里面,这个时候就是babe这个模块执行的工作已经结束了。

但是从这个点再往后其实还是需要有非常多的一些交叉验证的工作。因为之前其实只是我们分配的那些见证人作了一些验证,所以在后面其实我们是需要让fisherman和收集人也要来做验证工作。 

 

同时我们还需要这个验证,它是可以从上或者是从下发起验证的,所谓从下就是,某一个validator可能发现有一些事情不对了之后,他可以把这个消息广播到网上,然后让全网的validator来帮着一起来验证一下这件事情到底是对还是错。 

 

我们做了这么多的验证工作,它其实主要是为了防止两件事情:


第一个,是如何处理合谋做坏事。


第二个,是如何处理在多分叉下面的奖惩信息同步的问题。所以后面我们可以来看一下更多的安全保障里面都做了些什么内容。

 

平均来讲我们一个parachain会分给10个左右的validator,所以在这10个validator里面,可能超过1/2,也就是6个以上(包括6个) validator认可的1个 candidate receipt之后,这个区块就被认可了。所以在这里面其实还是会存在非常小的概率,也就是这些validdator合谋一起做一些坏事的情况。所以整个共识的机制其实也是会把出块和最终确认,也就是babe和grandpa这两个模块分开。 

 

babe其实只是去负责出块,对区块的最终的确认是在grandpa里面进行验证的。同时我们还会提供了一个监督机制,也就是让一些第三方的节点或者逻辑来对整个的共识过程进行监督,监督的角色就是 fisherman。 

 

我们这里其实是做了一个简单的计算,假设有1000个节点,然后10个一组,然后我们分100组,那么有6个节点是坏节点的概率是多少呢?

 

这里我们算了一下是1.53e-13,其实是一个非常小的概率。

 

然后我们又去把坏节点让它变多。那么假如说有10个坏节点,至少有6个被分到一组的概率又是多少呢?我们算了一下是3.23e-11。

 

然后假设是6秒一个区块的话,这样的一个概率相当于这些人要凑在一起的话,大概要花6000年的时间。但是这里其实有一个非常重要的点,我们其实仅仅是把坏节点的数据做了一个翻倍,甚至其实还没有到翻倍,只是加了66%,就从6个节点到了10个节点。


那么我们可以看到这个概率其实提升了两个数量级,说明了什么?

 

说明其实随着坏节点的数量的变多,他们会被分到一组的概率会显著的提升。所以再多的坏节点计算我们这边就不算了。但是大概的逻辑就是这样,所以基于这样的一个考虑,整个包含的设计其实也会去在做很多的二次验证的工作,来规避这种小概率事件的发生。

 

| 更多的安全保障 - 二次验证- STF

所以二次验证的第一块,其实我们是重新再去做一次stf的验证,所以说当babe生成了一个新的区块之后,整个全网的话又会启动一次新的验证的一个会话,relay chain会随机挑选一些 validator。对这个区块里面的所有的candidate receipt进行验证。

 

那么验证的过程其实包括:

首先,需要向其他的见证人发出请求去恢复出区块数据和有效性证明的一些数据,这块其实是对可用性进行了一个验证。 

 

第二个,是有了这些数据之后,其实它就可以做一个stf的验证了,这又重新做了一次, 这个是 validity的一个证明。最后它其实是会把验证的结果和区块里面的 candidate receipt做一个比较。相同的话,就认为说这个验证是通过了。

 

| 更多的安全保障 - 纠删码的有效性验证

这边还有一个关于纠删码的有效性验证,对于每一个生成的区块,都会在网络上广播给其他的见证人,那么每一个见证人在收到区块之后,他会执行一个 import block这样的一个操作,然后会在操作当中,他会对每一个 candidate receipt查看它对应的纠删码是不是保存在了本地,是不是保存在了见证人自己的本地的availability store里面。

如果没有在本地的话,这个节点就会在网络上发出一个警告信号,表示说我这边其实缺了一个数据。然后其他的节点收到了这样的一个警告信息之后,他们也会去查看他们自己有没有关于 candidate receipt相关的纠删码的chunk,如果没有的话,他们也会把警告再次发送到网络里面。 

 

那么如果最后有超过1/3的见证节点,都对某一个 candidate receipt发出了这样的一个warning数据之后,这个区块就作废了,因为这个区块里面包含的 candidate receipt,它的有效性验证没有通过,所以整个区块就全部作废。

 

| 更多的安全保障 - 二次验证 - Fishing Zone

接下来我们看一下collator和fisherman会做的一些工作。 

首先fisherman可以是parachain,也可以是relay chain的一个节点。成为fisherman需要抵押一定数量的 dot。fisherman是去负责这个区块当中所有的 candidate receipt的有效性验证,前面讲的其实是随机挑选的一些见证人,那么这个时候其实fisherman他是一个独立的角色,他也可以对区块里面的所有的candidate receipt进行一个验证。

 

然后callator呢?如果他的角色也是fisherman的时候,他就可以去负责纠删码这一块的可用性的验证。 

 

fisherman和collator一旦发现了这些问题之后,他们也会发出一些warning的信息。一旦有关于某一个candidate receipt的warning信息发出,就需要更多的validator来参与,对他们进行相关的认证。那么结果也和上一页是一样的。如果对于某一个candidate receipt来讲,超过1/3的validator都验证是无效的或者是不可用的话,那么这个 block就作废了。


这个部分其实和上面两篇是有一些类似的,不同的是说他们的发起人其实是一个第三方独立的角色,而不是我们relay chain上面的 validator。

 

我们前面讲的所有的二次验证,包括这边fisherman的这些验证,其实我们都是可以称为在一个Fishing Zone里面,是一个验证的区间。这个区间的验证工作过了之后,其实grandpa才会进入到它的投票区间,然后他们会对这些区块进行一个两轮的投票,这些区块就会被最终化的确定下来。 

 

| 更多的安全保障 - GRANDPA

因为我们这一次的分享主要是讲的跨链这一块的逻辑,所以关于babe和grandpa,我们就不会详细展开的去讲了。

我们这里其实要讲的主要的一个概念是说,对于在relay chain上面最终确定的那一些区块,parachain其实都是会基于这些最终确定的区块里面的candidate receipt为基础,然后在这个基础之上去延伸自己的后续的区块。那么一旦这里完成了之后,就相当于在relay chain上面我们的区块都已经最终被确定了。 

 

这样的话我们的parachain就可以从共享安全的环境当中受益。它主要的原因是说,因为如果要撤销在relay chain上面的一个区块的话,他是需要去攻破babe和grandpa这些共识的模块,那么难度是非常高的。 

 

| 更多的安全保障 - 分叉(Fork)

分叉主要的一个概念是基于我们前面说过的某一些验证工作没有通过的时候,其实整个区块就作废了。但其实我们整个区块链babe模块是不停的在块的,所以作废操作本身它是需要在多个分叉的环境下进行的,不是说仅仅是在一个分叉下,所以这样的话其实就会让整个事情又变得非常复杂。 

 

我们看一下有哪一些因素是需要注意的。


第一个,就是在一个分叉上面有做行为的validator,它也需要在所有的 fork上面进行被惩罚。这就相当于我在一条路上做了坏事,但其实无论别的路上知不知道别的分叉上知不知道,其实我都是会被惩罚掉的,这样子,其实就是进一步的缩减了validator去做恶的一个可能。 


第二个,就是有一些从parachain上过来的 block,它的candidate receipt其实并没有出现在所有的fork当中,所以可能是说有一些fork里面有 parachain的block,有一些是没有的。


第三个,其实我们是需要去禁止这样的场景,就是如果有一个valididator有一个作恶的行为,我们就不能允许他再去创建fork,然后去把自己作恶的行为给去掉,这样的话也是想去减低validator做恶的一个可能性。 


最后一个,就是出块的见证人,他如果发现某一个fork上存在有争议的 parablock,会导致 fork出现一个回滚的话,这里我们就可以有一种激励方式,让 validator不在 parablock上面。有一种激励方式,在其他的fork上面去构建后续的区块,就是在不包含 paragraph其他的一个fork上去构建。 

 

| 更多的安全保障 - 本地争议(Local Disputes)

关于争议其实还有两个内容,一个是本地争议,一个是远程争议。

 

我们先来看一下争议的定义,它大概的是说对于某一个parachain的block,它已经生成了 candidate receipt,也已经被打包了,在后续的验证过程当中出现了一些问题。所谓的出现的问题就是某些验证没有通过,无论是有效性或者是可用性的验证没有通过,在这种情况下,我们就称这个状况为争议。

 

所谓本地争议就是在当前的 fork当中存在的争议,所以对于本地争议来讲,它一定是在已经打包,但还没有最终finalized的那一些验证期里面的存在。我们其实是需要去保证本地争议相关的这些block,code和proof在 availability store里面都需要存在的。 

 

然后争议解决之后,在错误的那一方的validator会被slash。也就是说假设有一个 candidate receipt已经打包到了区块里,但因为发生了争议,其实最终让更多的其他的见证人也来进行验证,那么我们发现这个candidate receipt其实是有问题,他不应该打包进来。 

 

那么在这样的一个场合下面,其实就会把在过程当中所有认可了的candidate receipt的这些见证人,都进行slash惩罚,同时也会把slash惩罚同步到其他的fork上。然后做完这个之后,区块会回到争议前的一个位置,同时争议的区块也会进入黑名单,告诉其他的fork,因为这个争议区块其实就已经不可用了。 

 

| 更多的安全保障 - 远程争议(Remote Disputes)

远程争议讲的其实是在另一个fork里面部分或者全部解决的争议,争议的结论的数据其实是需要同步到其他的fork。

 

假设我其实是在A分支上,那么在B分支或者C分支上发生的一些争议的事情,对我A分支来讲其实就是远程争议。对但是对于 B分之本本身来讲,它上面发生的争议其实是一个本地争议。 

 

这个争议他其实是需要由parablock所在fork的所对应的验证者集来负责解决的。首先我们去确定它的可用性,如果可用性没有通过,其实就直接惩罚那些不支持争议的见证人。但如果可用性已经通过了,我们就可以把远程争议再replay到其他的fork当中。然后这里其实有一点需要保证的,就是在所有的fork里面都需要使用它原始的争议所在fork当中的验证者集合的 v r f。在每一个其他的分支上我接收到了别人的远程争议之后,其实都会按照上一节讲的一些逻辑,对这些争议进行本地的处理。 

 

关于分叉,本地争议和远程争议这几部分的内容,在整个波卡的代码库里其实还没有任何的代码。现在仅仅是写了相关的一些设计文档,所以我相信这几块的内容和逻辑在后面应该也还会有很多的修订和继续的一些更新。 


 

.07
关于波卡的TPS


最后我们来看一下关于波卡的 TPS的计算。之前我记得gavin应该也给过一个计算,他大概是简单的就是把1000×1000, 他他就说整个波卡的 t p s是100万。基于我们整个的前面的一个理解,我们来算一算,这个波卡的tps大概能够达到多少? 

 

那么每个parachain的tps是1000,然后总共我们是有100个parachain,是因为我们relay chain的总共最多可以有100个 validators,那么每个parachain分配10个,所以我会有100个parachain。 

 

然后这边有一个假设,就是在每一个relationchain store里面,每一组validator只验证一个Parachain,我相信这个可能到后面也可以做成并行的验证,但是目前为止应该是每组只验证一个parachain。

 

假设relay chain和parachain的block time都是三秒,那么我们这边做了一个计算,我们在三秒的过程当中,一个parachain总共有3000个交易,那么总共对100个parachain其实就是1000×3再乘以100这么多个交易。总共其实过去的是三秒,我们算tps的话,最终再除以3,这里最终算出来是10万的TPS.也就相当于是说整个relay chain是可以处理10万个 parachain上面的transaction。这就是大概关于波卡tps的一个计算。 

 

今天的分享我已经放到了github ,我的代码库地址:

https://www.github.com/alexxuyang

 

扫码进直播间,回看完整分享!

 


更多阅读:
Sub Dev 分享 | 当Substrate遇到传统业务应用
0基础想要入门区块链技术,应该如何学习?
Sub Dev 讨论 |   ink 比 Runtime 轻松太多了

扫码关注公众号,回复“1”加入开发者社群


Modified on

    您可能也对以下帖子感兴趣

    文章有问题?点此查看未经处理的缓存