1. 程式人生 > >公信寶gxs核心程式碼閱讀筆記1-剛剛開始(霜之小刀)

公信寶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的部分我也儘量說說他的意思