BTS 的 DPoS 共识之代码实现概述(下)

上文 本文基于的BitShares源代码GitHub地址如下:https://github.com/bitshares/bitshares-core

目录

5. 证人节点初始化

证人节点初始化的代码流程图:

留下有个疑问,整个程序最开始运行时,证人节点是如何初始化的呢?实际上初始数据是在init_genesis()方法中初始的:

// 初始创世数据
void database::init_genesis(const genesis_state_type& genesis_state)
{ try {

    //其他处理
    ......

   // 创建初始账户
   for( const auto& account : genesis_state.initial_accounts )
   {
      account_create_operation cop;
      cop.name = account.name;
      cop.registrar = GRAPHENE_TEMP_ACCOUNT;
      cop.owner = authority(1, account.owner_key, 1);
      if( account.active_key == public_key_type() )
      {
         cop.active = cop.owner;
         cop.options.memo_key = account.owner_key;
      }
      else
      {
         cop.active = authority(1, account.active_key, 1);
         cop.options.memo_key = account.active_key;
      }
      account_id_type account_id(apply_operation(genesis_eval_state, cop).get<object_id_type>());

      if( account.is_lifetime_member )
      {
          account_upgrade_operation op;
          op.account_to_upgrade = account_id;
          op.upgrade_to_lifetime_member = true;
          apply_operation(genesis_eval_state, op);
      }
   }

    //其他处理
    ......

   // 创建初始证人
   std::for_each(genesis_state.initial_witness_candidates.begin(), genesis_state.initial_witness_candidates.end(),
                 [&](const genesis_state_type::initial_witness_type& witness) {
      witness_create_operation op;
      op.witness_account = get_account_id(witness.owner_name);
      op.block_signing_key = witness.block_signing_key;
      apply_operation(genesis_eval_state, op);
   });

    //其他处理
    ......

   // 设置活跃证人
   modify(get_global_properties(), [&](global_property_object& p) {
      for( uint32_t i = 1; i <= genesis_state.initial_active_witnesses; ++i )
      {
         p.active_witnesses.insert(witness_id_type(i));
      }
   });

    //其他处理
    ......

   // 创建证人调度顺序
   create<witness_schedule_object>([&]( witness_schedule_object& wso )
   {
      for( const witness_id_type& wid : get_global_properties().active_witnesses )
         wso.current_shuffled_witnesses.push_back( wid );
   });

    //其他处理
    ......

} FC_CAPTURE_AND_RETHROW() }

整个init_genesis()方法都在进行各种属性值的初始,且创世数据是通过参数genesis_state传入,而该方法又在open()方法中调用:

// 打开指定路径的数据库,如果存在就新建
void database::open(
   const fc::path& data_dir,
   std::function<genesis_state_type()> genesis_loader,
   const std::string& db_version)
{
   try
   {

     //其他处理
    ......

    // 如果全局属性不存在,则初始创世数据
      if( !find(global_property_id_type()) )
         init_genesis(genesis_loader());

    //其他处理
    ......

   }
   FC_CAPTURE_LOG_AND_RETHROW( (data_dir) )
}

可以看到在open()方法中调用init_genesis()方法时,其参数是通过调用genesis_loader()方法的返回值对init_genesis()方法的实参进行赋值。而init_genesis()方法也是open()方法的参数之一,而open()方法又是在startup()中调用:

