或许要切换分叉库到主库。该函数会在controller_impl结构体中的push_block和push_confirmation两个函数中被调用。
if ( read_mode != db_read_mode::IRREVERSIBLE ) { // 在db读取模式不等于IRREVERSIBLE时,要调用maybe_switch_forks函数。 maybe_switch_forks( s ); }db读取模式为IRREVERSIBLE时,只关心当前不可逆区块的数据,而fork_db中不存在不可逆区块的数据。而其他三种读取模式都涉及到可逆区块以及未被确认的数据,因此要去maybe_switch_forks函数检查处理一番。
当fork_db头块的上一个块等于当前节点的头块时,说明有新块被接收,先到达fork_db中,执行:
apply_block( new_head->block, s ); // 将新块应用到主库中去。 fork_db.mark_in_current_chain( new_head, true ); // 在fork_db中将新块的属性in_current_chain标记为true。 fork_db.set_validity( new_head, true ); // 在fork_db中将新块的属性validity标记为true。 head = new_head; // 更新节点主库的头块为当前块。当fork_db头块的前一个块不等于主库头块且fork_db头块id也不等于当前节点的头块id时,说明fork_db最新的两个块都不等于主库头块。这时候fork_db是更长的一条链,因此要切换主库为fork_db链。切换的过程很复杂,此处不展开。
6. controller析构对fork_db的处理 my->fork_db.close();在controller析构时将fork_db关掉,因为它会生成irreversible信号到这个controller。如果db读取模式为IRREVERSIBLE,将应用最后一个不可逆区块,my需要成为指向有效controller_impl的指针。
void fork_database::close() { if( my->index.size() == 0 ) return; auto fork_db_dat = my->datadir / config::forkdb_filename; // 获取文件输出流。 std::ofstream out( fork_db_dat.generic_string().c_str(), std::ios::out | std::ios::binary | std::ofstream::trunc ); uint32_t num_blocks_in_fork_db = my->index.size(); // 将当前fork_db的区块数据打包到输出流,持久化到fork_db.dat文件中。 fc::raw::pack( out, unsigned_int{num_blocks_in_fork_db} ); for( const auto& s : my->index ) { fc::raw::pack( out, *s ); } if( my->head ) fc::raw::pack( out, my->head->id ); else fc::raw::pack( out, block_id_type() ); // 通常头块不是不可逆的。如果fork_db中只剩一个块就是头块,一般不会将它删除因为下一个区块需要从头块建立。不过可以在退出之前将这个区块作为不可逆区块从fork_db中删除。 auto lib = my->head->dpos_irreversible_blocknum; auto oldest = *my->index.get<by_block_num>().begin(); if( oldest->block_num <= lib ) { prune( oldest ); } my->index.clear(); } 7. controller::startup对fork_db的处理 my->head = my->fork_db.head();controller的startup周期时,会将fork_db的头块设置为主库头块(头块一般不是不可逆的)。
snapshot快照,顾名思义,可以为区块链提供临时快速备份的功能。
1. abstract_snapshot_row_writer该结构体位于命名空间eosio::chain::detail。提供了写入snapshot快照的能力,是所有关于快照写入的结构的基类。该结构体是一个抽象类型,包含四个成员函数:
write,参数为ostream_wrapper实例(同样在detail命名空间下定义)的引用。
write,重载参数为sha256的加密器。
to_variant,转型变体。
row_type_name,行类型名,字符串类型。
snapshot_row_writer继承了abstract_snapshot_row_writer,在构造该结构体实例时,要传入data数据被缓存在函数体。接着,实际上,write向两种数据类型的输出流中写入的时候,对象就是data,写入方法都是fc::raw::pack(out, data);,最终将内存中的data数据写入到输出流。to_variant函数也被实现了,转型的目标是data,返回转型后的variant对象。data类型是模板类型,row_type_name实现了通过boost::core::demangle库获得data的具体类型名。最后,对外提供了make_row_writer函数,接收任何类型的数据,初始化以上快照行写入的功能。
snapshot_writer进一步封装了写入功能,对外提供了write_row写入接口以及其他辅助功能接口。该类使用到了detail的内容,包括make_row_writer函数的类。
接着,定义了snapshot_writer_ptr是snapshot_writer实例的共享指针。
variant_snapshot_writer和ostream_snapshot_writer都是snapshot_writer的子类,根据不同的数据类型实现了不同的处理逻辑。
与上面相对的,是读取的部分,所有关于快照读取结构的基类。其包含三个成员虚函数:
provide,参数是std::istream的实例引用,说明是对标准库输入流的读取。
provide,重载参数是fc::variant的引用,对变体的读取。
row_type_name,行类型名,同上,字符串类型。