公信寶gxs核心程式碼閱讀筆記1-剛剛開始(霜之小刀)
公信寶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的部分我也儘量說說他的意思