void startup()
      { try {

     //其他处理
    ......

         auto initial_state = [this] {
            ilog("Initializing database...");
            // 通过命令行参数指定genesis.json文件路径
            if( _options->count("genesis-json") )
            {
               std::string genesis_str;
               // 1. 读取genesis.json文件
               fc::read_file_contents( _options->at("genesis-json").as<boost::filesystem::path>(), genesis_str );
               // 2. 从json字符串中获取创世状态数据
               genesis_state_type genesis = fc::json::from_string( genesis_str ).as<genesis_state_type>( 20 );
               bool modified_genesis = false;
               if( _options->count("genesis-timestamp") )
               {
                  genesis.initial_timestamp = fc::time_point_sec( fc::time_point::now() )
                                            + genesis.initial_parameters.block_interval
                                            + _options->at("genesis-timestamp").as<uint32_t>();
                  genesis.initial_timestamp -= ( genesis.initial_timestamp.sec_since_epoch()
                                                 % genesis.initial_parameters.block_interval );
                  modified_genesis = true;
                  std::cerr << "Used genesis timestamp:  " << genesis.initial_timestamp.to_iso_string()
                            << " (PLEASE RECORD THIS)\n";
               }
               if( _options->count("dbg-init-key") )
               {
                  std::string init_key = _options->at( "dbg-init-key" ).as<string>();
                  FC_ASSERT( genesis.initial_witness_candidates.size() >= genesis.initial_active_witnesses );
                  set_dbg_init_key( genesis, init_key );
                  modified_genesis = true;
                  std::cerr << "Set init witness key to " << init_key << "\n";
               }
               if( modified_genesis )
               {
                  std::cerr << "WARNING:  GENESIS WAS MODIFIED, YOUR CHAIN ID MAY BE DIFFERENT\n";
                  genesis_str += "BOGUS";
                  genesis.initial_chain_id = fc::sha256::hash( genesis_str );
               }
               else
                  genesis.initial_chain_id = fc::sha256::hash( genesis_str );
               return genesis;
            }
            // 如果没指定genesis.json文件,则初始为空串
            else
            {
               std::string egenesis_json;
               // 初始json文件为空串
               graphene::egenesis::compute_egenesis_json( egenesis_json );
               FC_ASSERT( egenesis_json != "" );
               FC_ASSERT( graphene::egenesis::get_egenesis_json_hash() == fc::sha256::hash( egenesis_json ) );
               auto genesis = fc::json::from_string( egenesis_json ).as<genesis_state_type>( 20 );
               genesis.initial_chain_id = fc::sha256::hash( egenesis_json );
               return genesis;
            }
         };

     //其他处理
    ......

         try
         {
         // 打开数据库
            _chain_db->open( _data_dir / "blockchain", initial_state, GRAPHENE_CURRENT_DB_VERSION );
         }
         catch( const fc::exception& e )
         {
            elog( "Caught exception ${e} in open(), you might want to force a replay", ("e", e.to_detail_string()) );
            throw;
         }

     //其他处理
    ......

      } FC_LOG_AND_RETHROW() }

startup()中包含了创世状态的实际初始过程initial_state函数指针(即genesis_loader()方法实际执行的是initial_state函数指针指向的代码),在initial_state的实现过程中通过命令行参数_options读取指定genesis.json文件的内容进行处理,而_options的赋值和startup()的调用都是在main()方法中进行的:

// 证人节点应用程序入口
int main(int argc, char** argv) {
    // 创建应用
   app::application* node = new app::application();

     //其他处理
    ......

    // 命令行参数变量
      bpo::variables_map options;

    // 注册证人插件
      auto witness_plug = node->register_plugin<witness_plugin::witness_plugin>();

     //其他处理
    ......

    // 命令行参数处理
      try
      {
         bpo::options_description cli, cfg;
         // 设置其他命令行参数
         node->set_program_options(cli, cfg);
         app_options.add(cli);
         cfg_options.add(cfg);
         //解析命令行参数
         bpo::store(bpo::parse_command_line(argc, argv, app_options), options);
      }
      catch (const boost::program_options::error& e)
      {
        std::cerr << "Error parsing command line: " << e.what() << "\n";
        return 1;
      }

     //其他处理
    ......

    // 程序初始化
      node->initialize(data_dir, options);
      node->initialize_plugins( options );

    // 启动程序
      node->startup();
    // 启动相关插件
      node->startup_plugins();

     //其他处理
    ......

}

到这里我们又跟踪代码到main()方法,至此包括初始化和共识的主要过程全部讲解完毕。

6. 创建genesis.json文件

创建genesis.json文件的代码流程图如下:

