【劉文彬】【源碼解讀】EOS測試插件:txn_test_gen_plugin.cpp

原文鏈接:醒者呆的博客園软啼,https://www.cnblogs.com/Evsward/p/txn_test_gen_plugin.html

本文內(nèi)容本屬于《【精解】EOS TPS 多維實測》的內(nèi)容,但由于在編寫時篇幅過長寡夹,所以我決定將這一部分單獨成文撰寫鳄抒,以便于理解将宪。

關(guān)鍵字:eos, txn_test_gen_plugin, signed_transaction, ordered_action_result, C++, EOS插件

txn_test_gen_plugin 插件

這個插件是官方開發(fā)用來測試塊打包交易量的上祈,這種方式由于是直接系統(tǒng)內(nèi)部調(diào)用來模擬transaction夺衍,沒有中間通訊的損耗,因此效率是非常高的蹂风,官方稱通過這個插件測試到了8000的tps結(jié)果卢厂,而就我的測試結(jié)果來講,沒有這么恐怖惠啄,但也能到2000了慎恒,熟不知,其他的測試手段撵渡,例如cleos融柬,eosjs可能只有百級的量。下面趋距,我們一同來研究一下這個插件是如何實現(xiàn)以上功能的粒氧,過程中,我們也會思考EOS插件的架構(gòu)體系节腐,以及實現(xiàn)方法外盯。通過本文的學(xué)習(xí),如果有好的想法翼雀,我們也可以自己開發(fā)一個功能強大的插件pr給eos饱苟,為EOS社區(qū)做出我們自己的貢獻。

關(guān)于txn_test_gen_plugin插件的使用狼渊,非常易于上手箱熬,本文不做分析,這方面可以直接參考官方文檔囤锉。

插件的整體架構(gòu)

插件代碼整體結(jié)構(gòu)中坦弟,我們上面介紹的核心功能的實現(xiàn)函數(shù)都是包含在一個結(jié)構(gòu)體struct txn_test_gen_plugin_impl中。剩余的其他代碼都是對插件本身的通訊進行描述官地,包括如何調(diào)用酿傍,如何響應(yīng)等,以及整個插件的生命周期的控制:

  • set_program_options驱入,設(shè)置參數(shù)的階段赤炒,是最開始的階段,內(nèi)容只設(shè)置了txn-reference-block-lag的值亏较,默認是0莺褒,-1代表最新頭區(qū)塊。
  • plugin_initialize雪情,這一時期就把包含核心功能的結(jié)構(gòu)體txn_test_gen_plugin_impl加載到程序運行時內(nèi)存中了遵岩,同時初始化標(biāo)志位txn_reference_block_lag為txn-reference-block-lag的值。
  • plugin_startup,我們通過基礎(chǔ)插件http_plugin的支持獲得了http接口的能力尘执,這一時期舍哄,就暴露出來本插件的對外接口。
  • plugin_shutdown誊锭,調(diào)用stop_generation函數(shù)表悬,重置標(biāo)志位running為false,計時器關(guān)閉丧靡,打印關(guān)閉提示日志蟆沫。

下面是對外暴露的三個接口之一的stop_generation函數(shù)的源碼:

void stop_generation() {
    if(!running)
        throw fc::exception(fc::invalid_operation_exception_code);
    timer.cancel();
    running = false;
    ilog("Stopping transaction generation test");
}

接下來,我們主要集中精力在結(jié)構(gòu)體txn_test_gen_plugin_impl上温治,研究路線是以剩余兩個接口分別為入口進行逐一分析饭庞。

create_test_accounts 接口

關(guān)于這個接口,調(diào)用方法是

curl --data-binary '["eosio", "5KQwrPbwdL6PhXujxW37FSSQZ1JiwsST4cqQzDeyXtP79zkvFD3"]' http://localhost:8888/v1/txn_test_gen/create_test_accounts

傳入的參數(shù)是eosio以及其私鑰罐盔。我們進入到函數(shù)create_test_accounts中去分析源碼但绕。

準(zhǔn)備知識

首先,整個函數(shù)涉及到的所有transaction都是打包存入到一個vector集合std::vector中去惶看。

