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