From 5c3c8d9a8a98655ba6e4fb4c6d50df3a322beecd Mon Sep 17 00:00:00 2001 From: Roman Olearski Date: Mon, 14 Nov 2016 20:18:44 +0100 Subject: [PATCH 01/10] fixing problem : tournament never ends if "bye" matches present adding tournaments' prize_pool to total balances --- libraries/chain/tournament_object.cpp | 97 ++++++++++++++++++++++++++- tests/common/database_fixture.cpp | 7 ++ 2 files changed, 101 insertions(+), 3 deletions(-) diff --git a/libraries/chain/tournament_object.cpp b/libraries/chain/tournament_object.cpp index ef4252fb..bc7130c1 100644 --- a/libraries/chain/tournament_object.cpp +++ b/libraries/chain/tournament_object.cpp @@ -179,6 +179,35 @@ namespace graphene { namespace chain { event.db.modify(tournament_details_obj, [&](tournament_details_object& tournament_details_obj){ tournament_details_obj.matches = matches; }); + + // OLEK + for (unsigned i = 0; i < num_matches_in_first_round; ++i) + { + const match_object& match = matches[i](event.db); + if (match.players.size() == 1) // is bye + { + unsigned tournament_num_matches = tournament_details_obj.matches.size(); + unsigned next_round_match_index = (i + tournament_num_matches + 1) / 2; + assert(next_round_match_index < tournament_num_matches); + const match_object& next_round_match = tournament_details_obj.matches[next_round_match_index](event.db); + event.db.modify(next_round_match, [&](match_object& next_match) { + next_match.players.emplace_back(match.players[0]); + if (next_match.players.size() > 1) // bye + bye + next_match.on_initiate_match(event.db); + + }); + } + } + + // OLEK + wdump((tournament_details_obj.matches[tournament_details_obj.matches.size() - 1])); + + for( match_id_type mid : tournament_details_obj.matches ) + { + wdump((mid(event.db))); + } + + } void on_entry(const match_completed& event, tournament_state_machine_& fsm) { @@ -213,15 +242,40 @@ namespace graphene { namespace chain { assert(event.match.match_winners.size() <= 1); event.db.modify(next_round_match, [&](match_object& next_match_obj) { - if (!event.match.match_winners.empty()) // if there is a winner + + // OLEK + wdump((event.match.get_state())); + wdump((event.match)); + wdump((other_match.get_state())); + wdump((other_match)); + + if (!event.match.match_winners.empty()) // if there is a winner { if (winner_index_in_next_match == 0) next_match_obj.players.insert(next_match_obj.players.begin(), *event.match.match_winners.begin()); else next_match_obj.players.push_back(*event.match.match_winners.begin()); } - if (other_match.get_state() == match_state::match_complete) + + //if (other_match.get_state() == match_state::match_complete) + // OLEK + if (!other_match.match_winners.empty()) + { +// // if other match was buy +// if (other_match.games.size() == 0 /*&& next_match_obj.players.size() < 2*/) +// { +// if (winner_index_in_next_match != 0) +// next_match_obj.players.insert(next_match_obj.players.begin(), *other_match.match_winners.begin()); +// else +// next_match_obj.players.push_back(*other_match.match_winners.begin()); +// } + // OLEK + wdump((next_match_obj.get_state())); + wdump((next_match_obj)); + next_match_obj.on_initiate_match(event.db); + } + }); } }; @@ -296,9 +350,45 @@ namespace graphene { namespace chain { fc_ilog(fc::logger::get("tournament"), "In was_final_match guard, returning ${value}", ("value", event.match.id == tournament_details_obj.matches[tournament_details_obj.matches.size()])); + + // OLEK + wdump((event.match.id)); + wdump((tournament_details_obj.matches[tournament_details_obj.matches.size() - 1])); + + for( match_id_type mid : tournament_details_obj.matches ) + { + wdump((mid(event.db))); + } + return event.match.id == tournament_details_obj.matches[tournament_details_obj.matches.size() - 1]; } - + +#if 0 + // OLEK + bool was_buy_match(const match_completed& event) + { + const tournament_details_object& tournament_details_obj = tournament_obj->tournament_details_id(event.db); + fc_ilog(fc::logger::get("tournament"), + "In was_buy_match guard, returning ${value}", + ("value", event.match.id == tournament_details_obj.matches[tournament_details_obj.matches.size()])); + + // OLEK + wdump((event.match.id)); + wdump((event.match)); + + /* + wdump((tournament_details_obj.matches[tournament_details_obj.matches.size() - 1])); + + for( match_id_type mid : tournament_details_obj.matches ) + { + wdump((mid(event.db))); + } + + return event.match.id == tournament_details_obj.matches[tournament_details_obj.matches.size() - 1]; + */ + return true; + } +#endif void register_player(const player_registered& event) { fc_ilog(fc::logger::get("tournament"), @@ -326,6 +416,7 @@ namespace graphene { namespace chain { _row < awaiting_start, start_time_arrived, in_progress >, // +---------------------------+-----------------------------+----------------------------+---------------------+----------------------+ _row < in_progress, match_completed, in_progress >, + //g_row < in_progress, match_completed, in_progress, &x::was_buy_match >, g_row < in_progress, match_completed, concluded, &x::was_final_match > // +---------------------------+-----------------------------+----------------------------+---------------------+----------------------+ > {}; diff --git a/tests/common/database_fixture.cpp b/tests/common/database_fixture.cpp index 0f717ecd..1bd48bc6 100644 --- a/tests/common/database_fixture.cpp +++ b/tests/common/database_fixture.cpp @@ -36,6 +36,7 @@ #include #include #include +#include #include @@ -154,11 +155,17 @@ void database_fixture::verify_asset_supplies( const database& db ) const simple_index& statistics_index = db.get_index_type>(); const auto& balance_index = db.get_index_type().indices(); const auto& settle_index = db.get_index_type().indices(); + const auto& tournaments_index = db.get_index_type().indices(); + map total_balances; map total_debts; share_type core_in_orders; share_type reported_core_in_orders; + for( const tournament_object& t : tournaments_index ) + if (t.get_state() != tournament_state::concluded) + total_balances[t.options.buy_in.asset_id] += t.prize_pool; + for( const account_balance_object& b : balance_index ) total_balances[b.asset_type] += b.balance; for( const force_settlement_object& s : settle_index ) From 278c9b1683b78d39fadf11e73e74a1cf56202846 Mon Sep 17 00:00:00 2001 From: Roman Olearski Date: Fri, 25 Nov 2016 11:23:06 +0100 Subject: [PATCH 02/10] solution proposal for ...issues/#7 Allow vesting core tokens to vote and receive dividends --- libraries/chain/db_maint.cpp | 135 ++++++++++++++++-- .../graphene/chain/vesting_balance_object.hpp | 21 ++- 2 files changed, 142 insertions(+), 14 deletions(-) diff --git a/libraries/chain/db_maint.cpp b/libraries/chain/db_maint.cpp index 51f7041d..ecda3646 100644 --- a/libraries/chain/db_maint.cpp +++ b/libraries/chain/db_maint.cpp @@ -47,6 +47,8 @@ #include #include +#define USE_VESTING_OBJECT_BY_ASSET_BALANCE_INDEX // vesting_balance_object by_asset_balance index needed + namespace graphene { namespace chain { template @@ -727,14 +729,15 @@ void schedule_pending_dividend_balances(database& db, const asset_dividend_data_object& dividend_data, const fc::time_point_sec& current_head_block_time, const account_balance_index& balance_index, + const vesting_balance_index& vesting_index, const total_distributed_dividend_balance_object_index& distributed_dividend_balance_index, const pending_dividend_payout_balance_for_holder_object_index& pending_payout_balance_index) { - dlog("Processing dividend payments for dividend holder asset type ${holder_asset} at time ${t}", + dlog("Processing dividend payments for dividend holder asset type ${holder_asset} at time ${t}", ("holder_asset", dividend_holder_asset_obj.symbol)("t", db.head_block_time())); auto current_distribution_account_balance_range = balance_index.indices().get().equal_range(boost::make_tuple(dividend_data.dividend_distribution_account)); - auto previous_distribution_account_balance_range = + auto previous_distribution_account_balance_range = distributed_dividend_balance_index.indices().get().equal_range(boost::make_tuple(dividend_holder_asset_obj.id)); // the current range is now all current balances for the distribution account, sorted by asset_type // the previous range is now all previous balances for this account, sorted by asset type @@ -744,7 +747,7 @@ void schedule_pending_dividend_balances(database& db, // get the list of accounts that hold nonzero balances of the dividend asset auto holder_balances_begin = balance_index.indices().get().lower_bound(boost::make_tuple(dividend_holder_asset_obj.id)); - auto holder_balances_end = + auto holder_balances_end = balance_index.indices().get().upper_bound(boost::make_tuple(dividend_holder_asset_obj.id, share_type())); uint32_t holder_account_count = std::distance(holder_balances_begin, holder_balances_end); uint64_t distribution_base_fee = gpo.parameters.current_fees->get().distribution_base_fee; @@ -752,6 +755,35 @@ void schedule_pending_dividend_balances(database& db, // the fee, in BTS, for distributing each asset in the account uint64_t total_fee_per_asset_in_core = distribution_base_fee + holder_account_count * (uint64_t)distribution_fee_per_holder; + std::map vesting_amounts; +#ifdef USE_VESTING_OBJECT_BY_ASSET_BALANCE_INDEX + // get only once a collection of accounts that hold nonzero vesting balances of the dividend asset + auto vesting_balances_begin = + vesting_index.indices().get().lower_bound(boost::make_tuple(dividend_holder_asset_obj.id)); + auto vesting_balances_end = + vesting_index.indices().get().upper_bound(boost::make_tuple(dividend_holder_asset_obj.id, share_type())); + for (const vesting_balance_object& vesting_balance_obj : boost::make_iterator_range(vesting_balances_begin, vesting_balances_end)) + { + vesting_amounts[vesting_balance_obj.owner] += vesting_balance_obj.balance.amount; + dlog("Vesting balance for account: ${owner}, amount: ${amount}", + ("owner", vesting_balance_obj.owner(db).name) + ("amount", vesting_balance_obj.balance.amount)); + } +#else + // get only once a collection of accounts that hold nonzero vesting balances of the dividend asset + const auto& vesting_balances = vesting_index.indices().get(); + for (const vesting_balance_object& vesting_balance_obj : vesting_balances) + { + if (vesting_balance_obj.balance.asset_id == dividend_holder_asset_obj.id && vesting_balance_obj.balance.amount) + { + vesting_amounts[vesting_balance_obj.owner] += vesting_balance_obj.balance.amount; + dlog("Vesting balance for account: ${owner}, amount: ${amount}", + ("owner", vesting_balance_obj.owner(db).name) + ("amount", vesting_balance_obj.balance.amount)); + } + } +#endif + auto current_distribution_account_balance_iter = current_distribution_account_balance_range.first; auto previous_distribution_account_balance_iter = previous_distribution_account_balance_range.first; dlog("Current balances in distribution account: ${current}, Previous balances: ${previous}", @@ -764,9 +796,24 @@ void schedule_pending_dividend_balances(database& db, share_type total_balance_of_dividend_asset; for (const account_balance_object& holder_balance_object : boost::make_iterator_range(holder_balances_begin, holder_balances_end)) if (holder_balance_object.owner != dividend_data.dividend_distribution_account) + { total_balance_of_dividend_asset += holder_balance_object.balance; - - + auto itr = vesting_amounts.find(holder_balance_object.owner); + if (itr != vesting_amounts.end()) + total_balance_of_dividend_asset += itr->second; +// // working, but potential performance gap? +// auto vesting_range = vesting_index.indices().get().equal_range(holder_balance_object.owner); +// for (const vesting_balance_object& vesting_balance : boost::make_iterator_range(vesting_range.first, vesting_range.second) +// { +// if (vesting_balance.balance.asset_id == dividend_holder_asset_obj.id) +// { +// total_balance_of_dividend_asset += vesting_balance.balance.amount; +// dlog("Vesting balances for account: ${owner}, amount: ${amount}", +// ("owner", vesting_balance.owner(db).name) +// ("amount", vesting_balance.balance.amount)); +// } +// } + } // loop through all of the assets currently or previously held in the distribution account while (current_distribution_account_balance_iter != current_distribution_account_balance_range.second || previous_distribution_account_balance_iter != previous_distribution_account_balance_range.second) @@ -892,13 +939,33 @@ void schedule_pending_dividend_balances(database& db, // credit each account with their portion, don't send any back to the dividend distribution account for (const account_balance_object& holder_balance_object : boost::make_iterator_range(holder_balances_begin, holder_balances_end)) - if (holder_balance_object.owner != dividend_data.dividend_distribution_account && - holder_balance_object.balance.value) + { + //if (holder_balance_object.owner != dividend_data.dividend_distribution_account && holder_balance_object.balance.value) + if (holder_balance_object.owner == dividend_data.dividend_distribution_account) continue; + + auto holder_balance = holder_balance_object.balance; + + auto itr = vesting_amounts.find(holder_balance_object.owner); + if (itr != vesting_amounts.end()) + holder_balance += itr->second; +// // working, but potential performance gap? +// auto vesting_range = vesting_index.indices().get().equal_range(holder_balance_object.owner); +// for (const vesting_balance_object& vesting_balance : boost::make_iterator_range(vesting_range.first, vesting_range.second)) +// { +// if (vesting_balance.balance.asset_id == dividend_holder_asset_obj.id) +// { +// holder_balance += vesting_balance.balance.amount; +// dlog("Vesting balances for account: ${owner}, amount: ${amount}", +// ("owner", vesting_balance.owner(db).name) +// ("amount", vesting_balance.balance.amount)); +// } +// } + if (holder_balance.value) { - fc::uint128_t amount_to_credit(delta_balance.value); - amount_to_credit *= holder_balance_object.balance.value; + fc::uint128_t amount_to_credit(delta_balance.value); + amount_to_credit *= holder_balance.value; amount_to_credit /= total_balance_of_dividend_asset.value; - wdump((delta_balance.value)(holder_balance_object.balance)(total_balance_of_dividend_asset)); + wdump((delta_balance.value)(holder_balance)(total_balance_of_dividend_asset)); share_type shares_to_credit((int64_t)amount_to_credit.to_uint64()); remaining_amount_to_distribute -= shares_to_credit; @@ -920,6 +987,7 @@ void schedule_pending_dividend_balances(database& db, pending_balance.pending_balance += shares_to_credit; }); } + } for (const auto& pending_payout : pending_payout_balance_index.indices()) dlog("Pending payout: ${account_name} -> ${amount}", @@ -928,7 +996,6 @@ void schedule_pending_dividend_balances(database& db, dlog("Remaining balance not paid out: ${amount}", ("amount", asset(remaining_amount_to_distribute, payout_asset_type))); - share_type distributed_amount = delta_balance - remaining_amount_to_distribute; if (previous_distribution_account_balance_iter == previous_distribution_account_balance_range.second || previous_distribution_account_balance_iter->dividend_payout_asset_type != payout_asset_type) @@ -1017,6 +1084,7 @@ void process_dividend_assets(database& db) ilog("In process_dividend_assets time ${time}", ("time", db.head_block_time())); const account_balance_index& balance_index = db.get_index_type(); + const vesting_balance_index& vbalance_index = db.get_index_type(); const total_distributed_dividend_balance_object_index& distributed_dividend_balance_index = db.get_index_type(); const pending_dividend_payout_balance_for_holder_object_index& pending_payout_balance_index = db.get_index_type(); @@ -1030,7 +1098,7 @@ void process_dividend_assets(database& db) fc::time_point_sec current_head_block_time = db.head_block_time(); schedule_pending_dividend_balances(db, dividend_holder_asset_obj, dividend_data, current_head_block_time, - balance_index, distributed_dividend_balance_index, pending_payout_balance_index); + balance_index, vbalance_index, distributed_dividend_balance_index, pending_payout_balance_index); if (dividend_data.options.next_payout_time && db.head_block_time() >= *dividend_data.options.next_payout_time) { @@ -1180,6 +1248,7 @@ void database::perform_chain_maintenance(const signed_block& next_block, const g struct vote_tally_helper { database& d; const global_property_object& props; + std::map vesting_amounts; vote_tally_helper(database& d, const global_property_object& gpo) : d(d), props(gpo) @@ -1188,6 +1257,33 @@ void database::perform_chain_maintenance(const signed_block& next_block, const g d._witness_count_histogram_buffer.resize(props.parameters.maximum_witness_count / 2 + 1); d._committee_count_histogram_buffer.resize(props.parameters.maximum_committee_count / 2 + 1); d._total_voting_stake = 0; + + const vesting_balance_index& vesting_index = d.get_index_type(); +#ifdef USE_VESTING_OBJECT_BY_ASSET_BALANCE_INDEX + auto vesting_balances_begin = + vesting_index.indices().get().lower_bound(boost::make_tuple(asset_id_type())); + auto vesting_balances_end = + vesting_index.indices().get().upper_bound(boost::make_tuple(asset_id_type(), share_type())); + for (const vesting_balance_object& vesting_balance_obj : boost::make_iterator_range(vesting_balances_begin, vesting_balances_end)) + { + vesting_amounts[vesting_balance_obj.owner] += vesting_balance_obj.balance.amount; + dlog("Vesting balance for account: ${owner}, amount: ${amount}", + ("owner", vesting_balance_obj.owner(d).name) + ("amount", vesting_balance_obj.balance.amount)); + } +#else + const auto& vesting_balances = vesting_index.indices().get(); + for (const vesting_balance_object& vesting_balance_obj : vesting_balances) + { + if (vesting_balance_obj.balance.asset_id == asset_id_type() && vesting_balance_obj.balance.amount) + { + vesting_amounts[vesting_balance_obj.owner] += vesting_balance_obj.balance.amount; + dlog("Vesting balance for account: ${owner}, amount: ${amount}", + ("owner", vesting_balance_obj.owner(d).name) + ("amount", vesting_balance_obj.balance.amount)); + } + } +#endif } void operator()(const account_object& stake_account) { @@ -1206,6 +1302,21 @@ void database::perform_chain_maintenance(const signed_block& next_block, const g + (stake_account.cashback_vb.valid() ? (*stake_account.cashback_vb)(d).balance.amount.value: 0) + d.get_balance(stake_account.get_id(), asset_id_type()).amount.value; + auto itr = vesting_amounts.find(stake_account.id); + if (itr != vesting_amounts.end()) + voting_stake += itr->second.value; +// // working, but potential performance gap? +// auto vesting_range = d.get_index_type().indices().get().equal_range(stake_account.id); +// for (const vesting_balance_object& vesting_balance : boost::make_iterator_range(vesting_range.first, vesting_range.second)) +// { +// if (vesting_balance.balance.asset_id == asset_id_type()) +// { +// voting_stake += vesting_balance.balance.amount.value; +// dlog("Vote_tally_helper vesting balances for account: ${owner}, amount: ${amount}", +// ("owner", vesting_balance.owner(d).name) +// ("amount", vesting_balance.balance.amount)); +// } +// } for( vote_id_type id : opinion_account.options.votes ) { uint32_t offset = id.instance(); diff --git a/libraries/chain/include/graphene/chain/vesting_balance_object.hpp b/libraries/chain/include/graphene/chain/vesting_balance_object.hpp index 210c6c58..49b3464c 100644 --- a/libraries/chain/include/graphene/chain/vesting_balance_object.hpp +++ b/libraries/chain/include/graphene/chain/vesting_balance_object.hpp @@ -31,8 +31,10 @@ #include #include +#include - +#define offset_d(i,f) (long(&(i)->f) - long(i)) +#define offset_s(t,f) offset_d((t*)1000, f) namespace graphene { namespace chain { using namespace graphene::db; @@ -171,13 +173,28 @@ namespace graphene { namespace chain { * @ingroup object_index */ struct by_account; + struct by_asset_balance; typedef multi_index_container< vesting_balance_object, indexed_by< ordered_unique< tag, member< object, object_id_type, &object::id > >, ordered_non_unique< tag, member - > + >, + ordered_unique< tag, + composite_key< + vesting_balance_object, + member_offset, + member_offset + //member + //member_offset + >, + composite_key_compare< + std::less< asset_id_type >, + std::greater< share_type > + //std::less< account_id_type > + > + > > > vesting_balance_multi_index_type; /** From 787019617f3258ed5c02d5ab065dd8b32f1c29ec Mon Sep 17 00:00:00 2001 From: Roman Olearski Date: Mon, 28 Nov 2016 12:36:18 +0100 Subject: [PATCH 03/10] Summary commiting changes made since the last commit modified: libraries/app/impacted.cpp modified: libraries/chain/db_init.cpp modified: libraries/chain/db_maint.cpp modified: libraries/chain/game_object.cpp modified: libraries/chain/include/graphene/chain/protocol/operations.hpp modified: libraries/chain/include/graphene/chain/protocol/tournament.hpp modified: libraries/chain/protocol/tournament.cpp modified: libraries/chain/tournament_object.cpp modified: libraries/wallet/wallet.cpp modified: programs/witness_node/main.cpp modified: tests/CMakeLists.txt modified: tests/common/database_fixture.cpp new file: tests/tournament/tournament_tests.cpp --- libraries/app/impacted.cpp | 5 +- libraries/chain/db_init.cpp | 14 +- libraries/chain/db_maint.cpp | 96 +- libraries/chain/game_object.cpp | 3 +- .../graphene/chain/protocol/operations.hpp | 3 +- .../graphene/chain/protocol/tournament.hpp | 46 + libraries/chain/protocol/tournament.cpp | 1 - libraries/chain/tournament_object.cpp | 141 +-- libraries/wallet/wallet.cpp | 13 + programs/witness_node/main.cpp | 2 +- tests/CMakeLists.txt | 4 + tests/common/database_fixture.cpp | 3 +- tests/tournament/tournament_tests.cpp | 1062 +++++++++++++++++ 13 files changed, 1231 insertions(+), 162 deletions(-) create mode 100644 tests/tournament/tournament_tests.cpp diff --git a/libraries/app/impacted.cpp b/libraries/app/impacted.cpp index 212ff67e..3974da10 100644 --- a/libraries/app/impacted.cpp +++ b/libraries/app/impacted.cpp @@ -222,7 +222,10 @@ struct get_impacted_account_visitor { _impacted.insert( op.player_account_id ); } - + void operator()( const tournament_payout_operation& op ) + { + _impacted.insert( op.payout_account_id ); + } }; void operation_get_impacted_accounts( const operation& op, flat_set& result ) diff --git a/libraries/chain/db_init.cpp b/libraries/chain/db_init.cpp index 77a3e9fd..ebd84db9 100644 --- a/libraries/chain/db_init.cpp +++ b/libraries/chain/db_init.cpp @@ -371,12 +371,12 @@ void database::init_genesis(const genesis_state_type& genesis_state) a.options.payout_interval = 7*24*60*60; a.dividend_distribution_account = TOURNAMENT_RAKE_FEE_ACCOUNT_ID; }); - const asset_bitasset_data_object& bit_asset = - create([&](asset_bitasset_data_object& a) { - a.current_feed.maintenance_collateral_ratio = 1750; - a.current_feed.maximum_short_squeeze_ratio = 1500; - a.current_feed_publication_time = genesis_state.initial_timestamp + fc::hours(1); - }); +// const asset_bitasset_data_object& bit_asset = +// create([&](asset_bitasset_data_object& a) { +// a.current_feed.maintenance_collateral_ratio = 1750; +// a.current_feed.maximum_short_squeeze_ratio = 1500; +// a.current_feed_publication_time = genesis_state.initial_timestamp + fc::hours(1); +// }); const asset_object& core_asset = create( [&]( asset_object& a ) { @@ -392,7 +392,7 @@ void database::init_genesis(const genesis_state_type& genesis_state) a.options.core_exchange_rate.quote.asset_id = asset_id_type(0); a.dynamic_asset_data_id = dyn_asset.id; a.dividend_data_id = div_asset.id; - a.bitasset_data_id = bit_asset.id; +// a.bitasset_data_id = bit_asset.id; }); assert( asset_id_type(core_asset.id) == asset().asset_id ); assert( get_balance(account_id_type(), asset_id_type()) == asset(dyn_asset.current_supply) ); diff --git a/libraries/chain/db_maint.cpp b/libraries/chain/db_maint.cpp index ecda3646..6d733e70 100644 --- a/libraries/chain/db_maint.cpp +++ b/libraries/chain/db_maint.cpp @@ -47,8 +47,6 @@ #include #include -#define USE_VESTING_OBJECT_BY_ASSET_BALANCE_INDEX // vesting_balance_object by_asset_balance index needed - namespace graphene { namespace chain { template @@ -756,7 +754,6 @@ void schedule_pending_dividend_balances(database& db, uint64_t total_fee_per_asset_in_core = distribution_base_fee + holder_account_count * (uint64_t)distribution_fee_per_holder; std::map vesting_amounts; -#ifdef USE_VESTING_OBJECT_BY_ASSET_BALANCE_INDEX // get only once a collection of accounts that hold nonzero vesting balances of the dividend asset auto vesting_balances_begin = vesting_index.indices().get().lower_bound(boost::make_tuple(dividend_holder_asset_obj.id)); @@ -769,20 +766,6 @@ void schedule_pending_dividend_balances(database& db, ("owner", vesting_balance_obj.owner(db).name) ("amount", vesting_balance_obj.balance.amount)); } -#else - // get only once a collection of accounts that hold nonzero vesting balances of the dividend asset - const auto& vesting_balances = vesting_index.indices().get(); - for (const vesting_balance_object& vesting_balance_obj : vesting_balances) - { - if (vesting_balance_obj.balance.asset_id == dividend_holder_asset_obj.id && vesting_balance_obj.balance.amount) - { - vesting_amounts[vesting_balance_obj.owner] += vesting_balance_obj.balance.amount; - dlog("Vesting balance for account: ${owner}, amount: ${amount}", - ("owner", vesting_balance_obj.owner(db).name) - ("amount", vesting_balance_obj.balance.amount)); - } - } -#endif auto current_distribution_account_balance_iter = current_distribution_account_balance_range.first; auto previous_distribution_account_balance_iter = previous_distribution_account_balance_range.first; @@ -801,18 +784,6 @@ void schedule_pending_dividend_balances(database& db, auto itr = vesting_amounts.find(holder_balance_object.owner); if (itr != vesting_amounts.end()) total_balance_of_dividend_asset += itr->second; -// // working, but potential performance gap? -// auto vesting_range = vesting_index.indices().get().equal_range(holder_balance_object.owner); -// for (const vesting_balance_object& vesting_balance : boost::make_iterator_range(vesting_range.first, vesting_range.second) -// { -// if (vesting_balance.balance.asset_id == dividend_holder_asset_obj.id) -// { -// total_balance_of_dividend_asset += vesting_balance.balance.amount; -// dlog("Vesting balances for account: ${owner}, amount: ${amount}", -// ("owner", vesting_balance.owner(db).name) -// ("amount", vesting_balance.balance.amount)); -// } -// } } // loop through all of the assets currently or previously held in the distribution account while (current_distribution_account_balance_iter != current_distribution_account_balance_range.second || @@ -940,7 +911,6 @@ void schedule_pending_dividend_balances(database& db, // credit each account with their portion, don't send any back to the dividend distribution account for (const account_balance_object& holder_balance_object : boost::make_iterator_range(holder_balances_begin, holder_balances_end)) { - //if (holder_balance_object.owner != dividend_data.dividend_distribution_account && holder_balance_object.balance.value) if (holder_balance_object.owner == dividend_data.dividend_distribution_account) continue; auto holder_balance = holder_balance_object.balance; @@ -948,25 +918,14 @@ void schedule_pending_dividend_balances(database& db, auto itr = vesting_amounts.find(holder_balance_object.owner); if (itr != vesting_amounts.end()) holder_balance += itr->second; -// // working, but potential performance gap? -// auto vesting_range = vesting_index.indices().get().equal_range(holder_balance_object.owner); -// for (const vesting_balance_object& vesting_balance : boost::make_iterator_range(vesting_range.first, vesting_range.second)) -// { -// if (vesting_balance.balance.asset_id == dividend_holder_asset_obj.id) -// { -// holder_balance += vesting_balance.balance.amount; -// dlog("Vesting balances for account: ${owner}, amount: ${amount}", -// ("owner", vesting_balance.owner(db).name) -// ("amount", vesting_balance.balance.amount)); -// } -// } - if (holder_balance.value) + + fc::uint128_t amount_to_credit(delta_balance.value); + amount_to_credit *= holder_balance.value; + amount_to_credit /= total_balance_of_dividend_asset.value; + share_type shares_to_credit((int64_t)amount_to_credit.to_uint64()); + if (shares_to_credit.value) { - fc::uint128_t amount_to_credit(delta_balance.value); - amount_to_credit *= holder_balance.value; - amount_to_credit /= total_balance_of_dividend_asset.value; wdump((delta_balance.value)(holder_balance)(total_balance_of_dividend_asset)); - share_type shares_to_credit((int64_t)amount_to_credit.to_uint64()); remaining_amount_to_distribute -= shares_to_credit; @@ -990,9 +949,10 @@ void schedule_pending_dividend_balances(database& db, } for (const auto& pending_payout : pending_payout_balance_index.indices()) - dlog("Pending payout: ${account_name} -> ${amount}", - ("account_name", pending_payout.owner(db).name) - ("amount", asset(pending_payout.pending_balance, pending_payout.dividend_payout_asset_type))); + if (pending_payout.pending_balance.value) + dlog("Pending payout: ${account_name} -> ${amount}", + ("account_name", pending_payout.owner(db).name) + ("amount", asset(pending_payout.pending_balance, pending_payout.dividend_payout_asset_type))); dlog("Remaining balance not paid out: ${amount}", ("amount", asset(remaining_amount_to_distribute, payout_asset_type))); @@ -1144,7 +1104,7 @@ void process_dividend_assets(database& db) { const pending_dividend_payout_balance_for_holder_object& pending_balance_object = *pending_balance_object_iter; - if (last_holder_account_id && *last_holder_account_id != pending_balance_object.owner) + if (last_holder_account_id && *last_holder_account_id != pending_balance_object.owner && payouts_for_this_holder.size()) { // we've moved on to a new account, generate the dividend payment virtual op for the previous one db.push_applied_operation(asset_dividend_distribution_operation(dividend_holder_asset_obj.id, @@ -1156,14 +1116,15 @@ void process_dividend_assets(database& db) } - if (is_authorized_asset(db, pending_balance_object.owner(db), pending_balance_object.dividend_payout_asset_type(db)) && + if (pending_balance_object.pending_balance.value && + is_authorized_asset(db, pending_balance_object.owner(db), pending_balance_object.dividend_payout_asset_type(db)) && is_asset_approved_for_distribution_account(pending_balance_object.dividend_payout_asset_type)) { dlog("Processing payout of ${asset} to account ${account}", ("asset", asset(pending_balance_object.pending_balance, pending_balance_object.dividend_payout_asset_type)) ("account", pending_balance_object.owner(db).name)); - db.adjust_balance(pending_balance_object.owner, + db.adjust_balance(pending_balance_object.owner, asset(pending_balance_object.pending_balance, pending_balance_object.dividend_payout_asset_type)); payouts_for_this_holder.insert(asset(pending_balance_object.pending_balance, @@ -1179,7 +1140,7 @@ void process_dividend_assets(database& db) ++pending_balance_object_iter; } // we will always be left with the last holder's data, generate the virtual op for it now. - if (last_holder_account_id) + if (last_holder_account_id && payouts_for_this_holder.size()) { // we've moved on to a new account, generate the dividend payment virtual op for the previous one db.push_applied_operation(asset_dividend_distribution_operation(dividend_holder_asset_obj.id, @@ -1259,7 +1220,6 @@ void database::perform_chain_maintenance(const signed_block& next_block, const g d._total_voting_stake = 0; const vesting_balance_index& vesting_index = d.get_index_type(); -#ifdef USE_VESTING_OBJECT_BY_ASSET_BALANCE_INDEX auto vesting_balances_begin = vesting_index.indices().get().lower_bound(boost::make_tuple(asset_id_type())); auto vesting_balances_end = @@ -1271,19 +1231,6 @@ void database::perform_chain_maintenance(const signed_block& next_block, const g ("owner", vesting_balance_obj.owner(d).name) ("amount", vesting_balance_obj.balance.amount)); } -#else - const auto& vesting_balances = vesting_index.indices().get(); - for (const vesting_balance_object& vesting_balance_obj : vesting_balances) - { - if (vesting_balance_obj.balance.asset_id == asset_id_type() && vesting_balance_obj.balance.amount) - { - vesting_amounts[vesting_balance_obj.owner] += vesting_balance_obj.balance.amount; - dlog("Vesting balance for account: ${owner}, amount: ${amount}", - ("owner", vesting_balance_obj.owner(d).name) - ("amount", vesting_balance_obj.balance.amount)); - } - } -#endif } void operator()(const account_object& stake_account) { @@ -1305,18 +1252,7 @@ void database::perform_chain_maintenance(const signed_block& next_block, const g auto itr = vesting_amounts.find(stake_account.id); if (itr != vesting_amounts.end()) voting_stake += itr->second.value; -// // working, but potential performance gap? -// auto vesting_range = d.get_index_type().indices().get().equal_range(stake_account.id); -// for (const vesting_balance_object& vesting_balance : boost::make_iterator_range(vesting_range.first, vesting_range.second)) -// { -// if (vesting_balance.balance.asset_id == asset_id_type()) -// { -// voting_stake += vesting_balance.balance.amount.value; -// dlog("Vote_tally_helper vesting balances for account: ${owner}, amount: ${amount}", -// ("owner", vesting_balance.owner(d).name) -// ("amount", vesting_balance.balance.amount)); -// } -// } + for( vote_id_type id : opinion_account.options.votes ) { uint32_t offset = id.instance(); diff --git a/libraries/chain/game_object.cpp b/libraries/chain/game_object.cpp index bc205f20..2bbcdd92 100644 --- a/libraries/chain/game_object.cpp +++ b/libraries/chain/game_object.cpp @@ -470,7 +470,8 @@ namespace graphene { namespace chain { { // we now know who played what, figure out if we have a winner const rock_paper_scissors_game_details& rps_game_details = game_details.get(); - if (rps_game_details.reveal_moves[0]->gesture == rps_game_details.reveal_moves[1]->gesture) + if (rps_game_details.reveal_moves[0] && rps_game_details.reveal_moves[1] && + rps_game_details.reveal_moves[0]->gesture == rps_game_details.reveal_moves[1]->gesture) ilog("The game was a tie, both players threw ${gesture}", ("gesture", rps_game_details.reveal_moves[0]->gesture)); else { diff --git a/libraries/chain/include/graphene/chain/protocol/operations.hpp b/libraries/chain/include/graphene/chain/protocol/operations.hpp index 6e8cd61b..f96f8658 100644 --- a/libraries/chain/include/graphene/chain/protocol/operations.hpp +++ b/libraries/chain/include/graphene/chain/protocol/operations.hpp @@ -97,7 +97,8 @@ namespace graphene { namespace chain { tournament_join_operation, game_move_operation, asset_update_dividend_operation, - asset_dividend_distribution_operation // VIRTUAL + asset_dividend_distribution_operation, // VIRTUAL + tournament_payout_operation // VIRTUAL > operation; /// @} // operations group diff --git a/libraries/chain/include/graphene/chain/protocol/tournament.hpp b/libraries/chain/include/graphene/chain/protocol/tournament.hpp index 6c5299d4..b9a71523 100644 --- a/libraries/chain/include/graphene/chain/protocol/tournament.hpp +++ b/libraries/chain/include/graphene/chain/protocol/tournament.hpp @@ -36,6 +36,13 @@ namespace graphene { namespace chain { + enum class payout_type + { + prize_award, + buyin_refund, + rake_fee + }; + typedef fc::static_variant game_specific_options; /** @@ -163,8 +170,38 @@ namespace graphene { namespace chain { void validate()const; }; + struct tournament_payout_operation : public base_operation + { + struct fee_parameters_type {}; + + asset fee; + + /// The account received payout + account_id_type payout_account_id; + + /// The tournament generated payout + tournament_id_type tournament_id; + + /// The payout amount + asset payout_amount; + + payout_type type; + + extensions_type extensions; + + account_id_type fee_payer()const { return payout_account_id; } + share_type calculate_fee(const fee_parameters_type&)const { return 0; } + void validate()const {} + }; + } } +FC_REFLECT_ENUM(graphene::chain::payout_type, + (prize_award) + (buyin_refund) + (rake_fee) + ) + FC_REFLECT_TYPENAME( graphene::chain::game_specific_options ) FC_REFLECT_TYPENAME( graphene::chain::game_specific_moves ) FC_REFLECT( graphene::chain::tournament_options, @@ -196,7 +233,16 @@ FC_REFLECT( graphene::chain::game_move_operation, (player_account_id) (move) (extensions)) +FC_REFLECT( graphene::chain::tournament_payout_operation, + (fee) + (payout_account_id) + (tournament_id) + (payout_amount) + (type) + (extensions)) + FC_REFLECT( graphene::chain::tournament_create_operation::fee_parameters_type, (fee) ) FC_REFLECT( graphene::chain::tournament_join_operation::fee_parameters_type, (fee) ) FC_REFLECT( graphene::chain::game_move_operation::fee_parameters_type, (fee) ) +FC_REFLECT( graphene::chain::tournament_payout_operation::fee_parameters_type, ) diff --git a/libraries/chain/protocol/tournament.cpp b/libraries/chain/protocol/tournament.cpp index f7faa93b..167072c5 100644 --- a/libraries/chain/protocol/tournament.cpp +++ b/libraries/chain/protocol/tournament.cpp @@ -61,5 +61,4 @@ void game_move_operation::validate()const { } - } } // namespace graphene::chain diff --git a/libraries/chain/tournament_object.cpp b/libraries/chain/tournament_object.cpp index bc7130c1..0ef5b364 100644 --- a/libraries/chain/tournament_object.cpp +++ b/libraries/chain/tournament_object.cpp @@ -180,11 +180,11 @@ namespace graphene { namespace chain { tournament_details_obj.matches = matches; }); - // OLEK + // find "bye" matches, complete missing player in the next match for (unsigned i = 0; i < num_matches_in_first_round; ++i) { const match_object& match = matches[i](event.db); - if (match.players.size() == 1) // is bye + if (match.players.size() == 1) // is "bye" { unsigned tournament_num_matches = tournament_details_obj.matches.size(); unsigned next_round_match_index = (i + tournament_num_matches + 1) / 2; @@ -192,21 +192,19 @@ namespace graphene { namespace chain { const match_object& next_round_match = tournament_details_obj.matches[next_round_match_index](event.db); event.db.modify(next_round_match, [&](match_object& next_match) { next_match.players.emplace_back(match.players[0]); - if (next_match.players.size() > 1) // bye + bye + if (next_match.players.size() > 1) // both previous matches were "bye" next_match.on_initiate_match(event.db); }); } } - // OLEK +#ifdef HELPFULL_DUMP_WHEN_SOLVING_BYE_MATCH_PROBLEM + wlog("###"); wdump((tournament_details_obj.matches[tournament_details_obj.matches.size() - 1])); - for( match_id_type mid : tournament_details_obj.matches ) - { wdump((mid(event.db))); - } - +#endif } void on_entry(const match_completed& event, tournament_state_machine_& fsm) @@ -243,13 +241,13 @@ namespace graphene { namespace chain { event.db.modify(next_round_match, [&](match_object& next_match_obj) { - // OLEK +#ifdef HELPFULL_DUMP_WHEN_SOLVING_BYE_MATCH_PROBLEM wdump((event.match.get_state())); wdump((event.match)); wdump((other_match.get_state())); wdump((other_match)); - - if (!event.match.match_winners.empty()) // if there is a winner +#endif + if (!event.match.match_winners.empty()) // if there is a winner { if (winner_index_in_next_match == 0) next_match_obj.players.insert(next_match_obj.players.begin(), *event.match.match_winners.begin()); @@ -257,22 +255,12 @@ namespace graphene { namespace chain { next_match_obj.players.push_back(*event.match.match_winners.begin()); } - //if (other_match.get_state() == match_state::match_complete) - // OLEK - if (!other_match.match_winners.empty()) + if (other_match.get_state() == match_state::match_complete) { -// // if other match was buy -// if (other_match.games.size() == 0 /*&& next_match_obj.players.size() < 2*/) -// { -// if (winner_index_in_next_match != 0) -// next_match_obj.players.insert(next_match_obj.players.begin(), *other_match.match_winners.begin()); -// else -// next_match_obj.players.push_back(*other_match.match_winners.begin()); -// } - // OLEK +#ifdef HELPFULL_DUMP_WHEN_SOLVING_BYE_MATCH_PROBLEM wdump((next_match_obj.get_state())); wdump((next_match_obj)); - +#endif next_match_obj.on_initiate_match(event.db); } @@ -296,7 +284,16 @@ namespace graphene { namespace chain { // for a period of time, not as a transfer back to the user; it doesn't matter // if they are currently authorized to transfer this asset, they never really // transferred it in the first place - event.db.adjust_balance(payer_pair.first, asset(payer_pair.second, fsm.tournament_obj->options.buy_in.asset_id)); + asset amount(payer_pair.second, fsm.tournament_obj->options.buy_in.asset_id); + event.db.adjust_balance(payer_pair.first, amount); + + // Generating a virtual operation that shows the payment + tournament_payout_operation op; + op.tournament_id = fsm.tournament_obj->id; + op.payout_amount = amount; + op.payout_account_id = payer_pair.first; + op.type = payout_type::buyin_refund; + event.db.push_applied_operation(op); } } }; @@ -305,29 +302,65 @@ namespace graphene { namespace chain { { void on_entry(const match_completed& event, tournament_state_machine_& fsm) { + tournament_object& tournament_obj = *fsm.tournament_obj; fc_ilog(fc::logger::get("tournament"), "Tournament ${id} is complete", - ("id", fsm.tournament_obj->id)); + ("id", tournament_obj.id)); + tournament_obj.end_time = event.db.head_block_time(); // Distribute prize money when a tournament ends #ifndef NDEBUG - const tournament_details_object& details = fsm.tournament_obj->tournament_details_id(event.db); + const tournament_details_object& details = tournament_obj.tournament_details_id(event.db); share_type total_prize = 0; for (const auto& payer_pair : details.payers) { total_prize += payer_pair.second; } - assert(total_prize == fsm.tournament_obj->prize_pool); + assert(total_prize == tournament_obj.prize_pool); #endif assert(event.match.match_winners.size() == 1); const account_id_type& winner = *event.match.match_winners.begin(); uint16_t rake_fee_percentage = event.db.get_global_properties().parameters.rake_fee_percentage; - share_type rake_amount = (fc::uint128_t(fsm.tournament_obj->prize_pool.value) * rake_fee_percentage / GRAPHENE_1_PERCENT / 100).to_uint64(); - event.db.adjust_balance(account_id_type(TOURNAMENT_RAKE_FEE_ACCOUNT_ID), - asset(rake_amount, fsm.tournament_obj->options.buy_in.asset_id)); - event.db.adjust_balance(winner, asset(fsm.tournament_obj->prize_pool - rake_amount, - fsm.tournament_obj->options.buy_in.asset_id)); - fsm.tournament_obj->end_time = event.db.head_block_time(); + share_type rake_amount = 0; + + const asset_object & asset_obj = tournament_obj.options.buy_in.asset_id(event.db); + optional dividend_id = asset_obj.dividend_data_id; + + if (dividend_id.valid()) + { + rake_amount = (fc::uint128_t(tournament_obj.prize_pool.value) * rake_fee_percentage / GRAPHENE_1_PERCENT / 100).to_uint64(); + } + asset won_prize(tournament_obj.prize_pool - rake_amount, tournament_obj.options.buy_in.asset_id); + tournament_payout_operation op; + + if (won_prize.amount.value) + { + // Adjusting balance of winner + event.db.adjust_balance(winner, won_prize); + + // Generating a virtual operation that shows the payment + op.tournament_id = tournament_obj.id; + op.payout_amount = won_prize; + op.payout_account_id = winner; + op.type = payout_type::prize_award; + event.db.push_applied_operation(op); + } + + if (dividend_id.valid() && rake_amount.value) + { + // Adjusting balance of dividend_distribution_account + const asset_dividend_data_id_type& asset_dividend_data_id_= *dividend_id; + const asset_dividend_data_object& dividend_obj = asset_dividend_data_id_(event.db); + const account_id_type& rake_account_id = dividend_obj.dividend_distribution_account; + asset rake(rake_amount, tournament_obj.options.buy_in.asset_id); + event.db.adjust_balance(rake_account_id, rake); + + // Generating a virtual operation that shows the payment + op.payout_amount = rake; + op.payout_account_id = rake_account_id; + op.type = payout_type::rake_fee; + event.db.push_applied_operation(op); + } } }; @@ -350,45 +383,16 @@ namespace graphene { namespace chain { fc_ilog(fc::logger::get("tournament"), "In was_final_match guard, returning ${value}", ("value", event.match.id == tournament_details_obj.matches[tournament_details_obj.matches.size()])); - - // OLEK +#ifdef HELPFULL_DUMP_WHEN_SOLVING_BYE_MATCH_PROBLEM + wlog("###"); wdump((event.match.id)); wdump((tournament_details_obj.matches[tournament_details_obj.matches.size() - 1])); - for( match_id_type mid : tournament_details_obj.matches ) - { wdump((mid(event.db))); - } - - return event.match.id == tournament_details_obj.matches[tournament_details_obj.matches.size() - 1]; - } - -#if 0 - // OLEK - bool was_buy_match(const match_completed& event) - { - const tournament_details_object& tournament_details_obj = tournament_obj->tournament_details_id(event.db); - fc_ilog(fc::logger::get("tournament"), - "In was_buy_match guard, returning ${value}", - ("value", event.match.id == tournament_details_obj.matches[tournament_details_obj.matches.size()])); - - // OLEK - wdump((event.match.id)); - wdump((event.match)); - - /* - wdump((tournament_details_obj.matches[tournament_details_obj.matches.size() - 1])); - - for( match_id_type mid : tournament_details_obj.matches ) - { - wdump((mid(event.db))); - } - - return event.match.id == tournament_details_obj.matches[tournament_details_obj.matches.size() - 1]; - */ - return true; - } #endif + return event.match.id == tournament_details_obj.matches[tournament_details_obj.matches.size() - 1]; + } + void register_player(const player_registered& event) { fc_ilog(fc::logger::get("tournament"), @@ -416,7 +420,6 @@ namespace graphene { namespace chain { _row < awaiting_start, start_time_arrived, in_progress >, // +---------------------------+-----------------------------+----------------------------+---------------------+----------------------+ _row < in_progress, match_completed, in_progress >, - //g_row < in_progress, match_completed, in_progress, &x::was_buy_match >, g_row < in_progress, match_completed, concluded, &x::was_final_match > // +---------------------------+-----------------------------+----------------------------+---------------------+----------------------+ > {}; diff --git a/libraries/wallet/wallet.cpp b/libraries/wallet/wallet.cpp index c31beed2..c0caf4fa 100644 --- a/libraries/wallet/wallet.cpp +++ b/libraries/wallet/wallet.cpp @@ -134,6 +134,7 @@ public: std::string operator()(const account_update_operation& op)const; std::string operator()(const asset_create_operation& op)const; std::string operator()(const asset_dividend_distribution_operation& op)const; + std::string operator()(const tournament_payout_operation& op)const; }; template @@ -3205,6 +3206,18 @@ std::string operation_printer::operator()(const asset_dividend_distribution_oper return ""; } +std::string operation_printer::operator()(const tournament_payout_operation& op)const +{ + asset_object payout_asset = wallet.get_asset(op.payout_amount.asset_id); + + out << "Tournament #" << std::string(object_id_type(op.tournament_id)) << " Payout : " + << "Account '" << wallet.get_account(op.payout_account_id).name + << "', Amount " << payout_asset.amount_to_pretty_string(op.payout_amount) << ", Type " + << (op.type == payout_type::buyin_refund ? "buyin refund" : (op.type == payout_type::rake_fee ? "rake fee" : "prize award")) + << "."; + return ""; +} + std::string operation_result_printer::operator()(const void_result& x) const { return ""; diff --git a/programs/witness_node/main.cpp b/programs/witness_node/main.cpp index f1f971b7..0995899f 100644 --- a/programs/witness_node/main.cpp +++ b/programs/witness_node/main.cpp @@ -75,7 +75,7 @@ int main(int argc, char** argv) { auto witness_plug = node->register_plugin(); auto history_plug = node->register_plugin(); auto market_history_plug = node->register_plugin(); - auto generate_genesis_plug = node->register_plugin(); + //auto generate_genesis_plug = node->register_plugin(); try { diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index b03d58a8..c1eced4e 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -17,6 +17,10 @@ file(GLOB PERFORMANCE_TESTS "performance/*.cpp") add_executable( performance_test ${PERFORMANCE_TESTS} ${COMMON_SOURCES} ) target_link_libraries( performance_test graphene_chain graphene_app graphene_account_history graphene_egenesis_none fc ${PLATFORM_SPECIFIC_LIBS} ) +file(GLOB TOURNAMENT_TESTS "tournament/*.cpp") +add_executable( tournament_test ${TOURNAMENT_TESTS} ${COMMON_SOURCES} ) +target_link_libraries( tournament_test graphene_chain graphene_app graphene_account_history graphene_egenesis_none fc ${PLATFORM_SPECIFIC_LIBS} ) + file(GLOB BENCH_MARKS "benchmarks/*.cpp") add_executable( chain_bench ${BENCH_MARKS} ${COMMON_SOURCES} ) target_link_libraries( chain_bench graphene_chain graphene_app graphene_account_history graphene_time graphene_egenesis_none fc ${PLATFORM_SPECIFIC_LIBS} ) diff --git a/tests/common/database_fixture.cpp b/tests/common/database_fixture.cpp index 1bd48bc6..f4ae577b 100644 --- a/tests/common/database_fixture.cpp +++ b/tests/common/database_fixture.cpp @@ -72,6 +72,7 @@ database_fixture::database_fixture() if( arg == "--show-test-names" ) std::cout << "running test " << boost::unit_test::framework::current_test_case().p_name << std::endl; } + auto ahplugin = app.register_plugin(); auto mhplugin = app.register_plugin(); init_account_pub_key = init_account_priv_key.get_public_key(); @@ -163,7 +164,7 @@ void database_fixture::verify_asset_supplies( const database& db ) share_type reported_core_in_orders; for( const tournament_object& t : tournaments_index ) - if (t.get_state() != tournament_state::concluded) + if (t.get_state() != tournament_state::concluded && t.get_state() != tournament_state::registration_period_expired) total_balances[t.options.buy_in.asset_id] += t.prize_pool; for( const account_balance_object& b : balance_index ) diff --git a/tests/tournament/tournament_tests.cpp b/tests/tournament/tournament_tests.cpp new file mode 100644 index 00000000..63f17999 --- /dev/null +++ b/tests/tournament/tournament_tests.cpp @@ -0,0 +1,1062 @@ +/* + * Copyright (c) 2015 Cryptonomex, Inc., and contributors. + * + * The MIT License + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#include +#include +#include + + +#include +#include +#include +#include "../common/database_fixture.hpp" +#include + +using namespace graphene::chain; + +// defined if "bye" matches fix available +#define BYE_MATCHES_FIXED + +#define RAND_MAX_MIN(MAX, MIN) (std::rand() % ((MAX) - (MIN) + 1) + (MIN)) + +BOOST_AUTO_TEST_SUITE(tournament_tests) + +// class performing operations necessary for creating tournaments, +// having players join the tournaments and playing tournaments to completion. +class tournaments_helper +{ +public: + + tournaments_helper(database_fixture& df) : df(df) + { + assets.insert(asset_id_type()); + current_asset_idx = 0; + optional dividend_account = get_asset_dividend_account(asset_id_type()); + if (dividend_account.valid()) + players.insert(*dividend_account); + } + + const std::set& list_tournaments() + { + return tournaments; + } + + std::map> list_players_balances() + { + std::map> result; + for (account_id_type player_id: players) + { + for( asset_id_type asset_id: assets) + { + asset a = df.db.get_balance(player_id, asset_id); + result[player_id][a.asset_id] = a.amount; + } + } + return result; + } + + std::map> get_players_fees() + { + return players_fees; + } + + void reset_players_fees() + { + for (account_id_type player_id: players) + { + for( asset_id_type asset_id: assets) + { + players_fees[player_id][asset_id] = 0; + } + } + } + + void create_asset(const account_id_type& issuer_account_id, + const string& symbol, + uint8_t precision, + asset_options& common, + const fc::ecc::private_key& sig_priv_key) + { + graphene::chain::database& db = df.db; + const chain_parameters& params = db.get_global_properties().parameters; + signed_transaction tx; + asset_create_operation op; + op.issuer = issuer_account_id; + op.symbol = symbol; + op.precision = precision; + op.common_options = common; + + tx.operations = {op}; + for( auto& op : tx.operations ) + db.current_fee_schedule().set_fee(op); + tx.validate(); + tx.set_expiration(db.head_block_time() + fc::seconds( params.block_interval * (params.maintenance_skip_slots + 1) * 3)); + df.sign(tx, sig_priv_key); + PUSH_TX(db, tx); + + assets.insert(asset_id_type(++current_asset_idx)); + } + + void update_dividend_asset(const asset_id_type asset_to_update_id, + dividend_asset_options new_options, + const fc::ecc::private_key& sig_priv_key) + { + graphene::chain::database& db = df.db; + const chain_parameters& params = db.get_global_properties().parameters; + signed_transaction tx; + asset_update_dividend_operation update_op; + + update_op.issuer = asset_to_update_id(db).issuer; + update_op.asset_to_update = asset_to_update_id; + update_op.new_options = new_options; + + tx.operations = {update_op}; + for( auto& op : tx.operations ) + db.current_fee_schedule().set_fee(op); + tx.validate(); + tx.set_expiration(db.head_block_time() + fc::seconds( params.block_interval * (params.maintenance_skip_slots + 1) * 3)); + df.sign(tx, sig_priv_key); + PUSH_TX(db, tx); + + optional dividend_account = get_asset_dividend_account(asset_to_update_id); + if (dividend_account.valid()) + players.insert(*dividend_account); + } + + optional get_asset_dividend_account(const asset_id_type& asset_id) + { + graphene::chain::database& db = df.db; + optional result; + const asset_object& asset_obj = asset_id(db); + + if (asset_obj.dividend_data_id.valid()) + { + const asset_dividend_data_object& dividend_data = (*asset_obj.dividend_data_id)(db); + result = dividend_data.dividend_distribution_account; + } + return result; + } + + const tournament_id_type create_tournament (const account_id_type& creator, + const fc::ecc::private_key& sig_priv_key, + asset buy_in, + uint32_t number_of_players = 2, + uint32_t time_per_commit_move = 3, + uint32_t time_per_reveal_move = 1, + uint32_t number_of_wins = 3, + uint32_t registration_deadline = 3600, + uint32_t start_delay = 3, + uint32_t round_delay = 3, + bool insurance_enabled = false + ) + { + if (current_tournament_idx.valid()) + current_tournament_idx = *current_tournament_idx + 1; + else + current_tournament_idx = 0; + + graphene::chain::database& db = df.db; + const chain_parameters& params = db.get_global_properties().parameters; + signed_transaction trx; + tournament_options options; + tournament_create_operation op; + rock_paper_scissors_game_options& game_options = options.game_options.get(); + + game_options.number_of_gestures = 3; + game_options.time_per_commit_move = time_per_commit_move; + game_options.time_per_reveal_move = time_per_reveal_move; + game_options.insurance_enabled = insurance_enabled; + + options.registration_deadline = db.head_block_time() + fc::seconds(registration_deadline + *current_tournament_idx); + options.buy_in = buy_in; + options.number_of_players = number_of_players; + options.start_delay = start_delay; + options.round_delay = round_delay; + options.number_of_wins = number_of_wins; + + op.creator = creator; + op.options = options; + trx.operations = {op}; + for( auto& op : trx.operations ) + db.current_fee_schedule().set_fee(op); + trx.validate(); + trx.set_expiration(db.head_block_time() + fc::seconds( params.block_interval * (params.maintenance_skip_slots + 1) * 3)); + df.sign(trx, sig_priv_key); + PUSH_TX(db, trx); + + tournament_id_type tournament_id = tournament_id_type(*current_tournament_idx); + tournaments.insert(tournament_id); + return tournament_id; + } + + void join_tournament(const tournament_id_type & tournament_id, + const account_id_type& player_id, + const account_id_type& payer_id, + const fc::ecc::private_key& sig_priv_key, + asset buy_in + ) + { + graphene::chain::database& db = df.db; + const chain_parameters& params = db.get_global_properties().parameters; + signed_transaction tx; + tournament_join_operation op; + + op.payer_account_id = payer_id; + op.buy_in = buy_in; + op.player_account_id = player_id; + op.tournament_id = tournament_id; + tx.operations = {op}; + for( auto& op : tx.operations ) + db.current_fee_schedule().set_fee(op); + tx.validate(); + tx.set_expiration(db.head_block_time() + fc::seconds( params.block_interval * (params.maintenance_skip_slots + 1) * 3)); + df.sign(tx, sig_priv_key); + PUSH_TX(db, tx); + + players.insert(player_id); + players_keys[player_id] = sig_priv_key; + } + + // stolen from cli_wallet + void rps_throw(const game_id_type& game_id, + const account_id_type& player_id, + rock_paper_scissors_gesture gesture, + const fc::ecc::private_key& sig_priv_key + ) + { + + graphene::chain::database& db = df.db; + const chain_parameters& params = db.get_global_properties().parameters; + + // check whether the gesture is appropriate for the game we're playing + game_object game_obj = game_id(db); + match_object match_obj = game_obj.match_id(db); + tournament_object tournament_obj = match_obj.tournament_id(db); + rock_paper_scissors_game_options game_options = tournament_obj.options.game_options.get(); + assert((int)gesture < game_options.number_of_gestures); + + account_object player_account_obj = player_id(db); + + // construct the complete throw, the commit, and reveal + rock_paper_scissors_throw full_throw; + rand_bytes((char*)&full_throw.nonce1, sizeof(full_throw.nonce1)); + rand_bytes((char*)&full_throw.nonce2, sizeof(full_throw.nonce2)); + full_throw.gesture = gesture; + + rock_paper_scissors_throw_commit commit_throw; + commit_throw.nonce1 = full_throw.nonce1; + std::vector full_throw_packed(fc::raw::pack(full_throw)); + commit_throw.throw_hash = fc::sha256::hash(full_throw_packed.data(), full_throw_packed.size()); + + rock_paper_scissors_throw_reveal reveal_throw; + reveal_throw.nonce2 = full_throw.nonce2; + reveal_throw.gesture = full_throw.gesture; + + // store off the reveal for applying after both players commit + committed_game_moves[commit_throw] = reveal_throw; + + signed_transaction tx; + game_move_operation move_operation; + move_operation.game_id = game_id; + move_operation.player_account_id = player_account_obj.id; + move_operation.move = commit_throw; + tx.operations = {move_operation}; + for( operation& op : tx.operations ) + { + asset f = db.current_fee_schedule().set_fee(op); + players_fees[player_id][f.asset_id] -= f.amount; + } + tx.validate(); + tx.set_expiration(db.head_block_time() + fc::seconds( params.block_interval * (params.maintenance_skip_slots + 1) * 3)); + df.sign(tx, sig_priv_key); + PUSH_TX(db, tx); + } + + // spaghetti programming + // walking through all tournaments, matches and games and throwing random moves + void play_games() + { + graphene::chain::database& db = df.db; + const chain_parameters& params = db.get_global_properties().parameters; + + for(const auto& tournament_id: tournaments) + { + const tournament_object& tournament = tournament_id(db); + const tournament_details_object& tournament_details = tournament.tournament_details_id(db); + rock_paper_scissors_game_options game_options = tournament.options.game_options.get(); + for(const auto& match_id: tournament_details.matches) + { + const match_object& match = match_id(db); + for(const auto& game_id: match.games ) + { + const game_object& game = game_id(db); + if (game.get_state() == game_state::expecting_commit_moves) + { + for(const auto& player_id: game.players) + { + if (players_keys.find(player_id) != players_keys.end()) + { + rps_throw(game_id, player_id, (rock_paper_scissors_gesture) (std::rand() % game_options.number_of_gestures), players_keys[player_id]); + } + } + } + else if (game.get_state() == game_state::expecting_reveal_moves) + { + const rock_paper_scissors_game_details& rps_details = game.game_details.get(); + + for (unsigned i = 0; i < 2; ++i) + { + if (rps_details.commit_moves.at(i) && + !rps_details.reveal_moves.at(i)) + { + const account_id_type& player_id = game.players[i]; + if (players_keys.find(player_id) != players_keys.end()) + { + { + auto iter = committed_game_moves.find(*rps_details.commit_moves.at(i)); + if (iter != committed_game_moves.end()) + { + const rock_paper_scissors_throw_reveal& reveal = iter->second; + + game_move_operation move_operation; + move_operation.game_id = game.id; + move_operation.player_account_id = player_id; + move_operation.move = reveal; + + signed_transaction tx; + tx.operations = {move_operation}; + for( auto& op : tx.operations ) + { + asset f = db.current_fee_schedule().set_fee(op); + players_fees[player_id][f.asset_id] -= f.amount; + } + tx.validate(); + tx.set_expiration(db.head_block_time() + fc::seconds( params.block_interval * (params.maintenance_skip_slots + 1) * 3)); + df.sign(tx, players_keys[player_id]); + PUSH_TX(db, tx); + } + } + } + } + } + } + } + } + } + } + +private: + database_fixture& df; + // index of last created tournament + fc::optional current_tournament_idx; + // index of last asset + uint64_t current_asset_idx; + // assets : core and maybe others + std::set assets; + // tournaments to be played + std::set tournaments; + // all players registered in tournaments + std::set players; + // players' private keys + std::map players_keys; + // total charges for moves made by every player + std::map> players_fees; + // store of commits and reveals + std::map committed_game_moves; + + // taken from rand.cpp + void rand_bytes(char* buf, int count) + { + fc::init_openssl(); + + int result = RAND_bytes((unsigned char*)buf, count); + if (result != 1) + FC_THROW("Error calling OpenSSL's RAND_bytes(): ${code}", ("code", (uint32_t)ERR_get_error())); + } +}; + +// Test of basic functionality creating two tournamenst, joinig players, +// playing tournaments to completion, distributing prize. +// Testing of "bye" matches handling can be performed if "bye" matches fix is available. +// Numbers of players 2+1 4+1 8+1 ... seem to be most critical for handling of "bye" matches. +// Moves are generated automatically. +BOOST_FIXTURE_TEST_CASE( simple, database_fixture ) +{ + try + { +#ifdef BYE_MATCHES_FIXED + #define TEST1_NR_OF_PLAYERS_NUMBER 3 + #define TEST2_NR_OF_PLAYERS_NUMBER 5 +#else + #define TEST1_NR_OF_PLAYERS_NUMBER 2 + #define TEST2_NR_OF_PLAYERS_NUMBER 4 +#endif + BOOST_TEST_MESSAGE("Hello simple tournament test"); + ACTORS((nathan)(alice)(bob)(carol)(dave)(ed)(frank)(george)(harry)(ike)); + + tournaments_helper tournament_helper(*this); + fc::ecc::private_key nathan_priv_key = fc::ecc::private_key::regenerate(fc::sha256::hash(string("nathan"))); + + BOOST_TEST_MESSAGE( "Giving folks some money" ); + transfer(committee_account, nathan_id, asset(1000000000)); + transfer(committee_account, alice_id, asset(2000000)); + transfer(committee_account, bob_id, asset(3000000)); + transfer(committee_account, carol_id, asset(4000000)); + transfer(committee_account, dave_id, asset(5000000)); +#if TEST2_NR_OF_PLAYERS_NUMBER > 4 + transfer(committee_account, ed_id, asset(6000000)); +#endif +#if TEST2_NR_OF_PLAYERS_NUMBER > 5 + transfer(committee_account, frank_id, asset(7000000)); +#endif +#if TEST2_NR_OF_PLAYERS_NUMBER > 6 + transfer(committee_account, george_id, asset(8000000)); +#endif +#if TEST2_NR_OF_PLAYERS_NUMBER > 7 + transfer(committee_account, harry_id, asset(9000000)); +#endif +#if TEST2_NR_OF_PLAYERS_NUMBER > 8 + transfer(committee_account, ike_id, asset(1000000)); +#endif + + BOOST_TEST_MESSAGE( "Preparing nathan" ); + upgrade_to_lifetime_member(nathan); + BOOST_CHECK(nathan.is_lifetime_member()); + + uint16_t tournaments_to_complete = 0; + asset buy_in = asset(12000); + tournament_id_type tournament_id; + + BOOST_TEST_MESSAGE( "Preparing a tournament" ); + tournament_id = tournament_helper.create_tournament (nathan_id, nathan_priv_key, buy_in, TEST1_NR_OF_PLAYERS_NUMBER); + BOOST_REQUIRE(tournament_id == tournament_id_type()); + + tournament_helper.join_tournament(tournament_id, alice_id, alice_id, fc::ecc::private_key::regenerate(fc::sha256::hash(string("alice"))), buy_in); + tournament_helper.join_tournament(tournament_id, bob_id, bob_id, fc::ecc::private_key::regenerate(fc::sha256::hash(string("bob"))), buy_in); +#if TEST1_NR_OF_PLAYERS_NUMBER > 2 + tournament_helper.join_tournament(tournament_id, carol_id, carol_id, fc::ecc::private_key::regenerate(fc::sha256::hash(string("carol"))), buy_in); +#endif + ++tournaments_to_complete; + + BOOST_TEST_MESSAGE( "Preparing another one" ); + buy_in = asset(13000); + tournament_id = tournament_helper.create_tournament (nathan_id, nathan_priv_key, buy_in, TEST2_NR_OF_PLAYERS_NUMBER); + BOOST_REQUIRE(tournament_id == tournament_id_type(1)); + tournament_helper.join_tournament(tournament_id, alice_id, alice_id, fc::ecc::private_key::regenerate(fc::sha256::hash(string("alice"))), buy_in); + tournament_helper.join_tournament(tournament_id, bob_id, bob_id, fc::ecc::private_key::regenerate(fc::sha256::hash(string("bob"))), buy_in); + tournament_helper.join_tournament(tournament_id, carol_id, carol_id, fc::ecc::private_key::regenerate(fc::sha256::hash(string("carol"))), buy_in); + tournament_helper.join_tournament(tournament_id, dave_id, dave_id, fc::ecc::private_key::regenerate(fc::sha256::hash(string("dave"))), buy_in); +#if TEST2_NR_OF_PLAYERS_NUMBER > 4 + tournament_helper.join_tournament(tournament_id, ed_id, ed_id, fc::ecc::private_key::regenerate(fc::sha256::hash(string("ed"))), buy_in); +#endif +#if TEST2_NR_OF_PLAYERS_NUMBER > 5 + tournament_helper.join_tournament(tournament_id, frank_id, frank_id, fc::ecc::private_key::regenerate(fc::sha256::hash(string("frank"))), buy_in); +#endif +#if TEST2_NR_OF_PLAYERS_NUMBER > 6 + tournament_helper.join_tournament(tournament_id, george_id, george_id, fc::ecc::private_key::regenerate(fc::sha256::hash(string("george"))), buy_in); +#endif +#if TEST2_NR_OF_PLAYERS_NUMBER > 7 + tournament_helper.join_tournament(tournament_id, harry_id, harry_id, fc::ecc::private_key::regenerate(fc::sha256::hash(string("harry"))), buy_in); +#endif +#if TEST2_NR_OF_PLAYERS_NUMBER > 8 + tournament_helper.join_tournament(tournament_id, ike_id, ike_id, fc::ecc::private_key::regenerate(fc::sha256::hash(string("ike"))), buy_in); +#endif + ++tournaments_to_complete; + + auto abc = [&] (string s) + { +#if 0 + wlog(s); + auto a = db.get_balance(alice_id, asset_id_type()); wdump(("# alice's balance") (a)); + auto b = db.get_balance(bob_id, asset_id_type()); wdump(("# bob's balance") (b)); + auto c = db.get_balance(carol_id, asset_id_type()); wdump(("# carol's balance") (c)); + auto d = db.get_balance(dave_id, asset_id_type()); wdump(("# dave's balance") (d)); +#if TEST2_NR_OF_PLAYERS_NUMBER > 4 + auto e = db.get_balance(ed_id, asset_id_type()); wdump(("# ed's balance") (e)); +#endif +#if TEST2_NR_OF_PLAYERS_NUMBER > 5 + auto f = db.get_balance(frank_id, asset_id_type()); wdump(("# frank's balance") (f)); +#endif +#if TEST2_NR_OF_PLAYERS_NUMBER > 6 + auto g = db.get_balance(george_id, asset_id_type()); wdump(("# george's balance") (g)); +#endif +#if TEST2_NR_OF_PLAYERS_NUMBER > 7 + auto h = db.get_balance(harry_id, asset_id_type()); wdump(("# harry's balance") (f)); +#endif +#if TEST2_NR_OF_PLAYERS_NUMBER > 8 + auto i = db.get_balance(ike_id, asset_id_type()); wdump(("# ike's balance") (i)); +#endif + + auto n = db.get_balance(nathan_id, asset_id_type()); wdump(("# nathan's balance") (n)); + auto r = db.get_balance(TOURNAMENT_RAKE_FEE_ACCOUNT_ID, asset_id_type()); wdump(("# rake's balance") (r)); +#endif + }; + +#if 0 + // trying to randomize automatic moves ? + auto n = std::rand() % 100; + for(int i = 0; i < n ; ++i) + db.get_random_bits(3); +#endif + abc("@ tournament awaiting start"); + BOOST_TEST_MESSAGE( "Generating blocks, waiting for tournaments' completion"); + generate_block(); + abc("@ after first generated block"); + std::set tournaments = tournament_helper.list_tournaments(); + std::map> players_balances = tournament_helper.list_players_balances(); + uint16_t rake_fee_percentage = db.get_global_properties().parameters.rake_fee_percentage; + + while(tournaments_to_complete > 0) + { + for(const auto& tournament_id: tournaments) + { + const tournament_object& tournament = tournament_id(db); + if (tournament.get_state() == tournament_state::concluded) { + const tournament_details_object& tournament_details = tournament.tournament_details_id(db); + const match_object& final_match = (tournament_details.matches[tournament_details.matches.size() - 1])(db); + + assert(final_match.match_winners.size() == 1); + const account_id_type& winner_id = *final_match.match_winners.begin(); + + const account_object winner = winner_id(db); + BOOST_TEST_MESSAGE( "The winner is " + winner.name ); + + share_type rake_amount = (fc::uint128_t(tournament.prize_pool.value) * rake_fee_percentage / GRAPHENE_1_PERCENT / 100).to_uint64(); + optional dividend_account = tournament_helper.get_asset_dividend_account(tournament.options.buy_in.asset_id); + if (dividend_account.valid()) + players_balances[*dividend_account][tournament.options.buy_in.asset_id] += rake_amount; + players_balances[winner_id][tournament.options.buy_in.asset_id] += tournament.prize_pool - rake_amount; + + tournaments.erase(tournament_id); + --tournaments_to_complete; + break; + } + } + generate_block(); + sleep(1); + } + + abc("@ tournament concluded"); + // checking if prizes were distributed correctly + BOOST_CHECK(tournaments.size() == 0); + std::map> last_players_balances = tournament_helper.list_players_balances(); + for (auto a: last_players_balances) + { + BOOST_TEST_MESSAGE( "Checking " + a.first(db).name + "'s balance " + std::to_string((uint64_t)(a.second[asset_id_type()].value)) ); + BOOST_CHECK(a.second[asset_id_type()] == players_balances[a.first][asset_id_type()]); + } + BOOST_TEST_MESSAGE("Bye simple tournament test\n"); + + } + catch (fc::exception& e) + { + edump((e.to_detail_string())); + throw; + } +} + +// Test of canceled tournament +// Checking buyin refund. +BOOST_FIXTURE_TEST_CASE( canceled, database_fixture ) +{ + try + { + BOOST_TEST_MESSAGE("Hello canceled tournament test"); + ACTORS((nathan)(alice)(bob)); + + tournaments_helper tournament_helper(*this); + fc::ecc::private_key nathan_priv_key = fc::ecc::private_key::regenerate(fc::sha256::hash(string("nathan"))); + + BOOST_TEST_MESSAGE( "Giving folks some money" ); + transfer(committee_account, nathan_id, asset(1000000000)); + transfer(committee_account, alice_id, asset(2000000)); + transfer(committee_account, bob_id, asset(3000000)); + + BOOST_TEST_MESSAGE( "Preparing nathan" ); + upgrade_to_lifetime_member(nathan); + + uint16_t tournaments_to_complete = 0; + asset buy_in = asset(12340); + tournament_id_type tournament_id; + + BOOST_TEST_MESSAGE( "Preparing a tournament" ); + tournament_id = tournament_helper.create_tournament (nathan_id, nathan_priv_key, buy_in, 3, 30, 30, 3, 5); + BOOST_REQUIRE(tournament_id == tournament_id_type()); + + tournament_helper.join_tournament(tournament_id, alice_id, alice_id, fc::ecc::private_key::regenerate(fc::sha256::hash(string("alice"))), buy_in); + tournament_helper.join_tournament(tournament_id, bob_id, bob_id, fc::ecc::private_key::regenerate(fc::sha256::hash(string("bob"))), buy_in); + ++tournaments_to_complete; + BOOST_TEST_MESSAGE( "Generating blocks, waiting for tournament's completion"); + std::set tournaments = tournament_helper.list_tournaments(); + std::map> players_balances = tournament_helper.list_players_balances(); + + while(tournaments_to_complete > 0) + { + for(const auto& tournament_id: tournaments) + { + const tournament_object& tournament = tournament_id(db); + BOOST_REQUIRE(tournament.get_state() != tournament_state::concluded); + + if (tournament.get_state() == tournament_state::registration_period_expired) { + + + const tournament_details_object& tournament_details = tournament.tournament_details_id(db); + for(auto payer : tournament_details.payers) + { + players_balances[payer.first][tournament.options.buy_in.asset_id] += payer.second; + } + + tournaments.erase(tournament_id); + --tournaments_to_complete; + break; + } + } + generate_block(); + sleep(1); + } + + // checking if buyins were refunded correctly + BOOST_CHECK(tournaments.size() == 0); + std::map> last_players_balances = tournament_helper.list_players_balances(); + for (auto a: last_players_balances) + { + BOOST_TEST_MESSAGE( "Checking " + a.first(db).name + "'s balance " + std::to_string((uint64_t)(a.second[asset_id_type()].value)) ); + BOOST_CHECK(a.second[asset_id_type()] == players_balances[a.first][asset_id_type()]); + } + BOOST_TEST_MESSAGE("Bye canceled tournament test\n"); + } + catch (fc::exception& e) + { + edump((e.to_detail_string())); + throw; + } +} + +// Test of few concurrently played tournaments having the same constant number of players. +// Tournament/s having even number use the core asset. +// Tournament/s having odd number use another asset. +// Moves are generated randomly. +// Checking prizes distribution for both assets is performed. +BOOST_FIXTURE_TEST_CASE( assets, database_fixture ) +{ + try + { + #define PLAYERS_NUMBER 8 + #define TOURNAMENTS_NUMBER 3 + #define DEF_SYMBOL "NEXT" + + BOOST_TEST_MESSAGE("Hello two assets tournament test"); + + ACTORS((nathan)); + fc::ecc::private_key nathan_priv_key = fc::ecc::private_key::regenerate(fc::sha256::hash(string("nathan"))); + transfer(committee_account, nathan_id, asset(1000000000)); + upgrade_to_lifetime_member(nathan); + BOOST_CHECK(nathan.is_lifetime_member()); + + tournaments_helper tournament_helper(*this); + // creating new asset + asset_options aoptions; + aoptions.max_market_fee = aoptions.max_supply = GRAPHENE_MAX_SHARE_SUPPLY; + aoptions.flags = 0; + aoptions.issuer_permissions = 79; + aoptions.core_exchange_rate.base.amount = 1; + aoptions.core_exchange_rate.base.asset_id = asset_id_type(0); + aoptions.core_exchange_rate.quote.amount = 1; + aoptions.core_exchange_rate.quote.asset_id = asset_id_type(1); + tournament_helper.create_asset(nathan_id, DEF_SYMBOL, 5, aoptions, nathan_priv_key); + + issue_uia(nathan_id, asset(GRAPHENE_MAX_SHARE_SUPPLY/2, asset_id_type(1))); + + dividend_asset_options doptions; + doptions.minimum_distribution_interval = 3*24*60*60; + doptions.minimum_fee_percentage = 10*GRAPHENE_1_PERCENT; + doptions.next_payout_time = db.head_block_time() + fc::hours(1); + doptions.payout_interval = 7*24*60*60; + tournament_helper.update_dividend_asset(asset_id_type(1), doptions, nathan_priv_key); + +#if 0 + auto tas = get_asset(GRAPHENE_SYMBOL); wdump((tas)); + auto das = get_asset(DEF_SYMBOL); wdump((das)); + + if (das.dividend_data_id.valid()) + { + auto div = (*das.dividend_data_id)(db); wdump((div)); + auto dda = div.dividend_distribution_account(db); wdump((dda)); + } + + auto nac = nathan_id(db); wdump(("# nathan's account") (nac)); + + auto nab0 = db.get_balance(nathan_id, asset_id_type(0)); wdump(("# nathans's balance 0") (nab0)); + auto nab1 = db.get_balance(nathan_id, asset_id_type(1)); wdump(("# nathans's balance 1") (nab1)); +#endif + + // creating actors + std::vector> actors; + for(unsigned i = 0; i < PLAYERS_NUMBER; ++i) + { + std::string name = "account" + std::to_string(i); + auto priv_key = generate_private_key(name); + const auto& account = create_account(name, priv_key.get_public_key()); + actors.emplace_back(name, account.id, priv_key); + transfer(committee_account, account.id, asset((uint64_t)100000000 * PLAYERS_NUMBER + 10000000 * (i+1))); + transfer(nathan_id, account.id, asset((uint64_t)200000000 * PLAYERS_NUMBER + 20000000 * (i+1), asset_id_type(1))); + } + + // creating tournaments, registering players + for(unsigned i = 0; i < TOURNAMENTS_NUMBER; ++i) + { + asset buy_in = asset(1000 * PLAYERS_NUMBER + 100 * i, asset_id_type(i%2)); + tournament_id_type tournament_id; + tournament_id = tournament_helper.create_tournament (nathan_id, nathan_priv_key, buy_in, PLAYERS_NUMBER, 30, 30); + + for (unsigned j = 0; j < PLAYERS_NUMBER; ++j) + { + auto a = actors[j]; + tournament_helper.join_tournament(tournament_id, std::get<1>(a), std::get<1>(a), std::get<2>(a), buy_in); + } + } + + uint16_t tournaments_to_complete = TOURNAMENTS_NUMBER; + std::set tournaments = tournament_helper.list_tournaments(); + std::map> players_balances = tournament_helper.list_players_balances(); + uint16_t rake_fee_percentage = db.get_global_properties().parameters.rake_fee_percentage; + + BOOST_TEST_MESSAGE( "Generating blocks, waiting for tournaments' completion"); + while(tournaments_to_complete > 0) + { + generate_block(); + tournament_helper.play_games(); + for(const auto& tournament_id: tournaments) + { + const tournament_object& tournament = tournament_id(db); + if (tournament.get_state() == tournament_state::concluded) { + const tournament_details_object& tournament_details = tournament.tournament_details_id(db); + const match_object& final_match = (tournament_details.matches[tournament_details.matches.size() - 1])(db); + + assert(final_match.match_winners.size() == 1); + const account_id_type& winner_id = *final_match.match_winners.begin(); + BOOST_TEST_MESSAGE( "The winner of " + std::string(object_id_type(tournament_id)) + " is " + winner_id(db).name + " " + std::string(object_id_type(winner_id))); + share_type rake_amount = (fc::uint128_t(tournament.prize_pool.value) * rake_fee_percentage / GRAPHENE_1_PERCENT / 100).to_uint64(); + optional dividend_account = tournament_helper.get_asset_dividend_account(tournament.options.buy_in.asset_id); + if (dividend_account.valid()) + players_balances[*dividend_account][tournament.options.buy_in.asset_id] += rake_amount; players_balances[winner_id][tournament.options.buy_in.asset_id] += tournament.prize_pool - rake_amount; + + tournaments.erase(tournament_id); + --tournaments_to_complete; + break; + } + } + sleep(1); + } + BOOST_CHECK(tournaments.size() == 0); + // checking if prizes were distributed correctly + std::map> last_players_balances = tournament_helper.list_players_balances(); + for (auto a: last_players_balances) + { + BOOST_TEST_MESSAGE( "Checking " + a.first(db).name + "'s " + GRAPHENE_SYMBOL + " balance " + std::to_string((uint64_t) (a.second[asset_id_type()].value))); + BOOST_CHECK(a.second[asset_id_type()] == players_balances[a.first][asset_id_type()]); + BOOST_TEST_MESSAGE( "Checking " + a.first(db).name + "'s " + DEF_SYMBOL + " balance " + std::to_string((uint64_t) (a.second[asset_id_type(1)].value))); + BOOST_CHECK(a.second[asset_id_type(1)] == players_balances[a.first][asset_id_type(1)]); + } + + BOOST_TEST_MESSAGE("Bye two assets tournament test\n"); + } + catch (fc::exception& e) + { + edump((e.to_detail_string())); + throw; + } +} + +// Test of concurrently played tournaments having +// 2, 4, 8 ... 64 players randomly registered from global pool +// and randomized number of wins, +// generates random moves, +// checks prizes distribution and fees calculation. +// No "bye" matches. +BOOST_FIXTURE_TEST_CASE( basic, database_fixture ) +{ + try + { + #define MIN_PLAYERS_NUMBER 2 + #define MAX_PLAYERS_NUMBER 64 + + #define MIN_WINS_NUMBER 2 + #define MAX_WINS_NUMBER 5 + + BOOST_TEST_MESSAGE("Hello basic tournament test"); + + ACTORS((nathan)); + fc::ecc::private_key nathan_priv_key = fc::ecc::private_key::regenerate(fc::sha256::hash(string("nathan"))); + transfer(committee_account, nathan_id, asset(1000000000)); + upgrade_to_lifetime_member(nathan); + BOOST_CHECK(nathan.is_lifetime_member()); + + // creating random order of numbers of players joining tournaments + std::vector players; + for(unsigned i = MIN_PLAYERS_NUMBER; i <= MAX_PLAYERS_NUMBER; i*=2) + { + players.emplace_back(i); + } + for (unsigned i = players.size() - 1; i >= 1; --i) + { + if (std::rand() % 2 == 0) continue; + unsigned j = std::rand() % i; + std::swap(players[i], players[j]); + } + + // creating a pool of actors + std::vector> actors; + for(unsigned i = 0; i < 3 * MAX_PLAYERS_NUMBER; ++i) + { + std::string name = "account" + std::to_string(i); + auto priv_key = generate_private_key(name); + const auto& account = create_account(name, priv_key.get_public_key()); + actors.emplace_back(name, account.id, priv_key); + transfer(committee_account, account.id, asset((uint64_t)1000000000 * players.size() + 100000000 * (i+1))); + } +#if 0 + enable_fees(); + wdump((db.get_global_properties())); +#endif + // creating tournaments, registering players + tournaments_helper tournament_helper(*this); + for (unsigned i = 0; i < players.size(); ++i) + { + auto number_of_players = players[i]; + auto number_of_wins = RAND_MAX_MIN(MAX_WINS_NUMBER, MIN_WINS_NUMBER); + BOOST_TEST_MESSAGE( "Preparing tournament with " + std::to_string(number_of_players) + " players and " + std::to_string(number_of_wins) + " wins" ); + + asset buy_in = asset(1000 * number_of_players + 100 * i); + tournament_id_type tournament_id; + tournament_id = tournament_helper.create_tournament (nathan_id, nathan_priv_key, buy_in, number_of_players, 30, 30, number_of_wins); + + for (unsigned j = 0; j < actors.size() && number_of_players > 0; ++j) + { + if (number_of_players < actors.size() - j && std::rand() % 2 == 0) continue; + auto a = actors[j]; + --number_of_players; + tournament_helper.join_tournament(tournament_id, std::get<1>(a), std::get<1>(a), std::get<2>(a), buy_in); + } + } + + uint16_t tournaments_to_complete = players.size(); + std::set tournaments = tournament_helper.list_tournaments(); + std::map> players_initial_balances = tournament_helper.list_players_balances(); + tournament_helper.reset_players_fees(); + uint16_t rake_fee_percentage = db.get_global_properties().parameters.rake_fee_percentage; +#if 0 + wlog( "Prepared tournaments:"); + for(const tournament_id_type& tid: tournament_helper.list_tournaments()) + { + const tournament_object tournament = tid(db); + //const tournament_details_object details = tournament.tournament_details_id(db); + wlog(" # ${i}, players count ${c}, wins number ${w}", ("i", tid.instance) ("c", tournament.registered_players) ("w", tournament.options.number_of_wins )); + } + +#endif + BOOST_TEST_MESSAGE( "Generating blocks, waiting for tournaments' completion"); + tournament_helper.reset_players_fees(); + while(tournaments_to_complete > 0) + { + generate_block(); + enable_fees(); + tournament_helper.play_games(); + for(const auto& tournament_id: tournaments) + { + const tournament_object& tournament = tournament_id(db); + if (tournament.get_state() == tournament_state::concluded) { + const tournament_details_object& tournament_details = tournament.tournament_details_id(db); + const match_object& final_match = (tournament_details.matches[tournament_details.matches.size() - 1])(db); + + assert(final_match.match_winners.size() == 1); + const account_id_type& winner_id = *final_match.match_winners.begin(); + BOOST_TEST_MESSAGE( "The winner of " + std::string(object_id_type(tournament_id)) + " is " + winner_id(db).name + " " + std::string(object_id_type(winner_id))); + share_type rake_amount = (fc::uint128_t(tournament.prize_pool.value) * rake_fee_percentage / GRAPHENE_1_PERCENT / 100).to_uint64(); + optional dividend_account = tournament_helper.get_asset_dividend_account(tournament.options.buy_in.asset_id); + if (dividend_account.valid()) + players_initial_balances[*dividend_account][tournament.options.buy_in.asset_id] += rake_amount; + players_initial_balances[winner_id][tournament.options.buy_in.asset_id] += tournament.prize_pool - rake_amount; + + tournaments.erase(tournament_id); + --tournaments_to_complete; + break; + } + } + sleep(1); + } + BOOST_CHECK(tournaments.size() == 0); + + // checking if prizes were distributed correctly and fees calculated properly + std::map> current_players_balances = tournament_helper.list_players_balances(); + std::map> players_paid_fees = tournament_helper.get_players_fees(); + for (auto a: current_players_balances) + { + BOOST_TEST_MESSAGE( "Checking " + a.first(db).name + "'s balance " + std::to_string((uint64_t)(a.second[asset_id_type()].value)) ); + BOOST_CHECK(a.second[asset_id_type()] == players_initial_balances[a.first][asset_id_type()] + players_paid_fees[a.first][asset_id_type()]); + } + + BOOST_TEST_MESSAGE("Bye basic tournament test\n"); + } + catch (fc::exception& e) + { + edump((e.to_detail_string())); + throw; + } + +} + +#ifdef BYE_MATCHES_FIXED +// Test of several concurrently played tournaments having +// randomized number of players registered from global pool, +// and randomized number of wins, +// generates random moves, +// checks prizes distribution. +// "bye" matches fix is required. +BOOST_FIXTURE_TEST_CASE( massive, database_fixture ) +{ + try + { + #define MIN_TOURNAMENTS_NUMBER 1 + #define MAX_TOURNAMENTS_NUMBER 10 + + #define MIN_PLAYERS_NUMBER 2 + #define MAX_PLAYERS_NUMBER 64 + + #define MIN_WINS_NUMBER 2 + #define MAX_WINS_NUMBER 5 + + BOOST_TEST_MESSAGE("Hello massive tournament test"); + + ACTORS((nathan)); + fc::ecc::private_key nathan_priv_key = fc::ecc::private_key::regenerate(fc::sha256::hash(string("nathan"))); + transfer(committee_account, nathan_id, asset(1000000000)); + upgrade_to_lifetime_member(nathan); + BOOST_CHECK(nathan.is_lifetime_member()); + + // creating a pool of actors + std::vector> actors; + for(unsigned i = 0; i < 2 * MAX_PLAYERS_NUMBER; ++i) + { + std::string name = "account" + std::to_string(i); + auto priv_key = generate_private_key(name); + const auto& account = create_account(name, priv_key.get_public_key()); + actors.emplace_back(name, account.id, priv_key); + transfer(committee_account, account.id, asset((uint64_t)1000000 * MAX_TOURNAMENTS_NUMBER + 100000 * (i+1))); + } + + // creating tournaments, registering players + tournaments_helper tournament_helper(*this); + unsigned number_of_tournaments = RAND_MAX_MIN(MAX_TOURNAMENTS_NUMBER, MIN_TOURNAMENTS_NUMBER); + for(unsigned i = 0; i < number_of_tournaments; ++i) + { + unsigned number_of_players = RAND_MAX_MIN(MAX_PLAYERS_NUMBER, MIN_PLAYERS_NUMBER); + unsigned number_of_wins = RAND_MAX_MIN(MAX_WINS_NUMBER, MIN_WINS_NUMBER); + BOOST_TEST_MESSAGE( "Preparing tournament with " + std::to_string(number_of_players) + " players and " + std::to_string(number_of_wins) + " wins" ); + + asset buy_in = asset(1000 * number_of_players + 100 * i); + tournament_id_type tournament_id; + tournament_id = tournament_helper.create_tournament (nathan_id, nathan_priv_key, buy_in, number_of_players, 30, 30, number_of_wins); + + for (unsigned j = 0; j < actors.size() && number_of_players > 0; ++j) + { + if (number_of_players < actors.size() - j && std::rand() % 2 == 0) continue; + auto a = actors[j]; + --number_of_players; + tournament_helper.join_tournament(tournament_id, std::get<1>(a), std::get<1>(a), std::get<2>(a), buy_in); + } + } + + uint16_t tournaments_to_complete = number_of_tournaments; + std::set tournaments = tournament_helper.list_tournaments(); + std::map> players_balances = tournament_helper.list_players_balances(); + uint16_t rake_fee_percentage = db.get_global_properties().parameters.rake_fee_percentage; + + BOOST_TEST_MESSAGE( "Generating blocks, waiting for tournaments' completion"); + while(tournaments_to_complete > 0) + { + generate_block(); + tournament_helper.play_games(); + for(const auto& tournament_id: tournaments) + { + const tournament_object& tournament = tournament_id(db); + if (tournament.get_state() == tournament_state::concluded) { + const tournament_details_object& tournament_details = tournament.tournament_details_id(db); + const match_object& final_match = (tournament_details.matches[tournament_details.matches.size() - 1])(db); + + assert(final_match.match_winners.size() == 1); + const account_id_type& winner_id = *final_match.match_winners.begin(); + BOOST_TEST_MESSAGE( "The winner of " + std::string(object_id_type(tournament_id)) + " is " + winner_id(db).name + " " + std::string(object_id_type(winner_id))); + share_type rake_amount = (fc::uint128_t(tournament.prize_pool.value) * rake_fee_percentage / GRAPHENE_1_PERCENT / 100).to_uint64(); + optional dividend_account = tournament_helper.get_asset_dividend_account(tournament.options.buy_in.asset_id); + if (dividend_account.valid()) + players_balances[*dividend_account][tournament.options.buy_in.asset_id] += rake_amount; + players_balances[winner_id][tournament.options.buy_in.asset_id] += tournament.prize_pool - rake_amount; + + tournaments.erase(tournament_id); + --tournaments_to_complete; + break; + } + } + sleep(1); + } + BOOST_CHECK(tournaments.size() == 0); +#if 0 + wlog( "Performed tournaments:"); + for(const tournament_id_type& tid: tournament_helper.list_tournaments()) + { + const tournament_object tournament = tid(db); + //const tournament_details_object details = tournament.tournament_details_id(db); + wlog(" # ${i}, players count ${c}, wins number ${w}", ("i", tid.instance) ("c", tournament.registered_players) ("w", tournament.options.number_of_wins )); + } +#endif + // checking if prizes were distributed correctly + std::map> last_players_balances = tournament_helper.list_players_balances(); + for (auto a: last_players_balances) + { + BOOST_TEST_MESSAGE( "Checking " + a.first(db).name + "'s balance " + std::to_string((uint64_t)(a.second[asset_id_type()].value)) ); + BOOST_CHECK(a.second[asset_id_type()] == players_balances[a.first][asset_id_type()]); + } + + BOOST_TEST_MESSAGE("Bye massive tournament test\n"); + } + catch (fc::exception& e) + { + edump((e.to_detail_string())); + throw; + } +} +#endif + +BOOST_AUTO_TEST_SUITE_END() + +//#define BOOST_TEST_MODULE "C++ Unit Tests for Graphene Blockchain Database" +#include +#include +#include + +boost::unit_test::test_suite* init_unit_test_suite(int argc, char* argv[]) { + std::srand(time(NULL)); + std::cout << "Random number generator seeded to " << time(NULL) << std::endl; + return nullptr; +} From 1018221812ce76ab369a63d9a9ad7118b14afda7 Mon Sep 17 00:00:00 2001 From: Eric Frias Date: Tue, 3 Jan 2017 16:44:56 -0500 Subject: [PATCH 04/10] quiet signed/unsigned warning --- tests/common/database_fixture.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/common/database_fixture.cpp b/tests/common/database_fixture.cpp index f4ae577b..125dc5e5 100644 --- a/tests/common/database_fixture.cpp +++ b/tests/common/database_fixture.cpp @@ -82,7 +82,7 @@ database_fixture::database_fixture() genesis_state.initial_timestamp = time_point_sec( GRAPHENE_TESTING_GENESIS_TIMESTAMP ); genesis_state.initial_active_witnesses = 10; - for( int i = 0; i < genesis_state.initial_active_witnesses; ++i ) + for( unsigned i = 0; i < genesis_state.initial_active_witnesses; ++i ) { auto name = "init"+fc::to_string(i); genesis_state.initial_accounts.emplace_back(name, From 2012fe6e90e0b9e1f760d166737db6b92390ad82 Mon Sep 17 00:00:00 2001 From: Eric Frias Date: Tue, 3 Jan 2017 16:45:14 -0500 Subject: [PATCH 05/10] Allow generate_genesis plugin to quietly do nothing when you don't specify the block number on the witness's command line. --- .../generate_genesis/generate_genesis.cpp | 29 ++++++++++--------- .../generate_genesis_plugin.hpp | 2 +- 2 files changed, 16 insertions(+), 15 deletions(-) diff --git a/libraries/plugins/generate_genesis/generate_genesis.cpp b/libraries/plugins/generate_genesis/generate_genesis.cpp index e11fc6b0..8f1b20d9 100644 --- a/libraries/plugins/generate_genesis/generate_genesis.cpp +++ b/libraries/plugins/generate_genesis/generate_genesis.cpp @@ -50,7 +50,7 @@ void generate_genesis_plugin::plugin_set_program_options( command_line_options.add_options() ("output-genesis-file,o", bpo::value()->default_value("genesis.json"), "Genesis file to create") ("output-csvlog-file,o", bpo::value()->default_value("log.csv"), "CSV log file to create") - ("snapshot-block-number", bpo::value()->default_value(0), "Block number at which to snapshot balances") + ("snapshot-block-number", bpo::value(), "Block number at which to snapshot balances") ; config_file_options.add(command_line_options); } @@ -67,32 +67,33 @@ void generate_genesis_plugin::plugin_initialize(const boost::program_options::va _genesis_filename = options["output-genesis-file"].as(); _csvlog_filename = options["output-csvlog-file"].as(); - _block_to_snapshot = options["snapshot-block-number"].as(); + if (options.count("snapshot-block-number")) + _block_to_snapshot = options["snapshot-block-number"].as(); database().applied_block.connect([this](const graphene::chain::signed_block& b){ block_applied(b); }); ilog("generate genesis plugin: plugin_initialize() end"); } FC_LOG_AND_RETHROW() } void generate_genesis_plugin::plugin_startup() { try { - ilog("generate genesis plugin: plugin_startup() begin"); chain::database& d = database(); - if (d.head_block_num() == _block_to_snapshot) + if (_block_to_snapshot) { - ilog("generate genesis plugin: already at snapshot block"); - generate_snapshot(); + if (d.head_block_num() == *_block_to_snapshot) + { + ilog("generate genesis plugin: already at snapshot block"); + generate_snapshot(); + } + else if (d.head_block_num() > *_block_to_snapshot) + elog("generate genesis plugin: already passed snapshot block, you must reindex to return to the snapshot state"); + else + elog("generate genesis plugin: waiting for block ${snapshot_block} to generate snapshot, current head is ${head}", + ("snapshot_block", _block_to_snapshot)("head", d.head_block_num())); } - else if (d.head_block_num() > _block_to_snapshot) - elog("generate genesis plugin: already passed snapshot block, you must reindex to return to the snapshot state"); - else - elog("generate genesis plugin: waiting for block ${snapshot_block} to generate snapshot, current head is ${head}", - ("snapshot_block", _block_to_snapshot)("head", d.head_block_num())); - - ilog("generate genesis plugin: plugin_startup() end"); } FC_CAPTURE_AND_RETHROW() } void generate_genesis_plugin::block_applied(const graphene::chain::signed_block& b) { - if (b.block_num() == _block_to_snapshot) + if (_block_to_snapshot && b.block_num() == *_block_to_snapshot) { ilog("generate genesis plugin: snapshot block has arrived"); generate_snapshot(); diff --git a/libraries/plugins/generate_genesis/include/graphene/generate_genesis/generate_genesis_plugin.hpp b/libraries/plugins/generate_genesis/include/graphene/generate_genesis/generate_genesis_plugin.hpp index 16e4f655..c1ffbaff 100644 --- a/libraries/plugins/generate_genesis/include/graphene/generate_genesis/generate_genesis_plugin.hpp +++ b/libraries/plugins/generate_genesis/include/graphene/generate_genesis/generate_genesis_plugin.hpp @@ -52,7 +52,7 @@ private: boost::program_options::variables_map _options; - uint32_t _block_to_snapshot; + fc::optional _block_to_snapshot; std::string _genesis_filename; std::string _csvlog_filename; }; From 2fd34ef6ab32c3e07f6010fedb8c49790cb53bca Mon Sep 17 00:00:00 2001 From: Roman Olearski Date: Sat, 1 Apr 2017 13:59:52 +0200 Subject: [PATCH 06/10] commiting last changes --- libraries/chain/db_init.cpp | 14 ----- libraries/chain/game_object.cpp | 45 +++++++++------- libraries/chain/match_object.cpp | 26 +++++++++- libraries/chain/tournament_object.cpp | 34 +++--------- tests/CMakeLists.txt | 12 +++-- tests/tournament/tournament_tests.cpp | 74 +++++++++++++++++---------- 6 files changed, 111 insertions(+), 94 deletions(-) diff --git a/libraries/chain/db_init.cpp b/libraries/chain/db_init.cpp index ebd84db9..c1863e67 100644 --- a/libraries/chain/db_init.cpp +++ b/libraries/chain/db_init.cpp @@ -371,12 +371,6 @@ void database::init_genesis(const genesis_state_type& genesis_state) a.options.payout_interval = 7*24*60*60; a.dividend_distribution_account = TOURNAMENT_RAKE_FEE_ACCOUNT_ID; }); -// const asset_bitasset_data_object& bit_asset = -// create([&](asset_bitasset_data_object& a) { -// a.current_feed.maintenance_collateral_ratio = 1750; -// a.current_feed.maximum_short_squeeze_ratio = 1500; -// a.current_feed_publication_time = genesis_state.initial_timestamp + fc::hours(1); -// }); const asset_object& core_asset = create( [&]( asset_object& a ) { @@ -392,7 +386,6 @@ void database::init_genesis(const genesis_state_type& genesis_state) a.options.core_exchange_rate.quote.asset_id = asset_id_type(0); a.dynamic_asset_data_id = dyn_asset.id; a.dividend_data_id = div_asset.id; -// a.bitasset_data_id = bit_asset.id; }); assert( asset_id_type(core_asset.id) == asset().asset_id ); assert( get_balance(account_id_type(), asset_id_type()) == asset(dyn_asset.current_supply) ); @@ -411,12 +404,6 @@ void database::init_genesis(const genesis_state_type& genesis_state) a.options.payout_interval = 7*24*60*60; a.dividend_distribution_account = TOURNAMENT_RAKE_FEE_ACCOUNT_ID; }); - const asset_bitasset_data_object& bit_asset1 = - create([&](asset_bitasset_data_object& a) { - a.current_feed.maintenance_collateral_ratio = 1750; - a.current_feed.maximum_short_squeeze_ratio = 1500; - a.current_feed_publication_time = genesis_state.initial_timestamp + fc::hours(1); - }); const asset_object& default_asset = create( [&]( asset_object& a ) { @@ -433,7 +420,6 @@ void database::init_genesis(const genesis_state_type& genesis_state) a.options.core_exchange_rate.quote.asset_id = asset_id_type(1); a.dynamic_asset_data_id = dyn_asset1.id; a.dividend_data_id = div_asset1.id; - a.bitasset_data_id = bit_asset1.id; }); assert( default_asset.id == asset_id_type(1) ); #endif diff --git a/libraries/chain/game_object.cpp b/libraries/chain/game_object.cpp index 2bbcdd92..02612a77 100644 --- a/libraries/chain/game_object.cpp +++ b/libraries/chain/game_object.cpp @@ -106,7 +106,6 @@ namespace graphene { namespace chain { fc_ilog(fc::logger::get("tournament"), "game ${id} received a commit move, still expecting another commit move", ("id", game.id)); - set_next_timeout(event.db, game); } }; struct expecting_reveal_moves : public msm::front::state<> @@ -131,14 +130,16 @@ namespace graphene { namespace chain { game_object& game = *fsm.game_obj; if (event.move.move.which() == game_specific_moves::tag::value) + { fc_ilog(fc::logger::get("tournament"), "game ${id} received a commit move, now expecting reveal moves", ("id", game.id)); + set_next_timeout(event.db, game); + } else fc_ilog(fc::logger::get("tournament"), "game ${id} received a reveal move, still expecting reveal moves", ("id", game.id)); - set_next_timeout(event.db, game); } }; @@ -146,9 +147,9 @@ namespace graphene { namespace chain { { void clear_next_timeout(database& db, game_object& game) { - const match_object& match_obj = game.match_id(db); - const tournament_object& tournament_obj = match_obj.tournament_id(db); - const rock_paper_scissors_game_options& game_options = tournament_obj.options.game_options.get(); + //const match_object& match_obj = game.match_id(db); + //const tournament_object& tournament_obj = match_obj.tournament_id(db); + //const rock_paper_scissors_game_options& game_options = tournament_obj.options.game_options.get(); game.next_timeout = fc::optional(); } void on_entry(const timeout& event, game_state_machine_& fsm) @@ -200,7 +201,7 @@ namespace graphene { namespace chain { const rock_paper_scissors_game_details& game_details = game_obj->game_details.get(); for (unsigned i = 0; i < game_details.commit_moves.size(); ++i) - if (!game_details.reveal_moves[i] && i != this_reveal_index) + if (game_details.commit_moves[i] && !game_details.reveal_moves[i] && i != this_reveal_index) return false; return true; } @@ -350,7 +351,7 @@ namespace graphene { namespace chain { void game_object::evaluate_move_operation(const database& db, const game_move_operation& op) const { - const match_object& match_obj = match_id(db); + //const match_object& match_obj = match_id(db); if (game_details.which() == game_specific_details::tag::value) { @@ -450,18 +451,22 @@ namespace graphene { namespace chain { const match_object& match_obj = match_id(db); const tournament_object& tournament_obj = match_obj.tournament_id(db); const rock_paper_scissors_game_options& game_options = tournament_obj.options.game_options.get(); - for (unsigned i = 0; i < 2; ++i) + + if (game_options.insurance_enabled) { - if (!rps_game_details.commit_moves[i] || - no_player_has_reveal_move) - { - struct rock_paper_scissors_throw_reveal reveal; - reveal.nonce2 = 0; - reveal.gesture = (rock_paper_scissors_gesture)db.get_random_bits(game_options.number_of_gestures); - rps_game_details.reveal_moves[i] = reveal; - ilog("Player ${player} failed to commit a move, generating a random move for them: ${gesture}", - ("player", i)("gesture", reveal.gesture)); - } + for (unsigned i = 0; i < 2; ++i) + { + if (!rps_game_details.commit_moves[i] || + no_player_has_reveal_move) + { + struct rock_paper_scissors_throw_reveal reveal; + reveal.nonce2 = 0; + reveal.gesture = (rock_paper_scissors_gesture)db.get_random_bits(game_options.number_of_gestures); + rps_game_details.reveal_moves[i] = reveal; + ilog("Player ${player} failed to commit a move, generating a random move for him: ${gesture}", + ("player", i)("gesture", reveal.gesture)); + } + } } } } @@ -500,8 +505,10 @@ namespace graphene { namespace chain { ilog("Player 0 didn't commit or reveal their move, player 1 wins"); winners.insert(players[1]); } - else if (rps_game_details.reveal_moves[1]) + else + { ilog("Neither player made a move, both players lose"); + } } diff --git a/libraries/chain/match_object.cpp b/libraries/chain/match_object.cpp index 3ea3d250..4f762deb 100644 --- a/libraries/chain/match_object.cpp +++ b/libraries/chain/match_object.cpp @@ -94,22 +94,39 @@ namespace graphene { namespace chain { "Match ${id} is complete", ("id", match.id)); + optional last_game_winner; std::map scores_by_player; for (const flat_set& game_winners : match.game_winners) for (const account_id_type& account_id : game_winners) + { ++scores_by_player[account_id]; + last_game_winner = account_id; + } + bool all_scores_same = true; optional high_scoring_account; unsigned high_score = 0; for (const auto& value : scores_by_player) if (value.second > high_score) { + if (high_scoring_account) + all_scores_same = false; high_score = value.second; high_scoring_account = value.first; } if (high_scoring_account) - match.match_winners.insert(*high_scoring_account); + { + if (all_scores_same && last_game_winner) + match.match_winners.insert(*last_game_winner); + else + match.match_winners.insert(*high_scoring_account); + } + else + { + match.match_winners.insert(match.players[event.db.get_random_bits(match.players.size())]); + } + match.end_time = event.db.head_block_time(); const tournament_object& tournament_obj = match.tournament_id(event.db); @@ -137,11 +154,16 @@ namespace graphene { namespace chain { typedef match_state_machine_ x; // makes transition table cleaner - // Guards bool was_final_game(const game_complete& event) { const tournament_object& tournament_obj = match_obj->tournament_id(event.db); + if (match_obj->games.size() >= tournament_obj.options.number_of_wins * 4) + { + wdump((match_obj->games.size())); + return true; + } + for (unsigned i = 0; i < match_obj->players.size(); ++i) { // this guard is called before the winner of the current game factored in to our running totals, diff --git a/libraries/chain/tournament_object.cpp b/libraries/chain/tournament_object.cpp index 0ef5b364..fa8830ee 100644 --- a/libraries/chain/tournament_object.cpp +++ b/libraries/chain/tournament_object.cpp @@ -199,13 +199,6 @@ namespace graphene { namespace chain { } } -#ifdef HELPFULL_DUMP_WHEN_SOLVING_BYE_MATCH_PROBLEM - wlog("###"); - wdump((tournament_details_obj.matches[tournament_details_obj.matches.size() - 1])); - for( match_id_type mid : tournament_details_obj.matches ) - wdump((mid(event.db))); -#endif - } void on_entry(const match_completed& event, tournament_state_machine_& fsm) { @@ -241,13 +234,7 @@ namespace graphene { namespace chain { event.db.modify(next_round_match, [&](match_object& next_match_obj) { -#ifdef HELPFULL_DUMP_WHEN_SOLVING_BYE_MATCH_PROBLEM - wdump((event.match.get_state())); - wdump((event.match)); - wdump((other_match.get_state())); - wdump((other_match)); -#endif - if (!event.match.match_winners.empty()) // if there is a winner + if (!event.match.match_winners.empty()) // if there is a winner { if (winner_index_in_next_match == 0) next_match_obj.players.insert(next_match_obj.players.begin(), *event.match.match_winners.begin()); @@ -257,10 +244,6 @@ namespace graphene { namespace chain { if (other_match.get_state() == match_state::match_complete) { -#ifdef HELPFULL_DUMP_WHEN_SOLVING_BYE_MATCH_PROBLEM - wdump((next_match_obj.get_state())); - wdump((next_match_obj)); -#endif next_match_obj.on_initiate_match(event.db); } @@ -376,21 +359,16 @@ namespace graphene { namespace chain { ("value", tournament_obj->registered_players == tournament_obj->options.number_of_players - 1)); return tournament_obj->registered_players == tournament_obj->options.number_of_players - 1; } - + bool was_final_match(const match_completed& event) { const tournament_details_object& tournament_details_obj = tournament_obj->tournament_details_id(event.db); + auto final_match_id = tournament_details_obj.matches[tournament_details_obj.matches.size() - 1]; + bool was_final = event.match.id == final_match_id; fc_ilog(fc::logger::get("tournament"), "In was_final_match guard, returning ${value}", - ("value", event.match.id == tournament_details_obj.matches[tournament_details_obj.matches.size()])); -#ifdef HELPFULL_DUMP_WHEN_SOLVING_BYE_MATCH_PROBLEM - wlog("###"); - wdump((event.match.id)); - wdump((tournament_details_obj.matches[tournament_details_obj.matches.size() - 1])); - for( match_id_type mid : tournament_details_obj.matches ) - wdump((mid(event.db))); -#endif - return event.match.id == tournament_details_obj.matches[tournament_details_obj.matches.size() - 1]; + ("value", was_final)); + return was_final; } void register_player(const player_registered& event) diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index c1eced4e..c8de1558 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -17,10 +17,6 @@ file(GLOB PERFORMANCE_TESTS "performance/*.cpp") add_executable( performance_test ${PERFORMANCE_TESTS} ${COMMON_SOURCES} ) target_link_libraries( performance_test graphene_chain graphene_app graphene_account_history graphene_egenesis_none fc ${PLATFORM_SPECIFIC_LIBS} ) -file(GLOB TOURNAMENT_TESTS "tournament/*.cpp") -add_executable( tournament_test ${TOURNAMENT_TESTS} ${COMMON_SOURCES} ) -target_link_libraries( tournament_test graphene_chain graphene_app graphene_account_history graphene_egenesis_none fc ${PLATFORM_SPECIFIC_LIBS} ) - file(GLOB BENCH_MARKS "benchmarks/*.cpp") add_executable( chain_bench ${BENCH_MARKS} ${COMMON_SOURCES} ) target_link_libraries( chain_bench graphene_chain graphene_app graphene_account_history graphene_time graphene_egenesis_none fc ${PLATFORM_SPECIFIC_LIBS} ) @@ -33,4 +29,12 @@ file(GLOB INTENSE_SOURCES "intense/*.cpp") add_executable( intense_test ${INTENSE_SOURCES} ${COMMON_SOURCES} ) target_link_libraries( intense_test graphene_chain graphene_app graphene_account_history graphene_egenesis_none fc ${PLATFORM_SPECIFIC_LIBS} ) +file(GLOB TOURNAMENT_TESTS "tournament/*.cpp") +add_executable( tournament_test ${TOURNAMENT_TESTS} ${COMMON_SOURCES} ) +target_link_libraries( tournament_test graphene_chain graphene_app graphene_account_history graphene_egenesis_none fc ${PLATFORM_SPECIFIC_LIBS} ) + +file(GLOB RANDOM_SOURCES "random/*.cpp") +add_executable( random_test ${RANDOM_SOURCES} ${COMMON_SOURCES} ) +target_link_libraries( random_test graphene_chain graphene_app graphene_egenesis_none fc ${PLATFORM_SPECIFIC_LIBS} ) + add_subdirectory( generate_empty_blocks ) diff --git a/tests/tournament/tournament_tests.cpp b/tests/tournament/tournament_tests.cpp index 63f17999..22da9a70 100644 --- a/tests/tournament/tournament_tests.cpp +++ b/tests/tournament/tournament_tests.cpp @@ -25,7 +25,6 @@ #include #include - #include #include #include @@ -289,13 +288,17 @@ public: tx.validate(); tx.set_expiration(db.head_block_time() + fc::seconds( params.block_interval * (params.maintenance_skip_slots + 1) * 3)); df.sign(tx, sig_priv_key); - PUSH_TX(db, tx); + if (game_obj.get_state() == game_state::expecting_commit_moves) // checking again + PUSH_TX(db, tx); } // spaghetti programming // walking through all tournaments, matches and games and throwing random moves - void play_games() + // optionaly skip generting randomly selected moves + void play_games(unsigned skip_some_commits = 0, unsigned skip_some_reveals = 0) { + //try + //{ graphene::chain::database& db = df.db; const chain_parameters& params = db.get_global_properties().parameters; @@ -310,20 +313,25 @@ public: for(const auto& game_id: match.games ) { const game_object& game = game_id(db); + const rock_paper_scissors_game_details& rps_details = game.game_details.get(); if (game.get_state() == game_state::expecting_commit_moves) { for(const auto& player_id: game.players) { - if (players_keys.find(player_id) != players_keys.end()) + if ( players_keys.find(player_id) != players_keys.end()) { - rps_throw(game_id, player_id, (rock_paper_scissors_gesture) (std::rand() % game_options.number_of_gestures), players_keys[player_id]); + if (!skip_some_commits || player_id.instance.value % skip_some_commits != game_id.instance.value % skip_some_commits) + { + auto iter = std::find(game.players.begin(), game.players.end(), player_id); + unsigned player_index = std::distance(game.players.begin(), iter); + if (!rps_details.commit_moves.at(player_index)) + rps_throw(game_id, player_id, (rock_paper_scissors_gesture) (std::rand() % game_options.number_of_gestures), players_keys[player_id]); + } } } } else if (game.get_state() == game_state::expecting_reveal_moves) { - const rock_paper_scissors_game_details& rps_details = game.game_details.get(); - for (unsigned i = 0; i < 2; ++i) { if (rps_details.commit_moves.at(i) && @@ -333,27 +341,32 @@ public: if (players_keys.find(player_id) != players_keys.end()) { { + auto iter = committed_game_moves.find(*rps_details.commit_moves.at(i)); if (iter != committed_game_moves.end()) { - const rock_paper_scissors_throw_reveal& reveal = iter->second; - - game_move_operation move_operation; - move_operation.game_id = game.id; - move_operation.player_account_id = player_id; - move_operation.move = reveal; - - signed_transaction tx; - tx.operations = {move_operation}; - for( auto& op : tx.operations ) + if (!skip_some_reveals || player_id.instance.value % skip_some_reveals != game_id.instance.value % skip_some_reveals) { - asset f = db.current_fee_schedule().set_fee(op); - players_fees[player_id][f.asset_id] -= f.amount; + const rock_paper_scissors_throw_reveal& reveal = iter->second; + + game_move_operation move_operation; + move_operation.game_id = game.id; + move_operation.player_account_id = player_id; + move_operation.move = reveal; + + signed_transaction tx; + tx.operations = {move_operation}; + for( auto& op : tx.operations ) + { + asset f = db.current_fee_schedule().set_fee(op); + players_fees[player_id][f.asset_id] -= f.amount; + } + tx.validate(); + tx.set_expiration(db.head_block_time() + fc::seconds( params.block_interval * (params.maintenance_skip_slots + 1) * 3)); + df.sign(tx, players_keys[player_id]); + if (game.get_state() == game_state::expecting_reveal_moves) // check again + PUSH_TX(db, tx); } - tx.validate(); - tx.set_expiration(db.head_block_time() + fc::seconds( params.block_interval * (params.maintenance_skip_slots + 1) * 3)); - df.sign(tx, players_keys[player_id]); - PUSH_TX(db, tx); } } } @@ -363,6 +376,12 @@ public: } } } + //} + //catch (fc::exception& e) + //{ + // edump((e.to_detail_string())); + // throw; + //} } private: @@ -447,7 +466,7 @@ BOOST_FIXTURE_TEST_CASE( simple, database_fixture ) asset buy_in = asset(12000); tournament_id_type tournament_id; - BOOST_TEST_MESSAGE( "Preparing a tournament" ); + BOOST_TEST_MESSAGE( "Preparing a tournament, insurance disabled" ); tournament_id = tournament_helper.create_tournament (nathan_id, nathan_priv_key, buy_in, TEST1_NR_OF_PLAYERS_NUMBER); BOOST_REQUIRE(tournament_id == tournament_id_type()); @@ -458,9 +477,10 @@ BOOST_FIXTURE_TEST_CASE( simple, database_fixture ) #endif ++tournaments_to_complete; - BOOST_TEST_MESSAGE( "Preparing another one" ); + BOOST_TEST_MESSAGE( "Preparing another one, insurance enabled" ); buy_in = asset(13000); - tournament_id = tournament_helper.create_tournament (nathan_id, nathan_priv_key, buy_in, TEST2_NR_OF_PLAYERS_NUMBER); + tournament_id = tournament_helper.create_tournament (nathan_id, nathan_priv_key, buy_in, TEST2_NR_OF_PLAYERS_NUMBER, + 3, 1, 3, 3600, 3, 3, true); BOOST_REQUIRE(tournament_id == tournament_id_type(1)); tournament_helper.join_tournament(tournament_id, alice_id, alice_id, fc::ecc::private_key::regenerate(fc::sha256::hash(string("alice"))), buy_in); tournament_helper.join_tournament(tournament_id, bob_id, bob_id, fc::ecc::private_key::regenerate(fc::sha256::hash(string("bob"))), buy_in); @@ -745,7 +765,7 @@ BOOST_FIXTURE_TEST_CASE( assets, database_fixture ) while(tournaments_to_complete > 0) { generate_block(); - tournament_helper.play_games(); + tournament_helper.play_games(3, 4); for(const auto& tournament_id: tournaments) { const tournament_object& tournament = tournament_id(db); From f9352821812941478375e460fe31e76797255be2 Mon Sep 17 00:00:00 2001 From: Roman Olearski Date: Sat, 1 Apr 2017 14:04:38 +0200 Subject: [PATCH 07/10] commiting last changes 2 --- tests/random.sh | 20 +++++ tests/random/random_tests.cpp | 154 ++++++++++++++++++++++++++++++++++ tests/random/readme | 10 +++ tests/tournaments.sh | 19 +++++ 4 files changed, 203 insertions(+) create mode 100755 tests/random.sh create mode 100644 tests/random/random_tests.cpp create mode 100644 tests/random/readme create mode 100755 tests/tournaments.sh diff --git a/tests/random.sh b/tests/random.sh new file mode 100755 index 00000000..925bd9a6 --- /dev/null +++ b/tests/random.sh @@ -0,0 +1,20 @@ +#!/bin/bash -e + +i=1 +while [ 0 ]; do + + echo "*** $i `date`" + if [ -f random-2 ]; then + mv random-2 random-2-last + fi + ./random_test --log_level=message 2> random-2 + echo "*** $i `date`" + echo + if [ "$1" = "-c" ]; then + sleep 2 + else + break + fi + i=$[i + 1] + +done \ No newline at end of file diff --git a/tests/random/random_tests.cpp b/tests/random/random_tests.cpp new file mode 100644 index 00000000..e224439a --- /dev/null +++ b/tests/random/random_tests.cpp @@ -0,0 +1,154 @@ +/* + * Copyright (c) 2015 Cryptonomex, Inc., and contributors. + * + * The MIT License + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#include +//#include +//#include + +#include +#include +#include +#include "../common/database_fixture.hpp" +#include + +#include + +using namespace graphene::chain; + +bool test_standard_rand = false; +bool all_tests = false; +bool game_is_over = false; + +void sig_handler(int signo) +{ + std::cout << "." << std::endl; + game_is_over = true; +} + +BOOST_AUTO_TEST_SUITE(tournament_tests) + +bool one_test(database_fixture& df, int test_nr = 0, int tsamples = 0, int psamples = 0, std::string options = "") +{ + game_is_over = false; + + std::string command = "dieharder -g 200 -d " + std::to_string(test_nr) ; + if (tsamples) + command += " -t " + std::to_string(tsamples); + if (psamples) + command += " -p " + std::to_string(psamples); + if (!options.empty()) + command += " " + options; + + FILE *io = popen(command.c_str(), "w"); + BOOST_CHECK(io); + if(!io) + return false; + + int r; + void *binary_data; + size_t binary_data_length = sizeof(r); + int m = 0; // 0 not generate blocks, > 0 generate block every m generated numbers 100 - 1000 are reasonable + int i = 0; + + while ( !game_is_over && !feof(io) && !ferror(io) ) + { + if (test_standard_rand) { + r = rand(); + } else { + if (i) { + --i; + df.generate_block(); + } else { + i = m; + } + r = df.db.get_random_bits((uint64_t)INT_MAX+1); + } + + binary_data = (void *) &r; + size_t l = + fwrite(binary_data, 1, binary_data_length, io); + if (l != binary_data_length) break; + //fflush(io); + } + + pclose(io); + return true; +} + +BOOST_FIXTURE_TEST_CASE( basic, database_fixture ) +{ + try + { + std::string o(" dieharder "); + o.append(all_tests ? "all" : "selected"). + append(" tests of "). + append(test_standard_rand ? "rand" : "get_random_bits"); + BOOST_TEST_MESSAGE("Hello" + o); + + std::vector selected = {0, 1, 3, 5, 6, 15}; +#if 1 + // trying to randomize starting point + int r = std::rand() % 100; + for(int i = 0; i < r ; ++i) + db.get_random_bits(INT_MAX); +#endif + for (int i = 0; i < 18; ++i) + { + if (!all_tests && std::find(selected.begin(), selected.end(), i) == selected.end()) + continue; + BOOST_TEST_MESSAGE("#" + std::to_string(i)); + if (!one_test(*this, i)) + break; + } + BOOST_TEST_MESSAGE("Bye" + o); + } + catch (fc::exception& e) + { + edump((e.to_detail_string())); + throw; + } +} + +BOOST_AUTO_TEST_SUITE_END() + +//#define BOOST_TEST_MODULE "C++ Unit Tests for Graphene Blockchain Database" +#include +#include +#include + +boost::unit_test::test_suite* init_unit_test_suite(int argc, char* argv[]) { + for (int i=1; i tournament-2 + echo + if [ "$1" = "-c" ]; then + sleep 2 + else + break + fi + i=$[i + 1] + +done \ No newline at end of file From 66208a2d9ebaf168e67bd9239b3f0d651409c1ca Mon Sep 17 00:00:00 2001 From: Roman Olearski Date: Thu, 6 Apr 2017 09:46:17 +0200 Subject: [PATCH 08/10] I. DEX Task: The Peerplays DEX should only allow UIA and sidechain assets to be paired (traded) with the core token (PPY). --- libraries/chain/market_evaluator.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/libraries/chain/market_evaluator.cpp b/libraries/chain/market_evaluator.cpp index 27c31ae4..3cd072f2 100644 --- a/libraries/chain/market_evaluator.cpp +++ b/libraries/chain/market_evaluator.cpp @@ -52,6 +52,11 @@ void_result limit_order_create_evaluator::do_evaluate(const limit_order_create_o if( _sell_asset->options.blacklist_markets.size() ) FC_ASSERT( _sell_asset->options.blacklist_markets.find(_receive_asset->id) == _sell_asset->options.blacklist_markets.end() ); + // I. DEX Task + FC_ASSERT(_receive_asset->id == asset_id_type() || _sell_asset->id == asset_id_type(), + "No asset in the trade is CORE."); + + FC_ASSERT( is_authorized_asset( d, *_seller, *_sell_asset ) ); FC_ASSERT( is_authorized_asset( d, *_seller, *_receive_asset ) ); From 94ba6266c6d4327705dd40ab943b8838b5dda702 Mon Sep 17 00:00:00 2001 From: Roman Olearski Date: Sat, 8 Apr 2017 13:45:05 +0200 Subject: [PATCH 09/10] Implementing missing functionality (5) Peerplays Blockchain pre-launch settings & modifications I II III --- libraries/chain/db_init.cpp | 1 + .../graphene/chain/protocol/tournament.hpp | 2 +- .../graphene/chain/tournament_evaluator.hpp | 3 - .../graphene/chain/tournament_object.hpp | 3 + libraries/chain/match_object.cpp | 5 +- libraries/chain/protocol/tournament.cpp | 2 +- libraries/chain/tournament_evaluator.cpp | 20 +---- libraries/chain/tournament_object.cpp | 17 +++-- .../wallet/include/graphene/wallet/wallet.hpp | 4 +- libraries/wallet/wallet.cpp | 2 +- tests/tournament/tournament_tests.cpp | 76 +++++++++++++++---- 11 files changed, 88 insertions(+), 47 deletions(-) diff --git a/libraries/chain/db_init.cpp b/libraries/chain/db_init.cpp index c1863e67..69bee98b 100644 --- a/libraries/chain/db_init.cpp +++ b/libraries/chain/db_init.cpp @@ -179,6 +179,7 @@ void database::initialize_evaluators() register_evaluator(); register_evaluator(); register_evaluator(); + register_evaluator(); } void database::initialize_indexes() diff --git a/libraries/chain/include/graphene/chain/protocol/tournament.hpp b/libraries/chain/include/graphene/chain/protocol/tournament.hpp index bc26910a..9138af48 100644 --- a/libraries/chain/include/graphene/chain/protocol/tournament.hpp +++ b/libraries/chain/include/graphene/chain/protocol/tournament.hpp @@ -153,7 +153,7 @@ namespace graphene { namespace chain { asset fee; - /// The account that payed the buy-in for the tournament + /// The account that is paying the fee account_id_type payer_account_id; /// The account that would play in the tournament, would receive any winnings. diff --git a/libraries/chain/include/graphene/chain/tournament_evaluator.hpp b/libraries/chain/include/graphene/chain/tournament_evaluator.hpp index e276ab13..18a0140d 100644 --- a/libraries/chain/include/graphene/chain/tournament_evaluator.hpp +++ b/libraries/chain/include/graphene/chain/tournament_evaluator.hpp @@ -33,9 +33,6 @@ namespace graphene { namespace chain { private: const tournament_object* _tournament_obj = nullptr; const tournament_details_object* _tournament_details_obj = nullptr; - //const account_object* _payer_account = nullptr; - //const account_object* _player_account = nullptr; - //const asset_object* _buy_in_back_asset_type = nullptr; public: typedef tournament_leave_operation operation_type; diff --git a/libraries/chain/include/graphene/chain/tournament_object.hpp b/libraries/chain/include/graphene/chain/tournament_object.hpp index 840509ce..814d019f 100644 --- a/libraries/chain/include/graphene/chain/tournament_object.hpp +++ b/libraries/chain/include/graphene/chain/tournament_object.hpp @@ -38,6 +38,9 @@ namespace graphene { namespace chain { /// List of payers who have contributed to the prize pool flat_map payers; + /// List of player payer pairs needed by torunament leave operation + flat_map players_payers; + /// List of all matches in this tournament. When the tournament starts, all matches /// are created. Matches in the first round will have players, matches in later /// rounds will not be populated. diff --git a/libraries/chain/match_object.cpp b/libraries/chain/match_object.cpp index 996ca04d..7819d21e 100644 --- a/libraries/chain/match_object.cpp +++ b/libraries/chain/match_object.cpp @@ -125,13 +125,12 @@ namespace graphene { namespace chain { } else { - // $$$ III. Rock Paper Scissors Game Need to review how Ties are dealt with. - short i = std::rand() % match.players.size(); // ! event.db.get_random_bits(match.players.size()) ; + // III. Rock Paper Scissors Game Need to review how Ties are dealt with. + short i = match.number_of_ties == match.games.size() ? 0 : event.db.get_random_bits(match.players.size()) ; match.match_winners.insert(match.players[i]); ++match.number_of_wins[i]; if (match.number_of_ties == match.games.size()) match.game_winners[match.game_winners.size()-1].insert(match.players[i]); - } match.end_time = event.db.head_block_time(); diff --git a/libraries/chain/protocol/tournament.cpp b/libraries/chain/protocol/tournament.cpp index cc5fa667..57e80bf3 100644 --- a/libraries/chain/protocol/tournament.cpp +++ b/libraries/chain/protocol/tournament.cpp @@ -59,7 +59,7 @@ share_type tournament_leave_operation::calculate_fee(const fee_parameters_type& void tournament_leave_operation::validate()const { - FC_ASSERT( fee.amount >= 0 ); + // todo FC_ASSERT( fee.amount >= 0 ); } diff --git a/libraries/chain/tournament_evaluator.cpp b/libraries/chain/tournament_evaluator.cpp index 75bb54ea..73271b30 100644 --- a/libraries/chain/tournament_evaluator.cpp +++ b/libraries/chain/tournament_evaluator.cpp @@ -186,23 +186,11 @@ namespace graphene { namespace chain { _tournament_details_obj = &_tournament_obj->tournament_details_id(d); FC_ASSERT(_tournament_details_obj->registered_players.find(op.player_account_id) != _tournament_details_obj->registered_players.end(), "Player is not registered for this tournament"); - FC_ASSERT(_tournament_details_obj->payers.find(op.payer_account_id) != _tournament_details_obj->payers.end(), - "Payer is not registered for this tournament"); -#if 0 - _payer_account = &op.payer_account_id(d); - _buy_in_back_asset_type = &(_tournament_obj->options.buy_in.asset_id(d)); - GRAPHENE_ASSERT(!_buy_in_back_asset_type->is_transfer_restricted(), - transfer_restricted_transfer_asset, - "Asset {asset} has transfer_restricted flag enabled", - ("asset", _buy_in_back_asset_type->id)); + //FC_ASSERT(_tournament_details_obj->payers.find(op.payer_account_id) != _tournament_details_obj->payers.end(), + // "Payer is not registered for this tournament"); - GRAPHENE_ASSERT(is_authorized_asset(d, *_payer_account, *_buy_in_back_asset_type), - transfer_from_account_not_whitelisted, - "payer account ${payer} is not whitelisted for asset ${asset}", - ("payer", op.payer_account_id) - ("asset", _buy_in_back_asset_type->id)); -#endif - FC_ASSERT(_tournament_obj->get_state() == tournament_state::accepting_registrations); + FC_ASSERT(_tournament_obj->get_state() == tournament_state::accepting_registrations || + _tournament_obj->get_state() == tournament_state::awaiting_start); FC_ASSERT(d.head_block_time() <= _tournament_obj->options.registration_deadline, "Registration deadline has already passed"); return void_result(); diff --git a/libraries/chain/tournament_object.cpp b/libraries/chain/tournament_object.cpp index 4b4c6d07..4adb5bb7 100644 --- a/libraries/chain/tournament_object.cpp +++ b/libraries/chain/tournament_object.cpp @@ -391,6 +391,7 @@ namespace graphene { namespace chain { event.db.modify(tournament_details_obj, [&](tournament_details_object& tournament_details_obj){ tournament_details_obj.payers[event.payer_id] += tournament_obj->options.buy_in.amount; tournament_details_obj.registered_players.insert(event.player_id); + tournament_details_obj.players_payers[event.player_id] = event.payer_id; }); ++tournament_obj->registered_players; tournament_obj->prize_pool += tournament_obj->options.buy_in.amount; @@ -399,19 +400,21 @@ namespace graphene { namespace chain { void unregister_player(const player_unregistered& event) { fc_ilog(fc::logger::get("tournament"), - "In register_player action, player_id is ${player_id}, payer_id is ${payer_id}", - ("player_id", event.player_id)("payer_id", event.payer_id)); + "In unregister_player action, player_id is ${player_id}", + ("player_id", event.player_id)); event.db.adjust_balance(event.payer_id, tournament_obj->options.buy_in); const tournament_details_object& tournament_details_obj = tournament_obj->tournament_details_id(event.db); event.db.modify(tournament_details_obj, [&](tournament_details_object& tournament_details_obj){ - tournament_details_obj.payers[event.payer_id] -= tournament_obj->options.buy_in.amount; - if (tournament_details_obj.payers[event.payer_id] <= 0) - tournament_details_obj.payers.erase(event.payer_id); + account_id_type payer_id = tournament_details_obj.players_payers[event.player_id]; + tournament_details_obj.payers[payer_id] -= tournament_obj->options.buy_in.amount; + if (tournament_details_obj.payers[payer_id] <= 0) + tournament_details_obj.payers.erase(payer_id); tournament_details_obj.registered_players.erase(event.player_id); + tournament_details_obj.players_payers.erase(event.player_id); }); - ++tournament_obj->registered_players; - tournament_obj->prize_pool += tournament_obj->options.buy_in.amount; + --tournament_obj->registered_players; + tournament_obj->prize_pool -= tournament_obj->options.buy_in.amount; } // Transition table for tournament diff --git a/libraries/wallet/include/graphene/wallet/wallet.hpp b/libraries/wallet/include/graphene/wallet/wallet.hpp index 14ee95ef..bd09660f 100644 --- a/libraries/wallet/include/graphene/wallet/wallet.hpp +++ b/libraries/wallet/include/graphene/wallet/wallet.hpp @@ -1501,13 +1501,13 @@ class wallet_api signed_transaction tournament_join( string payer_account, string player_account, tournament_id_type tournament_id, string buy_in_amount, string buy_in_asset_symbol, bool broadcast = false ); /** Leave an existing tournament - * @param payer_account the account that payed buy-in and the fee to join the tournament + * @param payer_account the account that is paying the fee * @param player_account the account that would be playing in the tournament * @param tournament_id the tournament the user wishes to leave * @param broadcast true if you wish to broadcast the transaction * @return the signed version of the transaction */ - signed_transaction tournament_leave( string payer_account, string player_account, tournament_id_type tournament_id, bool broadcast = false); + signed_transaction tournament_leave(string payer_account, string player_account, tournament_id_type tournament_id, bool broadcast = false); /** Get a list of upcoming tournaments * @param limit the number of tournaments to return diff --git a/libraries/wallet/wallet.cpp b/libraries/wallet/wallet.cpp index 8d282da5..40638e54 100644 --- a/libraries/wallet/wallet.cpp +++ b/libraries/wallet/wallet.cpp @@ -4859,8 +4859,8 @@ signed_transaction wallet_api::tournament_leave( string payer_account, bool broadcast) { FC_ASSERT( !is_locked() ); - account_object payer_account_obj = get_account(payer_account); account_object player_account_obj = get_account(player_account); + account_object payer_account_obj = get_account(payer_account); //graphene::chain::tournament_object tournament_obj = my->get_object(tournament_id); signed_transaction tx; diff --git a/tests/tournament/tournament_tests.cpp b/tests/tournament/tournament_tests.cpp index 1da5e3c4..8889f205 100644 --- a/tests/tournament/tournament_tests.cpp +++ b/tests/tournament/tournament_tests.cpp @@ -234,7 +234,34 @@ public: players.insert(player_id); players_keys[player_id] = sig_priv_key; - } + } + + void leave_tournament(const tournament_id_type & tournament_id, + const account_id_type& player_id, + const account_id_type& payer_id, + const fc::ecc::private_key& sig_priv_key + ) + { + graphene::chain::database& db = df.db; + const chain_parameters& params = db.get_global_properties().parameters; + signed_transaction tx; + tournament_leave_operation op; + + op.payer_account_id = payer_id; + op.player_account_id = player_id; + op.tournament_id = tournament_id; + tx.operations = {op}; + for( auto& op : tx.operations ) + db.current_fee_schedule().set_fee(op); + tx.validate(); + tx.set_expiration(db.head_block_time() + fc::seconds( params.block_interval * (params.maintenance_skip_slots + 1) * 3)); + df.sign(tx, sig_priv_key); + PUSH_TX(db, tx); + + //players.erase(player_id); + } + + // stolen from cli_wallet void rps_throw(const game_id_type& game_id, @@ -434,13 +461,14 @@ BOOST_FIXTURE_TEST_CASE( simple, database_fixture ) #define TEST2_NR_OF_PLAYERS_NUMBER 4 #endif BOOST_TEST_MESSAGE("Hello simple tournament test"); - ACTORS((nathan)(alice)(bob)(carol)(dave)(ed)(frank)(george)(harry)(ike)); + ACTORS((nathan)(alice)(bob)(carol)(dave)(ed)(frank)(george)(harry)(ike)(romek)); tournaments_helper tournament_helper(*this); fc::ecc::private_key nathan_priv_key = fc::ecc::private_key::regenerate(fc::sha256::hash(string("nathan"))); BOOST_TEST_MESSAGE( "Giving folks some money" ); transfer(committee_account, nathan_id, asset(1000000000)); + transfer(committee_account, romek_id, asset(2000000)); transfer(committee_account, alice_id, asset(2000000)); transfer(committee_account, bob_id, asset(3000000)); transfer(committee_account, carol_id, asset(4000000)); @@ -486,8 +514,13 @@ BOOST_FIXTURE_TEST_CASE( simple, database_fixture ) 3, 1, 3, 3600, 3, 3, true); BOOST_REQUIRE(tournament_id == tournament_id_type(1)); tournament_helper.join_tournament(tournament_id, alice_id, alice_id, fc::ecc::private_key::regenerate(fc::sha256::hash(string("alice"))), buy_in); + // romek joins but will leave + //tournament_helper.leave_tournament(tournament_id, romek_id, romek_id, fc::ecc::private_key::regenerate(fc::sha256::hash(string("romek")))); + tournament_helper.join_tournament(tournament_id, romek_id, romek_id, fc::ecc::private_key::regenerate(fc::sha256::hash(string("romek"))), buy_in); tournament_helper.join_tournament(tournament_id, bob_id, bob_id, fc::ecc::private_key::regenerate(fc::sha256::hash(string("bob"))), buy_in); tournament_helper.join_tournament(tournament_id, carol_id, carol_id, fc::ecc::private_key::regenerate(fc::sha256::hash(string("carol"))), buy_in); + // romek leaves + tournament_helper.leave_tournament(tournament_id, romek_id, romek_id, fc::ecc::private_key::regenerate(fc::sha256::hash(string("romek")))); tournament_helper.join_tournament(tournament_id, dave_id, dave_id, fc::ecc::private_key::regenerate(fc::sha256::hash(string("dave"))), buy_in); #if TEST2_NR_OF_PLAYERS_NUMBER > 4 tournament_helper.join_tournament(tournament_id, ed_id, ed_id, fc::ecc::private_key::regenerate(fc::sha256::hash(string("ed"))), buy_in); @@ -535,12 +568,7 @@ BOOST_FIXTURE_TEST_CASE( simple, database_fixture ) #endif }; -#if 0 - // trying to randomize automatic moves ? - auto n = std::rand() % 100; - for(int i = 0; i < n ; ++i) - db.get_random_bits(3); -#endif + abc("@ tournament awaiting start"); BOOST_TEST_MESSAGE( "Generating blocks, waiting for tournaments' completion"); generate_block(); @@ -599,6 +627,7 @@ BOOST_FIXTURE_TEST_CASE( simple, database_fixture ) } #endif +#if 1 // Test of handling ties, creating two tournamenst, joinig players, // All generated moves are identical. BOOST_FIXTURE_TEST_CASE( ties, database_fixture ) @@ -711,6 +740,7 @@ BOOST_FIXTURE_TEST_CASE( ties, database_fixture ) throw; } } +#endif // Test of canceled tournament // Checking buyin refund. @@ -1074,8 +1104,8 @@ BOOST_FIXTURE_TEST_CASE( massive, database_fixture ) { try { - #define MIN_TOURNAMENTS_NUMBER 1 - #define MAX_TOURNAMENTS_NUMBER 10 + #define MIN_TOURNAMENTS_NUMBER 7 + #define MAX_TOURNAMENTS_NUMBER 13 #define MIN_PLAYERS_NUMBER 2 #define MAX_PLAYERS_NUMBER 64 @@ -1093,7 +1123,7 @@ BOOST_FIXTURE_TEST_CASE( massive, database_fixture ) // creating a pool of actors std::vector> actors; - for(unsigned i = 0; i < 2 * MAX_PLAYERS_NUMBER; ++i) + for(unsigned i = 0; i < 3 * MAX_PLAYERS_NUMBER; ++i) { std::string name = "account" + std::to_string(i); auto priv_key = generate_private_key(name); @@ -1114,14 +1144,34 @@ BOOST_FIXTURE_TEST_CASE( massive, database_fixture ) asset buy_in = asset(1000 * number_of_players + 100 * i); tournament_id_type tournament_id; tournament_id = tournament_helper.create_tournament (nathan_id, nathan_priv_key, buy_in, number_of_players, 30, 30, number_of_wins); + const tournament_object& tournament = tournament_id(db); - for (unsigned j = 0; j < actors.size() && number_of_players > 0; ++j) + for (unsigned j = 0; j < actors.size()-1 && number_of_players > 0; ++j) { if (number_of_players < actors.size() - j && std::rand() % 2 == 0) continue; auto a = actors[j]; - --number_of_players; tournament_helper.join_tournament(tournament_id, std::get<1>(a), std::get<1>(a), std::get<2>(a), buy_in); + if (j == i) + { + BOOST_TEST_MESSAGE("Player " + std::get<0>(a) + " is leaving tournament " + std::to_string(i) + + ", when tournament state is " + std::to_string((int)tournament.get_state())); + tournament_helper.leave_tournament(tournament_id, std::get<1>(a), std::get<1>(a), std::get<2>(a)); + continue; + } + --number_of_players; + if (!number_of_players) + { + BOOST_TEST_MESSAGE("Player " + std::get<0>(a) + " is leaving tournament " + std::to_string(i) + + ", when tournament state is " + std::to_string((int)tournament.get_state())); + tournament_helper.leave_tournament(tournament_id, std::get<1>(a), std::get<1>(a), std::get<2>(a)); + ++j; + a = actors[j]; + BOOST_TEST_MESSAGE("Player " + std::get<0>(a) + " is joinig tournament " + std::to_string(i) + + ", when tournament state is " + std::to_string((int)tournament.get_state())); + tournament_helper.join_tournament(tournament_id, std::get<1>(a), std::get<1>(a), std::get<2>(a), buy_in); + } } + BOOST_TEST_MESSAGE("Tournament " + std::to_string(i) + " is in state " + std::to_string((int)tournament.get_state())); } uint16_t tournaments_to_complete = number_of_tournaments; From c16454890cf185012fd0e7041a34b6fa72894036 Mon Sep 17 00:00:00 2001 From: Roman Olearski Date: Sat, 8 Apr 2017 20:16:05 +0200 Subject: [PATCH 10/10] Implementing missing functionality (6) Peerplays Blockchain pre-launch settings & modifications I II III --- .../graphene/chain/tournament_object.hpp | 1 + .../wallet/include/graphene/wallet/wallet.hpp | 1 + libraries/wallet/wallet.cpp | 18 ++++++++++++++++-- tests/tournaments.sh | 11 ++++++++--- 4 files changed, 26 insertions(+), 5 deletions(-) diff --git a/libraries/chain/include/graphene/chain/tournament_object.hpp b/libraries/chain/include/graphene/chain/tournament_object.hpp index 814d019f..c4a9bdfd 100644 --- a/libraries/chain/include/graphene/chain/tournament_object.hpp +++ b/libraries/chain/include/graphene/chain/tournament_object.hpp @@ -235,6 +235,7 @@ FC_REFLECT_DERIVED(graphene::chain::tournament_details_object, (graphene::db::ob (tournament_id) (registered_players) (payers) + (players_payers) (matches)) //FC_REFLECT_TYPENAME(graphene::chain::tournament_object) // manually serialized FC_REFLECT(graphene::chain::tournament_object, (creator)) diff --git a/libraries/wallet/include/graphene/wallet/wallet.hpp b/libraries/wallet/include/graphene/wallet/wallet.hpp index bd09660f..9a02a17b 100644 --- a/libraries/wallet/include/graphene/wallet/wallet.hpp +++ b/libraries/wallet/include/graphene/wallet/wallet.hpp @@ -1733,6 +1733,7 @@ FC_API( graphene::wallet::wallet_api, (receive_blind_transfer) (tournament_create) (tournament_join) + (tournament_leave) (rps_throw) (get_upcoming_tournaments) (get_tournament) diff --git a/libraries/wallet/wallet.cpp b/libraries/wallet/wallet.cpp index 40638e54..fcb83538 100644 --- a/libraries/wallet/wallet.cpp +++ b/libraries/wallet/wallet.cpp @@ -2521,14 +2521,28 @@ public: tournament_object tournament = result.as(); tournament_details_object tournament_details = _remote_db->get_objects({result["tournament_details_id"].as()})[0].as(); tournament_state state = tournament.get_state(); - if (state == tournament_state::awaiting_start) + if (state == tournament_state::accepting_registrations) + { + ss << "Tournament is accepting registrations\n"; + ss << "Players " << tournament.registered_players << "/" << tournament.options.number_of_players << ":\n"; + for (const account_id_type& player : tournament_details.registered_players) + ss << "\t" << get_account(player).name << "\n"; + } + else if (state == tournament_state::registration_period_expired) + { + ss << "Tournament registration period expired\n"; + ss << "Players " << tournament.registered_players << "/" << tournament.options.number_of_players << ":\n"; + for (const account_id_type& player : tournament_details.registered_players) + ss << "\t" << get_account(player).name << "\n"; + } + else if (state == tournament_state::awaiting_start) { ss << "Tournament starts at " << tournament.start_time->to_iso_string() << "\n"; ss << "Players:\n"; for (const account_id_type& player : tournament_details.registered_players) ss << "\t" << get_account(player).name << "\n"; } - else if (state == tournament_state::in_progress || + else if (state == tournament_state::in_progress || state == tournament_state::concluded) { unsigned num_matches = tournament_details.matches.size(); diff --git a/tests/tournaments.sh b/tests/tournaments.sh index 9ec39147..d0e925f8 100755 --- a/tests/tournaments.sh +++ b/tests/tournaments.sh @@ -1,13 +1,18 @@ #!/bin/bash -e i=1 +file='tournament-2' +file2=$file while [ 0 ]; do echo "*** $i `date`" - if [ -f tournament-2 ]; then - mv tournament-2 tournament-2-last + if [ "$1" = "-c" ]; then + file2=$file-`date +%Y-%m-%d:%H:%M:%S` + + elif [ -f $file2 ]; then + mv $file2 $file2-last fi - ./tournament_test --log_level=message 2> tournament-2 + ./tournament_test --log_level=message 2> $file2 echo if [ "$1" = "-c" ]; then sleep 2