diff --git a/libraries/chain/betting_market_evaluator.cpp b/libraries/chain/betting_market_evaluator.cpp index fe9ebc14..98173cfe 100644 --- a/libraries/chain/betting_market_evaluator.cpp +++ b/libraries/chain/betting_market_evaluator.cpp @@ -126,6 +126,18 @@ void_result bet_place_evaluator::do_evaluate(const bet_place_operation& op) FC_ASSERT(bet_object::get_matching_amount(op.amount_to_bet.amount, op.backer_multiplier, op.back_or_lay) != 0, "Bet cannot be matched"); +#if 0 + bet_object simulated_bet; + simulated_bet.bettor_id = op.bettor_id; + simulated_bet.betting_market_id = op.betting_market_id; + simulated_bet.amount_to_bet = op.amount_to_bet; + simulated_bet.backer_multiplier = op.backer_multiplier; + simulated_bet.amount_reserved_for_fees = op.amount_reserved_for_fees; + simulated_bet.back_or_lay = op.back_or_lay; + + share_type required_deposit = get_required_deposit_for_bet(simulated_bet); +#endif + // verify they reserved enough to cover the percentage fee uint16_t percentage_fee = current_params.current_fees->get().percentage_fee; fc::uint128_t minimum_percentage_fee_calculation = op.amount_to_bet.amount.value; diff --git a/libraries/chain/db_bet.cpp b/libraries/chain/db_bet.cpp index 971c8467..2c5c9374 100644 --- a/libraries/chain/db_bet.cpp +++ b/libraries/chain/db_bet.cpp @@ -104,6 +104,13 @@ void database::resolve_betting_market(const betting_market_object& betting_marke remove(betting_market); } +#if 0 +void database::get_required_deposit_for_bet(const betting_market_object& betting_market, + betting_market_resolution_type resolution) +{ +} +#endif + bool maybe_cull_small_bet( database& db, const bet_object& bet_object_to_cull ) { /** diff --git a/libraries/chain/include/graphene/chain/betting_market_object.hpp b/libraries/chain/include/graphene/chain/betting_market_object.hpp index 930868db..d234ac17 100644 --- a/libraries/chain/include/graphene/chain/betting_market_object.hpp +++ b/libraries/chain/include/graphene/chain/betting_market_object.hpp @@ -225,13 +225,160 @@ struct compare_bet_by_odds { } }; +struct compare_bet_by_bettor_then_odds { + bool operator()(const bet_object& lhs, const bet_object& rhs) const + { + return compare(lhs.betting_market_id, lhs.bettor_id, lhs.back_or_lay, lhs.backer_multiplier, lhs.id, + rhs.betting_market_id, rhs.bettor_id, rhs.back_or_lay, rhs.backer_multiplier, rhs.id); + } + + template + bool operator() (const std::tuple& lhs, const bet_object& rhs) const + { + return compare(std::get<0>(lhs), rhs.betting_market_id); + } + + template + bool operator() (const bet_object& lhs, const std::tuple& rhs) const + { + return compare(lhs.betting_market_id, std::get<0>(rhs)); + } + + template + bool operator() (const std::tuple& lhs, const bet_object& rhs) const + { + return compare(std::get<0>(lhs), std::get<1>(lhs), rhs.betting_market_id, rhs.bettor_id); + } + + template + bool operator() (const bet_object& lhs, const std::tuple& rhs) const + { + return compare(lhs.betting_market_id, lhs.bettor_id, std::get<0>(rhs), std::get<1>(rhs)); + } + + template + bool operator() (const std::tuple& lhs, const bet_object& rhs) const + { + return compare(std::get<0>(lhs), std::get<1>(lhs), std::get<2>(lhs), + rhs.betting_market_id, rhs.bettor_id, rhs.back_or_lay); + } + + template + bool operator() (const bet_object& lhs, const std::tuple& rhs) const + { + return compare(lhs.betting_market_id, lhs.bettor_id, lhs.back_or_lay, + std::get<0>(rhs), std::get<1>(rhs), std::get<2>(rhs)); + } + template + bool operator() (const std::tuple& lhs, const bet_object& rhs) const + { + return compare(std::get<0>(lhs), std::get<1>(lhs), std::get<2>(lhs), std::get<3>(lhs), + rhs.betting_market_id, rhs.bettor_id, rhs.back_or_lay, rhs.backer_multiplier); + } + + template + bool operator() (const bet_object& lhs, const std::tuple& rhs) const + { + return compare(lhs.betting_market_id, lhs.bettor_id, lhs.back_or_lay, lhs.backer_multiplier, lhs.id, + std::get<0>(rhs), std::get<1>(rhs), std::get<2>(rhs), std::get<3>(rhs)); + } + template + bool operator() (const std::tuple& lhs, const bet_object& rhs) const + { + return compare(std::get<0>(lhs), std::get<1>(lhs), std::get<2>(lhs), std::get<3>(lhs), std::get<4>(lhs), + rhs.betting_market_id, rhs.bettor_id, rhs.back_or_lay, rhs.backer_multiplier, rhs.id); + } + template + bool operator() (const bet_object& lhs, const std::tuple& rhs) const + { + return compare(lhs.betting_market_id, lhs.bettor_id, lhs.back_or_lay, lhs.backer_multiplier, lhs.id, + std::get<0>(rhs), std::get<1>(rhs), std::get<2>(rhs), std::get<3>(rhs), std::get<4>(rhs)); + } + bool compare(const betting_market_id_type& lhs_betting_market_id, const betting_market_id_type& rhs_betting_market_id) const + { + return lhs_betting_market_id < rhs_betting_market_id; + } + bool compare(const betting_market_id_type& lhs_betting_market_id, const account_id_type& lhs_bettor_id, + const betting_market_id_type& rhs_betting_market_id, const account_id_type& rhs_bettor_id) const + { + if (lhs_betting_market_id < rhs_betting_market_id) + return true; + if (lhs_betting_market_id > rhs_betting_market_id) + return false; + return lhs_bettor_id < rhs_bettor_id; + } + + bool compare(const betting_market_id_type& lhs_betting_market_id, const account_id_type& lhs_bettor_id, + bet_type lhs_bet_type, + const betting_market_id_type& rhs_betting_market_id, const account_id_type& rhs_bettor_id, + bet_type rhs_bet_type) const + { + if (lhs_betting_market_id < rhs_betting_market_id) + return true; + if (lhs_betting_market_id > rhs_betting_market_id) + return false; + if (lhs_bettor_id < rhs_bettor_id) + return true; + if (lhs_bettor_id > rhs_bettor_id) + return false; + return lhs_bet_type < rhs_bet_type; + } + bool compare(const betting_market_id_type& lhs_betting_market_id, const account_id_type& lhs_bettor_id, + bet_type lhs_bet_type, + bet_multiplier_type lhs_backer_multiplier, + const betting_market_id_type& rhs_betting_market_id, const account_id_type& rhs_bettor_id, + bet_type rhs_bet_type, + bet_multiplier_type rhs_backer_multiplier) const + { + if (lhs_betting_market_id < rhs_betting_market_id) + return true; + if (lhs_betting_market_id > rhs_betting_market_id) + return false; + if (lhs_bettor_id < rhs_bettor_id) + return true; + if (lhs_bettor_id > rhs_bettor_id) + return false; + if (lhs_bet_type < rhs_bet_type) + return true; + if (lhs_bet_type > rhs_bet_type) + return false; + return lhs_backer_multiplier < rhs_backer_multiplier; + } + bool compare(const betting_market_id_type& lhs_betting_market_id, const account_id_type& lhs_bettor_id, + bet_type lhs_bet_type, + bet_multiplier_type lhs_backer_multiplier, const bet_id_type& lhs_bet_id, + const betting_market_id_type& rhs_betting_market_id, const account_id_type& rhs_bettor_id, + bet_type rhs_bet_type, + bet_multiplier_type rhs_backer_multiplier, const bet_id_type& rhs_bet_id) const + { + if (lhs_betting_market_id < rhs_betting_market_id) + return true; + if (lhs_betting_market_id > rhs_betting_market_id) + return false; + if (lhs_bettor_id < rhs_bettor_id) + return true; + if (lhs_bettor_id > rhs_bettor_id) + return false; + if (lhs_bet_type < rhs_bet_type) + return true; + if (lhs_bet_type > rhs_bet_type) + return false; + if (lhs_backer_multiplier < rhs_backer_multiplier) + return true; + if (lhs_backer_multiplier > rhs_backer_multiplier) + return false; + return lhs_bet_id < rhs_bet_id; + } +}; + struct by_odds {}; +struct by_bettor_and_odds {}; typedef multi_index_container< bet_object, indexed_by< ordered_unique< tag, member< object, object_id_type, &object::id > >, - ordered_unique< tag, identity, compare_bet_by_odds > > > bet_object_multi_index_type; - + ordered_unique< tag, identity, compare_bet_by_odds >, + ordered_unique< tag, identity, compare_bet_by_bettor_then_odds > > > bet_object_multi_index_type; typedef generic_index bet_object_index; struct by_bettor_betting_market{}; diff --git a/libraries/plugins/CMakeLists.txt b/libraries/plugins/CMakeLists.txt index 128d8aa1..01cd2e3c 100644 --- a/libraries/plugins/CMakeLists.txt +++ b/libraries/plugins/CMakeLists.txt @@ -2,3 +2,4 @@ add_subdirectory( witness ) add_subdirectory( account_history ) add_subdirectory( market_history ) add_subdirectory( delayed_node ) +add_subdirectory( bookie ) diff --git a/libraries/plugins/bookie/CMakeLists.txt b/libraries/plugins/bookie/CMakeLists.txt new file mode 100644 index 00000000..02b81c0b --- /dev/null +++ b/libraries/plugins/bookie/CMakeLists.txt @@ -0,0 +1,21 @@ +file(GLOB HEADERS "include/graphene/bookie/*.hpp") + +add_library( graphene_bookie + bookie_plugin.cpp + ) + +target_link_libraries( graphene_bookie graphene_chain graphene_app ) +target_include_directories( graphene_bookie + PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/include" ) + +if(MSVC) + set_source_files_properties( bookie_plugin.cpp PROPERTIES COMPILE_FLAGS "/bigobj" ) +endif(MSVC) + +install( TARGETS + graphene_bookie + + RUNTIME DESTINATION bin + LIBRARY DESTINATION lib + ARCHIVE DESTINATION lib +) diff --git a/libraries/plugins/bookie/bookie_plugin.cpp b/libraries/plugins/bookie/bookie_plugin.cpp new file mode 100644 index 00000000..b46b836f --- /dev/null +++ b/libraries/plugins/bookie/bookie_plugin.cpp @@ -0,0 +1,342 @@ +/* + * 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 +#include +#include +#include + +#include +#include + +#include + +#if 0 +# ifdef DEFAULT_LOGGER +# undef DEFAULT_LOGGER +# endif +# define DEFAULT_LOGGER "bookie_plugin" +#endif + +namespace graphene { namespace bookie { + +namespace detail +{ + +class persistent_event_object : public graphene::db::abstract_object +{ + public: + static const uint8_t space_id = bookie_objects; + static const uint8_t type_id = persistent_event_object_type; + + event_id_type event_object_id; + + internationalized_string_type name; + + internationalized_string_type season; + + optional start_time; + + event_group_id_type event_group_id; + + vector competitors; + + event_status status; + vector scores; +}; +typedef object_id persistent_event_id_type; + +struct by_event_id; +typedef multi_index_container< + persistent_event_object, + indexed_by< + ordered_unique, member >, + ordered_unique, member > > > persistent_event_object_multi_index_type; + +typedef generic_index persistent_event_object_index; + + +class events_by_competitor_index : public secondary_index +{ + public: + virtual ~events_by_competitor_index() {} + + virtual void object_inserted( const object& obj ) override; + virtual void object_removed( const object& obj ) override; + virtual void about_to_modify( const object& before ) override; + virtual void object_modified( const object& after ) override; + protected: + + map > competitor_to_events; +}; + +void events_by_competitor_index::object_inserted( const object& obj ) +{ + const persistent_event_object& event_obj = *boost::polymorphic_downcast(&obj); + for (const competitor_id_type& competitor_id : event_obj.competitors) + competitor_to_events[competitor_id].insert(event_obj.id); + for (const competitor_id_type& competitor_id : event_obj.competitors) + competitor_to_events[competitor_id].insert(event_obj.id); +} +void events_by_competitor_index::object_removed( const object& obj ) +{ + const persistent_event_object& event_obj = *boost::polymorphic_downcast(&obj); + for (const competitor_id_type& competitor_id : event_obj.competitors) + competitor_to_events[competitor_id].erase(event_obj.id); +} +void events_by_competitor_index::about_to_modify( const object& before ) +{ + object_removed(before); +} +void events_by_competitor_index::object_modified( const object& after ) +{ + object_inserted(after); +} + + +class bookie_plugin_impl +{ + public: + bookie_plugin_impl(bookie_plugin& _plugin) + : _self( _plugin ) + { } + virtual ~bookie_plugin_impl(); + + + /** + * Called After a block has been applied and committed. The callback + * should not yield and should execute quickly. + */ + void on_objects_changed(const vector& changed_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. + */ + void on_block_applied( const signed_block& b ); + + graphene::chain::database& database() + { + return _self.database(); + } + + bookie_plugin& _self; + flat_set _tracked_accounts; +}; + +bookie_plugin_impl::~bookie_plugin_impl() +{ + return; +} + +void bookie_plugin_impl::on_objects_changed(const vector& changed_object_ids) +{ + graphene::chain::database& db = database(); + auto& event_id_index = db.get_index_type().indices().get(); + + for (const object_id_type& changed_object_id : changed_object_ids) + { + if (changed_object_id.space() == event_id_type::space_id && + 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) + { + ilog("Modifying persistent event object ${id}", ("id", changed_event_id)); + db.modify(*old_event_obj, [&](persistent_event_object& saved_event_obj) { + saved_event_obj.name = new_event_obj->name; + saved_event_obj.season = new_event_obj->season; + saved_event_obj.start_time = new_event_obj->start_time;; + saved_event_obj.event_group_id = new_event_obj->event_group_id; + saved_event_obj.competitors = new_event_obj->competitors; + saved_event_obj.status = new_event_obj->status; + saved_event_obj.scores = new_event_obj->scores; + }); + } + 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.event_object_id = new_event_obj->id; + saved_event_obj.name = new_event_obj->name; + saved_event_obj.season = new_event_obj->season; + saved_event_obj.start_time = new_event_obj->start_time;; + saved_event_obj.event_group_id = new_event_obj->event_group_id; + saved_event_obj.competitors = new_event_obj->competitors; + saved_event_obj.status = new_event_obj->status; + saved_event_obj.scores = new_event_obj->scores; + }); + } + } + } +} + +void bookie_plugin_impl::on_block_applied( const signed_block& ) +{ + graphene::chain::database& db = database(); + const vector >& hist = db.get_applied_operations(); + for( const optional< operation_history_object >& o_op : hist ) + { + // add to the operation history index + const auto& oho = db.create( [&]( operation_history_object& h ) + { + if( o_op.valid() ) + h = *o_op; + } ); + + if( !o_op.valid() ) + { + ilog( "removing failed operation with ID: ${id}", ("id", oho.id) ); + db.remove( oho ); + continue; + } + + const operation_history_object& op = *o_op; + + // get the set of accounts this operation applies to + flat_set impacted; + vector other; + operation_get_required_authorities( op.op, impacted, impacted, other ); + + if( op.op.which() == operation::tag< account_create_operation >::value ) + impacted.insert( oho.result.get() ); + else + graphene::app::operation_get_impacted_accounts( op.op, impacted ); + + for( auto& a : other ) + for( auto& item : a.account_auths ) + impacted.insert( item.first ); + + // for each operation this account applies to that is in the config link it into the history + if( _tracked_accounts.size() == 0 ) + { + for( auto& account_id : impacted ) + { + // we don't do index_account_keys here anymore, because + // that indexing now happens in observers' post_evaluate() + + // add history + const auto& stats_obj = account_id(db).statistics(db); + const auto& ath = db.create( [&]( account_transaction_history_object& obj ){ + obj.operation_id = oho.id; + obj.next = stats_obj.most_recent_op; + }); + db.modify( stats_obj, [&]( account_statistics_object& obj ){ + obj.most_recent_op = ath.id; + }); + } + } + else + { + for( auto account_id : _tracked_accounts ) + { + if( impacted.find( account_id ) != impacted.end() ) + { + // add history + const auto& stats_obj = account_id(db).statistics(db); + const auto& ath = db.create( [&]( account_transaction_history_object& obj ){ + obj.operation_id = oho.id; + obj.next = stats_obj.most_recent_op; + }); + db.modify( stats_obj, [&]( account_statistics_object& obj ){ + obj.most_recent_op = ath.id; + }); + } + } + } + } +} +} // end namespace detail + +bookie_plugin::bookie_plugin() : + my( new detail::bookie_plugin_impl(*this) ) +{ +} + +bookie_plugin::~bookie_plugin() +{ +} + +std::string bookie_plugin::plugin_name()const +{ + return "bookie"; +} + +void bookie_plugin::plugin_set_program_options( + boost::program_options::options_description& cli, + boost::program_options::options_description& cfg + ) +{ + cli.add_options() + ("track-account", boost::program_options::value>()->composing()->multitoken(), "Account ID to track history for (may specify multiple times)") + ; + cfg.add(cli); +} + +void bookie_plugin::plugin_initialize(const boost::program_options::variables_map& options) +{ + database().applied_block.connect( [&]( const signed_block& b){ my->on_block_applied(b); } ); + database().changed_objects.connect([&](const vector& changed_object_ids){ my->on_objects_changed(changed_object_ids); }); + auto event_index = database().add_index >(); + event_index->add_secondary_index(); + + LOAD_VALUE_SET(options, "tracked-accounts", my->_tracked_accounts, graphene::chain::account_id_type); +} + +void bookie_plugin::plugin_startup() +{ +} + +flat_set bookie_plugin::tracked_accounts() const +{ + return my->_tracked_accounts; +} + +} } +FC_REFLECT_DERIVED( graphene::bookie::detail::persistent_event_object, (graphene::db::object), (event_object_id)(name)(season)(start_time)(event_group_id)(status)(competitors)(scores) ) + diff --git a/libraries/plugins/bookie/include/graphene/bookie/bookie_plugin.hpp b/libraries/plugins/bookie/include/graphene/bookie/bookie_plugin.hpp new file mode 100644 index 00000000..1d146ad0 --- /dev/null +++ b/libraries/plugins/bookie/include/graphene/bookie/bookie_plugin.hpp @@ -0,0 +1,79 @@ +/* + * 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. + */ +#pragma once + +#include +#include + +#include + +namespace graphene { namespace bookie { +using namespace chain; + +// +// Plugins should #define their SPACE_ID's so plugins with +// conflicting SPACE_ID assignments can be compiled into the +// same binary (by simply re-assigning some of the conflicting #defined +// SPACE_ID's in a build script). +// +// Assignment of SPACE_ID's cannot be done at run-time because +// various template automagic depends on them being known at compile +// time. +// +enum spaces { + bookie_objects = 6 +}; +enum bookie_object_type +{ + persistent_event_object_type, + BOOKIE_OBJECT_TYPE_COUNT ///< Sentry value which contains the number of different object types +}; + + +namespace detail +{ + class bookie_plugin_impl; +} + +class bookie_plugin : public graphene::app::plugin +{ + public: + bookie_plugin(); + virtual ~bookie_plugin(); + + std::string plugin_name()const override; + virtual void plugin_set_program_options( + boost::program_options::options_description& cli, + boost::program_options::options_description& cfg) override; + virtual void plugin_initialize(const boost::program_options::variables_map& options) override; + virtual void plugin_startup() override; + + flat_set tracked_accounts()const; + + friend class detail::bookie_plugin_impl; + std::unique_ptr my; +}; + +} } //graphene::bookie + diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index b03d58a8..9b42051b 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -8,25 +8,25 @@ endif() file(GLOB UNIT_TESTS "tests/*.cpp") add_executable( chain_test ${UNIT_TESTS} ${COMMON_SOURCES} ) -target_link_libraries( chain_test graphene_chain graphene_app graphene_account_history graphene_egenesis_none fc ${PLATFORM_SPECIFIC_LIBS} ) +target_link_libraries( chain_test graphene_chain graphene_app graphene_account_history graphene_bookie graphene_egenesis_none fc ${PLATFORM_SPECIFIC_LIBS} ) if(MSVC) set_source_files_properties( tests/serialization_tests.cpp PROPERTIES COMPILE_FLAGS "/bigobj" ) endif(MSVC) 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} ) +target_link_libraries( performance_test graphene_chain graphene_app graphene_account_history graphene_bookie 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} ) +target_link_libraries( chain_bench graphene_chain graphene_app graphene_account_history graphene_bookie graphene_time graphene_egenesis_none fc ${PLATFORM_SPECIFIC_LIBS} ) file(GLOB APP_SOURCES "app/*.cpp") add_executable( app_test ${APP_SOURCES} ) -target_link_libraries( app_test graphene_app graphene_account_history graphene_net graphene_chain graphene_time graphene_egenesis_none fc ${PLATFORM_SPECIFIC_LIBS} ) +target_link_libraries( app_test graphene_app graphene_account_history graphene_bookie graphene_net graphene_chain graphene_time graphene_egenesis_none fc ${PLATFORM_SPECIFIC_LIBS} ) 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} ) +target_link_libraries( intense_test graphene_chain graphene_app graphene_account_history graphene_bookie graphene_egenesis_none fc ${PLATFORM_SPECIFIC_LIBS} ) add_subdirectory( generate_empty_blocks ) diff --git a/tests/common/database_fixture.cpp b/tests/common/database_fixture.cpp index 61103c90..adf684c9 100644 --- a/tests/common/database_fixture.cpp +++ b/tests/common/database_fixture.cpp @@ -26,6 +26,7 @@ #include #include +#include #include @@ -79,6 +80,7 @@ database_fixture::database_fixture() } auto ahplugin = app.register_plugin(); auto mhplugin = app.register_plugin(); + auto bookieplugin = app.register_plugin(); init_account_pub_key = init_account_priv_key.get_public_key(); boost::program_options::variables_map options; @@ -104,9 +106,12 @@ database_fixture::database_fixture() ahplugin->plugin_initialize(options); mhplugin->plugin_set_app(&app); mhplugin->plugin_initialize(options); + bookieplugin->plugin_set_app(&app); + bookieplugin->plugin_initialize(options); ahplugin->plugin_startup(); mhplugin->plugin_startup(); + bookieplugin->plugin_startup(); generate_block(); diff --git a/tests/tests/operation_tests2.cpp b/tests/tests/operation_tests2.cpp index cdd553cd..a936c6a0 100644 --- a/tests/tests/operation_tests2.cpp +++ b/tests/tests/operation_tests2.cpp @@ -1891,3 +1891,36 @@ BOOST_AUTO_TEST_CASE( cancel ) BOOST_AUTO_TEST_SUITE_END() +struct simple_bet_test_fixture_2 : database_fixture { + betting_market_id_type capitals_win_betting_market_id; + simple_bet_test_fixture_2() + { + ACTORS( (alice)(bob) ); + CREATE_ICE_HOCKEY_BETTING_MARKET(); + + // give alice and bob 10k each + transfer(account_id_type(), alice_id, asset(10000)); + transfer(account_id_type(), bob_id, asset(10000)); + + // alice backs 1000 at 1:1, matches + place_bet(alice_id, capitals_win_market.id, bet_type::back, asset(1000, asset_id_type()), 2 * GRAPHENE_BETTING_ODDS_PRECISION, 20); + place_bet(bob_id, capitals_win_market.id, bet_type::lay, asset(1000, asset_id_type()), 2 * GRAPHENE_BETTING_ODDS_PRECISION, 20); + + // now alice lays at 2500 at 1:1. This should require a deposit of 500, with the remaining 200 being funded from exposure + place_bet(alice_id, capitals_win_market.id, bet_type::lay, asset(2500, asset_id_type()), 2 * GRAPHENE_BETTING_ODDS_PRECISION, 50); + + // match the bet bit by bit. bob matches 500 of alice's 2500 bet. This effectively cancels half of bob's lay position + // so he immediately gets 500 back. It reduces alice's back position, but doesn't return any money to her (all 2000 of her exposure + // was already "promised" to her lay bet, so the 500 she would have received is placed in her refundable_unmatched_bets) + place_bet(bob_id, capitals_win_market.id, bet_type::back, asset(500, asset_id_type()), 2 * GRAPHENE_BETTING_ODDS_PRECISION, 10); + + // match another 500, which will fully cancel bob's lay position and return the other 500 he had locked up in his position. + // alice's back position is now canceled, 1500 remains of her unmatched lay bet, and the 500 from canceling her position has + // been moved to her refundable_unmatched_bets + place_bet(bob_id, capitals_win_market.id, bet_type::back, asset(500, asset_id_type()), 2 * GRAPHENE_BETTING_ODDS_PRECISION, 10); + + capitals_win_betting_market_id = capitals_win_market.id; + } +}; + +