trxs是一個事務(wù)集,它包含很多的trx六孵,而其中每一個trx包含一個actions集合vector

一纬黎、準(zhǔn)備賬戶

trxs的第一個trx,內(nèi)容為賬戶創(chuàng)建:

  • 定義3個賬戶:txn.test.a劫窒,txn.test.b, txn.test.t
  • 輔助功能:controller& cc = app().get_plugin<chain_plugin>().chain();本今,通過cc可以隨時調(diào)用本地區(qū)塊鏈上的任意信息。
  • 通過fc::crypto::private_key::regenerate函數(shù)分別生成他們的私鑰主巍,要傳入生成秘鑰的seed冠息。
  • 通過私鑰直接調(diào)用get_public_key()即可獲得公鑰
  • 設(shè)置每個賬戶的owner和active權(quán)限對應(yīng)的公鑰,一般來講他們是相同的
  • 賬戶的創(chuàng)建者均為我們外部調(diào)用create_test_accounts接口時傳入的賬戶eosio孕索,注意:eosio的私鑰是通過字符串傳入的逛艰,要通過fc::crypto::private_key轉(zhuǎn)換成私鑰對象
  • 將每一個賬戶的創(chuàng)建組裝好成為一個action,存入trx的actions集合中去搞旭。
  • trx的actions成員已經(jīng)設(shè)置完畢散怖,完成剩余trx的組裝工作,包括
    • expiration肄渗,通過cc獲得當(dāng)前頭區(qū)塊的時間镇眷,加上延遲時間,這里是30s翎嫡,fc::seconds(30)
    • reference_block欠动,值為通過cc獲取當(dāng)前的頭區(qū)塊,意思為本transaction的引用區(qū)塊惑申,所有的信息是引用的這個區(qū)塊為頭區(qū)塊的環(huán)境
    • sign具伍,簽名翅雏,使用的是創(chuàng)建者eosio的私鑰對象,上面我們已經(jīng)準(zhǔn)備好了沿猜,簽名的數(shù)據(jù)是data的摘要
      • 當(dāng)前trx的actions中的元素的data并不是如文首的transaction中的data的加密串的結(jié)構(gòu)枚荣,而是明文的,這里的加密是數(shù)字摘要技術(shù)啼肩,感興趣的朋友可以去《應(yīng)用密碼學(xué)初探》進行了解橄妆。
      • 摘要的源碼函數(shù)是:sig_digest(chain_id, context_free_data),其中參數(shù)使用到了chain_id祈坠,而context_free_data就是上面提到的明文data內(nèi)容害碾,所以它是要與鏈id一起做數(shù)字摘要的(這一點我在使用eosjs嘗試自己做摘要的時候并未想到)

這一部分的源碼展示如下:

name newaccountA("txn.test.a");
name newaccountB("txn.test.b");
name newaccountC("txn.test.t");
name creator(init_name);

abi_def currency_abi_def = fc::json::from_string(eosio_token_abi).as<abi_def>();

controller& cc = app().get_plugin<chain_plugin>().chain();
auto chainid = app().get_plugin<chain_plugin>().get_chain_id();

fc::crypto::private_key txn_test_receiver_A_priv_key = fc::crypto::private_key::regenerate(fc::sha256(std::string(64, 'a')));
fc::crypto::private_key txn_test_receiver_B_priv_key = fc::crypto::private_key::regenerate(fc::sha256(std::string(64, 'b')));
fc::crypto::private_key txn_test_receiver_C_priv_key = fc::crypto::private_key::regenerate(fc::sha256(std::string(64, 'c')));
fc::crypto::public_key  txn_text_receiver_A_pub_key = txn_test_receiver_A_priv_key.get_public_key();
fc::crypto::public_key  txn_text_receiver_B_pub_key = txn_test_receiver_B_priv_key.get_public_key();
fc::crypto::public_key  txn_text_receiver_C_pub_key = txn_test_receiver_C_priv_key.get_public_key();
fc::crypto::private_key creator_priv_key = fc::crypto::private_key(init_priv_key);

