diff --git a/libraries/app/CMakeLists.txt b/libraries/app/CMakeLists.txt index 5dbaadc0..a6157560 100644 --- a/libraries/app/CMakeLists.txt +++ b/libraries/app/CMakeLists.txt @@ -13,7 +13,7 @@ add_library( graphene_app # need to link graphene_debug_witness because plugins aren't sufficiently isolated #246 #target_link_libraries( graphene_app graphene_market_history graphene_account_history graphene_chain fc graphene_db graphene_net graphene_utilities graphene_debug_witness ) -target_link_libraries( graphene_app graphene_market_history graphene_account_history graphene_accounts_list graphene_chain fc graphene_db graphene_net graphene_time graphene_utilities graphene_debug_witness ) +target_link_libraries( graphene_app graphene_market_history graphene_account_history graphene_accounts_list graphene_chain fc graphene_db graphene_net graphene_time graphene_utilities graphene_debug_witness graphene_bookie ) target_include_directories( graphene_app PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/include" "${CMAKE_CURRENT_SOURCE_DIR}/../egenesis/include" ) diff --git a/libraries/app/api.cpp b/libraries/app/api.cpp index 18a5c40e..6c6359c2 100644 --- a/libraries/app/api.cpp +++ b/libraries/app/api.cpp @@ -111,6 +111,12 @@ namespace graphene { namespace app { if( _app.get_plugin( "debug_witness" ) ) _debug_api = std::make_shared< graphene::debug_witness::debug_api >( std::ref(_app) ); } + else if( api_name == "bookie_api" ) + { + // can only enable this API if the plugin was loaded + if( _app.get_plugin( "bookie" ) ) + _bookie_api = std::make_shared(std::ref(_app)); + } return; } @@ -269,6 +275,12 @@ namespace graphene { namespace app { return *_debug_api; } + fc::api login_api::bookie() const + { + FC_ASSERT(_bookie_api); + return *_bookie_api; + } + #if 0 vector get_relevant_accounts( const object* obj ) { diff --git a/libraries/app/application.cpp b/libraries/app/application.cpp index 2d321589..fc82a175 100644 --- a/libraries/app/application.cpp +++ b/libraries/app/application.cpp @@ -228,6 +228,9 @@ namespace detail { wsc->register_api(login->database()); wsc->register_api(fc::api(login)); + + wsc->register_api(fc::api(login)); + c->set_session_data( wsc ); std::string username = "*"; @@ -455,6 +458,7 @@ namespace detail { wild_access.allowed_apis.push_back( "network_broadcast_api" ); wild_access.allowed_apis.push_back( "history_api" ); wild_access.allowed_apis.push_back( "crypto_api" ); + wild_access.allowed_apis.push_back( "bookie_api" ); _apiaccess.permission_map["*"] = wild_access; } @@ -463,6 +467,7 @@ namespace detail { reset_websocket_tls_server(); } FC_LOG_AND_RETHROW() } + optional< api_access_info > get_api_access_info(const string& username)const { optional< api_access_info > result; @@ -1083,3 +1088,7 @@ void application::startup_plugins() // namespace detail } } + +// for some reason, g++ isn't instantiating this function on its own, force it to here +template +fc::api_id_type fc::api::register_api(fc::api_connection&) const; diff --git a/libraries/app/include/graphene/app/api.hpp b/libraries/app/include/graphene/app/api.hpp index da7613f3..d4532b42 100644 --- a/libraries/app/include/graphene/app/api.hpp +++ b/libraries/app/include/graphene/app/api.hpp @@ -32,6 +32,7 @@ #include #include +#include #include @@ -359,6 +360,8 @@ namespace graphene { namespace app { fc::api asset()const; /// @brief Retrieve the debug API (if available) fc::api debug()const; + /// @brief Retrieve the bookie API (if available) + fc::api bookie()const; /// @brief Called to enable an API, not reflected. void enable_api( const string& api_name ); @@ -373,6 +376,7 @@ namespace graphene { namespace app { optional< fc::api > _crypto_api; optional< fc::api > _asset_api; optional< fc::api > _debug_api; + optional< fc::api > _bookie_api; }; }} // graphene::app @@ -441,4 +445,5 @@ FC_API(graphene::app::login_api, (crypto) (asset) (debug) + (bookie) ) diff --git a/libraries/plugins/bookie/CMakeLists.txt b/libraries/plugins/bookie/CMakeLists.txt index 02b81c0b..12d84e3c 100644 --- a/libraries/plugins/bookie/CMakeLists.txt +++ b/libraries/plugins/bookie/CMakeLists.txt @@ -2,6 +2,7 @@ file(GLOB HEADERS "include/graphene/bookie/*.hpp") add_library( graphene_bookie bookie_plugin.cpp + bookie_api.cpp ) target_link_libraries( graphene_bookie graphene_chain graphene_app ) diff --git a/libraries/plugins/bookie/bookie_api.cpp b/libraries/plugins/bookie/bookie_api.cpp new file mode 100644 index 00000000..b9b6f3a3 --- /dev/null +++ b/libraries/plugins/bookie/bookie_api.cpp @@ -0,0 +1,123 @@ +#include +#include +#include +#include + +#include + +#include +#include +#include + +#include + +#include + +#include +#include + +namespace graphene { namespace bookie { + +namespace detail { + +class bookie_api_impl +{ + public: + bookie_api_impl(graphene::app::application& _app); + + binned_order_book get_binned_order_book(graphene::chain::betting_market_id_type betting_market_id, int32_t precision); + std::shared_ptr get_plugin(); + + graphene::app::application& app; +}; + +bookie_api_impl::bookie_api_impl(graphene::app::application& _app) : app(_app) +{} + + +binned_order_book bookie_api_impl::get_binned_order_book(graphene::chain::betting_market_id_type betting_market_id, int32_t precision) +{ + std::shared_ptr db = app.chain_database(); + const auto& bet_odds_idx = db->get_index_type().indices().get(); + + graphene::chain::bet_multiplier_type bin_size = GRAPHENE_BETTING_ODDS_PRECISION; + if (precision > 0) + for (int32_t i = 0; i < precision; ++i) { + FC_ASSERT(bin_size > (GRAPHENE_BETTING_MIN_MULTIPLIER - GRAPHENE_BETTING_ODDS_PRECISION), "invalid precision"); + bin_size /= 10; + } + else if (precision < 0) + for (int32_t i = 0; i > precision; --i) { + FC_ASSERT(bin_size < (GRAPHENE_BETTING_MAX_MULTIPLIER - GRAPHENE_BETTING_ODDS_PRECISION), "invalid precision"); + bin_size *= 10; + } + + binned_order_book result; + + // use a bet_object here for convenience. we really only use it to track the amount, odds, and back_or_lay + // note, the current bin is accumulating the matching bets, so it will be of type 'lay' when we're + // binning 'back' bets, and vice versa + fc::optional current_bin; + + auto flush_current_bin = [¤t_bin, &result]() + { + if (current_bin) // do nothing if the current bin is empty + { + order_bin current_order_bin; + + current_order_bin.backer_multiplier = current_bin->backer_multiplier; + current_order_bin.amount_to_bet = current_bin->get_matching_amount();; + if (current_bin->back_or_lay == bet_type::lay) + result.aggregated_back_bets.emplace_back(std::move(current_order_bin)); + else // current_bin is aggregating back positions + result.aggregated_lay_bets.emplace_back(std::move(current_order_bin)); + + current_bin.reset(); + } + }; + + for (auto bet_odds_iter = bet_odds_idx.lower_bound(std::make_tuple(betting_market_id)); + bet_odds_iter != bet_odds_idx.end() && betting_market_id == bet_odds_iter->betting_market_id; + ++bet_odds_iter) + { + if (current_bin && + (bet_odds_iter->back_or_lay == current_bin->back_or_lay || + bet_odds_iter->backer_multiplier > current_bin->backer_multiplier)) + flush_current_bin(); + + if (!current_bin) + { + // if there is no current bin, create one appropriate for the bet we're processing + current_bin = graphene::chain::bet_object(); + current_bin->backer_multiplier = (bet_odds_iter->backer_multiplier + bin_size - 1) / bin_size * bin_size; + current_bin->amount_to_bet.amount = 0; + current_bin->back_or_lay = bet_odds_iter->back_or_lay == bet_type::back ? bet_type::lay : bet_type::back; + } + + current_bin->amount_to_bet.amount += bet_odds_iter->get_matching_amount(); + } + if (current_bin) + flush_current_bin(); + + return result; +} + +std::shared_ptr bookie_api_impl::get_plugin() +{ + return app.get_plugin("bookie"); +} + +} // detail + +bookie_api::bookie_api(graphene::app::application& app) : + my(std::make_shared(app)) +{ +} + +binned_order_book bookie_api::get_binned_order_book(graphene::chain::betting_market_id_type betting_market_id, int32_t precision) +{ + return my->get_binned_order_book(betting_market_id, precision); +} + +} } // graphene::bookie + diff --git a/libraries/plugins/bookie/bookie_plugin.cpp b/libraries/plugins/bookie/bookie_plugin.cpp index 0779e963..acbd0a3d 100644 --- a/libraries/plugins/bookie/bookie_plugin.cpp +++ b/libraries/plugins/bookie/bookie_plugin.cpp @@ -215,77 +215,7 @@ void bookie_plugin_impl::on_objects_changed(const vector& change 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; - }); - } - } - } - } + const vector >& hist = db.get_applied_operations(); } } // end namespace detail @@ -308,24 +238,27 @@ void bookie_plugin::plugin_set_program_options( 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); + //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); } ); + 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); }); 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); + //LOAD_VALUE_SET(options, "tracked-accounts", my->_tracked_accounts, graphene::chain::account_id_type); + ilog("bookie plugin: plugin_startup() end"); } void bookie_plugin::plugin_startup() { + ilog("bookie plugin: plugin_startup()"); } flat_set bookie_plugin::tracked_accounts() const diff --git a/libraries/plugins/bookie/include/graphene/bookie/bookie_api.hpp b/libraries/plugins/bookie/include/graphene/bookie/bookie_api.hpp new file mode 100644 index 00000000..b3e1d402 --- /dev/null +++ b/libraries/plugins/bookie/include/graphene/bookie/bookie_api.hpp @@ -0,0 +1,54 @@ +#pragma once + +#include +#include + +#include +#include + +#include + +namespace graphene { namespace app { + class application; +} } + +namespace graphene { namespace bookie { + +namespace detail { + class bookie_api_impl; +} + +struct order_bin { + graphene::chain::share_type amount_to_bet; + graphene::chain::bet_multiplier_type backer_multiplier; +}; + +struct binned_order_book { + std::vector aggregated_back_bets; + std::vector aggregated_lay_bets; +}; + +class bookie_api +{ + public: + bookie_api(graphene::app::application& app); + + /** + * Returns the current order book, binned according to the given precision. + * precision = 1 means bin using one decimal place: (1 - 1.1], (1.1 - 1.2], etc. + * precision = 2 would bin on (1 - 1.01], (1.01 - 1.02] + */ + binned_order_book get_binned_order_book(graphene::chain::betting_market_id_type betting_market_id, int32_t precision); + + std::shared_ptr my; +}; + +} } + +FC_REFLECT(graphene::bookie::order_bin, (amount_to_bet)(backer_multiplier)) +FC_REFLECT(graphene::bookie::binned_order_book, (aggregated_back_bets)(aggregated_lay_bets)) + +FC_API(graphene::bookie::bookie_api, + (get_binned_order_book) + ) + diff --git a/programs/debug_node/CMakeLists.txt b/programs/debug_node/CMakeLists.txt index 8ec7362b..232d265e 100644 --- a/programs/debug_node/CMakeLists.txt +++ b/programs/debug_node/CMakeLists.txt @@ -10,7 +10,7 @@ if( GPERFTOOLS_FOUND ) endif() target_link_libraries( debug_node - PRIVATE graphene_app graphene_account_history graphene_market_history graphene_witness graphene_debug_witness graphene_chain graphene_egenesis_full fc ${CMAKE_DL_LIBS} ${PLATFORM_SPECIFIC_LIBS} ) + PRIVATE graphene_app graphene_account_history graphene_market_history graphene_witness graphene_debug_witness graphene_bookie graphene_chain graphene_egenesis_full fc ${CMAKE_DL_LIBS} ${PLATFORM_SPECIFIC_LIBS} ) install( TARGETS debug_node diff --git a/programs/witness_node/CMakeLists.txt b/programs/witness_node/CMakeLists.txt index 7535d3ad..efb4acf8 100644 --- a/programs/witness_node/CMakeLists.txt +++ b/programs/witness_node/CMakeLists.txt @@ -11,7 +11,7 @@ endif() # We have to link against graphene_debug_witness because deficiency in our API infrastructure doesn't allow plugins to be fully abstracted #246 target_link_libraries( witness_node - PRIVATE graphene_app graphene_account_history graphene_market_history graphene_witness graphene_chain graphene_debug_witness graphene_egenesis_full fc ${CMAKE_DL_LIBS} ${PLATFORM_SPECIFIC_LIBS} ) + PRIVATE graphene_app graphene_account_history graphene_market_history graphene_witness graphene_chain graphene_debug_witness graphene_bookie graphene_egenesis_full fc ${CMAKE_DL_LIBS} ${PLATFORM_SPECIFIC_LIBS} ) # also add dependencies to graphene_generate_genesis graphene_generate_uia_sharedrop_genesis if you want those plugins install( TARGETS diff --git a/programs/witness_node/main.cpp b/programs/witness_node/main.cpp index 20f31127..ee084174 100644 --- a/programs/witness_node/main.cpp +++ b/programs/witness_node/main.cpp @@ -29,6 +29,7 @@ #include //#include //#include +#include #include #include @@ -80,6 +81,7 @@ int main(int argc, char** argv) { //auto generate_genesis_plug = node->register_plugin(); //auto generate_uia_sharedrop_genesis_plug = node->register_plugin(); auto list_plug = node->register_plugin(); + auto bookie_plug = node->register_plugin(); try {