最后,讲解一个BitShares中有意思事情,也是上一节留的一点尾巴,就是BitSharesgenesis.json文件其实不用手动编写,而是调用程序自动生成的,这一点的Ethereum不一样。之前学习Ethereum时,genesis.json弄好好久才弄好,官方文档给的实例确定几个核心字段,最后在论坛中才找到解决方法:

BitSharesgenesis.json文件是通过运行以下指令创建的(请参考官网:GENESIS CONFIGURATION):

programs/witness_node/witness_node --create-genesis-json genesis/my-genesis.json
  • 1

而在程序中,又是怎么执行,请回头看main()方法,在该方法中调用了initialize()方法:

// 程序初始化
void application::initialize(const fc::path& data_dir, const boost::program_options::variables_map& options)
{
   my->_data_dir = data_dir;
   my->_options = &options;

    // 如果执行程序有"create-genesis-json"参数
   if( options.count("create-genesis-json") )
   {
      fc::path genesis_out = options.at("create-genesis-json").as<boost::filesystem::path>();
      // 创建genesis.json文件内容
      genesis_state_type genesis_state = detail::create_example_genesis();
      if( fc::exists(genesis_out) )
      {
         try {
            genesis_state = fc::json::from_file(genesis_out).as<genesis_state_type>( 20 );
         } catch(const fc::exception& e) {
            std::cerr << "Unable to parse existing genesis file:\n" << e.to_string()
                      << "\nWould you like to replace it? [y/N] ";
            char response = std::cin.get();
            if( toupper(response) != 'Y' )
               return;
         }

         std::cerr << "Updating genesis state in file " << genesis_out.generic_string() << "\n";
      } else {
         std::cerr << "Creating example genesis state in file " << genesis_out.generic_string() << "\n";
      }
      // 保存到genesis.json文件
      fc::json::save_to_file(genesis_state, genesis_out);

      std::exit(EXIT_SUCCESS);
   }

     //其他处理
    ......
}

initialize()方法中,调用了create_example_genesis()方法创建genesis.json文件内容,其实现如下:

// 创建genesis.json实例文件
genesis_state_type create_example_genesis() {
      // 生成密钥
      auto nathan_key = fc::ecc::private_key::regenerate(fc::sha256::hash(string("nathan")));
      dlog("Allocating all stake to ${key}", ("key", utilities::key_to_wif(nathan_key)));
      genesis_state_type initial_state;
      // 设置各种创始值
      initial_state.initial_parameters.current_fees = fee_schedule::get_default();//->set_all_fees(GRAPHENE_BLOCKCHAIN_PRECISION);
      // 设置初始活跃证人数量
      initial_state.initial_active_witnesses = GRAPHENE_DEFAULT_MIN_WITNESS_COUNT;
      initial_state.initial_timestamp = time_point_sec(time_point::now().sec_since_epoch() /
            initial_state.initial_parameters.block_interval *
            initial_state.initial_parameters.block_interval);
      for( uint64_t i = 0; i < initial_state.initial_active_witnesses; ++i )
      {
         auto name = "init"+fc::to_string(i);
         // 设置初始账户
         initial_state.initial_accounts.emplace_back(name,
                                                     nathan_key.get_public_key(),
                                                     nathan_key.get_public_key(),
                                                     true);
         initial_state.initial_committee_candidates.push_back({name});
         // 设置初始证人替补
         initial_state.initial_witness_candidates.push_back({name, nathan_key.get_public_key()});
      }

      initial_state.initial_accounts.emplace_back("nathan", nathan_key.get_public_key());
      initial_state.initial_balances.push_back({nathan_key.get_public_key(),
                                                GRAPHENE_SYMBOL,
                                                GRAPHENE_MAX_SHARE_SUPPLY});
      initial_state.initial_chain_id = fc::sha256::hash( "BOGUS" );

      return initial_state;
   }

7. DPoS共识代码流程图

本人 ProcessOn 上 BTS的DPoS共识代码流程图 原图链接请点击:BTS的DPoS共识代码流程图

原文链接:https://blog.csdn.net/ggq89/article/details/80068306