From 3e41f726a792805ec84edbea20ff391b911a7187 Mon Sep 17 00:00:00 2001 From: Daniel Larimer Date: Tue, 21 Jul 2015 15:51:22 -0400 Subject: [PATCH] Issue #45 Market Subscription Standardization When subscribing to markets the callback will now receive something that looks like this: [ "REMOVED ID1", "REMOVED ID2", ..., { "id": "UPDATED ID3", data... }, { "id": "UPDATED ID4", data... }, ... [ FILL_OPERATION_TYPE_ID, { fill order operation data } ], [ FILL_OPERATION_TYPE_ID, { fill order operation data } ], [ FILL_OPERATION_TYPE_ID, { fill order operation data } ], [ FILL_OPERATION_TYPE_ID, { fill order operation data } ] ] When ever an order is removed from the order book, its ID will be included as a string. When an order is modified the new order data is included as an object. And the operations describing how orders were matched and what fees are paid will be included as an operation, aka array. Also added means to unsubscribe from full account data (issue #166) --- libraries/app/api.cpp | 96 ++++++++++++++++--- libraries/app/include/graphene/app/api.hpp | 7 +- .../graphene/chain/market_evaluator.hpp | 7 ++ 3 files changed, 98 insertions(+), 12 deletions(-) diff --git a/libraries/app/api.cpp b/libraries/app/api.cpp index f16c91d4..d751c31a 100644 --- a/libraries/app/api.cpp +++ b/libraries/app/api.cpp @@ -160,6 +160,26 @@ namespace graphene { namespace app { return result; } + void database_api::unsubscribe_from_accounts( const vector& names_or_ids ) + { + for (const std::string& account_name_or_id : names_or_ids) + { + const account_object* account = nullptr; + if (std::isdigit(account_name_or_id[0])) + account = _db.find(fc::variant(account_name_or_id).as()); + else + { + const auto& idx = _db.get_index_type().indices().get(); + auto itr = idx.find(account_name_or_id); + if (itr != idx.end()) + account = &*itr; + } + if (account == nullptr) + continue; + + _account_subscriptions.erase(account->id); + } + } std::map database_api::get_full_accounts(std::function callback, const vector& names_or_ids) @@ -690,29 +710,59 @@ namespace graphene { namespace app { } } - _broadcast_removed_complete = fc::async([=](){ - for( const auto& item : broadcast_queue ) - { - auto sub = _account_subscriptions.find(item.first); - if( sub != _account_subscriptions.end() ) - sub->second( fc::variant(item.second ) ); - } - }); + if( broadcast_queue.size() ) + { + _broadcast_removed_complete = fc::async([=](){ + for( const auto& item : broadcast_queue ) + { + auto sub = _account_subscriptions.find(item.first); + if( sub != _account_subscriptions.end() ) + sub->second( fc::variant(item.second ) ); + } + }); + } + } + if( _market_subscriptions.size() ) + { + map< pair, vector > broadcast_queue; + for( const auto& obj : objs ) + { + const limit_order_object* order = dynamic_cast(obj); + if( order ) + { + auto sub = _market_subscriptions.find( order->get_market() ); + if( sub != _market_subscriptions.end() ) + broadcast_queue[order->get_market()].emplace_back( order->id ); + } + } + if( broadcast_queue.size() ) + { + _broadcast_removed_complete = fc::async([=](){ + for( const auto& item : broadcast_queue ) + { + auto sub = _market_subscriptions.find(item.first); + if( sub != _market_subscriptions.end() ) + sub->second( fc::variant(item.second ) ); + } + }); + } } } void database_api::on_objects_changed(const vector& ids) { - vector my_objects; - map > broadcast_queue; + vector my_objects; + map > broadcast_queue; + map< pair, vector > market_broadcast_queue; for(auto id : ids) { if(_subscriptions.find(id) != _subscriptions.end()) my_objects.push_back(id); + const object* obj = nullptr; if( _account_subscriptions.size() ) { - const object* obj = _db.find_object( id ); + obj = _db.find_object( id ); if( obj ) { vector relevant = get_relevant_accounts( obj ); @@ -724,6 +774,21 @@ namespace graphene { namespace app { } } } + + if( _market_subscriptions.size() ) + { + if( !_account_subscriptions.size() ) obj = _db.find_object( id ); + if( obj ) + { + const limit_order_object* order = dynamic_cast(obj); + if( order ) + { + auto sub = _market_subscriptions.find( order->get_market() ); + if( sub != _market_subscriptions.end() ) + market_broadcast_queue[order->get_market()].emplace_back( order->id ); + } + } + } } @@ -738,6 +803,12 @@ namespace graphene { namespace app { if( sub != _account_subscriptions.end() ) sub->second( fc::variant(item.second ) ); } + for( const auto& item : market_broadcast_queue ) + { + auto sub = _market_subscriptions.find(item.first); + if( sub != _market_subscriptions.end() ) + sub->second( fc::variant(item.second ) ); + } for(auto id : my_objects) { // just incase _usbscriptions changed between filter and broadcast @@ -766,6 +837,7 @@ namespace graphene { namespace app { if(_market_subscriptions.size() == 0) return; + const auto& ops = _db.get_applied_operations(); map< std::pair, vector> > subscribed_markets_ops; for(const auto& op : ops) @@ -773,9 +845,11 @@ namespace graphene { namespace app { std::pair market; switch(op.op.which()) { + /* This is sent via the object_changed callback case operation::tag::value: market = op.op.get().get_market(); break; + */ case operation::tag::value: market = op.op.get().get_market(); break; diff --git a/libraries/app/include/graphene/app/api.hpp b/libraries/app/include/graphene/app/api.hpp index 42be161c..be457af2 100644 --- a/libraries/app/include/graphene/app/api.hpp +++ b/libraries/app/include/graphene/app/api.hpp @@ -149,11 +149,15 @@ namespace graphene { namespace app { * accounts. If any of the strings in @ref names_or_ids cannot be tied to an account, that input will be * ignored. All other accounts will be retrieved and subscribed. * - * TODO: Describe the return value and argument to callback in detail */ std::map get_full_accounts(std::function callback, const vector& names_or_ids); + /** + * Stop receiving updates generated by get_full_accounts() + */ + void unsubscribe_from_accounts( const vector& names_or_ids ); + /** * @brief Get limit orders in a given market * @param a ID of asset being sold @@ -498,6 +502,7 @@ FC_API(graphene::app::database_api, (get_account_count) (lookup_accounts) (get_full_accounts) + (unsubscribe_from_accounts) (get_account_balances) (get_named_account_balances) (lookup_asset_symbols) diff --git a/libraries/chain/include/graphene/chain/market_evaluator.hpp b/libraries/chain/include/graphene/chain/market_evaluator.hpp index cd316491..48f4d5f6 100644 --- a/libraries/chain/include/graphene/chain/market_evaluator.hpp +++ b/libraries/chain/include/graphene/chain/market_evaluator.hpp @@ -27,6 +27,13 @@ namespace graphene { namespace chain { share_type for_sale; ///< asset id is sell_price.base.asset_id price sell_price; + pair get_market()const + { + auto tmp = std::make_pair( sell_price.base.asset_id, sell_price.quote.asset_id ); + if( tmp.first > tmp.second ) std::swap( tmp.first, tmp.second ); + return tmp; + } + asset amount_for_sale()const { return asset( for_sale, sell_price.base.asset_id ); } asset amount_to_receive()const { return amount_for_sale() * sell_price; } };