EOS 源碼分析 -- cleos

從 main 函數開始

首先注冊 command, option 以及 subcommand ,每個 command 都是一個 App 對象,并為每個 App 對象設置回調set_callback
獲取程序啟動時的指定參數,解析參數,從 App 對象的樹形結構中,找到指定的command 的,然后執行對應的回調函數。

設置命令及參數

  • 聲名第一個App類的對象,相當于cleos,再看指針,通過App的成員函數add_subcommand()實現,成功地實現了鏈表
  • add_subcommand 函數 將 option 添加到 vector subcommands_ 里,返回一個 App對象, 返回的 App 對象可以繼續 add_subcommand,形成一個樹型結構。
  • require_subcommand 函數指定該 command 不是一個單獨有效的命令,需要一個subcommand.
  • add_option 函數將 創建 Option 對象,并將指針保存在 vector options_ 中,每個對象可以無限擴展配置選項。
  • option 調用 required() 方法,表示這個option需要一個參數
  • add_flag 添加flag (不帶參數),內部會調用 add_option 方法。
  • set_callback 函數給每個 App 對象設置一個回調.

option 對象

屬性

  • expected_ :該 option 需要幾個參數
  • required_ : 是否需要參數
  • pname_ : 參數名,且不含前綴---
  • snames_ : 參數名,以- 為前綴
  • lnames_ : 參數名,以-- 為前綴

解析參數

解析參數調用 app.parse(argc, argv);

parse

將命令行參數,存放在vector args 中,

std::vector<std::string> parse(int argc, char **argv) {
        name_ = argv[0];
        std::vector<std::string> args;
        for(int i = argc - 1; i > 0; i--)
            args.emplace_back(argv[i]);
        return parse(args);
    }

調用另一個parse方法

/// The real work is done here. Expects a reversed vector.
    /// Changes the vector to the remaining options.
    std::vector<std::string> &parse(std::vector<std::string> &args) {
        _validate();
        _parse(args);
        run_callback();
        return args;
    }

_validate

首先_validate這個方法里檢查option選項有沒有沖突的

/// Check the options to make sure there are no conficts.
    ///
    /// Currenly checks to see if mutiple positionals exist with -1 args
    void _validate() const {
        auto count = std::count_if(std::begin(options_), std::end(options_), [](const Option_p &opt) {
            return opt->get_expected() == -1 && opt->get_positional();
        });
        if(count > 1)
            throw InvalidError(name_ + ": Too many positional arguments with unlimited expected args");
        for(const App_p &app : subcommands_)
            app->_validate();
    }

expected 解釋如下

/// The number of expected values, 0 for flag, -1 for unlimited vector
    int expected_{1};

即, 0 代表的是添加的 flag 選項, -1 代表無限制的vector 選項
如果有相同名字的 vector 類型參數被指定,回拋出異常。

_parse

  • 函數 void _parse(std::vector<std::string> &args) 中, 首先循環調用 _parse_single 方法,處理參數所有參數。
  • 處理當前app對象的所有 option 對象,當該option被解析過后,調用該option的回調函數,將 解析后的 results_ 作為參數,如下
/// Process the callback
    void run_callback() const {
        if(!callback_(results_))
            throw ConversionError(get_name() + "=" + detail::join(results_));
        if(!validators_.empty()) {
            for(const std::string &result : results_)
                for(const std::function<bool(std::string)> &vali : validators_)
                    if(!vali(result))
                        throw ValidationError(get_name() + "=" + result);
        }
    }

_parse_single

_parse_single 函數將參數分成POSITIONAL_MARK、SUBCOMMAND、LONG、SHORT、NONE五個種類。

  • SUBCOMMAND 代表解析的該參數在subcommands列表中
  • LONG 代表參數 --XXX
  • SHORT 代表參數 -XXX
  • NONE 代表參數 XXX
  • POSITIONAL_MARK 攢不知什么作用

_parse_subcommand

