为了账号安全,请及时绑定邮箱和手机立即绑定

#区块链#Bitshares学习笔记:节点启动浅析

标签:
区块链

原文链接:https://juejin.im/post/5b0e3ca9f265da092a2b77bf

前言

最近准备学习区块链的底层技术,打算以Bitshares公链为学习例子。为什么要选择  Bitshares 呢?主要是因为自己接触的第一个区块链就是 Bitshares ,而它跟目前很火的公链 EOS 以及公信宝(GXB)都是用一个叫 Graphene 的底层工具库开发的,同属 BM 的杰作。另外 Graphene 作为高性能区块链工具库催生了很多优秀的区块链项目,以 Bitshares 为切入点去了解 Graphene 也是相当不错的。在学习之余,分享一下自己的学习笔记,一方面希望能为社区做一点贡献,另一个方面是希望有大佬能在小编理解错时指点一下,小编目前对c++还不熟悉,可能犯很多低级错误,望见谅。

适合什么人看

整个学习流程小编只会从比较宏观的角度去分析一个区块链程序是怎样跑起来的,其中涉及什么模块,模块之间又是如何交织支撑起区块链运行的。适合对区块链行业感兴趣但还是持着观望态度的程序员,希望这些受众在看完整个系列后,获得的知识能够帮助个人在合适的时机转到区块链行业。 

本章主要是讲节点启动流程,以及涉及的数据库和网络模块分析。

代码地址:https://github.com/bitshares/bitshares-core

首先看一下witness_node的程序入口(对于不必要的代码都隐藏了,用备注来代替)

https://img1.sycdn.imooc.com//5b0eb4e70001ce5906570393.jpghttps://img1.sycdn.imooc.com//5b0eb4ee0001ce5906570393.jpghttps://img1.sycdn.imooc.com//5b0eb4f90001ce5906570393.jpg

转到node->startup()内部

https://img1.sycdn.imooc.com//5b0eb5310001c37606560320.jpg

这里我们重点关注一下数据库和p2p的启动过程,毕竟这两个是区块链骨架的核心。

先从数据库着手:(个人习惯带着问题或猜想去阅读源码,这样更加有目的性和趣味性,而不是单纯的看和记)

问题1:既然区块链的数据是以一个个连续的区块来保存,那么如果要查询一个用户的余额,要怎么查询,这个数据存在了哪?

数据库启动过程:

https://img1.sycdn.imooc.com//5b0eb56d0001eea006490356.jpg

可以看出数据库分为两个部分,一个是 block(区块)数据库,存储的是每一个区块的原始数据;另一个是 object(对象)数据库,它是从 block 数据库解析每一个区块数据后得出的区块链中各个对象当前状态的数据库。打个比方,区块1包含创建用户b,区块2包含用户b作为见证人获得10bts, object 数据库解析了区块1之后,用户列表多了一个用户b,解析区块2后状态变成用户b存款有10bts。


用一个表达式来表示:

parse(nextblock, state) = nextstate


object 数据库的启动过程:


文件libraries/db/object_database.cpp void object_database::open(const fc::path& data_dir) 

{
   // 读取不同object的数据,bts中不同的数据类型都会有对应的object_id
   
  // 格式是x.x.x,分别代表spaceID,typeID,index,spaceID的区别还没明白,typeID就是区分不同对象类型,index就是同一对象类型不同实体
   
 // 例如1.3.前缀代表资产,1.2.前缀代表用户,1.2.99代表第99个用户,1.3.113代表第113个资产
   
 // 这里可以看出每个对象类型的数据都是由单独的文件保存的
   _index[space][type]->open( _data_dir / "object_database" / space/ type);
 } 
文件libraries/db/include/graphene/db/index.hpp
virtual void open( const path& db )override
 { 
   // 创建内存映射文件
   
   // 从内存映射文件反序列化数据并保存起来,具体序列号和反序列化的实现是在/libraries/fc/include/fc/io/raw.hpp
 }


作者:roths
链接:https://juejin.im/post/5b0e3ca9f265da092a2b77bf
来源:掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

object 数据库为了避免每次启动都要重新解析所有块来生成对象的状态,把状态都保存到了文件里,启动的时候再从文件中解析对象状态。

数据库模块的数据流向图:

https://img1.sycdn.imooc.com//5b0eb62e0001047006390304.jpg

除了区块数据库以外,节点还维护了对象数据库,用来保存数据对象的状态,我们查余额的时候实际上是查询对象数据库的内容,而不是直接从区块数据库查内容。

p2p启动流程:

问题1:如何在一开始的时候发现存在的并且有效的节点?(冷启动问题)

https://img1.sycdn.imooc.com//5b0eb6490001e7ea06700568.jpg

原来冷启动连接的p2p节点是写死的,域名形式的节点可能被墙,ip形式的可能不稳定,两者都有刚好互补。

连接到p2p网络的操作有很多(为了阅读体验,只保留了要说明的代码,其余的都用一句注释来概括了)

文件/bitshares-core/libraries/net/node.cpp void node_impl::connect_to_p2p_network()
{    
  // 循环监听是否有p2p连接请求,为了减轻dos攻击影响,设置10毫米间隔    
  // 循环连接,连接20个节点后进入10秒睡眠,不断重复该过程    
  // 向其他节点请求同步区块    
  _fetch_sync_items_loop_done = fc::async( [=]() { fetch_sync_items_loop(); }, "fetch_sync_items_loop" );    
  // 向其他节点请求最新的数据    
  _fetch_item_loop_done = fc::async( [=]() { fetch_items_loop(); }, "fetch_items_loop" );    
  // 将节点收到的交易广播到区块链网络中   
  _advertise_inventory_loop_done = fc::async( [=]() { advertise_inventory_loop(); }, "advertise_inventory_loop" );   
  // 循环关闭超时没响应的节点,向其他有效节点发送心跳包    
  // 循环请求当前p2p网络节点信息,15分钟一次    
  // 统计网络下载和上传速率,每秒一次    
  // 节点的状态log,每分钟一次  } 
}

网络连接时创建了很多定时任务来维护p2p网络和保证数据同步,其中fetch_sync_items_loop、fetch_items_loop、advertise_inventory_loop这三个任务跟区块链的业务逻辑比较相关,分别是用于从其他节点同步区块数据、从其他节点获取区块数据、广播数据到其他节点。

看到这里,大致知道了节点启动时是走了怎么样的流程,涉及了什么:

1.启动过程主要涉及数据库加载和p2p网络加载

2.数据库分为区块数据库和对象数据库,对象数据库是通过解析区块数据库得出的

3.p2p网络模块主要负责维护p2p网络和保证数据同步

那么Bitshares是怎么校验数据的正确性?数据不正确的时候怎么处理?

这个下一篇再讲了,脑壳疼,小编很少写文章,会存在很多自身无法发现的问题条,希望各位路过的大佬多多斧正,谢谢~


作者:roths
链接:https://juejin.im/post/5b0e3ca9f265da092a2b77bf
来源:掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。


点击查看更多内容
1人点赞

若觉得本文不错,就分享一下吧!

评论

作者其他优质文章

正在加载中
感谢您的支持,我会继续努力的~
扫码打赏,你说多少就多少
赞赏金额会直接到老师账户
支付方式
打开微信扫一扫,即可进行扫码打赏哦
今天注册有机会得

100积分直接送

付费专栏免费学

大额优惠券免费领

立即参与 放弃机会
意见反馈 帮助中心 APP下载
官方微信

举报

0/150
提交
取消