大家好, 本文正式开启架构篇.
如果你还有印象的话, 我们之前在 源码结构 中曾简要的介绍过 bitshares 源码中的每个模块, 这个篇章将会和大家探讨各个模块之间的调用关系, 节点运行时模块之前如何交互, 逐步为大家呈现出一条清晰地脉络.
与整个节点的运行最相关的要数 app 模块了, 所以 app 模块就作为架构篇的第一个模块在本文介绍. 下文中 app 模块我们将其称之为节点实例模块或者直接成为节点实例, 这个名字将更能够表达此模块的含义.
节点实例
节点实例模块在整个系统中无疑是位于顶端的, 系统中各个组件都是由它触发激活并监控他们的状态. 节点在启动时会调用 app::initialize()
以及 app::startup()
方法, app::initialize()
中主要的工作就是注册插件到自己的静态变量中, app::startup()
才包含了节点启动的主要流程.
下面我们就来详细看看节点启动流程.
节点实例启动
genesis 加载进内存
第一步就是加载创世信息 genesis.json, 如果你没指定 —genesis-json
(如果你要跑主链的话也不需要指定), egenesis 组件会负责加载默认创世信息 (参见 默认的 Genesis 创世信息), 创世信息加载进内存后会被存储在 genesis_state_type
类对象中, genesis_state_type
类的定义和 genesis.json 中的字段是一一对应的, 只有一个例外是 genesis_state_type
中包含一个 initial_chain_id
字段, 这个字段是在创世信息全加载进内存后对这块内存求哈希算出来的, 所以在 genesis.json 中没有对应.
initial_chain_id
是很重要的字段, 它被用来标识不同节点所运行的是不是同一条链. 所以现在我们知道了, 所以只要不同节点使用相同的创世信息, 它们跑的就一同一条链; 而你如果修改哪怕只是创世信息中的一个微不足道的字段, 其 hash 值都会改变, 你也将不能与其它节点通信 - 实际上这已经创建了一条新链.
数据库打开
如果看过之前的数据库篇, 想必对这个过程会亲切一些, 没错, 这个过程做的就是从磁盘加载对象索引, 打开区块数据文件流, 必要的话还会重建部分对象索引.
另外要注意的是, 如果是节点的第一次启动, 数据库打开这个过程的末尾还会对上面加载的创世信息进行 apply, 这个工作很重要, 创新信息的很多内容和正常链上内容无二, 所以它也要上链, 总不能每次启动节点都将它是放在内存里的. init_genesis()
方法就是做这个工作的.
init_genesis
init_genesis()
在节点第一次启动时被调用, 它读取前面加载到内存中的创世信息并 apply, 这其中包含了大量的工作, 我们选一些主要的说明一下:
创建特殊账户
这个过程会创建区块链上的一些特殊账户对象, 并且是不用走检验直接在索引上创建, 用的是数据库篇说过的 object_database::create()
方法
特殊账户的个数由创世信息的 immutable_parameters.num_special_accounts
字段指定, 对目前的 bitshares 主链来说这个值是 100.
首先创建的是 committee-account, witness_account, relaxed-committee-account, null-account, temp-account, proxy-to-self 这 6 个特殊账户, 这六个账户的 id 依次为 1.2.0 ~ 1.2.5.
实际上目前特殊账户也就这 6 个, 创世信息中指定的 100 个是为了预留以备不时之需. 紧接着要为剩下的 94 个特殊账户预留位置, 预留位置的方式很简单粗暴, 就是创建 94 个账户然后再立马把它们删除, 这样索引结构中 6 ~ 99 号 id 就也被占用了, 新创建的账户将只能从 100 号开始, 如果你用 bitshares 的区块浏览器查看, 就会发现从 1.2.6 ~ 1.2.99 这 94 个账户至今还是空的.
在 数据库篇 ~ 索引模型 中我们说到过创建对象时有个 set_next_id()
方法可以显式指定要创建的对象的 id, 所以将来要创建 6 ~ 99 号账户的话, 就可以发起一个账户创建交易, 其中指定账户的 id.
创建核心资产与特殊资产
核心资产也就是 BTS 资产, 特殊资产是一些名字以 “SPECIAL_” 打头的资产, 估计也是留作备用. 它们的创建套路和创建初始账户一致, 不再敖述.
创建全局对象
包括全局属性对象 (global_property_object), 动态全局属性对象 (dynamic_global_property_object), 链属性对象 (chain_property_object), 以及区块总结对象 (block_summary_object).
创建初始账户
上面创建的特殊账户是不属于个人的账户, 这里创建的 “初始账户” 实际上都是一些个人账户了, 这些账户可以说是这条链上的 “创世账户”.
不过这些账户毕竟是个人账户, 这些账户都是当初社区最早的那批参与者们, 账户信息也都是他们自己提供的. 既然是人提供的信息, 那就不是 100% 可信的, 所以这些账户的创建不能像特殊账户那样直接创建加索引, 而是要走操作创建, 检验上链的过程, 所以创建这些账户时会看到用的是 apply_operation
方法.
初始账户们由创世信息的 initial_accounts
字段指定, 总共有 90653 个账户.
创建初始资产
和初始账户一样, 初始资产也是一些 “个人” 资产. 创建过程和初始账户一样, 不再敖述.
其它
其它的还有创建初始余额, 初始见证人, 委员会, 提案, 套路和上述一致.
顺便说一句, 初始账户, 初始资产, 初始余额这几个信息几乎是占据了默认创世信息的全部, 默认创世文件 genesis.json 总共 35M 之大, 而其中 99.99% 都是这级部分信息贡献的.
p2p 网络与 rpc 服务初始化
数据库搞好之后, 下一步就是 p2p 网络的建立以及 rpc 服务的启动.
篇幅关系, 这两部分留到后面继续介绍吧, 敬请期待.
Posted from my blog with SteemPress : http://cifer.me/2018/05/01/graphene-%e6%ba%90%e7%a0%81%e9%98%85%e8%af%bb-%e6%9e%b6%e6%9e%84%e7%af%87-%e8%8a%82%e7%82%b9%e5%ae%9e%e4%be%8b%e5%90%af%e5%8a%a8%e6%b5%81/