Wire Sysio Wire Sysion 1.0.0
Loading...
Searching...
No Matches
test_trx_full.cpp
Go to the documentation of this file.
1#define BOOST_TEST_MODULE full_producer_trxs
2#include <boost/test/included/unit_test.hpp>
3
5
7
11#include <sysio/chain/trace.hpp>
12#include <sysio/chain/name.hpp>
13
15
16namespace sysio::test::detail {
17using namespace sysio::chain::literals;
18struct testit {
20
21 testit( uint64_t id = 0 )
22 :id(id){}
23
25 return chain::config::system_account_name;
26 }
27
29 return "testit"_n;
30 }
31};
32}
34
35namespace {
36
37using namespace sysio;
38using namespace sysio::chain;
39using namespace sysio::test::detail;
40
41auto make_unique_trx( const chain_id_type& chain_id ) {
42
43 static uint64_t nextid = 0;
44 ++nextid;
45
46 account_name creator = config::system_account_name;
47
49 // if a transaction expires after it was aborted then it will not be included in a block
50 trx.expiration = fc::time_point::now() + fc::seconds( nextid % 20 == 0 ? 0 : 60 ); // fail some transactions via expired
51 if( nextid % 15 == 0 ) { // fail some for invalid unlinkauth
52 trx.actions.emplace_back( vector<permission_level>{{creator, config::active_name}},
53 unlinkauth{} );
54 trx.actions.emplace_back( vector<permission_level>{{creator, config::active_name}},
55 testit{nextid} );
56 } else {
57 trx.actions.emplace_back( vector<permission_level>{{creator, config::active_name}},
58 testit{ nextid % 7 == 0 ? 0 : nextid} ); // fail some for duplicates
59 }
60 if( nextid % 13 == 0 ) { // fail some for invalid signature
61 auto bad_priv_key = private_key_type::regenerate<fc::ecc::private_key_shim>(fc::sha256::hash(std::string("kevin")));
62 trx.sign( bad_priv_key, chain_id );
63 } else {
65 trx.sign( priv_key, chain_id );
66 }
67
68 return std::make_shared<packed_transaction>( std::move(trx) );
69}
70
71// verify all trxs are in blocks only once
72bool verify_equal( const std::deque<packed_transaction_ptr>& trxs, const std::deque<block_state_ptr>& all_blocks) {
73 std::set<transaction_id_type> trxs_ids; // trx can appear more than once if they were aborted
74 std::set<transaction_id_type> blk_trxs_ids;
75
76 for( const auto& trx : trxs ) {
77 trxs_ids.emplace( trx->id() );
78 }
79 for( const auto& bs : all_blocks ) {
80 for( const auto& trx_receipt : bs->block->transactions ) {
81 const auto& trx = std::get<packed_transaction>( trx_receipt.trx ).get_transaction();
82 blk_trxs_ids.emplace( trx.id() );
83 }
84 }
85 BOOST_CHECK_EQUAL( trxs_ids.size(), blk_trxs_ids.size() );
86
87 for( const auto& trx : trxs ) {
88 BOOST_CHECK( blk_trxs_ids.count( trx->id() ) == 1 );
89 }
90
91 return true;
92}
93
94
95}
96
97BOOST_AUTO_TEST_SUITE(ordered_trxs_full)
98
99// Integration test of producer_plugin
100// Test verifies that transactions are processed, reported to caller, and not lost
101// even when blocks are aborted and some transactions fail.
103 boost::filesystem::path temp = boost::filesystem::temp_directory_path() / boost::filesystem::unique_path();
104
105 try {
106 std::promise<std::tuple<producer_plugin*, chain_plugin*>> plugin_promise;
107 std::future<std::tuple<producer_plugin*, chain_plugin*>> plugin_fut = plugin_promise.get_future();
108 std::thread app_thread( [&]() {
110 std::vector<const char*> argv =
111 {"test", "--data-dir", temp.c_str(), "--config-dir", temp.c_str(),
112 "-p", "sysio", "-e", "--max-transaction-time", "475", "--disable-subjective-billing=true" };
113 appbase::app().initialize<chain_plugin, producer_plugin>( argv.size(), (char**) &argv[0] );
115 plugin_promise.set_value(
117 appbase::app().exec();
118 } );
119
120 auto[prod_plug, chain_plug] = plugin_fut.get();
121 auto chain_id = chain_plug->get_chain_id();
122
123 std::deque<block_state_ptr> all_blocks;
124 std::promise<void> empty_blocks_promise;
125 std::future<void> empty_blocks_fut = empty_blocks_promise.get_future();
126 auto ab = chain_plug->chain().accepted_block.connect( [&](const block_state_ptr& bsp) {
127 static int num_empty = std::numeric_limits<int>::max();
128 all_blocks.push_back( bsp );
129 if( bsp->block->transactions.empty() ) {
130 --num_empty;
131 if( num_empty == 0 ) empty_blocks_promise.set_value();
132 } else { // we want a few empty blocks after we have some non-empty blocks
133 num_empty = 10;
134 }
135 } );
136 auto bs = chain_plug->chain().block_start.connect( [&]( uint32_t bn ) {
137 } );
138
139 std::deque<packed_transaction_ptr> trxs;
140 std::atomic<size_t> next_calls = 0;
141 std::atomic<size_t> num_posts = 0;
142 std::atomic<size_t> trace_with_except = 0;
143 std::atomic<bool> trx_match = true;
144 const size_t num_pushes = 4242;
145 for( size_t i = 1; i <= num_pushes; ++i ) {
146 auto ptrx = make_unique_trx( chain_id );
147 dlog( "posting ${id}", ("id", ptrx->id()) );
148 app().post( priority::low, [ptrx, &next_calls, &num_posts, &trace_with_except, &trx_match, &trxs]() {
149 ++num_posts;
150 bool return_failure_traces = false; // 2.2.x+ = num_posts % 2 == 0;
152 false, // persist_until_expiried
153 false, // read_only
154 false, // return_failure_traces
155 [ptrx, &next_calls, &trace_with_except, &trx_match, &trxs, return_failure_traces]
156 (const std::variant<fc::exception_ptr, transaction_trace_ptr>& result) {
157 if( !std::holds_alternative<fc::exception_ptr>( result ) && !std::get<chain::transaction_trace_ptr>( result )->except ) {
158 if( std::get<chain::transaction_trace_ptr>( result )->id == ptrx->id() ) {
159 trxs.push_back( ptrx );
160 } else {
161 elog( "trace not for trx ${id}: ${t}",
162 ("id", ptrx->id())("t", fc::json::to_pretty_string(*std::get<chain::transaction_trace_ptr>(result))) );
163 trx_match = false;
164 }
165 } else if( !return_failure_traces && !std::holds_alternative<fc::exception_ptr>( result ) && std::get<chain::transaction_trace_ptr>( result )->except ) {
166 elog( "trace with except ${e}",
167 ("e", fc::json::to_pretty_string( *std::get<chain::transaction_trace_ptr>( result ) )) );
168 ++trace_with_except;
169 }
170 ++next_calls;
171 });
172 });
173 if( i % (num_pushes/3) == 0 ) {
174 // need to sleep or a fast machine might put all trxs in one block
175 usleep( config::block_interval_us / 2 );
176 }
177 if( i % 200 == 0 ) {
178 // get_integrity_hash aborts block and places aborted trxs into unapplied_transaction_queue
179 // verifying that aborting block does not lose transactions
180 app().post(priority::high, [](){
181 app().find_plugin<producer_plugin>()->get_integrity_hash();
182 });
183 }
184 }
185
186 empty_blocks_fut.wait_for(std::chrono::seconds(15));
187
188 BOOST_CHECK_EQUAL( trace_with_except, 0 ); // should not have any traces with except in it
189 BOOST_CHECK( all_blocks.size() > 3 ); // should have a few blocks otherwise test is running too fast
190 BOOST_CHECK_EQUAL( num_pushes, num_posts );
191 BOOST_CHECK_EQUAL( num_pushes, next_calls );
192 BOOST_CHECK( trx_match.load() );
193
194 appbase::app().quit();
195 app_thread.join();
196
197 BOOST_REQUIRE( verify_equal(trxs, all_blocks ) );
198
199 } catch ( ... ) {
200 bfs::remove_all( temp );
201 throw;
202 }
203 bfs::remove_all( temp );
204}
205
206
207BOOST_AUTO_TEST_SUITE_END()
abstract_plugin * find_plugin(const string &name) const
bool initialize(int argc, char **argv)
Looks for the –plugin commandline / config option and calls initialize on those plugins.
auto post(int priority, Func &&func)
auto get_method() -> std::enable_if_t< is_method_decl< MethodDecl >::value, typename MethodDecl::method_type & >
static private_key regenerate(const typename KeyType::data_type &data)
static string to_pretty_string(const variant &v, const yield_function_t &yield, const output_formatting format=output_formatting::stringify_large_ints_and_doubles)
Definition json.cpp:775
logger & set_log_level(log_level e)
Definition logger.cpp:100
static logger get(const fc::string &name=DEFAULT_LOGGER)
Definition logger.cpp:88
static sha256 hash(const char *d, uint32_t dlen)
Definition sha256.cpp:44
static time_point now()
Definition time.cpp:14
char ** argv
#define dlog(FORMAT,...)
Definition logger.hpp:101
#define DEFAULT_LOGGER
Definition logger.hpp:7
#define elog(FORMAT,...)
Definition logger.hpp:130
application & app()
Definition bn.h:56
constexpr microseconds seconds(int64_t s)
Definition time.hpp:32
sysio::chain::action_name action_name
std::shared_ptr< block_state > block_state_ptr
#define FC_REFLECT(TYPE, MEMBERS)
Specializes fc::reflector for TYPE.
Definition reflect.hpp:311
unsigned int uint32_t
Definition stdint.h:126
unsigned __int64 uint64_t
Definition stdint.h:136
static constexpr int high
Immutable except for fc::from_variant.
Definition name.hpp:43
const signature_type & sign(const private_key_type &key, const chain_id_type &chain_id)
time_point_sec expiration
the time at which a transaction expires
vector< action > actions
transaction_id_type id() const
static account_name get_account()
static action_name get_name()
BOOST_AUTO_TEST_CASE(producer)