//create some test accounts
{
    signed_transaction trx;
    
    //create "A" account
    {
        auto owner_auth   = eosio::chain::authority{1, {{txn_text_receiver_A_pub_key, 1}}, {}};
        auto active_auth  = eosio::chain::authority{1, {{txn_text_receiver_A_pub_key, 1}}, {}};
        trx.actions.emplace_back(vector<chain::permission_level>{{creator,"active"}}, newaccount{creator, newaccountA, owner_auth, active_auth});
    }
    //create "B" account
    {
        auto owner_auth   = eosio::chain::authority{1, {{txn_text_receiver_B_pub_key, 1}}, {}};
        auto active_auth  = eosio::chain::authority{1, {{txn_text_receiver_B_pub_key, 1}}, {}};
        trx.actions.emplace_back(vector<chain::permission_level>{{creator,"active"}}, newaccount{creator, newaccountB, owner_auth, active_auth});
    }
    //create "txn.test.t" account
    {
        auto owner_auth   = eosio::chain::authority{1, {{txn_text_receiver_C_pub_key, 1}}, {}};
        auto active_auth  = eosio::chain::authority{1, {{txn_text_receiver_C_pub_key, 1}}, {}};
        trx.actions.emplace_back(vector<chain::permission_level>{{creator,"active"}}, newaccount{creator, newaccountC, owner_auth, active_auth});
    }
    trx.expiration = cc.head_block_time() + fc::seconds(30);
    trx.set_reference_block(cc.head_block_id());
    trx.sign(creator_priv_key, chainid);
    trxs.emplace_back(std::move(trx));
}

二、token相關(guān)

trxs的第二個trx赦拘,內(nèi)容為token創(chuàng)建和issue慌随,為賬戶轉(zhuǎn)賬為之后的測試做準(zhǔn)備

  • 為賬戶txn.test.t設(shè)置eosio.token合約,之前在操作cleos set contract的時候可以通過打印結(jié)果發(fā)現(xiàn)躺同,是有setcode和setabi兩個步驟的阁猜。
    • setcode handler:
      • 設(shè)置handler的賬戶為txn.test.t
      • 將wasm設(shè)置為handler的code,wasm是通過eosio.token合約的eosio_token_wast文件獲取的蹋艺,vector<uint8_t> wasm = wast_to_wasm(std::string(eosio_token_wast))
      • 將handler加上相關(guān)權(quán)限組裝成action裝入trx的actions集合中剃袍。
    • setabi handler:
      • 設(shè)置handler的賬戶為txn.test.t
      • 設(shè)置handler的abi,將文件eosio_token_abi(json格式的)轉(zhuǎn)成json轉(zhuǎn)儲為abi_def結(jié)構(gòu)捎谨,然后通過fc::raw::pack操作將結(jié)果賦值給abi
      • 將handler加上相關(guān)權(quán)限組裝成action裝入trx的actions集合中民效。
  • 使用賬戶txn.test.t創(chuàng)建token,標(biāo)志位CUR涛救,總發(fā)行量十億畏邢,裝成action裝入trx的actions集合中。
  • issue CUR 給txn.test.t 600枚CUR检吆,裝成action裝入trx的actions集合中舒萎。
  • 從txn.test.t轉(zhuǎn)賬給txn.test.a 200枚CUR,裝成action裝入trx的actions集合中咧栗。
  • 從txn.test.t轉(zhuǎn)賬給txn.test.b 200枚CUR逆甜,裝成action裝入trx的actions集合中。
  • trx的actions成員已經(jīng)設(shè)置完畢致板,完成剩余trx的組裝工作(同上)交煞,這里只介紹不同的部分
    • max_net_usage_words,指定了網(wǎng)絡(luò)資源的最大使用限制為5000個詞斟或。

這一部分的源碼展示如下:

