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阅读 219,753评论 6 508
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,668评论 3 396
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 166,090评论 0 356
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 59,010评论 1 295
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 68,054评论 6 395
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,806评论 1 308
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,484评论 3 420
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,380评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,873评论 1 319
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 38,021评论 3 338
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,158评论 1 352
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,838评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,499评论 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,044评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,159评论 1 272
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,449评论 3 374
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,136评论 2 356

推荐阅读更多精彩内容