或许我们还都记得美国队长的勇敢、神武,为了捍卫自由和保卫人民而拥有的坚不可摧的盾牌,但我们还记得那个瘦弱到不堪一击的史蒂夫.罗杰斯么?血清的注射让他变成了很多人心目中的英雄。那么我们又可曾想过,美国队长还会变成以前的那个因瘦弱的身躯而一直被人嘲笑的史蒂夫.罗杰斯么,或许真的已经变不回去了,因为血清的注射是一个不可逆的过程,就如同eos中区块产生、确认之后是一个不可逆的过程一样。下面结合源码,承接上篇文章,出块、广播之后,对eos的上链过程进行一个简单的分析。
在前两篇文章中分别介绍了eos系统中从出块流程和信号槽广播机制的实现介绍了出块之后区块的信息如何广播的net_plugin的,具体广播到net_plugin之后又做了什么操作,我们在接下来的文章中会谈到。区块上链的主要流程包括以下几个步骤:
- 单节点区块验证(controller::sign_block)
- 区块确认(controller::commit_block)
- 分叉数据库存储(fork_db::add)
- 删除老的区块(fork_db::prune)
- 触发不可逆处理信号(fork_db::signal irreversible)
- 不可逆槽函数处理(controller::on_irreversible)
- 入库(db.commit)
在以前的文章中我们谈到,区块生产之后需要有个节点确认的过程,在单节点部署的情况下本节点产生区块自然而然的本节点会直接确认掉,无需对其他节点的权限进行校验且不需要DPoS机制中超出2/3以上的节点对该区块进行确认。此处,为了方便解释,我们把区块确认的代码拿出来,如图1示,具体到sign这个函数,其内部分别调用了hash库中的一些内容,最终将结果赋值给已生成区块,其中进行了一系列hash转换,较为繁琐,感兴趣的同学可以尝试着去调试一下。
图1 单节点确认区块
区块确认被当前节点确认之后,会进入上链的流程。在controller.cpp的commit_block中,会将当前已生产且被当前节点确认的去add到fork_db中,如图2中标注1所示:
图2 区块确认
关于eos系统中的数据库使用我们在接下来的文章中也会逐步的介绍,今天暂时先看下fork_db的功能及实现,图3和图4是对整个fork_db的概览,通过其注释内容我们可以看出fork_db其实并未将区块存入持久化的数据库,而是类似于一个临时变量的存储。简单的将其注释内容翻译如下:
图3 fork_db的结构
fork_database可以用来轻量管理所有潜在未确认的区块的状态信息。当接收到一个新的区块的时候,会被添加到fork_database中来。fork_database跟踪了最长的链且记录了最后一个不可逆区块的编号。我们在上篇文章介绍emit信号槽实现的时候提到了,区块确认不可逆之后也会将区块信息广播至net_plugin中去,当这个信号量发出去之后,所有的在这个区块之前的信息就都被释放掉了。
基于此,我们可以简单的认为区块生成之后以一个临时变量的形式存储在fork_db中,等下一个区块确认不可逆之后会将其从fork_db中抹去,并保存至持久化的数据库中去。上面我们一直提到不可逆信号量的发射,可以看到在fork_db中定义了一个信号量irreversible,如图4中标注1所示:
图4 信号量irreversible
注释中对其描述为,当确认一个区块不可逆即刻将信号广播出去,一旦其不可逆,在其不为头区块的情况下,将会从fork_db中移除。那么这个移除的过程是怎么实现的呢,让我们回到fork_db的add上来。
图5 fork_database::add
由于fork_database并非真实存在的数据库,只是用来临时的存储一些数据,类似于我们将数据写入到内存中去。在这里,当区块信息add成功之后会获取当前区块的lib以及上一区块的相关信息,通过判断,如果dpos不可逆区块的lib大于在此之前区块信息的lib,则将老的区块信息清除。
在这里我们举个简单的例子,区块编号为10的区块信息add到fork_db中来,也就是在此之前dpos_irreversible_blocknum为9,而获取到的oldest区块编号为8,此时就会调用prune将编号为8之前的区块信息从fork_db中移除出去。下面让我们来看看prune具体做了什么操作,如图6所示:
图6 fork_db::prune
prune会根据block_state的id从fork_db遍历所有已存在的数据(index中)去查找是否存在该区块信息,如果存在则将其以irreversible信号的形式连接到槽函数on_irreversible中去,之后将其从index中移除,这是个异步的过程。现在我们可以看到,基本上已经实现了这个不可逆的过程,但还有一个关键步骤,也就是这些区块信息的数据存储我们还没有做。我们来继续on_irreversible,如图7所示:
图7 controller::on_irreversible
到了这里,我们看到首先将区块信息存到数据库中,此处的db.commit是和fork_db不同的,我们可以看到关于db的声明,是chainbase::database的一种。关于eos中所使用的数据库相关操作,内容也较多。我们本篇主要是承接前几篇文章中区块生成、区块广播、区块上链,到这里区块真正的入库了,也算完成了整个区块生产的过程。最后依然是使用emit的方式,将不可逆的区块信息广播至net_plugin中去。
通过这三篇文章我们对源码中区块的产生应该有了一定的了解,这三篇也可以看做一个简短的系列,其实我们在eos系统中每一次有意义的操作如我们经常做的交易,都可以看成一个区块生成的过程。接下来的源码阅读方向暂未确定,也可根据读者需求去做转换,另外本公众号前三篇文已不再适配当前代码,可适当忽略。
我们通篇在提不可逆,不可逆正是区块链技术的一大特征,区块链同时还具有可追溯性这个特征,想到当下那些恶贯满盈的假疫苗生产者、销售者,区块链技术应用于这些行业或许会让这些人颤抖,颤抖到他们不敢再开出恶之花。
如果你觉得我的文章对你有一定的帮助,请点击文章末尾的喜欢该作者。