diff --git a/libraries/plugins/bookie/bookie_plugin.cpp b/libraries/plugins/bookie/bookie_plugin.cpp index f7a3f8b8..86065674 100644 --- a/libraries/plugins/bookie/bookie_plugin.cpp +++ b/libraries/plugins/bookie/bookie_plugin.cpp @@ -111,6 +111,9 @@ class bookie_plugin_impl */ void on_objects_changed(const vector& changed_object_ids); + void on_objects_new(const vector& new_object_ids); + void on_objects_removed(const vector& removed_object_ids); + /** this method is called as a callback after a block is applied * and will process/index all operations that were applied in the block. */ @@ -149,8 +152,54 @@ bookie_plugin_impl::~bookie_plugin_impl() { } +void bookie_plugin_impl::on_objects_new(const vector& new_object_ids) +{ + //idump((new_object_ids)); + graphene::chain::database& db = database(); + auto& event_id_index = db.get_index_type().indices().get(); + auto& betting_market_group_id_index = db.get_index_type().indices().get(); + auto& betting_market_id_index = db.get_index_type().indices().get(); + + for (const object_id_type& new_object_id : new_object_ids) + { + if (new_object_id.space() == event_id_type::space_id && + new_object_id.type() == event_id_type::type_id) + { + event_id_type new_event_id = new_object_id; + ilog("Creating new persistent event object ${id}", ("id", new_event_id)); + db.create([&](persistent_event_object& saved_event_obj) { + saved_event_obj.ephemeral_event_object = new_event_id(db); + }); + } + else if (new_object_id.space() == betting_market_group_object::space_id && + new_object_id.type() == betting_market_group_object::type_id) + { + betting_market_group_id_type new_betting_market_group_id = new_object_id; + ilog("Creating new persistent betting_market_group object ${id}", ("id", new_betting_market_group_id)); + db.create([&](persistent_betting_market_group_object& saved_betting_market_group_obj) { + saved_betting_market_group_obj.ephemeral_betting_market_group_object = new_betting_market_group_id(db); + }); + } + else if (new_object_id.space() == betting_market_object::space_id && + new_object_id.type() == betting_market_object::type_id) + { + betting_market_id_type new_betting_market_id = new_object_id; + ilog("Creating new persistent betting_market object ${id}", ("id", new_betting_market_id)); + db.create([&](persistent_betting_market_object& saved_betting_market_obj) { + saved_betting_market_obj.ephemeral_betting_market_object = new_betting_market_id(db); + }); + } + } +} + +void bookie_plugin_impl::on_objects_removed(const vector& removed_object_ids) +{ + //idump((removed_object_ids)); +} + void bookie_plugin_impl::on_objects_changed(const vector& changed_object_ids) { + //idump((changed_object_ids)); graphene::chain::database& db = database(); auto& event_id_index = db.get_index_type().indices().get(); auto& betting_market_group_id_index = db.get_index_type().indices().get(); @@ -162,109 +211,63 @@ void bookie_plugin_impl::on_objects_changed(const vector& change changed_object_id.type() == event_id_type::type_id) { event_id_type changed_event_id = changed_object_id; - const event_object* new_event_obj = nullptr; - try - { - new_event_obj = &changed_event_id(db); - } - catch (fc::exception& e) - { - } - // new_event_obj should point to the now-changed event_object, or null if it was removed from the database - const persistent_event_object* old_event_obj = nullptr; auto persistent_event_iter = event_id_index.find(changed_event_id); if (persistent_event_iter != event_id_index.end()) old_event_obj = &*persistent_event_iter; - - // and old_event_obj is a pointer to our saved copy, or nullptr if it is a new object - if (old_event_obj && new_event_obj) + + if (old_event_obj) { ilog("Modifying persistent event object ${id}", ("id", changed_event_id)); db.modify(*old_event_obj, [&](persistent_event_object& saved_event_obj) { - saved_event_obj.ephemeral_event_object = *new_event_obj; - }); - } - else if (new_event_obj) - { - ilog("Creating new persistent event object ${id}", ("id", changed_event_id)); - db.create([&](persistent_event_object& saved_event_obj) { - saved_event_obj.ephemeral_event_object = *new_event_obj; + saved_event_obj.ephemeral_event_object = changed_event_id(db); }); } + else + elog("Received change notification on event ${event_id} that we didn't know about", ("event_id", changed_event_id)); } else if (changed_object_id.space() == betting_market_group_object::space_id && changed_object_id.type() == betting_market_group_object::type_id) { betting_market_group_id_type changed_betting_market_group_id = changed_object_id; - const betting_market_group_object* new_betting_market_group_obj = nullptr; - try - { - new_betting_market_group_obj = &changed_betting_market_group_id(db); - } - catch (fc::exception& e) - { - } - // new_betting_market_group_obj should point to the now-changed event_object, or null if it was removed from the database - const persistent_betting_market_group_object* old_betting_market_group_obj = nullptr; auto persistent_betting_market_group_iter = betting_market_group_id_index.find(changed_betting_market_group_id); if (persistent_betting_market_group_iter != betting_market_group_id_index.end()) old_betting_market_group_obj = &*persistent_betting_market_group_iter; - // and old_betting_market_group_obj is a pointer to our saved copy, or nullptr if it is a new object - if (old_betting_market_group_obj && new_betting_market_group_obj) + if (old_betting_market_group_obj) { ilog("Modifying persistent betting_market_group object ${id}", ("id", changed_betting_market_group_id)); db.modify(*old_betting_market_group_obj, [&](persistent_betting_market_group_object& saved_betting_market_group_obj) { - saved_betting_market_group_obj.ephemeral_betting_market_group_object = *new_betting_market_group_obj; - }); - } - else if (new_betting_market_group_obj) - { - ilog("Creating new persistent betting_market_group object ${id}", ("id", changed_betting_market_group_id)); - db.create([&](persistent_betting_market_group_object& saved_betting_market_group_obj) { - saved_betting_market_group_obj.ephemeral_betting_market_group_object = *new_betting_market_group_obj; + saved_betting_market_group_obj.ephemeral_betting_market_group_object = changed_betting_market_group_id(db); }); } + else + elog("Received change notification on betting market group ${betting_market_group_id} that we didn't know about", + ("betting_market_group_id", changed_betting_market_group_id)); } else if (changed_object_id.space() == betting_market_object::space_id && changed_object_id.type() == betting_market_object::type_id) { betting_market_id_type changed_betting_market_id = changed_object_id; - const betting_market_object* new_betting_market_obj = nullptr; - try - { - new_betting_market_obj = &changed_betting_market_id(db); - } - catch (fc::exception& e) - { - } - // new_betting_market_obj should point to the now-changed event_object, or null if it was removed from the database - const persistent_betting_market_object* old_betting_market_obj = nullptr; auto persistent_betting_market_iter = betting_market_id_index.find(changed_betting_market_id); if (persistent_betting_market_iter != betting_market_id_index.end()) old_betting_market_obj = &*persistent_betting_market_iter; - // and old_betting_market_obj is a pointer to our saved copy, or nullptr if it is a new object - if (old_betting_market_obj && new_betting_market_obj) + if (old_betting_market_obj) { ilog("Modifying persistent betting_market object ${id}", ("id", changed_betting_market_id)); db.modify(*old_betting_market_obj, [&](persistent_betting_market_object& saved_betting_market_obj) { - saved_betting_market_obj.ephemeral_betting_market_object = *new_betting_market_obj; - }); - } - else if (new_betting_market_obj) - { - ilog("Creating new persistent betting_market object ${id}", ("id", changed_betting_market_id)); - db.create([&](persistent_betting_market_object& saved_betting_market_obj) { - saved_betting_market_obj.ephemeral_betting_market_object = *new_betting_market_obj; + saved_betting_market_obj.ephemeral_betting_market_object = changed_betting_market_id(db); }); } + else + elog("Received change notification on betting market ${betting_market_id} that we didn't know about", + ("betting_market_id", changed_betting_market_id)); } } } @@ -276,61 +279,58 @@ void bookie_plugin_impl::on_block_applied( const signed_block& ) for( const optional& o_op : hist ) { if( !o_op.valid() ) - { continue; - } + const operation_history_object& op = *o_op; if( op.op.which() == operation::tag::value ) { - const bet_matched_operation& bet_matched_op = op.op.get(); - //idump((bet_matched_op)); - const asset& amount_bet = bet_matched_op.amount_bet; - // object may no longer exist - //const bet_object& bet = bet_matched_op.bet_id(db); - auto& persistent_bets_by_bet_id = db.get_index_type().indices().get(); - auto bet_iter = persistent_bets_by_bet_id.find(bet_matched_op.bet_id); - assert(bet_iter != persistent_bets_by_bet_id.end()); - if (bet_iter != persistent_bets_by_bet_id.end()) - { - db.modify(*bet_iter, [&]( persistent_bet_object& obj ) { - obj.amount_matched += amount_bet.amount; - }); - const bet_object& bet_obj = bet_iter->ephemeral_bet_object; + const bet_matched_operation& bet_matched_op = op.op.get(); + //idump((bet_matched_op)); + const asset& amount_bet = bet_matched_op.amount_bet; + // object may no longer exist + //const bet_object& bet = bet_matched_op.bet_id(db); + auto& persistent_bets_by_bet_id = db.get_index_type().indices().get(); + auto bet_iter = persistent_bets_by_bet_id.find(bet_matched_op.bet_id); + assert(bet_iter != persistent_bets_by_bet_id.end()); + if (bet_iter != persistent_bets_by_bet_id.end()) + { + db.modify(*bet_iter, [&]( persistent_bet_object& obj ) { + obj.amount_matched += amount_bet.amount; + }); + const bet_object& bet_obj = bet_iter->ephemeral_bet_object; - const betting_market_object& betting_market = bet_obj.betting_market_id(db); // TODO: this needs to look at the persistent version - const betting_market_group_object& betting_market_group = betting_market.group_id(db); // TODO: as does this - db.modify( betting_market_group, [&]( betting_market_group_object& obj ){ - obj.total_matched_bets_amount += amount_bet.amount; - }); - } + const betting_market_object& betting_market = bet_obj.betting_market_id(db); // TODO: this needs to look at the persistent version + const betting_market_group_object& betting_market_group = betting_market.group_id(db); // TODO: as does this + db.modify( betting_market_group, [&]( betting_market_group_object& obj ){ + obj.total_matched_bets_amount += amount_bet.amount; + }); + } } else if( op.op.which() == operation::tag::value ) { - FC_ASSERT(op.result.which() == operation_result::tag::value); - //object_id_type object_id = op.result.get(); - event_id_type object_id = op.result.get(); - FC_ASSERT( db.find_object(object_id), "invalid event specified" ); - const event_create_operation& event_create_op = op.op.get(); - for(const std::pair& pair : event_create_op.name) - { - localized_event_strings[pair.first].insert(event_string(object_id, pair.second)); - } + FC_ASSERT(op.result.which() == operation_result::tag::value); + //object_id_type object_id = op.result.get(); + event_id_type object_id = op.result.get(); + FC_ASSERT( db.find_object(object_id), "invalid event specified" ); + const event_create_operation& event_create_op = op.op.get(); + for(const std::pair& pair : event_create_op.name) + localized_event_strings[pair.first].insert(event_string(object_id, pair.second)); } else if( op.op.which() == operation::tag::value ) { - const event_update_operation& event_create_op = op.op.get(); - if (!event_create_op.new_name.valid()) - continue; - event_id_type event_id = event_create_op.event_id; - for(const std::pair& pair : *event_create_op.new_name) - { - // try insert - std::pair result = - localized_event_strings[pair.first].insert(event_string(event_id, pair.second)); - if (!result.second) - // update string only - result.first->second = pair.second; - } + const event_update_operation& event_create_op = op.op.get(); + if (!event_create_op.new_name.valid()) + continue; + event_id_type event_id = event_create_op.event_id; + for(const std::pair& pair : *event_create_op.new_name) + { + // try insert + std::pair result = + localized_event_strings[pair.first].insert(event_string(event_id, pair.second)); + if (!result.second) + // update string only + result.first->second = pair.second; + } } } } @@ -406,6 +406,10 @@ void bookie_plugin::plugin_initialize(const boost::program_options::variables_ma ilog("bookie plugin: plugin_startup() begin"); database().applied_block.connect( [&]( const signed_block& b){ my->on_block_applied(b); } ); database().changed_objects.connect([&](const vector& changed_object_ids, const fc::flat_set& impacted_accounts){ my->on_objects_changed(changed_object_ids); }); + database().new_objects.connect([this](const vector& ids, const flat_set& impacted_accounts) { my->on_objects_new(ids); }); + database().removed_objects.connect([this](const vector& ids, const vector& objs, const flat_set& impacted_accounts) { my->on_objects_removed(ids); }); + + //auto event_index = database().add_index >(); database().add_index >(); diff --git a/tests/betting/betting_tests.cpp b/tests/betting/betting_tests.cpp index 84fbf73a..148bd6aa 100644 --- a/tests/betting/betting_tests.cpp +++ b/tests/betting/betting_tests.cpp @@ -350,7 +350,13 @@ BOOST_AUTO_TEST_CASE(persistent_objects_test) try { ACTORS( (alice)(bob) ); - CREATE_ICE_HOCKEY_BETTING_MARKET(); + { + // we're generating blocks, and the hacky way we keep references to the objects generated + // in this macro doesn't work, so do this in an anonymous scope to prevent us from using + // the variables it declares outside later in the function + + CREATE_ICE_HOCKEY_BETTING_MARKET(); + } graphene::bookie::bookie_api bookie_api(app); transfer(account_id_type(), alice_id, asset(10000000)); @@ -360,23 +366,32 @@ BOOST_AUTO_TEST_CASE(persistent_objects_test) BOOST_REQUIRE_EQUAL(get_balance(bob_id, asset_id_type()), bob_expected_balance.value); BOOST_REQUIRE_EQUAL(get_balance(alice_id, asset_id_type()), alice_expected_balance.value); + BOOST_REQUIRE_EQUAL(db.get_index_type().indices().get().size(), 1); + const betting_market_group_object& caps_vs_blackhawks_moneyline_betting_market = *db.get_index_type().indices().get().begin(); + betting_market_group_id_type caps_vs_blackhawks_moneyline_betting_market_id = caps_vs_blackhawks_moneyline_betting_market.id; + BOOST_REQUIRE_EQUAL(db.get_index_type().indices().get().size(), 2); + const betting_market_object& capitals_win_market = *db.get_index_type().indices().get().begin(); + betting_market_id_type capitals_win_market_id = capitals_win_market.id; + const betting_market_object& blackhawks_win_market = *std::next(db.get_index_type().indices().get().begin()); + betting_market_id_type blackhawks_win_market_id = blackhawks_win_market.id; + // lay 46 at 1.94 odds (50:47) -- this is too small to be placed on the books and there's // nothing for it to match, so it should be canceled - bet_id_type automatically_canceled_bet_id = place_bet(alice_id, capitals_win_market.id, bet_type::lay, asset(46, asset_id_type()), 194 * GRAPHENE_BETTING_ODDS_PRECISION / 100); + bet_id_type automatically_canceled_bet_id = place_bet(alice_id, capitals_win_market_id, bet_type::lay, asset(46, asset_id_type()), 194 * GRAPHENE_BETTING_ODDS_PRECISION / 100); BOOST_CHECK_MESSAGE(!db.find(automatically_canceled_bet_id), "Bet should have been canceled, but the blockchain still knows about it"); fc::variants objects_from_bookie = bookie_api.get_objects({automatically_canceled_bet_id}); BOOST_REQUIRE_EQUAL(objects_from_bookie.size(), 1); BOOST_CHECK_MESSAGE(objects_from_bookie[0]["id"].as() == automatically_canceled_bet_id, "Bookie Plugin didn't return a deleted bet it"); // lay 47 at 1.94 odds (50:47) -- this bet should go on the order books normally - bet_id_type first_bet_on_books = place_bet(alice_id, capitals_win_market.id, bet_type::lay, asset(47, asset_id_type()), 194 * GRAPHENE_BETTING_ODDS_PRECISION / 100); + bet_id_type first_bet_on_books = place_bet(alice_id, capitals_win_market_id, bet_type::lay, asset(47, asset_id_type()), 194 * GRAPHENE_BETTING_ODDS_PRECISION / 100); BOOST_CHECK_MESSAGE(db.find(first_bet_on_books), "Bet should exist on the blockchain"); objects_from_bookie = bookie_api.get_objects({first_bet_on_books}); BOOST_REQUIRE_EQUAL(objects_from_bookie.size(), 1); BOOST_CHECK_MESSAGE(objects_from_bookie[0]["id"].as() == first_bet_on_books, "Bookie Plugin didn't return a bet that is currently on the books"); // place a bet that exactly matches 'first_bet_on_books', should result in empty books (thus, no bet_objects from the blockchain) - bet_id_type matching_bet = place_bet(bob_id, capitals_win_market.id, bet_type::back, asset(50, asset_id_type()), 194 * GRAPHENE_BETTING_ODDS_PRECISION / 100); + bet_id_type matching_bet = place_bet(bob_id, capitals_win_market_id, bet_type::back, asset(50, asset_id_type()), 194 * GRAPHENE_BETTING_ODDS_PRECISION / 100); BOOST_CHECK_MESSAGE(!db.find(first_bet_on_books), "Bet should have been filled, but the blockchain still knows about it"); BOOST_CHECK_MESSAGE(!db.find(matching_bet), "Bet should have been filled, but the blockchain still knows about it"); generate_blocks(1); // the bookie plugin doesn't detect matches until a block is generated @@ -386,12 +401,29 @@ BOOST_AUTO_TEST_CASE(persistent_objects_test) BOOST_CHECK_MESSAGE(objects_from_bookie[0]["id"].as() == first_bet_on_books, "Bookie Plugin didn't return a bet that has been filled"); BOOST_CHECK_MESSAGE(objects_from_bookie[1]["id"].as() == matching_bet, "Bookie Plugin didn't return a bet that has been filled"); + resolve_betting_market_group(caps_vs_blackhawks_moneyline_betting_market_id, + {{capitals_win_market_id, betting_market_resolution_type::cancel}, + {blackhawks_win_market_id, betting_market_resolution_type::cancel}}); + generate_blocks(1); + + // test get_matched_bets_for_bettor std::vector alice_matched_bets = bookie_api.get_matched_bets_for_bettor(alice_id); BOOST_REQUIRE_EQUAL(alice_matched_bets.size(), 1); BOOST_CHECK(alice_matched_bets[0].amount_matched == 47); std::vector bob_matched_bets = bookie_api.get_matched_bets_for_bettor(bob_id); BOOST_REQUIRE_EQUAL(bob_matched_bets.size(), 1); BOOST_CHECK(bob_matched_bets[0].amount_matched == 50); + + // test getting markets + // test that we cannot get them from the database directly + BOOST_CHECK_THROW(capitals_win_market_id(db), fc::exception); + BOOST_CHECK_THROW(blackhawks_win_market_id(db), fc::exception); + + objects_from_bookie = bookie_api.get_objects({capitals_win_market_id, blackhawks_win_market_id}); + BOOST_REQUIRE_EQUAL(objects_from_bookie.size(), 2); + idump((objects_from_bookie)); + BOOST_CHECK(!objects_from_bookie[0].is_null()); + BOOST_CHECK(!objects_from_bookie[1].is_null()); } FC_LOG_AND_RETHROW() } @@ -657,8 +689,8 @@ BOOST_AUTO_TEST_CASE( not_win ) try { resolve_betting_market_group(moneyline_betting_markets_id, - {{capitals_win_betting_market_id, betting_market_resolution_type::not_win}, - {blackhawks_win_betting_market_id, betting_market_resolution_type::cancel}}); + {{capitals_win_betting_market_id, betting_market_resolution_type::not_win}, + {blackhawks_win_betting_market_id, betting_market_resolution_type::cancel}}); GET_ACTOR(alice); GET_ACTOR(bob); @@ -680,8 +712,8 @@ BOOST_AUTO_TEST_CASE( cancel ) try { resolve_betting_market_group(moneyline_betting_markets_id, - {{capitals_win_betting_market_id, betting_market_resolution_type::cancel}, - {blackhawks_win_betting_market_id, betting_market_resolution_type::cancel}}); + {{capitals_win_betting_market_id, betting_market_resolution_type::cancel}, + {blackhawks_win_betting_market_id, betting_market_resolution_type::cancel}}); GET_ACTOR(alice); GET_ACTOR(bob);