欢迎大家继续阅读, 看完了上一节 节点实例 这个公共组件, 这节我们就来介绍一下比特股系统中最重要的部分 --- 见证人, 的配置与启动过程.
见证人配置
在 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 了.
当上述所有检查都没有问题就会执行出块了. 出块部分我们留作后续讨论.
我一直想知道见证人到底怎么搞的。这篇解惑了。
这篇拙作只能说明见证人冰山一角 (捂脸
有个问题请教:
白皮书上说持有Eos 代币的人可以对未来基于eos 上线的区块链上代币的可交易性有决定作用,这个在eos代码中怎么体现的?
我今天也刚看了这部分代码~ 疑惑的点是dpos..和洗牌的部分... 然后又留着共识篇再讲 = .=
这块大概是每小时更新一遍 active_witnesses, 然后每当所有 active_witnesses 都轮完一轮, 就洗牌, 具体可以先看一下 database::update_witness_schedule ^_^
好的 感谢!!
Good posg
好,又开始分析了