提交 b348a5df 编写于 作者: K Kevin Heifner 提交者: Matt Witherspoon

Merge pull request #814 from EOSIO/txn_test_gen_plug_stat-214

STAT-214: Add plugin for generating test transactions inside of eosd

(reapplied for noon branch)
上级 ec70cbbf
......@@ -9,3 +9,4 @@ add_subdirectory(producer_plugin)
#add_subdirectory(account_history_api_plugin)
add_subdirectory(wallet_plugin)
add_subdirectory(wallet_api_plugin)
add_subdirectory(txn_test_gen_plugin)
\ No newline at end of file
file(GLOB HEADERS "include/eosio/txn_test_gen_plugin/*.hpp")
add_library( txn_test_gen_plugin
txn_test_gen_plugin.cpp
${HEADERS} )
target_link_libraries( txn_test_gen_plugin appbase fc http_plugin chain_plugin )
target_include_directories( txn_test_gen_plugin PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/include" )
install( TARGETS
txn_test_gen_plugin
RUNTIME DESTINATION bin
LIBRARY DESTINATION lib
ARCHIVE DESTINATION lib
)
install( FILES ${HEADERS} DESTINATION "include/eosio/txn_test_gen_plugin" )
# txn\_test\_gen\_plugin
This plugin provides a way to generate a given amount of test transactions per second. It runs internally to eosd to reduce overhead.
## Setup
Create the two accounts used for transactions. Must give account & private key used for creation.
```
curl --data-binary '["inita", "5KQwrPbwdL6PhXujxW37FSSQZ1JiwsST4cqQzDeyXtP79zkvFD3"]' http://localhost:7777/v1/txn_test_gen/create_test_accounts
```
Wait a block, and then fund those accounts
```
curl --data-binary '["inita", "5KQwrPbwdL6PhXujxW37FSSQZ1JiwsST4cqQzDeyXtP79zkvFD3"]' http://localhost:7777/v1/txn_test_gen/fund_accounts
```
## Starting/Stopping
Start generation:
```
curl --data-binary '["one", 400]' http://localhost:7777/v1/txn_test_gen/start_generation
```
First parameter is some "salt" added to the transaction memos to ensure if this test is being run on other nodes concurrently no identical transactions are created. The second parameter is the requested transactions per second. This knob is currently quite crude so you don't always get what you ask for. Also, 400 is the limit.
Stop generation
```
curl http://localhost:7777/v1/txn_test_gen/stop_generation
```
Generation of transactions is also stopped if a transaction fails to be accepted for any reason.
\ No newline at end of file
/**
* @file
* @copyright defined in eos/LICENSE.txt
*/
#pragma once
#include <appbase/application.hpp>
#include <eosio/http_plugin/http_plugin.hpp>
namespace eosio {
using namespace appbase;
class txn_test_gen_plugin : public appbase::plugin<txn_test_gen_plugin> {
public:
txn_test_gen_plugin();
~txn_test_gen_plugin();
APPBASE_PLUGIN_REQUIRES((http_plugin))
virtual void set_program_options(options_description&, options_description& cfg) override;
void plugin_initialize(const variables_map& options);
void plugin_startup();
void plugin_shutdown();
private:
std::unique_ptr<struct txn_test_gen_plugin_impl> my;
};
}
/**
* @file
* @copyright defined in eos/LICENSE.txt
*/
#include <eosio/txn_test_gen_plugin/txn_test_gen_plugin.hpp>
#include <eosio/chain_plugin/chain_plugin.hpp>
#include <eos/utilities/key_conversion.hpp>
#include <fc/variant.hpp>
#include <fc/io/json.hpp>
#include <fc/exception/exception.hpp>
#include <fc/reflect/variant.hpp>
#include <boost/asio/high_resolution_timer.hpp>
#include <boost/algorithm/clamp.hpp>
namespace eosio { namespace detail {
struct txn_test_gen_empty {};
}}
FC_REFLECT(eosio::detail::txn_test_gen_empty, );
namespace eosio {
using namespace eosio::chain;
#define CALL(api_name, api_handle, call_name, INVOKE, http_response_code) \
{std::string("/v1/" #api_name "/" #call_name), \
[this](string, string body, url_response_callback cb) mutable { \
try { \
if (body.empty()) body = "{}"; \
INVOKE \
cb(http_response_code, fc::json::to_string(result)); \
} catch (fc::eof_exception& e) { \
error_results results{400, "Bad Request", e.to_string()}; \
cb(400, fc::json::to_string(results)); \
elog("Unable to parse arguments: ${args}", ("args", body)); \
} catch (fc::exception& e) { \
error_results results{500, "Internal Service Error", e.to_detail_string()}; \
cb(500, fc::json::to_string(results)); \
elog("Exception encountered while processing ${call}: ${e}", ("call", #api_name "." #call_name)("e", e)); \
} \
}}
#define INVOKE_V_R_R(api_handle, call_name, in_param0, in_param1) \
const auto& vs = fc::json::json::from_string(body).as<fc::variants>(); \
api_handle->call_name(vs.at(0).as<in_param0>(), vs.at(1).as<in_param1>()); \
eosio::detail::txn_test_gen_empty result;
#define INVOKE_V_V(api_handle, call_name) \
api_handle->call_name(); \
eosio::detail::txn_test_gen_empty result;
static std::vector<name> sort_names( std::vector<name>&& names ) {
std::sort( names.begin(), names.end() );
auto itr = std::unique( names.begin(), names.end() );
names.erase( itr, names.end() );
return names;
}
struct txn_test_gen_plugin_impl {
void create_test_accounts(const std::string& init_name, const std::string& init_priv_key) {
name newaccountA("txn.test.a");
name newaccountB("txn.test.b");
name creator(init_name);
chain_controller& cc = app().get_plugin<chain_plugin>().chain();
chain::chain_id_type chainid;
app().get_plugin<chain_plugin>().get_chain_id(chainid);
uint64_t stake = 10000;
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::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::private_key creator_priv_key = fc::crypto::private_key(init_priv_key);
signed_transaction trx;
auto memo = fc::variant(fc::time_point::now()).as_string() + " " + fc::variant(fc::time_point::now().time_since_epoch()).as_string();
//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}}, {}};
auto recovery_auth = eosio::chain::authority{1, {}, {{{creator, "active"}, 1}}};
trx.write_scope = sort_names({creator,config::system_account_name});
trx.actions.emplace_back(vector<chain::permission_level>{{creator,"active"}}, contracts::newaccount{creator, newaccountA, owner_auth, active_auth, recovery_auth, stake});
}
//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}}, {}};
auto recovery_auth = eosio::chain::authority{1, {}, {{{creator, "active"}, 1}}};
trx.write_scope = sort_names({creator,config::system_account_name});
trx.actions.emplace_back(vector<chain::permission_level>{{creator,"active"}}, contracts::newaccount{creator, newaccountB, owner_auth, active_auth, recovery_auth, stake});
}
trx.expiration = cc.head_block_time() + fc::seconds(30);
trx.set_reference_block(cc.head_block_id());
trx.sign(creator_priv_key, chainid);
cc.push_transaction(trx);
}
void fund_accounts(const std::string& fund_name, const std::string& fund_priv_key) {
name newaccountA("txn.test.a");
name newaccountB("txn.test.b");
name fundor(fund_name);
fc::crypto::private_key fundor_priv_key = fc::crypto::private_key(fund_priv_key);
chain_controller& cc = app().get_plugin<chain_plugin>().chain();
chain::chain_id_type chainid;
app().get_plugin<chain_plugin>().get_chain_id(chainid);
uint64_t balance = 10000;
auto memo = fc::variant(fc::time_point::now()).as_string() + " " + fc::variant(fc::time_point::now().time_since_epoch()).as_string();
signed_transaction trx;
trx.write_scope = sort_names({fundor,newaccountA,newaccountB});
trx.actions.emplace_back(vector<chain::permission_level>{{fundor,"active"}}, contracts::transfer{fundor, newaccountA, balance, memo});
trx.actions.emplace_back(vector<chain::permission_level>{{fundor,"active"}}, contracts::transfer{fundor, newaccountB, balance, memo});
trx.expiration = cc.head_block_time() + fc::seconds(30);
trx.set_reference_block(cc.head_block_id());
trx.sign(fundor_priv_key, chainid);
cc.push_transaction(trx);
}
void start_generation(const std::string& salt, const uint64_t& persecond) {
if(running)
throw fc::exception(fc::invalid_operation_exception_code);
running = true;
memo_salt = salt;
timer_timeout = 1000/boost::algorithm::clamp(persecond/2, 1, 200);
ilog("Started transaction test plugin; performing ${p} transactions/second", ("p", 1000/timer_timeout*2));
arm_timer(boost::asio::high_resolution_timer::clock_type::now());
}
void arm_timer(boost::asio::high_resolution_timer::time_point s) {
timer.expires_at(s + std::chrono::milliseconds(timer_timeout));
timer.async_wait([this](auto ec) {
if(ec)
return;
try {
send_transaction();
}
catch(fc::exception e) {
elog("pushing transaction failed: ${e}", ("e", e.to_detail_string()));
stop_generation();
return;
}
arm_timer(timer.expires_at());
});
}
void send_transaction() {
chain_controller& cc = app().get_plugin<chain_plugin>().chain();
chain::chain_id_type chainid;
app().get_plugin<chain_plugin>().get_chain_id(chainid);
name sender("txn.test.a");
name recipient("txn.test.b");
std::string memo = memo_salt + fc::variant(fc::time_point::now()).as_string() + " " + fc::variant(fc::time_point::now().time_since_epoch()).as_string();
//make transaction a->b
{
signed_transaction trx;
trx.write_scope = sort_names({sender,recipient});
trx.actions.emplace_back(vector<chain::permission_level>{{sender,"active"}}, contracts::transfer{sender, recipient, 1, memo});
trx.expiration = cc.head_block_time() + fc::seconds(30);
trx.set_reference_block(cc.head_block_id());
fc::crypto::private_key creator_priv_key = fc::crypto::private_key::regenerate(fc::sha256(std::string(64, 'a')));
trx.sign(creator_priv_key, chainid);
cc.push_transaction(trx);
}
//make transaction b->a
{
signed_transaction trx;
trx.write_scope = sort_names({sender,recipient});
trx.actions.emplace_back(vector<chain::permission_level>{{recipient,"active"}}, contracts::transfer{recipient, sender, 1, memo});
trx.expiration = cc.head_block_time() + fc::seconds(30);
trx.set_reference_block(cc.head_block_id());
fc::crypto::private_key b_priv_key = fc::crypto::private_key::regenerate(fc::sha256(std::string(64, 'b')));
trx.sign(b_priv_key, chainid);
cc.push_transaction(trx);
}
}
void stop_generation() {
if(!running)
throw fc::exception(fc::invalid_operation_exception_code);
timer.cancel();
running = false;
ilog("Stopping transaction generation test");
}
boost::asio::high_resolution_timer timer{app().get_io_service()};
bool running{false};
unsigned timer_timeout;
std::string memo_salt;
};
txn_test_gen_plugin::txn_test_gen_plugin() {}
txn_test_gen_plugin::~txn_test_gen_plugin() {}
void txn_test_gen_plugin::set_program_options(options_description&, options_description& cfg) {
}
void txn_test_gen_plugin::plugin_initialize(const variables_map& options) {
}
void txn_test_gen_plugin::plugin_startup() {
app().get_plugin<http_plugin>().add_api({
CALL(txn_test_gen, my, create_test_accounts, INVOKE_V_R_R(my, create_test_accounts, std::string, std::string), 200),
CALL(txn_test_gen, my, fund_accounts, INVOKE_V_R_R(my, fund_accounts, std::string, std::string), 200),
CALL(txn_test_gen, my, stop_generation, INVOKE_V_V(my, stop_generation), 200),
CALL(txn_test_gen, my, start_generation, INVOKE_V_R_R(my, start_generation, std::string, uint64_t), 200)
});
my.reset(new txn_test_gen_plugin_impl);
}
void txn_test_gen_plugin::plugin_shutdown() {
try {
my->stop_generation();
}
catch(fc::exception e) {
}
}
}
......@@ -33,7 +33,7 @@ target_link_libraries( eosiod
# PRIVATE account_history_api_plugin account_history_plugin db_plugin
PRIVATE chain_api_plugin producer_plugin chain_plugin
PRIVATE wallet_api_plugin
PRIVATE net_plugin net_api_plugin
PRIVATE net_plugin net_api_plugin txn_test_gen_plugin
PRIVATE http_plugin
PRIVATE eosio_chain fc ${CMAKE_DL_LIBS} ${PLATFORM_SPECIFIC_LIBS} )
......
......@@ -13,6 +13,7 @@
//#include <eosio/account_history_plugin/account_history_plugin.hpp>
//#include <eosio/account_history_api_plugin/account_history_api_plugin.hpp>
#include <eosio/wallet_api_plugin/wallet_api_plugin.hpp>
#include <eosio/txn_test_gen_plugin/txn_test_gen_plugin.hpp>
#include <fc/log/logger_config.hpp>
#include <fc/exception/exception.hpp>
......@@ -34,6 +35,7 @@ int main(int argc, char** argv)
// app().register_plugin<account_history_api_plugin>();
app().register_plugin<net_plugin>();
app().register_plugin<net_api_plugin>();
app().register_plugin<txn_test_gen_plugin>();
app().register_plugin<wallet_api_plugin>();
if(!app().initialize<chain_plugin, http_plugin>(argc, argv))
return -1;
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册