//set txn.test.t contract to eosio.token & initialize it
{
    signed_transaction trx;
    vector<uint8_t> wasm = wast_to_wasm(std::string(eosio_token_wast));
    setcode handler;
    handler.account = newaccountC;
    handler.code.assign(wasm.begin(), wasm.end());
    trx.actions.emplace_back( vector<chain::permission_level>{{newaccountC,"active"}}, handler);
    
    {
        setabi handler;
        handler.account = newaccountC;
        handler.abi = fc::raw::pack(json::from_string(eosio_token_abi).as<abi_def>());
        trx.actions.emplace_back( vector<chain::permission_level>{{newaccountC,"active"}}, handler);
    }
    {
        action act;
        act.account = N(txn.test.t);
        act.name = N(create);
        act.authorization = vector<permission_level>{{newaccountC,config::active_name}};
        act.data = eosio_token_serializer.variant_to_binary("create", fc::json::from_string("{\"issuer\":\"txn.test.t\",\"maximum_supply\":\"1000000000.0000 CUR\"}}"));
        trx.actions.push_back(act);
    }
    {
        action act;
        act.account = N(txn.test.t);
        act.name = N(issue);
        act.authorization = vector<permission_level>{{newaccountC,config::active_name}};
        act.data = eosio_token_serializer.variant_to_binary("issue", fc::json::from_string("{\"to\":\"txn.test.t\",\"quantity\":\"600.0000 CUR\",\"memo\":\"\"}"));
        trx.actions.push_back(act);
    }
    {
        action act;
        act.account = N(txn.test.t);
        act.name = N(transfer);
        act.authorization = vector<permission_level>{{newaccountC,config::active_name}};
        act.data = eosio_token_serializer.variant_to_binary("transfer", fc::json::from_string("{\"from\":\"txn.test.t\",\"to\":\"txn.test.a\",\"quantity\":\"200.0000 CUR\",\"memo\":\"\"}"));
        trx.actions.push_back(act);
    }
    {
        action act;
        act.account = N(txn.test.t);
        act.name = N(transfer);
        act.authorization = vector<permission_level>{{newaccountC,config::active_name}};
        act.data = eosio_token_serializer.variant_to_binary("transfer", fc::json::from_string("{\"from\":\"txn.test.t\",\"to\":\"txn.test.b\",\"quantity\":\"200.0000 CUR\",\"memo\":\"\"}"));
        trx.actions.push_back(act);
    }
    
    trx.expiration = cc.head_block_time() + fc::seconds(30);
    trx.set_reference_block(cc.head_block_id());
    trx.max_net_usage_words = 5000;
    trx.sign(txn_test_receiver_C_priv_key, chainid);
    trxs.emplace_back(std::move(trx));
}

發(fā)起請求

目前trxs集合已經(jīng)包含了兩個trx元素素征,其中每個trx包含了多個action。下面要將trxs推送到鏈上執(zhí)行

  • push_transactions函數(shù),遍歷trxs元素御毅,每個trx單獨發(fā)送push_next_transaction
  • push_next_transaction函數(shù)根欧,首先將trx取出通過packed_transaction函數(shù)進行組裝成post的結(jié)構(gòu)
  • packed_transaction函數(shù),通過set_transaction函數(shù)對trx進行摘撿端蛆,使用pack_transaction函數(shù)進行組裝
  • pack_transaction函數(shù)凤粗,就是調(diào)用了一下上面提過的fc::raw::pack操作,然后通過accept_transaction函數(shù)向鏈發(fā)起請求
  • accept_transaction函數(shù)今豆,是chain_plugin的一個函數(shù)嫌拣,它內(nèi)部調(diào)用了incoming_transaction_async_method異步發(fā)起交易請求。

這部分代碼比較雜呆躲,分為幾個部分:

push_transactions函數(shù):

void push_transactions( std::vector<signed_transaction>&& trxs, const std::function<void(fc::exception_ptr)>& next ) {
    auto trxs_copy = std::make_shared<std::decay_t<decltype(trxs)>>(std::move(trxs));
    push_next_transaction(trxs_copy, 0, next);
}

push_next_transaction函數(shù):

static void push_next_transaction(const std::shared_ptr<std::vector<signed_transaction>>& trxs, size_t index, const std::function<void(const fc::exception_ptr&)>& next ) {
      chain_plugin& cp = app().get_plugin<chain_plugin>();
      cp.accept_transaction( packed_transaction(trxs->at(index)), [=](const fc::static_variant<fc::exception_ptr, transaction_trace_ptr>& result){
         if (result.contains<fc::exception_ptr>()) {
            next(result.get<fc::exception_ptr>());
         } else {
            if (index + 1 < trxs->size()) {
               push_next_transaction(trxs, index + 1, next);
            } else {
               next(nullptr);
            }
         }
      });
   }

