controller函数的具体实现内容,一般是对参数的校验,然后通过my来调用controller_impl结构体的具体函数来处理。所以controller的核心功能实现是在controller_impl结构体中,下面查看其成员属性:
self,controller实例的引用。
db, chainbase::database的一个实例,用于存储区块全数据,是区块进入不可修改的block_log之前的缓冲地带,包括本地的,同步过来的,未承认的,已承认的等等。
reversible_blocks,同样也是chainbase::database的一个实例,但它是用来存储那些已经成功被应用但仍旧是可逆的特殊区块。
blog,block_log类实例,是区块链不可逆数据的存储对象。这部分内容在数据存储结构部分已有详细解释,此处不再赘述。
pending,处于pending状态的一个区块的包装。
head,block_state_ptr结构体是所有区块的统一数据结构,head代表头区块对象。
fork_db,fork_database类实例,分叉库。
wasmif,wasm_interface类实例,是webassembly虚拟机接口的实例。
resource_limits,resource_limits_manager资源限制管理器实例。
authorization,authorization_manager认证权限管理器实例。
conf,controller::config前文介绍的配置config的实例。
chain_id,chain_id_type类型,代表区块链当前id。
replaying,是否允许重播,默认初始化为false。
replay_head_time,重播的头区块时间。
read_mode,数据库读取模式,默认初始话为SPECULATIVE
in_trx_requiring_checks,事务中是否需要检查,默认为false。如果为true的话,通常会被跳过的检查不会被跳过。例如身份验证。
subjective_cpu_leeway,剩余的cpu资源,以微妙计算。
trusted_producer_light_validation,可信的生产者执行轻量级校验,默认为false。
snapshot_head_block,快照的头区块号。
handler_key,处理者的键,元素为scope和action组成的二元组。
apply_handlers,应用操作的处理者,元素为以handler_key为键,std::function<void(apply_context&)>为值的map作为值,账户名作为键的复杂map。
unapplied_transactions,未应用的事务map,以sha256加密串作为键,transaction_metadata_ptr为值。pop_block函数或者abort_block函数为执行完毕的事务,如果再次被其他区块应用会从这个列表中移除,生产者在调度新事务打包到区块里时可以查询这个列表。
剩下的内容为controller_impl的众多功能函数的实现了,这些内容都是需要与其他程序组合使用,例如插件程序,或者智能合约,因此在接下来的篇章中,将会重新按照一个功能入口研究完整的使用脉络。而在这些功能中有两个内容需要在此处研究清楚,一个是fork_database,另一个是snapshot。下面逐一展开分析。
fork_database在fork_database.hpp文件中声明。管理了轻量级状态数据,是由未确认的潜在区块产生的。当本地节点接收receive到新的区块时,它们将被推入fork数据库。fork数据库跟踪最长的链,以及最新不可逆块号。所有大于最新不可逆块号的区块将会在发出“irreversible”不可逆信号以后被释放掉,区块已经成功上链变为不可逆,因此fork库没必要再存储。分叉库提供了很多函数,例如通过区块id获取区块、通过区块号获取区块、插入区块包括set和add各种重载函数、删除区块、获取头区块、通过id获取两个分支、设置区块标志位等。
1. fork_database构造器在controller_impl的构造函数体中会被调用。
controller_impl( const controller::config& cfg, controller& s ) :self(s), db( cfg.state_dir, cfg.read_only ? database::read_only : database::read_write, cfg.state_size ), reversible_blocks( cfg.blocks_dir/config::reversible_blocks_dir_name, cfg.read_only ? database::read_only : database::read_write, cfg.reversible_cache_size ), blog( cfg.blocks_dir ), fork_db( cfg.state_dir ), // 调用fork_db构造器,传入一个文件路径。 wasmif( cfg.wasm_runtime ), resource_limits( db ), authorization( s, db ), conf( cfg ), chain_id( cfg.genesis.compute_chain_id() ), read_mode( cfg.read_mode )进入构造器。
fork_database::fork_database( const fc::path& data_dir ):my( new fork_database_impl() ) { my->datadir = data_dir; if (!fc::is_directory(my->datadir)) fc::create_directories(my->datadir); auto fork_db_dat = my->datadir / config::forkdb_filename; // 在该目录下创建一个文件forkdb.dat if( fc::exists( fork_db_dat ) ) { // 如果该文件已存在 string content; fc::read_file_contents( fork_db_dat, content ); // 将其读到内存中 fc::datastream<const char*> ds( content.data(), content.size() ); unsigned_int size; fc::raw::unpack( ds, size ); // 按照区块结构解析 for( uint32_t i = 0, n = size.value; i < n; ++i ) { // 遍历所有区块 block_state s; fc::raw::unpack( ds, s ); set( std::make_shared<block_state>( move( s ) ) ); // 逐一插入到数据库fork_database中 } block_id_type head_id; fc::raw::unpack( ds, head_id ); my->head = get_block( head_id ); // 处理fork_database的头区块数据 fc::remove( fork_db_dat ); // 删除持久化文件forkdb.dat。 } }文件forkdb.dat也位于节点数据目录中,是前文介绍唯一没有说到的文件,这里补齐。
2. irreversible信号