From 440fdb566bfe5a9c8805c1c76d247bc9f0959b9a Mon Sep 17 00:00:00 2001 From: Eric Frias Date: Sun, 29 Apr 2018 18:59:15 -0400 Subject: [PATCH] Fix bug causing a cancel of one event in an event group to cancel other events --- libraries/chain/db_bet.cpp | 47 +++++++++++++------- libraries/fc | 2 +- tests/betting/betting_tests.cpp | 77 ++++++++++++++++++++++++++++++++- 3 files changed, 109 insertions(+), 17 deletions(-) diff --git a/libraries/chain/db_bet.cpp b/libraries/chain/db_bet.cpp index e56756e6..b8e3ecb1 100644 --- a/libraries/chain/db_bet.cpp +++ b/libraries/chain/db_bet.cpp @@ -282,15 +282,32 @@ void database::settle_betting_market_group(const betting_market_group_object& be void database::remove_completed_events() { const auto& event_index = get_index_type().indices().get(); - auto canceled_events = boost::make_iterator_range(event_index.equal_range(event_status::canceled)); - auto settled_events = boost::make_iterator_range(event_index.equal_range(event_status::settled)); - for (const event_object& event : boost::join(canceled_events, settled_events)) { - dlog("removing event ${id}", ("id", event.id)); + + auto canceled_event_iter = event_index.lower_bound(event_status::canceled); + while (canceled_event_iter != event_index.end() && canceled_event_iter->get_status() == event_status::canceled) + { + const event_object& event = *canceled_event_iter; + ++canceled_event_iter; + dlog("removing canceled event ${id}", ("id", event.id)); + remove(event); + } + + auto settled_event_iter = event_index.lower_bound(event_status::settled); + while (settled_event_iter != event_index.end() && settled_event_iter->get_status() == event_status::settled) + { + const event_object& event = *settled_event_iter; + ++settled_event_iter; + dlog("removing settled event ${id}", ("id", event.id)); remove(event); } } -share_type adjust_betting_position(database& db, account_id_type bettor_id, betting_market_id_type betting_market_id, bet_type back_or_lay, share_type bet_amount, share_type matched_amount) +share_type adjust_betting_position(database& db, + account_id_type bettor_id, + betting_market_id_type betting_market_id, + bet_type back_or_lay, + share_type bet_amount, + share_type matched_amount) { try { assert(bet_amount >= 0); @@ -304,8 +321,8 @@ share_type adjust_betting_position(database& db, account_id_type bettor_id, bett if (itr == index.end()) { db.create([&](betting_market_position_object& position) { - position.bettor_id = bettor_id; - position.betting_market_id = betting_market_id; + position.bettor_id = bettor_id; + position.betting_market_id = betting_market_id; position.pay_if_payout_condition = back_or_lay == bet_type::back ? bet_amount + matched_amount : 0; position.pay_if_not_payout_condition = back_or_lay == bet_type::lay ? bet_amount + matched_amount : 0; position.pay_if_canceled = bet_amount; @@ -314,8 +331,8 @@ share_type adjust_betting_position(database& db, account_id_type bettor_id, bett }); } else { db.modify(*itr, [&](betting_market_position_object& position) { - assert(position.bettor_id == bettor_id); - assert(position.betting_market_id == betting_market_id); + assert(position.bettor_id == bettor_id); + assert(position.betting_market_id == betting_market_id); position.pay_if_payout_condition += back_or_lay == bet_type::back ? bet_amount + matched_amount : 0; position.pay_if_not_payout_condition += back_or_lay == bet_type::lay ? bet_amount + matched_amount : 0; position.pay_if_canceled += bet_amount; @@ -420,12 +437,12 @@ int match_bet(database& db, const bet_object& taker_bet, const bet_object& maker assert(taker_amount_to_match <= taker_bet.amount_to_bet.amount); assert(taker_amount_to_match / taker_odds_ratio * taker_odds_ratio == taker_amount_to_match); { - // verify we're getting the odds we expect - fc::uint128_t payout_128 = maker_amount_to_match.value; - payout_128 += taker_amount_to_match.value; - payout_128 *= GRAPHENE_BETTING_ODDS_PRECISION; - payout_128 /= maker_bet.back_or_lay == bet_type::back ? maker_amount_to_match.value : taker_amount_to_match.value; - assert(payout_128.to_uint64() == maker_bet.backer_multiplier); + // verify we're getting the odds we expect + fc::uint128_t payout_128 = maker_amount_to_match.value; + payout_128 += taker_amount_to_match.value; + payout_128 *= GRAPHENE_BETTING_ODDS_PRECISION; + payout_128 /= maker_bet.back_or_lay == bet_type::back ? maker_amount_to_match.value : taker_amount_to_match.value; + assert(payout_128.to_uint64() == maker_bet.backer_multiplier); } #endif diff --git a/libraries/fc b/libraries/fc index 8df97c6b..b1bba0a5 160000 --- a/libraries/fc +++ b/libraries/fc @@ -1 +1 @@ -Subproject commit 8df97c6b3543c6d555377ceddc884cf2011c68e4 +Subproject commit b1bba0a5d7a3d06a8e5b35ae2a17812919cbe0b3 diff --git a/tests/betting/betting_tests.cpp b/tests/betting/betting_tests.cpp index 3815af5f..22599012 100644 --- a/tests/betting/betting_tests.cpp +++ b/tests/betting/betting_tests.cpp @@ -32,6 +32,7 @@ #include #include +#include #include #include @@ -48,6 +49,18 @@ #include //#include +struct enable_betting_logging_config { + enable_betting_logging_config() + { + fc::logger::get("betting").add_appender(fc::appender::get("stdout")); + fc::logger::get("betting").set_log_level(fc::log_level::debug); + } + ~enable_betting_logging_config() { + fc::logger::get("betting").remove_appender(fc::appender::get("stdout")); + } +}; +BOOST_GLOBAL_FIXTURE( enable_betting_logging_config ); + using namespace graphene::chain; using namespace graphene::chain::test; using namespace graphene::chain::keywords; @@ -766,12 +779,14 @@ BOOST_AUTO_TEST_CASE( testnet_witness_block_production_error ) CREATE_ICE_HOCKEY_BETTING_MARKET(false, 0); create_betting_market_group({{"en", "Unused"}}, capitals_vs_blackhawks.id, betting_market_rules.id, asset_id_type(), false, 0); generate_blocks(1); + const betting_market_group_object& unused_betting_markets = *db.get_index_type().indices().get().rbegin(); BOOST_TEST_MESSAGE("setting the event in progress"); update_event(capitals_vs_blackhawks.id, _status = event_status::in_progress); generate_blocks(1); BOOST_CHECK(capitals_vs_blackhawks.get_status() == event_status::in_progress); BOOST_CHECK(moneyline_betting_markets.get_status() == betting_market_group_status::in_play); + BOOST_CHECK(unused_betting_markets.get_status() == betting_market_group_status::in_play); BOOST_TEST_MESSAGE("setting the event to finished"); update_event(capitals_vs_blackhawks.id, _status = event_status::finished); @@ -780,7 +795,7 @@ BOOST_AUTO_TEST_CASE( testnet_witness_block_production_error ) BOOST_CHECK(moneyline_betting_markets.get_status() == betting_market_group_status::closed); BOOST_CHECK(capitals_win_market.get_status() == betting_market_status::unresolved); BOOST_CHECK(blackhawks_win_market.get_status() == betting_market_status::unresolved); - //BOOST_CHECK(unused_betting_market_group.get_status() == betting_market_group_status::closed); + BOOST_CHECK(unused_betting_markets.get_status() == betting_market_group_status::closed); BOOST_TEST_MESSAGE("setting the event to canceled"); update_event(capitals_vs_blackhawks.id, _status = event_status::canceled); @@ -788,6 +803,66 @@ BOOST_AUTO_TEST_CASE( testnet_witness_block_production_error ) } FC_LOG_AND_RETHROW() } +BOOST_AUTO_TEST_CASE( cancel_one_event_in_group ) +{ + // test that creates an event group with two events in it. We walk one event through the + // usual sequence and cancel it, verify that it doesn't alter the other event in the group + try + { + CREATE_ICE_HOCKEY_BETTING_MARKET(false, 0); + + // create a second event in the same betting market group + create_event({{"en", "Boston Bruins/Pittsburgh Penguins"}}, {{"en", "2016-17"}}, nhl.id); + generate_blocks(1); + const event_object& bruins_vs_penguins = *db.get_index_type().indices().get().rbegin(); + create_betting_market_group({{"en", "Moneyline"}}, bruins_vs_penguins.id, betting_market_rules.id, asset_id_type(), false, 0); + generate_blocks(1); + const betting_market_group_object& bruins_penguins_moneyline_betting_markets = *db.get_index_type().indices().get().rbegin(); + create_betting_market(bruins_penguins_moneyline_betting_markets.id, {{"en", "Boston Bruins win"}}); + generate_blocks(1); + const betting_market_object& bruins_win_market = *db.get_index_type().indices().get().rbegin(); + create_betting_market(bruins_penguins_moneyline_betting_markets.id, {{"en", "Pittsburgh Penguins win"}}); + generate_blocks(1); + const betting_market_object& penguins_win_market = *db.get_index_type().indices().get().rbegin(); + (void)bruins_win_market; (void)penguins_win_market; + + // check the initial state + BOOST_CHECK(capitals_vs_blackhawks.get_status() == event_status::upcoming); + BOOST_CHECK(bruins_vs_penguins.get_status() == event_status::upcoming); + + BOOST_TEST_MESSAGE("setting the capitals_vs_blackhawks event to in-progress, leaving bruins_vs_penguins in upcoming"); + update_event(capitals_vs_blackhawks.id, _status = event_status::in_progress); + generate_blocks(1); + BOOST_CHECK(capitals_vs_blackhawks.get_status() == event_status::in_progress); + BOOST_CHECK(moneyline_betting_markets.get_status() == betting_market_group_status::in_play); + + BOOST_CHECK(bruins_vs_penguins.get_status() == event_status::upcoming); + BOOST_CHECK(bruins_penguins_moneyline_betting_markets.get_status() == betting_market_group_status::upcoming); + BOOST_CHECK(bruins_win_market.get_status() == betting_market_status::unresolved); + BOOST_CHECK(penguins_win_market.get_status() == betting_market_status::unresolved); + + BOOST_TEST_MESSAGE("setting the capitals_vs_blackhawks event to finished"); + update_event(capitals_vs_blackhawks.id, _status = event_status::finished); + generate_blocks(1); + BOOST_CHECK(capitals_vs_blackhawks.get_status() == event_status::finished); + BOOST_CHECK(moneyline_betting_markets.get_status() == betting_market_group_status::closed); + BOOST_CHECK(capitals_win_market.get_status() == betting_market_status::unresolved); + BOOST_CHECK(blackhawks_win_market.get_status() == betting_market_status::unresolved); + BOOST_CHECK(bruins_vs_penguins.get_status() == event_status::upcoming); + BOOST_CHECK(bruins_penguins_moneyline_betting_markets.get_status() == betting_market_group_status::upcoming); + BOOST_CHECK(bruins_win_market.get_status() == betting_market_status::unresolved); + BOOST_CHECK(penguins_win_market.get_status() == betting_market_status::unresolved); + + BOOST_TEST_MESSAGE("setting the capitals_vs_blackhawks event to canceled"); + update_event(capitals_vs_blackhawks.id, _status = event_status::canceled); + generate_blocks(1); + BOOST_CHECK(bruins_vs_penguins.get_status() == event_status::upcoming); + BOOST_CHECK(bruins_penguins_moneyline_betting_markets.get_status() == betting_market_group_status::upcoming); + BOOST_CHECK(bruins_win_market.get_status() == betting_market_status::unresolved); + BOOST_CHECK(penguins_win_market.get_status() == betting_market_status::unresolved); + + } FC_LOG_AND_RETHROW() +} BOOST_AUTO_TEST_SUITE_END()