packed_transaction函數(shù)异逐,set_transaction函數(shù)以及pack_transaction函數(shù)的代碼都屬于本插件源碼之外的EOS庫源碼,由于本身代碼量也較少插掂,含義在上面已經(jīng)完全解釋過了灰瞻,這里不再粘貼源碼。

accept_transaction函數(shù)也是EOS的庫源碼

void chain_plugin::accept_transaction(const chain::packed_transaction& trx, next_function<chain::transaction_trace_ptr> next) {
    my->incoming_transaction_async_method(std::make_shared<packed_transaction>(trx), false, std::forward<decltype(next)>(next));
}

incoming_transaction_async_method(app().get_method<incoming::methods::transaction_async>())

start_generation 接口

該接口的調(diào)用方法是:

curl --data-binary '["", 20, 20]' http://localhost:8888/v1/txn_test_gen/start_generation

參數(shù)列表為:

  • 第一個參數(shù)為 salt辅甥,一般用于“加鹽”加密算法的值酝润,這里我們可以留空。

  • 第二個參數(shù)為 period璃弄,發(fā)送交易的間隔時間袍祖,單位為ms,這里是20谢揪。

  • 第三個參數(shù)為 batch_size,每個發(fā)送間隔周期內(nèi)打包交易的數(shù)量捐凭,這里也是20拨扶。

    翻譯過來就是:每20ms提交20筆交易。

接下來茁肠,以start_generation 函數(shù)為入口進行源碼分析患民。

start_generation 函數(shù)

  • 校驗:
    • period的取值范圍為(1, 2500)
    • batch_size的取值范圍為(1, 250)
    • batch_size必須是2的倍數(shù),batch_size & 1結(jié)果為假0才可以垦梆,這是一個位運算匹颤,與&,所以batch_size的值轉(zhuǎn)為二進制時末位不能為1托猩,所以就是2的倍數(shù)即可印蓖。
    • 對標(biāo)志位running的控制。

這部分代碼展示如下:

if(running)
    throw fc::exception(fc::invalid_operation_exception_code);
if(period < 1 || period > 2500)
    throw fc::exception(fc::invalid_operation_exception_code);
if(batch_size < 1 || batch_size > 250)
    throw fc::exception(fc::invalid_operation_exception_code);
if(batch_size & 1)
    throw fc::exception(fc::invalid_operation_exception_code);

running = true;
  • 定義兩個action京腥,分別是:
    • 賬戶txn.test.a給txn.test.b轉(zhuǎn)賬1000枚CUR
    • txn.test.b轉(zhuǎn)給txn.test.a同樣1000枚CUR

這部分代碼展示如下:

//create the actions here
act_a_to_b.account = N(txn.test.t);
act_a_to_b.name = N(transfer);
act_a_to_b.authorization = vector<permission_level>{{name("txn.test.a"),config::active_name}};
act_a_to_b.data = eosio_token_serializer.variant_to_binary("transfer", fc::json::from_string(fc::format_string("{\"from\":\"txn.test.a\",\"to\":\"txn.test.b\",\"quantity\":\"1.0000 CUR\",\"memo\":\"${l}\"}", fc::mutable_variant_object()("l", salt))));

act_b_to_a.account = N(txn.test.t);
act_b_to_a.name = N(transfer);
act_b_to_a.authorization = vector<permission_level>{{name("txn.test.b"),config::active_name}};
act_b_to_a.data = eosio_token_serializer.variant_to_binary("transfer", fc::json::from_string(fc::format_string("{\"from\":\"txn.test.b\",\"to\":\"txn.test.a\",\"quantity\":\"1.0000 CUR\",\"memo\":\"${l}\"}", fc::mutable_variant_object()("l", salt))));

接下來赦肃,是對參數(shù)period和batch_size的儲存為結(jié)構(gòu)體作用域的變量以供結(jié)構(gòu)體內(nèi)其他函數(shù)調(diào)用,然后打印日志,最后調(diào)用arm_timer函數(shù)他宛。

