--- 摄于 2017 年 9 月 藏川线前段
最近突然感觉忙了许多,不知道是不是错觉,这个系列跳票了一周,其实 auth 模块很多东西在上一篇并没有仔细描述,auth 里面有很多小细节,稍不注意就会出错,这一块细讲很容易陷入代码细节中出不来,偏离这个系列的主题——尽量讲整体思路,减少代码带来的阅读障碍,让更多的人更容易知道 cita 的整体思路。顺便一说,同步机制的介绍放在下一期,个人觉得需要再整体介绍一下 cita 的流程。
从这一篇开始,视角就从单个微服务走向了多个微服务的联动,篇数不定,也许后面会更深入讨论代码,也许不会。
jsonrpc -> auth -> bft -> executor -> chain 单节点
one auth -> network -> other auth 节点间转发交易
network -> auth -> bft -> executor -> chain 接到其他节点交易
one bft <-> network <-> other bft 共识
大致上,整体的流程就如上面所示,我们从创世块开始说起:
cita 作为联盟链,与大部分公链不一样,创世块并不是完全确定的,在每一次创建链的时候,都需要重新生成创世块,在生成创世块的时候,会有很多合约参数需要填写,这些参数大部分是生成之后无法修改,也就是写在了创世块中,小部分可以在链运行期间通过系统合约调整,或者是修改配置文件。
创世块生成之后,依次将各个节点启动,Executor 发现没有数据库实例,就会读取创世块(genesis.json)信息,并执行,将结果存入数据库,并发送给 Chain,Chain 在第一次启动的时候,发现没有数据库信息,会生成一个 0 高度的 header,并等待 Executor 的创世块信息,当接到创世块信息后,存储,并广播自身状态(包括高度信息和 systemconfig),从这时候开始,单节点的“永动机”就开始按照既定的规则往下执行了。
之后就是 Auth 从交易池里面提取交易打包成 block 发送给 Bft,Bft 发现当前高度是自己出块,提起 proposal ,签名,并发送给 Network,Network 广播该消息,其他节点的 Network 接到 已签名的 proposal,同时将这个消息发送给 Executor 和 Bft,Executor 的状态机开始预执行,并等待最终的共识结果,Bft 将 proposal 中的交易发送给本节点的 Auth 验证,并根据返回消息对 proposal 进行投票,然后将投票完的信息再次发送给 Network 进行广播,该 proposal 被 2/3 以上的共识节点签名后,Bft 发送共识好的 block 给 Executor 和 Chain,Executor 发现与预执行 proposal 一致,直接将结果提交到数据库,并发送给 Chain,Chain 接到结果,验证通过后,提交到数据库,并广播当前状态,发送当前块的 Transaction 给 Auth,Auth 删除交易池中对应的 Transaction,并再次打包一个 block 给 Bft,周而复始,往前推进。
上诉流程,无论是否有交易,都将继续执行,没有交易的时候,就出空块。这个流程的角度是从 CITA 节点上,从上帝视角看到的。下面我们从一个交易的发送来看,交易的流转过程:
交易上有一个细节字段,叫
valid_until_block
,这个字段的作用是表明,这笔交易在这个高度之前是有效的,如果超过这个高度还没打包到块中,这个交易就会被丢弃,也就是无效了,避免交易长时间放在交易池中无人管理
用户将签名好的交易发送到 Jsonrpc 接口,Jsonrpc 判断这是一个交易,将它转发给本节点的 Auth,Auth 发现这个交易没什么问题并且不重复,向 Jsonrpc 返回一个交易 Hash,这个交易 Hash 就是用户接到的回执,同时将它发送给本节点的 Network,并加入交易池中,其他节点的 Auth 接到 Network 发来的交易后,验证通过,再次广播到除来源节点的其他节点,并加入本节点的交易池。
接下来,Auth 执行到打包新块的时间点,从交易池中获取交易,并根据交易上的 quota
值扣除 Block quota limit
,直到交易池无交易或者 Block quota limit
无法支持下一个交易的 quota
数。Auth 将打包好的新块发送给 Bft,进入共识阶段。
交易随着 proposal 的提起,给到了各个节点的 Executor,如果顺利的话,Executor 预执行 proposal 完成,并与之后接到的共识结果一致,Executor 将执行完成的状态提交数据库,并将执行结果给 Chain,Chain 同时将执行结果落盘,并广播新的状态。
这个时候,用户用发送交易的回执 hash,通过 getTransactionReceipt
接口,就可以查询到交易的执行结果,耗费的 gas ,落盘高度等等信息,如果非常不幸,返回的结果是 null
,那么这个交易就因为种种原因被丢弃掉了,很大可能是 valid_until_block
设置问题。
这里再说一下共识,为应该是下一篇的同步做一下铺垫。CITA 采用的共识算法时 PBFT 的变种,2/3 以上投票就可以完成共识,接到了这个消息的节点就自顾自去出块了,但是,要注意的是,由于包括网络在内的种种因素,有些节点并没有收到这个消息,或者它的投票被其他节点延时收到或者干脆没有收到,这些节点也就并不知道共识结果,那么它们就会存在与链上状态不一致的情况,这时候,就需要同步机制去处理这些节点的不一致情况,不一致的状态下,这些节点并不能参与共识投票,只有等追上了整体高度才能再次参与共识。
希望这个系列的文章能够帮更多人理解 cita,吸引更多的开发者参与其中。要改进的地方还有很多,要走的路还很远。
请登录后评论
评论区
加载更多