Graphene 源码阅读 – 架构篇 – 见证人配置与启动

欢迎大家继续阅读, 看完了上一节 节点实例 这个公共组件, 这节我们就来介绍一下比特股系统中最重要的部分 — 见证人, 的配置与启动过程.

见证人配置

在 bitshares 中, 见证人功能是在 witness 插件里实现的, 类似的还有 cli_wallet, delayed_node 都是通过独立的插件提供, 这些插件的公共部分就是上一节我们介绍的 节点实例.

见证人在启动时依赖 config.ini 这个配置文件, 如果 config.ini 不存在会使用代码中的默认配置创建它, 实际上 config.ini 也是所有插件的配置文件, 但本节我们只关注见证人相关的配置, 配置和解释如下:

  • enable-stale-production
    正常情况下, 当见证人节点启动时, 会去其它节点同步区块, 当区块没有同步到全网最新状态时, 是不允许见证人生产区块的, 而此配置项置为 true 的话, 会无视这一规则.
  • required-participation
    DPoS 协议中有一个参与度的概念, 如果一条链参与度太低的话说明这条链已经不被很多见证人认可, 理应不该再在此链上出块. 此配置项就是用来设定参与度阈值.
  • witness-id
    此项配置的是当前节点实例要跑的是哪个见证人
  • private-key
    此项配置的是见证人的签名密钥

运行出块

见证人的运行就是作为一个调度单元, 每隔 1 秒就被调度起来生产区块的过程. 这个过程的执行流如下:

app::startup_plugin() => witness_plugin::plugin_startup() => schedule_production_loop() => block_production_loop() => maybe_produce_block()
                                                                                                               ^                                                 |
                                                                                                               |____________________________|

这里面很重要的一个工作就是 maybe_produce_block(), 这个方法的返回结果决定了要不要出块, 我们重点看下这个方法.

首先会检查区块是否同步到最新, enable-stale-production 配置项会影响这个判断, 如果 enable-stale-production 设为 true 了, 那这里的 _production_enabled 就会是 true.

217    // If the next block production opportunity is in the present or future, we're synced.
218    if( !_production_enabled )
219    {
220       if( db.get_slot_time(1) >= now )
221          _production_enabled = true;
222       else
223          return block_production_condition::not_synced;
224    }

接下来这段代码会判断是否到达出块时间, 关于这段代码看似简短其实背后也有文章, 我们在 出块判断逻辑 一文中有过一次稍微详细的介绍, 感兴趣的可以去看看. 这里仅补充一下为什么会没达到出块时间.

实际上原因也很简单, 因为见证人的调度是每秒都会调度的, 然而出块时间却可能是 3s, 10s 甚至是 20s, 所以见证人被调度执行时, 自然是可能还没有到达出块时间的.

226    // is anyone scheduled to produce now or one second in the future?
227    uint32_t slot = db.get_slot_at_time( now );
228    if( slot == 0 )
229    {
230       capture("next_time", db.get_slot_time(1));
231       return block_production_condition::not_time_yet;
232    }

如果出块时间也满足的话, 会继续判断这轮是不是轮到自己出块, 这部分涉及到 DPoS 见证人洗牌相关问题, 我们留到共识篇再详细讨论.

244    graphene::chain::witness_id_type scheduled_witness = db.get_scheduled_witness( slot );
245    // we must control the witness scheduled to produce the next block.
246    if( _witnesses.find( scheduled_witness ) == _witnesses.end() )
247    {
248       capture("scheduled_witness", scheduled_witness);
249       return block_production_condition::not_my_turn;
250    }

再继续会相继检查是否有配置签名私钥以及当前主链的参与率是否达到要求, 这就是上面说的 private-key 以及 required-participation 配置项的作用.

252    fc::time_point_sec scheduled_time = db.get_slot_time( slot );
253    graphene::chain::public_key_type scheduled_key = scheduled_witness( db ).signing_key;
254    auto private_key_itr = _private_keys.find( scheduled_key );
255
256    if( private_key_itr == _private_keys.end() )
257    {
258       capture("scheduled_key", scheduled_key);
259       return block_production_condition::no_private_key;
260    }
261
262    uint32_t prate = db.witness_participation_rate();
263    if( prate < _required_witness_participation )
264    {
265       capture("pct", uint32_t(100*uint64_t(prate) / GRAPHENE_1_PERCENT));
266       return block_production_condition::low_participation;
267    }

最后一项要检查的是当前要出的块的理论出块时间和当前实际时间相差多大, 如果相差了 500ms 以上, 那也不会允许出块, 如果你运行过见证人, 也许有看到过

Not producing block because node didn't wake up within 500ms of the slot time.

这样的错误, 这就是我们所说的 miss 了.

当上述所有检查都没有问题就会执行出块了. 出块部分我们留作后续讨论.

赞赏

微信赞赏支付宝赞赏

发表评论

电子邮件地址不会被公开。 必填项已用*标注

This site uses Akismet to reduce spam. Learn how your comment data is processed.