timer_timeout = period; // timer_timeout是結(jié)構(gòu)體的成員變量
batch = batch_size/2; // batch是結(jié)構(gòu)體的成員變量
ilog("Started transaction test plugin; performing ${p} transactions every ${m}ms", ("p", batch_size)("m", period));
arm_timer(boost::asio::high_resolution_timer::clock_type::now());

arm_timer 函數(shù)

從start_generation 函數(shù)過來船侧,傳入的參數(shù)是當(dāng)前時間now,該函數(shù)主要功能是對計時器的初始化操作(計時器與文首的stop_generation函數(shù)中的關(guān)閉計時器呼應(yīng))厅各。具體內(nèi)容可分為兩部分:

  • 設(shè)定計時器的過期時間镜撩,值為start_generation 接口的參數(shù)period與now相加的值,即從現(xiàn)在開始队塘,過period這么久袁梗,當(dāng)前計時器對象timer就過期。

  • 設(shè)定計時器的異步定時任務(wù)人灼,任務(wù)體直接調(diào)用send_transaction函數(shù)围段,對函數(shù)的返回值進行處理,如果有報錯信息(一般是服務(wù)中止)則調(diào)用stop_generation函數(shù)關(guān)閉插件投放。

      注意stop_generation函數(shù)關(guān)閉的是定時任務(wù)的無限遞歸奈泪,中止定時任務(wù),停止發(fā)送測試交易灸芳。但它并沒有停止插件服務(wù)涝桅,我們?nèi)耘f可以通過再次請求插件接口啟動無限測試交易。
    

這部分代碼如下:

void arm_timer(boost::asio::high_resolution_timer::time_point s) {
    timer.expires_at(s + std::chrono::milliseconds(timer_timeout));
    timer.async_wait([this](const boost::system::error_code& ec) {
        if(!running || ec)
            return;
        
        send_transaction([this](const fc::exception_ptr& e){
            if (e) {
                elog("pushing transaction failed: ${e}", ("e", e->to_detail_string()));
                stop_generation();
            } else { // 如果沒有終止報錯烙样,則無限遞歸調(diào)用arm_timer函數(shù)冯遂,遞歸時傳入的參數(shù)代替上面的now是當(dāng)前timer對象的過期時間,這樣在新的遞歸調(diào)用中谒获,timer的創(chuàng)建會以這個時間再加上period蛤肌,無間隔繼續(xù)執(zhí)行。
                arm_timer(timer.expires_at());
            }
        });
    });
}

send_transaction 函數(shù)

這個函數(shù)是本插件的核心功能部分批狱,主要是發(fā)送測試交易裸准,對transaction的處理,將我們上面start_generation 函數(shù)中設(shè)置的兩個action打包到transaction中去赔硫,以及對transaction各項屬性的設(shè)置炒俱。具體步驟為:

  • 聲明trxs,并為其設(shè)置大小為start_generation 接口中batch_size的值爪膊。

    std::vector<signed_transaction> trxs;
    trxs.reserve(2*batch);

接下來纽匙,與上面介紹的create_test_accounts 接口的賬戶準(zhǔn)備過程相同碴卧,準(zhǔn)備私鑰公鑰急膀,不多介紹钝腺。繼續(xù)準(zhǔn)備trx的參數(shù):

  • nonce,是用來賦值context_free_actions的
  • context_free_actions:官方介紹一大堆小槐,總之就是正常action是需要代價的拇派,要確權(quán)荷辕,要占用主網(wǎng)資源什么的,所以搞了一個context_free_actions件豌,字面意思就是上下文免費的action疮方,這里權(quán)當(dāng)測試用,填入的數(shù)據(jù)也是隨機nonce組裝的茧彤。
  • abi_serializer骡显,用來序列化abi的,傳入的system_account_name的abi值曾掂,它是在這里被賦值惫谤,然而是在結(jié)構(gòu)體的作用域中被調(diào)用的。
  • reference_block_num的處理珠洗,引用區(qū)塊溜歪,上面我們也提到過,而這里面增加了一層判斷许蓖,是根據(jù)標(biāo)志位txn_reference_block_lag的值來比較蝴猪,也就是說reference_block_num最后的值是最新區(qū)塊號減去txn_reference_block_lag的值,但是最小值為0膊爪,不可為負數(shù)自阱。
  • 通過reference_block_num獲得reference_block_id

