源代码解读 [EOS 源码分析] 34 - EOS 系统如何更新区块生产节点?

Admin · 2019年12月21日 · 28 次阅读
本帖已被设为精华帖!

EOS 源码分析系列文章,由技术作者松果撰写。为帮助更多开发者了解,特获授权转载至此。感谢松果兄支持。

img

上一篇文章介绍了EOS系统合约eosio.system的整体概况,这篇文章开始介绍eosio.system合约中的一些业务细节。

EOS系统如何更新区块生产节点?

EOS系统使用的共识机制是DPOS,EOS代币持有者通过投票选出21个节点作为区块生产者,投票时刻都在进行,因此前21个生产节点也是处于不断变化中的。

区块生产节点的更新是通过system_contract类中声明的私有成员函数update_elected_producers实现的,update_elected_producers函数在onblock这个Action中被调用,onblock是一个由系统调用的Action,用于管理出块动作、更新生产者区块信息和链上的短账户名拍卖信息。

update_elected_producers函数声明如下:

void update_elected_producers( const block_timestamp& timestamp );

定义在voting.cpp中:

img

首先更新全局参数(global表)中的last_producer_schedule_update字段,它记录上次区块生产计划更新时间,每隔60.5秒更新一次;

然后从producers表查询EOS链上的所有已注册节点,这里使用了二级索引prototalvote,后者在eosio.system.hpp中的定义如下:

typedef eosio::multi_index< "producers"_n, producer_info,
                            indexed_by<"prototalvote"_n, const_mem_fun<producer_info, double, &producer_info::by_votes>  >
                          > producers_table;

使用prototalvote可以让节点按照当前投票顺序排名,定义一个集合top_producers,它保存投票数最高的前21个节点。

通过遍历二级索引的查询结构,把投票数最高的21个节点添加到top_producers中,遍历过程中包含对节点的数据验证:

for ( auto it = idx.cbegin(); it != idx.cend() && top_producers.size() < 21 && 0 < it->total_votes && it->active(); ++it ) {
  top_producers.emplace_back( std::pair<eosio::producer_key,uint16_t>({{it->owner, it->producer_key}, it->location}) );
}

检测完top_producers中的节点数量是否符合last_producer_schedule_size(21个)后,调用sort函数把选出的前21个节点按照名称排序:

std::sort( top_producers.begin(), top_producers.end() );

接下来新建一个向量集合producers,拷贝top_producers的部分数据(producer_key)到producers中,再调用:

set_proposed_producers( producers )

完成21个区块生产节点的更新,同时更新全局参数last_producer_schedule_size

set_proposed_producers函数定义在eosio.cdteosiolib.cpp(eosio.cdt/libraries/eosiolib/eosiolib.cpp)中:

img

内部调用了CAPI的set_proposed_producers函数:

__attribute__((eosio_wasm_import))
int64_t set_proposed_producers( char *producer_data, uint32_t producer_data_size );

这是一个eosio_wasm_import属性的函数,在wasm_interface.cpp(eos/libraries/chain/wasm_interface.cpp)中实现:

img

经过一系列数据验证后,调用了controller的set_proposed_producers函数:

context.control.set_proposed_producers( std::move(producers) );

定义在controller.cpp(eos/libraries/chain/controller.cpp)中,关键代码如下:

img

sch是一个shared_producer_schedule_type类型对象,有一个成员变量producers;

sch被赋值给gp.proposed_schedule,gp是一个global_property_object类型对象,有一个shared_producer_schedule_type类型的成员变量;

db是一个chainbase::database类型对象,调用其modify方法更新了数据中的gpo表,它保存global_property_object类型对象;

到这里,新的21个区块生产节点被更新到EOS系统数据库。

更多内容

币圈信息站开发目录

EOS开发系列目录

共收到 0 条回复
Admin 将本帖设为了精华贴 12月21日 17:51
需要 登录 后方可回复, 如果你还没有账号请点击这里 注册