Controller:EOS区块链核心控制器 (4)

上面讲到了,fork_database拥有一个公有成员irreversible信号。这个信号在controller_impl结构体的宏SET_APP_HANDLER中被使用:

fork_db.irreversible.connect( [&]( auto b ) { on_irreversible(b); });

这段代码其实是boost的信号槽机制,信号有一个connect操作,其参数是一个slot插槽,可将插槽连接到信号上,最终返回一个connection对象代表这段连接关系,可以灵活控制连接开关。插槽的类型可以是任意对象,这段代码中是一个lambda表达式,调用了on_irreversible函数。
接下来,去fork_database查询该信号的触发位置,出现在prune函数中的一段代码,

auto itr = my->index.find( h->id ); // h是prune入参,const block_state_ptr& h if( itr != my->index.end() ) { irreversible(*itr); my->index.erase(itr); }

在table中查询入参区块,查找到以后,会触发信号irreversible并携带区块源数据发射。然后执行fork_database的删除操作将目标区块从分叉库中删除。
irreversible信号携带区块被发射后,由于上面宏的作用,会调用controller_impl的on_irreversible函数,并按照lambda表达式的规则将区块传入。该函数会将入参区块变为不可逆,处理成功以后,下面截取了这部分相关代码:

... fork_db.mark_in_current_chain(head, true); fork_db.set_validity(head, true); } emit(self.irreversible_block, s);

这两行是该函数对fork_db的全部操作,将fork_db的属性in_current_chain和validated置为true。在on_irreversible函数的最后,它也发射了一个自己的信号,注意发射方式采用了关键字emit,也携带了操作的区块数据。

信号触发可以有两种方式,使用关键字emit(signal,param)和直接调用signal(param)。

这个信号本来是与这一小节的内容不相干,但既然分析到这了,还是希望能有个闭环,那么来看一下该信号的连接槽位置,如图所示。

image

可以看到,区块不可逆的信号在net_plugin,chain_plugin,mongo_db_plugin,producer_plugin四个插件代码中得到了运用,也说明这四个插件是非常关心区块不可逆的状态变化的。至于他们具体是如何运用的,在相关部分会有详细介绍。

3. initialize_fork_db

初始化fork_db,主要工作是从创世块状态设置fork_db的头块。头块的数据结构是区块状态对象,构造头块时,要先构造区块头状态对象,包括:

active_schedule,活动的出块安排,默认为初始出块安排。

pending_schedule,等待中的出块安排,默认为初始出块安排。

pending_schedule_hash,等待中的出块安排的单向哈希值。

header.timestamp,等于创世块配置文件genesis中的timestamp值。

header.action_mroot,action的Merkel树根,创世块的值为链id值,该值是通过加密算法计算出的。

id,块id。

block_num,块号。

构建好区块头以后,接着构建区块体,构建完成以后,将完整头块插入到空的fork_db中。

4. commit_block -> add_to_fork_db

提交区块函数,无论提交是否成功,都不再保留活动的pending块。该函数有一个参数add_to_fork_db,是否加入fork_db。在producer_plugin生产者生产区块的逻辑中,提交区块调用controller对象的commit_block函数:

void controller::commit_block() { validate_db_available_size(); // 校验db数据库的大小 validate_reversible_available_size(); // 校验reversible数据库的大小 my->commit_block(true); // 调用controller_impl结构体中的的commit_block函数,并且传入true }

从这条逻辑过来的提交区块,会执行add_to_fork_db,而commit_block函数的另一处调用是在应用区块部分,没有触发add_to_fork_db。至于commit_block函数的内容不在此处展开,只看fork_db相关的内容:

if (add_to_fork_db) { pending->_pending_block_state->validated = true; // 将pending区块对象的状态属性validated置为true,标记已校验。 auto new_bsp = fork_db.add(pending->_pending_block_state); // 将pending区块添加至fork_db。 emit(self.accepted_block_header, pending->_pending_block_state); // 发射controller的accepted_block_header信号,携带pending区块状态对象。 head = fork_db.head(); // 将当前节点的头块设置为fork_db的头块。 // 校验pending区块是否最终成功同时变为fork_db以及主节点的头块。 EOS_ASSERT(new_bsp == head, fork_database_exception, "committed block did not become the new head in fork database"); } 5. maybe_switch_forks

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/zyyyfy.html