這部分代碼如下:

controller& cc = app().get_plugin<chain_plugin>().chain();
auto chainid = app().get_plugin<chain_plugin>().get_chain_id();

fc::crypto::private_key a_priv_key = fc::crypto::private_key::regenerate(fc::sha256(std::string(64, 'a')));
fc::crypto::private_key b_priv_key = fc::crypto::private_key::regenerate(fc::sha256(std::string(64, 'b')));

static uint64_t nonce = static_cast<uint64_t>(fc::time_point::now().sec_since_epoch()) << 32;
abi_serializer eosio_serializer(cc.db().find<account_object, by_name>(config::system_account_name)->get_abi());

uint32_t reference_block_num = cc.last_irreversible_block_num();
if (txn_reference_block_lag >= 0) {
    reference_block_num = cc.head_block_num();
    if (reference_block_num <= (uint32_t)txn_reference_block_lag) {
        reference_block_num = 0;
    } else {
        reference_block_num -= (uint32_t)txn_reference_block_lag;
    }
}

block_id_type reference_block_id = cc.get_block_id_for_num(reference_block_num);

接下來,就是循環(huán)打包trx米酬,我們設(shè)置的batch_size好比是20沛豌,現(xiàn)在我們已有兩個action,每個action對應(yīng)一個trx赃额,則循環(huán)只需要執(zhí)行10次加派,每次執(zhí)行兩個trx即可實現(xiàn),每個trx相關(guān)的屬性在上一階段都已準(zhǔn)備好跳芳。直接看代碼吧哼丈。

for(unsigned int i = 0; i < batch; ++i) {
    {
        signed_transaction trx;
        trx.actions.push_back(act_a_to_b);
        trx.context_free_actions.emplace_back(action({}, config::null_account_name, "nonce", fc::raw::pack(nonce++)));
        trx.set_reference_block(reference_block_id);
        trx.expiration = cc.head_block_time() + fc::seconds(30);
        trx.max_net_usage_words = 100;
        trx.sign(a_priv_key, chainid);
        trxs.emplace_back(std::move(trx));
    }
    {
        signed_transaction trx;
        trx.actions.push_back(act_b_to_a);
        trx.context_free_actions.emplace_back(action({}, config::null_account_name, "nonce", fc::raw::pack(nonce++)));
        trx.set_reference_block(reference_block_id);
        trx.expiration = cc.head_block_time() + fc::seconds(30);
        trx.max_net_usage_words = 100;
        trx.sign(b_priv_key, chainid);
        trxs.emplace_back(std::move(trx));
    }
}

最后,執(zhí)行

push_transactions(std::move(trxs), next);

這個部分與create_test_accounts 接口發(fā)起請求的部分一致筛严,這里不再重復(fù)展示。

總結(jié)

到這里為止饶米,我們已經(jīng)完全分析透了txn_test_gen_plugin 插件的內(nèi)容桨啃。本文首先從大體上介紹了插件的架構(gòu),生命周期檬输,通訊請求與返回照瘾。接著介紹了核心結(jié)構(gòu)體的內(nèi)容,然后以對外接口為入口丧慈,沿著一條線將每個功能的實現(xiàn)完整地研究清楚析命。通過本文的學(xué)習(xí)主卫,我們對于EOS插件的體系有了初步深刻的理解,同時我們也完全搞清楚了txn_test_gen_plugin 插件的功能鹃愤,以及它為什么會達到一個比較高的tps的表現(xiàn)簇搅。

參考資料

  • EOSIO/eos
  • eos官方文檔

相關(guān)文章和視頻推薦

圓方圓學(xué)院匯集大批區(qū)塊鏈名師,打造精品的區(qū)塊鏈技術(shù)課程软吐。 在各大平臺都長期有優(yōu)質(zhì)免費公開課瘩将,歡迎報名收看。

