公信宝gxs核心代码阅读笔记1-刚刚开始(霜之小刀)
欢迎转载和引用,若有问题请联系
若有疑问,请联系
Email : [email protected]
QQ:2279557541
1、测试环境简介
这里我使用的是mbp,苹果的开发环境,由于公信宝的工程师都使用的是苹果的环境,所以使用苹果环境编译源码比较容易通过,另外linux的环境应该也是可以的,不过我编译没有成功,如果一定需要在linux环境下编译的,遇到可以在去公信宝的开发社区里面去咨询。公信宝的工程师们还是相当热情的。
我简单看了下,要看懂这部分代码的基本框架并不需要太多的前置知识,主要就是c++,boost,cmake就足够了。
而关于代码阅读这块,出于我个人的习惯,我使用的是qtcreator导入CMakeList.txt
2、代码的下载与编译
其实在公信宝的github上是有说如何编译的,位置在
https://github.com/gxchain/gxb-core
我就把里面的内容转述过来就好了
首先是利用brew安装工具
brew install wget cmake git openssl autoconf automake doxygen libtool
然后是编译代码
git clone https://github.com/gxchain/gxb-core.git
cd gxb-core
git submodule update --init --recursive
mkdir build && cd build
cmake -DCMAKE_BUILD_TYPE=Release ..
make
上面是官方的原文
不过在这里插一点其他的,
一个是我编译的时候碰到点问题,主要是在cmake这块,官方文档上提到的
-DOPENSSL_INCLUDE_DIR, -DOPENSSL_SSL_LIBRARY, and -DOPENSSL_CRYPTO_LIBRARY -DBOOST_ROOT
等参数我全都用上了才能编译通过,这些参数简单来说就是用来指定2个第三方库的路径的,分别是openssl和boost。
另外,我们看到了开始brew了openssl的库,但是这里的boost库从哪里来呢?
直接执行源码中的script/boost_install.sh就可以了,这个会自动的下载并编译boost库,时间会比较长要有心理准备。
还有,如果直接make,编译速度会很慢,这里建议使用
make -j4
由于我的笔记本是双核4线程的,所以这里使用j4这个参数,比单线程编译确实是快的不是一点点。
3、main.cpp入口程序概览
using namespace graphene;
namespace bpo = boost::program_options;
//==========
#include <QApplication>
#include "MainWidget.h"
/*int main(int argc, char *argv[])
{
QApplication app(argc, argv);
MainWidget widget;
widget.show();
return app.exec();
}*/
void write_default_logging_config_to_stream(std::ostream& out, bool log_file = false);
fc::optional<fc::logging_config> load_logging_config_from_ini_file(const fc::path& config_ini_filename);
int main(int argc, char** argv)
{
app::application* node = new app::application();
fc::oexception unhandled_exception;
try {
//这里定义了两个选项参数
bpo::options_description app_options("Graphene Witness Node");
bpo::options_description cfg_options("Graphene Witness Node");
app_options.add_options()
("help,h", "Print this help message and exit.")
("data-dir,d", bpo::value<boost::filesystem::path>()->default_value("witness_node_data_dir"), "Directory containing databases, configuration file, etc.")
;
bpo::variables_map options;
//这里是在注册各个模块,其实呢,就是把模块同note引擎关联起来,让note起到管理者和连接器的作用
auto witness_plug = node->register_plugin<witness_plugin::witness_plugin>();
auto debug_witness_plug = node->register_plugin<debug_witness_plugin::debug_witness_plugin>();
auto history_plug = node->register_plugin<account_history::account_history_plugin>();
auto market_history_plug = node->register_plugin<market_history::market_history_plugin>();
auto delayed_plug = node->register_plugin<delayed_node::delayed_node_plugin>();
try
{
bpo::options_description cli, cfg;
//在note中填充命令行选项和配置选项,
//另外上面不是注册了这么多个插件么,
//这里也会将各个插件需要的命令行选项和配置选项填充进来
node->set_program_options(cli, cfg);
//把填充过的cli的参数,添加到app_option里面
app_options.add(cli);
//配置选项都加到cfg中
cfg_options.add(cfg);
//通过app_option解析命令行,并保存到option中,以备后面使用,这个option呢
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;
}
//==================================
//下面都是对参数的处理
if( options.count("help") )
{//help参数的处理,直接打印出所有参数及其描述
std::cout << app_options << "\n";
return 0;
}
if( options.count("version") )
{//版本信息的处理
std::cout << "Version: " << graphene::utilities::git_revision_description << "\n";
std::cout << "SHA: " << graphene::utilities::git_revision_sha << "\n";
std::cout << "Timestamp: " << fc::get_approximate_relative_time_string(fc::time_point_sec(graphene::utilities::git_revision_unix_timestamp)) << "\n";
return 0;
}
fc::path data_dir;
if( options.count("data-dir") )
{//数据存储目录的处理,这里应该是区块链数据
data_dir = options["data-dir"].as<boost::filesystem::path>();
if( data_dir.is_relative() )
data_dir = fc::current_path() / data_dir;
}
fc::path config_ini_path = data_dir / "config.ini";
if( fc::exists(config_ini_path) )
{//配置文件的处理
boost::container::flat_set<std::string> seen;
bpo::options_description unique_options("Graphene Witness Node");
for( const boost::shared_ptr<bpo::option_description> od : cfg_options.options() )
{//对cfg_options进行一道过滤,去除重复的参数,然后保存到unique_option
const std::string name = od->long_name();
if( seen.find(name) != seen.end() ) continue;
seen.insert(name);
unique_options.add( od );
}
// 将config.ini文件中的参数也保存到options中
bpo::store(bpo::parse_config_file<char>(config_ini_path.preferred_string().c_str(), unique_options, true), options);
// try to get logging options from the config file.
try
{//通过配置文件配置日志模块
fc::optional<fc::logging_config> logging_config = load_logging_config_from_ini_file(config_ini_path);
if (logging_config)
fc::configure_logging(*logging_config);
}
catch (const fc::exception&)
{
wlog("Error parsing logging config from config file ${config}, using default config", ("config", config_ini_path.preferred_string()));
}
}
else
{
ilog("Writing new config file at ${path}", ("path", config_ini_path));
if( !fc::exists(data_dir) )
fc::create_directories(data_dir);
boost::container::flat_set<std::string> seen;
std::ofstream out_cfg(config_ini_path.preferred_string());
for( const boost::shared_ptr<bpo::option_description> od : cfg_options.options() )
{//遍历配置文件
//首先将不为空的描述进行注释进行注释
if( !od->description().empty() )
out_cfg << "# " << od->description() << "\n";
boost::any store;
//对没有默认值的参数输出“#xxx=”类似于提示吧
if( !od->semantic()->apply_default(store) )
out_cfg << "# " << od->long_name() << " = \n";
else {
/*其他的直接按照以下格式输出
# 描述符
参数名称 = 参数值
*/
const std::string name = od->long_name();
if( seen.find(name) != seen.end() ) continue;
seen.insert(name);
auto example = od->format_parameter();
if( example.empty() )
// This is a boolean switch
out_cfg << od->long_name() << " = " << "false\n";
else {
// The string is formatted "arg (=<interesting part>)"
example.erase(0, 6);
example.erase(example.length()-1);
out_cfg << od->long_name() << " = " << example << "\n";
}
}
out_cfg << "\n";
}
//通过log-file这个参数,把log的一些参数写到配置文件中
if (options.count("log-file"))
write_default_logging_config_to_stream(out_cfg, true);
else
write_default_logging_config_to_stream(out_cfg, false);
out_cfg.close();
// read the default logging config we just wrote out to the file and start using it
fc::optional<fc::logging_config> logging_config = load_logging_config_from_ini_file(config_ini_path);
if (logging_config)
fc::configure_logging(*logging_config);
}
//===============================
//下面是对各前面加载插件的初始化啊,之类的
bpo::notify(options);
node->initialize(data_dir, options);
node->initialize_plugins( options );
node->startup();
node->startup_plugins();
fc::promise<int>::ptr exit_promise = new fc::promise<int>("UNIX Signal Handler");
fc::set_signal_handler([&exit_promise](int signal) {
elog( "Caught SIGINT attempting to exit cleanly" );
exit_promise->set_value(signal);
}, SIGINT);
fc::set_signal_handler([&exit_promise](int signal) {
elog( "Caught SIGTERM attempting to exit cleanly" );
exit_promise->set_value(signal);
}, SIGTERM);
ilog("Started witness node on a chain with ${h} blocks.", ("h", node->chain_database()->head_block_num()));
ilog("Chain ID is ${id}", ("id", node->chain_database()->get_chain_id()) );
int signal = exit_promise->wait();
ilog("Exiting from signal ${n}", ("n", signal));
node->shutdown_plugins();
node->shutdown();
delete node;
return 0;
} catch( const fc::exception& e ) {
// deleting the node can yield, so do this outside the exception handler
unhandled_exception = e;
}
if (unhandled_exception)
{
elog("Exiting with error:\n${e}", ("e", unhandled_exception->to_detail_string()));
node->shutdown();
delete node;
return 1;
}
}
可以看出整个main函数还是不难理解的
基本就是
- 配置命令行参数
- 配置配置文件参数
- 注册各个模块
- 配置和启动各个模块
代码中我加了不少的注释,写的都是看这个文件大概看得懂的部分,具体的,会在后面对每一段做一一的解释,今天就写这么多了
另外这一块涉及到了不少boost的相关内容,我也没有详细解释,后面对小块进行说明的时候对于boost的部分我也尽量说说他的意思