如果找到對應名字的 subcommand (com)對象,彈出最后一個參數,執行 com->_parse(args)

_parse_long

取出當前參數,--name=value 格式,找出對應的option,設置給result 屬性

_parse_short

取出當前參數,-name 格式, 查找對應 option 對象,根據 option 對象的 expected 字段,接受剩余的 參數,并設置給 optionresult 屬性

_parse_positional

如果當前已接受的option 參數個數還未到達expected的數量,添加到vector parse_order_ 中。

callback

每個 command 或者 option 都可以設置一個回調, 解析命令行的參數后,會依次調用這些 callback

以新建一個賬戶為例:

createAccount->set_callback([this] {
    if( !active_key_str.size() )
       active_key_str = owner_key_str;
    public_key_type owner_key, active_key;
    try {
       owner_key = public_key_type(owner_key_str);
    } EOS_RETHROW_EXCEPTIONS(public_key_type_exception, "Invalid owner public key: ${public_key}", ("public_key", owner_key_str));
    try {
       active_key = public_key_type(active_key_str);
    } EOS_RETHROW_EXCEPTIONS(public_key_type_exception, "Invalid active public key: ${public_key}", ("public_key", active_key_str));
    auto create = create_newaccount(creator, account_name, owner_key, active_key);
    if (!simple) {
       if ( buy_ram_eos.empty() && buy_ram_bytes_in_kbytes == 0) {
            .......
          send_actions( { create, buyram, delegate } );
       } else {
          send_actions( { create, buyram } );
       }
    } else {
       send_actions( { create } );
    }
});

這段回調最終走到 send_actions , 再到 push_actions

void send_actions(std::vector<chain::action>&& actions, int32_t extra_kcpu = 1000, packed_transaction::compression_type compression = packed_transaction::none ) {
   auto result = push_actions( move(actions), extra_kcpu, compression);

   if( tx_print_json ) {
      cout << fc::json::to_pretty_string( result ) << endl;
   } else {
      print_result( result );
   }
}

fc::variant push_actions(std::vector<chain::action>&& actions, int32_t extra_kcpu, packed_transaction::compression_type compression = packed_transaction::none ) {
   signed_transaction trx;
   trx.actions = std::forward<decltype(actions)>(actions);

   return push_transaction(trx, extra_kcpu, compression);
}

打包交易數據后,然后會走到push_transaction

fc::variant push_transaction( signed_transaction& trx, int32_t extra_kcpu = 1000, packed_transaction::compression_type compression = packed_transaction::none ) {
   
   ......
   
   if (!tx_dont_broadcast) {
      return call(push_txn_func, packed_transaction(trx, compression));
   } else {
      return fc::variant(trx);
   }
}

最終會走到 call(push_txn_func, packed_transaction(trx, compression));
跟蹤push_txn_func會發現是個字符串

   const string chain_func_base = "/v1/chain";
   const string push_txn_func = chain_func_base + "/push_transaction";

call 函數最終調用:

template<typename T>
fc::variant call( const std::string& url,
                  const std::string& path,
                  const T& v ) {
   try {
      eosio::client::http::connection_param *cp = new eosio::client::http::connection_param(context, parse_url(url) + path,
              no_verify ? false : true, headers);

      return eosio::client::http::do_http_call( *cp, fc::variant(v), print_request, print_response );
   }
   catch(boost::system::system_error& e) {
        ......   
   }
}

do_http_call 函數就將打包好的參數,path 信息以http請求的形式發送出去了,返回值給cleos在命令行輸出

?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 227,967評論 6 531
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,273評論 3 415
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 175,870評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,742評論 1 309
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,527評論 6 407
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,010評論 1 322
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,108評論 3 440
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,250評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,769評論 1 333
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,656評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,853評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,371評論 5 358
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,103評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,472評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,717評論 1 281
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,487評論 3 390
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,815評論 2 372

推薦閱讀更多精彩內容