公開課地址:https://ke.qq.com/course/345101

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末凹耙,一起剝皮案震驚了整個濱河市姿现,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌肖抱,老刑警劉巖备典,帶你破解...
    沈念sama閱讀 206,126評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異意述,居然都是意外死亡提佣,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,254評論 2 382
  • 文/潘曉璐 我一進店門欲险,熙熙樓的掌柜王于貴愁眉苦臉地迎上來镐依,“玉大人,你說我怎么就攤上這事天试』笨牵” “怎么了?”我有些...
    開封第一講書人閱讀 152,445評論 0 341
  • 文/不壞的土叔 我叫張陵喜每,是天一觀的道長务唐。 經(jīng)常有香客問我,道長带兜,這世上最難降的妖魔是什么枫笛? 我笑而不...
    開封第一講書人閱讀 55,185評論 1 278
  • 正文 為了忘掉前任,我火速辦了婚禮刚照,結(jié)果婚禮上刑巧,老公的妹妹穿的比我還像新娘。我一直安慰自己无畔,他們只是感情好啊楚,可當(dāng)我...
    茶點故事閱讀 64,178評論 5 371
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著浑彰,像睡著了一般恭理。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上郭变,一...
    開封第一講書人閱讀 48,970評論 1 284
  • 那天颜价,我揣著相機與錄音涯保,去河邊找鬼。 笑死周伦,一個胖子當(dāng)著我的面吹牛夕春,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播横辆,決...
    沈念sama閱讀 38,276評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼撇他,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了狈蚤?” 一聲冷哼從身側(cè)響起困肩,我...
    開封第一講書人閱讀 36,927評論 0 259
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎脆侮,沒想到半個月后锌畸,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,400評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡靖避,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 35,883評論 2 323
  • 正文 我和宋清朗相戀三年潭枣,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片幻捏。...
    茶點故事閱讀 37,997評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡盆犁,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出篡九,到底是詐尸還是另有隱情谐岁,我是刑警寧澤,帶...
    沈念sama閱讀 33,646評論 4 322
  • 正文 年R本政府宣布榛臼,位于F島的核電站伊佃,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏沛善。R本人自食惡果不足惜航揉,卻給世界環(huán)境...
    茶點故事閱讀 39,213評論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望金刁。 院中可真熱鬧帅涂,春花似錦、人聲如沸尤蛮。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,204評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽抵屿。三九已至,卻和暖如春捅位,著一層夾襖步出監(jiān)牢的瞬間轧葛,已是汗流浹背搂抒。 一陣腳步聲響...
    開封第一講書人閱讀 31,423評論 1 260
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留尿扯,地道東北人求晶。 一個月前我還...
    沈念sama閱讀 45,423評論 2 352
  • 正文 我出身青樓,卻偏偏與公主長得像衷笋,于是被迫代替她去往敵國和親芳杏。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 42,722評論 2 345

推薦閱讀更多精彩內(nèi)容

  • eos項目(v1.0.9)主要可以分為以下八個模塊:1.編譯模塊2.主程序模塊3.依賴庫模塊4.插件模塊5.智能合...
    vdes閱讀 2,763評論 0 1
  • 2018 9.5 星期三 天氣晴 今天是孩子開學(xué)第三天辟宗!分了班已經(jīng)正常上課了爵赵。 今天早上...
    往事隨風(fēng)_452e閱讀 139評論 1 2
  • 周檢視: 1.早睡早起 有5天都沒早睡 2.學(xué)會ps摳圖和修圖 完成100% 3.練習(xí)高倍速閱讀,讀完《少有人走...
    黎黎小倩閱讀 155評論 0 0
  • 一、策劃主旨 為了弘揚傳統(tǒng)文化的底蘊容客,營造和諧的社會環(huán)境秕铛,傳承中華文明,謳歌純潔友誼缩挑、給廣大青年員工提供一個陶冶情...
    羅迷你閱讀 576評論 0 0
  • “會當(dāng)凌絕頂但两,一覽眾山小”杜甫的一首《望岳》寫出了乍一望見泰山時,高興得不知怎樣形容才好的那種揣摹勁和驚嘆仰慕之情...
    e7e6b5ea3118閱讀 437評論 0 0