diff --git a/libraries/app/CMakeLists.txt b/libraries/app/CMakeLists.txt index e8dd9cad09..71762b1d90 100644 --- a/libraries/app/CMakeLists.txt +++ b/libraries/app/CMakeLists.txt @@ -3,6 +3,7 @@ file(GLOB EGENESIS_HEADERS "../egenesis/include/graphene/app/*.hpp") add_library( graphene_app api.cpp + api_objects.cpp application.cpp util.cpp database_api.cpp diff --git a/libraries/app/api_objects.cpp b/libraries/app/api_objects.cpp new file mode 100644 index 0000000000..e002f061ae --- /dev/null +++ b/libraries/app/api_objects.cpp @@ -0,0 +1,91 @@ +/* + * Copyright (c) 2017 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 + +namespace graphene { namespace app { + +market_ticker::market_ticker(const market_ticker_object& mto, + const fc::time_point_sec& now, + const asset_object& asset_base, + const asset_object& asset_quote, + const order_book& orders) +{ + time = now; + base = asset_base.symbol; + quote = asset_quote.symbol; + percent_change = "0"; + lowest_ask = "0"; + highest_bid = "0"; + + fc::uint128_t bv; + fc::uint128_t qv; + price latest_price = asset( mto.latest_base, mto.base ) / asset( mto.latest_quote, mto.quote ); + if( mto.base != asset_base.id ) + latest_price = ~latest_price; + latest = price_to_string( latest_price, asset_base, asset_quote ); + if( mto.last_day_base != 0 && mto.last_day_quote != 0 // has trade data before 24 hours + && ( mto.last_day_base != mto.latest_base || mto.last_day_quote != mto.latest_quote ) ) // price changed + { + price last_day_price = asset( mto.last_day_base, mto.base ) / asset( mto.last_day_quote, mto.quote ); + if( mto.base != asset_base.id ) + last_day_price = ~last_day_price; + percent_change = price_diff_percent_string( last_day_price, latest_price ); + } + if( asset_base.id == mto.base ) + { + bv = mto.base_volume; + qv = mto.quote_volume; + } + else + { + bv = mto.quote_volume; + qv = mto.base_volume; + } + base_volume = uint128_amount_to_string( bv, asset_base.precision ); + quote_volume = uint128_amount_to_string( qv, asset_quote.precision ); + + if(!orders.asks.empty()) + lowest_ask = orders.asks[0].price; + if(!orders.bids.empty()) + highest_bid = orders.bids[0].price; +} + +market_ticker::market_ticker(const fc::time_point_sec& now, + const asset_object& asset_base, + const asset_object& asset_quote) +{ + time = now; + base = asset_base.symbol; + quote = asset_quote.symbol; + latest = "0"; + lowest_ask = "0"; + highest_bid = "0"; + percent_change = "0"; + base_volume = "0"; + quote_volume = "0"; +} + +} } // graphene::app diff --git a/libraries/app/database_api.cpp b/libraries/app/database_api.cpp index fe879b731e..981a2eac84 100644 --- a/libraries/app/database_api.cpp +++ b/libraries/app/database_api.cpp @@ -22,392 +22,24 @@ * THE SOFTWARE. */ -#include +#include "database_api_impl.hxx" + #include #include #include -#include #include -#include #include #include -#include #include -#include #include -#include -#include - -#define GET_REQUIRED_FEES_MAX_RECURSION 4 - -typedef std::map< std::pair, std::vector > market_queue_type; - template class fc::api; namespace graphene { namespace app { -class database_api_impl : public std::enable_shared_from_this -{ - public: - explicit database_api_impl( graphene::chain::database& db, const application_options* app_options ); - ~database_api_impl(); - - - // Objects - fc::variants get_objects( const vector& ids, optional subscribe )const; - - // Subscriptions - void set_subscribe_callback( std::function cb, bool notify_remove_create ); - void set_auto_subscription( bool enable ); - void set_pending_transaction_callback( std::function cb ); - void set_block_applied_callback( std::function cb ); - void cancel_all_subscriptions(bool reset_callback, bool reset_market_subscriptions); - - // Blocks and transactions - optional get_block_header(uint32_t block_num)const; - map> get_block_header_batch(const vector block_nums)const; - optional get_block(uint32_t block_num)const; - processed_transaction get_transaction( uint32_t block_num, uint32_t trx_in_block )const; - - // Globals - chain_property_object get_chain_properties()const; - global_property_object get_global_properties()const; - fc::variant_object get_config()const; - chain_id_type get_chain_id()const; - dynamic_global_property_object get_dynamic_global_properties()const; - - // Keys - vector> get_key_references( vector key )const; - bool is_public_key_registered(string public_key) const; - - // Accounts - account_id_type get_account_id_from_string(const std::string& name_or_id)const; - vector> get_accounts( const vector& account_names_or_ids, - optional subscribe )const; - std::map get_full_accounts( const vector& names_or_ids, - optional subscribe ); - optional get_account_by_name( string name )const; - vector get_account_references( const std::string account_id_or_name )const; - vector> lookup_account_names(const vector& account_names)const; - map lookup_accounts( const string& lower_bound_name, - uint32_t limit, - optional subscribe )const; - uint64_t get_account_count()const; - - // Balances - vector get_account_balances(const std::string& account_name_or_id, const flat_set& assets)const; - vector get_named_account_balances(const std::string& name, const flat_set& assets)const; - vector get_balance_objects( const vector
& addrs )const; - vector get_vested_balances( const vector& objs )const; - vector get_vesting_balances( const std::string account_id_or_name )const; - - // Assets - uint64_t get_asset_count()const; - asset_id_type get_asset_id_from_string(const std::string& symbol_or_id)const; - vector> get_assets( const vector& asset_symbols_or_ids, - optional subscribe )const; - vector list_assets(const string& lower_bound_symbol, uint32_t limit)const; - vector> lookup_asset_symbols(const vector& symbols_or_ids)const; - vector get_assets_by_issuer(const std::string& issuer_name_or_id, - asset_id_type start, uint32_t limit)const; - - // Markets / feeds - vector get_limit_orders(const std::string& a, const std::string& b, uint32_t limit)const; - vector get_account_limit_orders( const string& account_name_or_id, - const string &base, - const string "e, uint32_t limit, - optional ostart_id, - optional ostart_price ); - vector get_call_orders(const std::string& a, uint32_t limit)const; - vector get_call_orders_by_account(const std::string& account_name_or_id, - asset_id_type start, uint32_t limit)const; - vector get_settle_orders(const std::string& a, uint32_t limit)const; - vector get_settle_orders_by_account(const std::string& account_name_or_id, - force_settlement_id_type start, uint32_t limit)const; - vector get_margin_positions( const std::string account_id_or_name )const; - vector get_collateral_bids(const std::string& asset, uint32_t limit, uint32_t start)const; - - void subscribe_to_market(std::function callback, const std::string& a, const std::string& b); - void unsubscribe_from_market(const std::string& a, const std::string& b); - - market_ticker get_ticker( const string& base, const string& quote, bool skip_order_book = false )const; - market_volume get_24_volume( const string& base, const string& quote )const; - order_book get_order_book( const string& base, const string& quote, unsigned limit = 50 )const; - vector get_top_markets( uint32_t limit )const; - vector get_trade_history( const string& base, const string& quote, fc::time_point_sec start, fc::time_point_sec stop, unsigned limit = 100 )const; - vector get_trade_history_by_sequence( const string& base, const string& quote, int64_t start, fc::time_point_sec stop, unsigned limit = 100 )const; - - // Witnesses - vector> get_witnesses(const vector& witness_ids)const; - fc::optional get_witness_by_account(const std::string account_id_or_name)const; - map lookup_witness_accounts(const string& lower_bound_name, uint32_t limit)const; - uint64_t get_witness_count()const; - - // Committee members - vector> get_committee_members(const vector& committee_member_ids)const; - fc::optional get_committee_member_by_account(const std::string account_id_or_name)const; - map lookup_committee_member_accounts(const string& lower_bound_name, uint32_t limit)const; - uint64_t get_committee_count()const; - - // Workers - vector get_all_workers()const; - vector> get_workers_by_account(const std::string account_id_or_name)const; - uint64_t get_worker_count()const; - - // Votes - vector lookup_vote_ids( const vector& votes )const; - - // Authority / validation - std::string get_transaction_hex(const signed_transaction& trx)const; - std::string get_transaction_hex_without_sig(const signed_transaction& trx)const; - - set get_required_signatures( const signed_transaction& trx, const flat_set& available_keys )const; - set get_potential_signatures( const signed_transaction& trx )const; - set
get_potential_address_signatures( const signed_transaction& trx )const; - bool verify_authority( const signed_transaction& trx )const; - bool verify_account_authority( const string& account_name_or_id, const flat_set& signers )const; - processed_transaction validate_transaction( const signed_transaction& trx )const; - vector< fc::variant > get_required_fees( const vector& ops, const std::string& asset_id_or_symbol )const; - - // Proposed transactions - vector get_proposed_transactions( const std::string account_id_or_name )const; - - // Blinded balances - vector get_blinded_balances( const flat_set& commitments )const; - - // Withdrawals - vector get_withdraw_permissions_by_giver(const std::string account_id_or_name, withdraw_permission_id_type start, uint32_t limit)const; - vector get_withdraw_permissions_by_recipient(const std::string account_id_or_name, withdraw_permission_id_type start, uint32_t limit)const; - - // HTLC - optional get_htlc( htlc_id_type id, optional subscribe ) const; - vector get_htlc_by_from(const std::string account_id_or_name, htlc_id_type start, uint32_t limit) const; - vector get_htlc_by_to(const std::string account_id_or_name, htlc_id_type start, uint32_t limit) const; - vector list_htlcs(const htlc_id_type lower_bound_id, uint32_t limit) const; - - //private: - static string price_to_string( const price& _price, const asset_object& _base, const asset_object& _quote ); - - // Decides whether to subscribe using member variables and given parameter - bool get_whether_to_subscribe( optional subscribe )const - { - if( !_subscribe_callback ) - return false; - if( subscribe.valid() ) - return *subscribe; - return _enabled_auto_subscription; - } - - // Note: - // Different type of object_id objects could become identical after packed. - // For example, both `account_id_type a=1.2.0` and `asset_id_type b=1.3.0` will become `0` after packed. - // In order to avoid collision, we don't use a template function here, instead, we implicitly convert all - // object IDs to `object_id_type` when subscribing. - // - // If need to subscribe to other data types, override this function with the types as parameter. - // For example, we had a `get_subscription_key( const public_key_type& item )` function here, which was - // removed lately since we no longer subscribe to public keys. - vector get_subscription_key( const object_id_type& item )const - { - return fc::raw::pack(item); - } - - template - void subscribe_to_item( const T& item )const - { - if( !_subscribe_callback ) - return; - - vector key = get_subscription_key( item ); - if( !_subscribe_filter.contains( key.data(), key.size() ) ) - { - _subscribe_filter.insert( key.data(), key.size() ); - } - } - - template - bool is_subscribed_to_item( const T& item )const - { - if( !_subscribe_callback ) - return false; - - vector key = get_subscription_key( item ); - return _subscribe_filter.contains( key.data(), key.size() ); - } - - bool is_impacted_account( const flat_set& accounts) - { - if( !_subscribed_accounts.size() || !accounts.size() ) - return false; - - return std::any_of(accounts.begin(), accounts.end(), [this](const account_id_type& account) { - return _subscribed_accounts.find(account) != _subscribed_accounts.end(); - }); - } - - const std::pair get_order_market( const force_settlement_object& order ) - { - // TODO cache the result to avoid repeatly fetching from db - asset_id_type backing_id = order.balance.asset_id( _db ).bitasset_data( _db ).options.short_backing_asset; - auto tmp = std::make_pair( order.balance.asset_id, backing_id ); - if( tmp.first > tmp.second ) std::swap( tmp.first, tmp.second ); - return tmp; - } - - const account_object* get_account_from_string( const std::string& name_or_id, bool throw_if_not_found = true ) const - { - // TODO cache the result to avoid repeatly fetching from db - FC_ASSERT( name_or_id.size() > 0); - const account_object* account = nullptr; - if (std::isdigit(name_or_id[0])) - account = _db.find(fc::variant(name_or_id, 1).as(1)); - else - { - const auto& idx = _db.get_index_type().indices().get(); - auto itr = idx.find(name_or_id); - if (itr != idx.end()) - account = &*itr; - } - if(throw_if_not_found) - FC_ASSERT( account, "no such account" ); - return account; - } - - template - extended_asset_object extend_asset( ASSET&& a )const - { - asset_id_type id = a.id; - extended_asset_object result = extended_asset_object( std::forward( a ) ); - if( amount_in_collateral_index ) - { - result.total_in_collateral = amount_in_collateral_index->get_amount_in_collateral( id ); - if( result.bitasset_data_id.valid() ) - result.total_backing_collateral = amount_in_collateral_index->get_backing_collateral( id ); - } - return result; - } - - const asset_object* get_asset_from_string( const std::string& symbol_or_id, bool throw_if_not_found = true ) const - { - // TODO cache the result to avoid repeatly fetching from db - FC_ASSERT( symbol_or_id.size() > 0); - const asset_object* asset = nullptr; - if (std::isdigit(symbol_or_id[0])) - asset = _db.find(fc::variant(symbol_or_id, 1).as(1)); - else - { - const auto& idx = _db.get_index_type().indices().get(); - auto itr = idx.find(symbol_or_id); - if (itr != idx.end()) - asset = &*itr; - } - if(throw_if_not_found) - FC_ASSERT( asset, "no such asset" ); - return asset; - } - vector> get_assets( const vector& asset_ids, - optional subscribe = optional() )const - { - bool to_subscribe = get_whether_to_subscribe( subscribe ); - vector> result; result.reserve(asset_ids.size()); - std::transform(asset_ids.begin(), asset_ids.end(), std::back_inserter(result), - [this,to_subscribe](asset_id_type id) -> optional { - if(auto o = _db.find(id)) - { - if( to_subscribe ) - subscribe_to_item( id ); - return extend_asset( *o ); - } - return {}; - }); - return result; - } - vector get_limit_orders(const asset_id_type a, const asset_id_type b, const uint32_t limit)const - { - uint64_t api_limit_get_limit_orders=_app_options->api_limit_get_limit_orders; - FC_ASSERT( limit <= api_limit_get_limit_orders ); - - const auto& limit_order_idx = _db.get_index_type(); - const auto& limit_price_idx = limit_order_idx.indices().get(); - - vector result; - result.reserve(limit*2); - - uint32_t count = 0; - auto limit_itr = limit_price_idx.lower_bound(price::max(a,b)); - auto limit_end = limit_price_idx.upper_bound(price::min(a,b)); - while(limit_itr != limit_end && count < limit) - { - result.push_back(*limit_itr); - ++limit_itr; - ++count; - } - count = 0; - limit_itr = limit_price_idx.lower_bound(price::max(b,a)); - limit_end = limit_price_idx.upper_bound(price::min(b,a)); - while(limit_itr != limit_end && count < limit) - { - result.push_back(*limit_itr); - ++limit_itr; - ++count; - } - - return result; - } - - template - const std::pair get_order_market( const T& order ) - { - return order.get_market(); - } - - template - void enqueue_if_subscribed_to_market(const object* obj, market_queue_type& queue, bool full_object=true) - { - const T* order = dynamic_cast(obj); - FC_ASSERT( order != nullptr); - - const auto& market = get_order_market( *order ); - - auto sub = _market_subscriptions.find( market ); - if( sub != _market_subscriptions.end() ) { - queue[market].emplace_back( full_object ? obj->to_variant() : fc::variant(obj->id, 1) ); - } - } - - void broadcast_updates( const vector& updates ); - void broadcast_market_updates( const market_queue_type& queue); - void handle_object_changed(bool force_notify, bool full_object, const vector& ids, const flat_set& impacted_accounts, std::function find_object); - - /** called every time a block is applied to report the objects that were changed */ - void on_objects_new(const vector& ids, const flat_set& impacted_accounts); - void on_objects_changed(const vector& ids, const flat_set& impacted_accounts); - void on_objects_removed(const vector& ids, const vector& objs, const flat_set& impacted_accounts); - void on_applied_block(); - - bool _notify_remove_create = false; - mutable fc::bloom_filter _subscribe_filter; - std::set _subscribed_accounts; - std::function _subscribe_callback; - std::function _pending_trx_callback; - std::function _block_applied_callback; - bool _enabled_auto_subscription = true; - - boost::signals2::scoped_connection _new_connection; - boost::signals2::scoped_connection _change_connection; - boost::signals2::scoped_connection _removed_connection; - boost::signals2::scoped_connection _applied_block_connection; - boost::signals2::scoped_connection _pending_trx_connection; - map< pair, std::function > _market_subscriptions; - graphene::chain::database& _db; - const application_options* _app_options = nullptr; - const graphene::api_helper_indexes::amount_in_collateral_index* amount_in_collateral_index; -}; - ////////////////////////////////////////////////////////////////////// // // // Constructors // @@ -423,24 +55,29 @@ database_api_impl::database_api_impl( graphene::chain::database& db, const appli :_db(db), _app_options(app_options) { dlog("creating database api ${x}", ("x",int64_t(this)) ); - _new_connection = _db.new_objects.connect([this](const vector& ids, const flat_set& impacted_accounts) { + _new_connection = _db.new_objects.connect([this](const vector& ids, + const flat_set& impacted_accounts) { on_objects_new(ids, impacted_accounts); }); - _change_connection = _db.changed_objects.connect([this](const vector& ids, const flat_set& impacted_accounts) { + _change_connection = _db.changed_objects.connect([this](const vector& ids, + const flat_set& impacted_accounts) { on_objects_changed(ids, impacted_accounts); }); - _removed_connection = _db.removed_objects.connect([this](const vector& ids, const vector& objs, const flat_set& impacted_accounts) { + _removed_connection = _db.removed_objects.connect([this](const vector& ids, + const vector& objs, + const flat_set& impacted_accounts) { on_objects_removed(ids, objs, impacted_accounts); }); _applied_block_connection = _db.applied_block.connect([this](const signed_block&){ on_applied_block(); }); _pending_trx_connection = _db.on_pending_transaction.connect([this](const signed_transaction& trx ){ - if( _pending_trx_callback ) _pending_trx_callback( fc::variant(trx, GRAPHENE_MAX_NESTED_OBJECTS) ); + if( _pending_trx_callback ) + _pending_trx_callback( fc::variant(trx, GRAPHENE_MAX_NESTED_OBJECTS) ); }); try { amount_in_collateral_index = &_db.get_index_type< primary_index< call_order_index > >() - .get_secondary_index(); + .get_secondary_index(); } catch( fc::assert_exception& e ) { @@ -454,71 +91,6 @@ database_api_impl::~database_api_impl() dlog("freeing database api ${x}", ("x",int64_t(this)) ); } -////////////////////////////////////////////////////////////////////// -// // -// Market ticker constructor // -// // -////////////////////////////////////////////////////////////////////// -market_ticker::market_ticker(const market_ticker_object& mto, - const fc::time_point_sec& now, - const asset_object& asset_base, - const asset_object& asset_quote, - const order_book& orders) -{ - time = now; - base = asset_base.symbol; - quote = asset_quote.symbol; - percent_change = "0"; - lowest_ask = "0"; - highest_bid = "0"; - - fc::uint128_t bv; - fc::uint128_t qv; - price latest_price = asset( mto.latest_base, mto.base ) / asset( mto.latest_quote, mto.quote ); - if( mto.base != asset_base.id ) - latest_price = ~latest_price; - latest = database_api_impl::price_to_string( latest_price, asset_base, asset_quote ); - if( mto.last_day_base != 0 && mto.last_day_quote != 0 // has trade data before 24 hours - && ( mto.last_day_base != mto.latest_base || mto.last_day_quote != mto.latest_quote ) ) // price changed - { - price last_day_price = asset( mto.last_day_base, mto.base ) / asset( mto.last_day_quote, mto.quote ); - if( mto.base != asset_base.id ) - last_day_price = ~last_day_price; - percent_change = price_diff_percent_string( last_day_price, latest_price ); - } - if( asset_base.id == mto.base ) - { - bv = mto.base_volume; - qv = mto.quote_volume; - } - else - { - bv = mto.quote_volume; - qv = mto.base_volume; - } - base_volume = uint128_amount_to_string( bv, asset_base.precision ); - quote_volume = uint128_amount_to_string( qv, asset_quote.precision ); - - if(!orders.asks.empty()) - lowest_ask = orders.asks[0].price; - if(!orders.bids.empty()) - highest_bid = orders.bids[0].price; -} -market_ticker::market_ticker(const fc::time_point_sec& now, - const asset_object& asset_base, - const asset_object& asset_quote) -{ - time = now; - base = asset_base.symbol; - quote = asset_quote.symbol; - latest = "0"; - lowest_ask = "0"; - highest_bid = "0"; - percent_change = "0"; - base_volume = "0"; - quote_volume = "0"; -} - ////////////////////////////////////////////////////////////////////// // // // Objects // @@ -648,7 +220,8 @@ map> database_api::get_block_header_batch(const return my->get_block_header_batch( block_nums ); } -map> database_api_impl::get_block_header_batch(const vector block_nums) const +map> database_api_impl::get_block_header_batch( + const vector block_nums) const { map> results; for (const uint32_t block_num : block_nums) @@ -870,92 +443,6 @@ vector> database_api_impl::get_accounts( const vector database_api::get_account_limit_orders( const string& account_name_or_id, const string &base, - const string "e, uint32_t limit, optional ostart_id, optional ostart_price) -{ - return my->get_account_limit_orders( account_name_or_id, base, quote, limit, ostart_id, ostart_price ); -} - -vector database_api_impl::get_account_limit_orders( const string& account_name_or_id, const string &base, - const string "e, uint32_t limit, optional ostart_id, optional ostart_price) -{ - FC_ASSERT( limit <= 101 ); - - vector results; - uint32_t count = 0; - - const account_object* account = get_account_from_string(account_name_or_id); - if (account == nullptr) - return results; - - auto assets = lookup_asset_symbols( {base, quote} ); - FC_ASSERT( assets[0], "Invalid base asset symbol: ${s}", ("s",base) ); - FC_ASSERT( assets[1], "Invalid quote asset symbol: ${s}", ("s",quote) ); - - auto base_id = assets[0]->id; - auto quote_id = assets[1]->id; - - if (ostart_price.valid()) { - FC_ASSERT(ostart_price->base.asset_id == base_id, "Base asset inconsistent with start price"); - FC_ASSERT(ostart_price->quote.asset_id == quote_id, "Quote asset inconsistent with start price"); - } - - const auto& index_by_account = _db.get_index_type().indices().get(); - limit_order_multi_index_type::index::type::const_iterator lower_itr; - limit_order_multi_index_type::index::type::const_iterator upper_itr; - - // if both order_id and price are invalid, query the first page - if ( !ostart_id.valid() && !ostart_price.valid() ) - { - lower_itr = index_by_account.lower_bound(std::make_tuple(account->id, price::max(base_id, quote_id))); - } - else if ( ostart_id.valid() ) - { - // in case of the order been deleted during page querying - const limit_order_object *p_loo = _db.find(*ostart_id); - - if ( !p_loo ) - { - if ( ostart_price.valid() ) - { - lower_itr = index_by_account.lower_bound(std::make_tuple(account->id, *ostart_price, *ostart_id)); - } - else - { - // start order id been deleted, yet not provided price either - FC_THROW("Order id invalid (maybe just been canceled?), and start price not provided"); - } - } - else - { - const limit_order_object &loo = *p_loo; - - // in case of the order not belongs to specified account or market - FC_ASSERT(loo.sell_price.base.asset_id == base_id, "Order base asset inconsistent"); - FC_ASSERT(loo.sell_price.quote.asset_id == quote_id, "Order quote asset inconsistent with order"); - FC_ASSERT(loo.seller == account->get_id(), "Order not owned by specified account"); - - lower_itr = index_by_account.lower_bound(std::make_tuple(account->id, loo.sell_price, *ostart_id)); - } - } - else - { - // if reach here start_price must be valid - lower_itr = index_by_account.lower_bound(std::make_tuple(account->id, *ostart_price)); - } - - upper_itr = index_by_account.upper_bound(std::make_tuple(account->id, price::min(base_id, quote_id))); - - // Add the account's orders - for ( ; lower_itr != upper_itr && count < limit; ++lower_itr, ++count) - { - const limit_order_object &order = *lower_itr; - results.emplace_back(order); - } - - return results; -} - std::map database_api::get_full_accounts( const vector& names_or_ids, optional subscribe ) { @@ -994,20 +481,23 @@ std::map database_api_impl::get_full_accounts( const acnt.registrar_name = account->registrar(_db).name; acnt.referrer_name = account->referrer(_db).name; acnt.lifetime_referrer_name = account->lifetime_referrer(_db).name; - acnt.votes = lookup_vote_ids( vector(account->options.votes.begin(),account->options.votes.end()) ); + acnt.votes = lookup_vote_ids( vector( account->options.votes.begin(), + account->options.votes.end() ) ); if (account->cashback_vb) { acnt.cashback_balance = account->cashback_balance(_db); } - size_t api_limit_get_full_accounts_lists = static_cast(_app_options->api_limit_get_full_accounts_lists); + size_t api_limit_get_full_accounts_lists = static_cast( + _app_options->api_limit_get_full_accounts_lists ); // Add the account's proposals auto required_approvals_itr = proposals_by_account._account_to_proposals.find( account->id ); if( required_approvals_itr != proposals_by_account._account_to_proposals.end() ) { - acnt.proposals.reserve( std::min(required_approvals_itr->second.size(), api_limit_get_full_accounts_lists) ); + acnt.proposals.reserve( std::min(required_approvals_itr->second.size(), + api_limit_get_full_accounts_lists) ); for( auto proposal_id : required_approvals_itr->second ) { if(acnt.proposals.size() >= api_limit_get_full_accounts_lists) { @@ -1031,7 +521,8 @@ std::map database_api_impl::get_full_accounts( const } // Add the account's vesting balances - auto vesting_range = _db.get_index_type().indices().get().equal_range(account->id); + auto vesting_range = _db.get_index_type().indices().get() + .equal_range(account->id); for(auto itr = vesting_range.first; itr != vesting_range.second; ++itr) { if(acnt.vesting_balances.size() >= api_limit_get_full_accounts_lists) { @@ -1042,7 +533,8 @@ std::map database_api_impl::get_full_accounts( const } // Add the account's orders - auto order_range = _db.get_index_type().indices().get().equal_range(account->id); + auto order_range = _db.get_index_type().indices().get() + .equal_range(account->id); for(auto itr = order_range.first; itr != order_range.second; ++itr) { if(acnt.limit_orders.size() >= api_limit_get_full_accounts_lists) { @@ -1060,7 +552,8 @@ std::map database_api_impl::get_full_accounts( const } acnt.call_orders.emplace_back(*itr); } - auto settle_range = _db.get_index_type().indices().get().equal_range(account->id); + auto settle_range = _db.get_index_type().indices().get() + .equal_range(account->id); for(auto itr = settle_range.first; itr != settle_range.second; ++itr) { if(acnt.settle_orders.size() >= api_limit_get_full_accounts_lists) { @@ -1228,12 +721,14 @@ uint64_t database_api_impl::get_account_count()const // // ////////////////////////////////////////////////////////////////////// -vector database_api::get_account_balances(const std::string& account_name_or_id, const flat_set& assets)const +vector database_api::get_account_balances( const std::string& account_name_or_id, + const flat_set& assets )const { return my->get_account_balances( account_name_or_id, assets ); } -vector database_api_impl::get_account_balances(const std::string& account_name_or_id, const flat_set& assets)const +vector database_api_impl::get_account_balances( const std::string& account_name_or_id, + const flat_set& assets )const { const account_object* account = get_account_from_string(account_name_or_id); account_id_type acnt = account->id; @@ -1242,7 +737,8 @@ vector database_api_impl::get_account_balances(const std::string& account { // if the caller passes in an empty list of assets, return balances for all assets the account owns const auto& balance_index = _db.get_index_type< primary_index< account_balance_index > >(); - const auto& balances = balance_index.get_secondary_index< balances_by_account_index >().get_account_balances( acnt ); + const auto& balances = balance_index.get_secondary_index< balances_by_account_index >() + .get_account_balances( acnt ); for( const auto balance : balances ) result.push_back( balance.second->get_balance() ); } @@ -1257,7 +753,8 @@ vector database_api_impl::get_account_balances(const std::string& account return result; } -vector database_api::get_named_account_balances(const std::string& name, const flat_set& assets)const +vector database_api::get_named_account_balances( const std::string& name, + const flat_set& assets )const { return my->get_account_balances( name, assets ); } @@ -1319,7 +816,8 @@ vector database_api_impl::get_vesting_balances( const st { const account_id_type account_id = get_account_from_string(account_id_or_name)->id; vector result; - auto vesting_range = _db.get_index_type().indices().get().equal_range(account_id); + auto vesting_range = _db.get_index_type().indices().get() + .equal_range(account_id); std::for_each(vesting_range.first, vesting_range.second, [&result](const vesting_balance_object& balance) { result.emplace_back(balance); @@ -1340,14 +838,16 @@ asset_id_type database_api::get_asset_id_from_string(const std::string& symbol_o return my->get_asset_from_string( symbol_or_id )->id; } -vector> database_api::get_assets( const vector& asset_symbols_or_ids, - optional subscribe )const +vector> database_api::get_assets( + const vector& asset_symbols_or_ids, + optional subscribe )const { return my->get_assets( asset_symbols_or_ids, subscribe ); } -vector> database_api_impl::get_assets(const vector& asset_symbols_or_ids, - optional subscribe )const +vector> database_api_impl::get_assets( + const vector& asset_symbols_or_ids, + optional subscribe )const { bool to_subscribe = get_whether_to_subscribe( subscribe ); vector> result; result.reserve(asset_symbols_or_ids.size()); @@ -1424,12 +924,14 @@ vector database_api_impl::get_assets_by_issuer(const std: return result; } -vector> database_api::lookup_asset_symbols(const vector& symbols_or_ids)const +vector> database_api::lookup_asset_symbols( + const vector& symbols_or_ids )const { return my->lookup_asset_symbols( symbols_or_ids ); } -vector> database_api_impl::lookup_asset_symbols(const vector& symbols_or_ids)const +vector> database_api_impl::lookup_asset_symbols( + const vector& symbols_or_ids )const { const auto& assets_by_symbol = _db.get_index_type().indices().get(); vector > result; @@ -1458,10 +960,8 @@ vector database_api::get_limit_orders(std::string a, std::st return my->get_limit_orders( a, b, limit ); } -/** - * @return the limit orders for both sides of the book for the two assets specified up to limit number on each side. - */ -vector database_api_impl::get_limit_orders(const std::string& a, const std::string& b, uint32_t limit)const +vector database_api_impl::get_limit_orders( const std::string& a, const std::string& b, + uint32_t limit )const { uint64_t api_limit_get_limit_orders=_app_options->api_limit_get_limit_orders; FC_ASSERT( limit <= api_limit_get_limit_orders ); @@ -1472,6 +972,94 @@ vector database_api_impl::get_limit_orders(const std::string return get_limit_orders(asset_a_id, asset_b_id, limit); } +vector database_api::get_account_limit_orders( + const string& account_name_or_id, const string &base, const string "e, + uint32_t limit, optional ostart_id, optional ostart_price ) +{ + return my->get_account_limit_orders( account_name_or_id, base, quote, limit, ostart_id, ostart_price ); +} + +vector database_api_impl::get_account_limit_orders( + const string& account_name_or_id, const string &base, const string "e, + uint32_t limit, optional ostart_id, optional ostart_price ) +{ + FC_ASSERT( limit <= 101 ); + + vector results; + uint32_t count = 0; + + const account_object* account = get_account_from_string(account_name_or_id); + if (account == nullptr) + return results; + + auto assets = lookup_asset_symbols( {base, quote} ); + FC_ASSERT( assets[0], "Invalid base asset symbol: ${s}", ("s",base) ); + FC_ASSERT( assets[1], "Invalid quote asset symbol: ${s}", ("s",quote) ); + + auto base_id = assets[0]->id; + auto quote_id = assets[1]->id; + + if (ostart_price.valid()) { + FC_ASSERT(ostart_price->base.asset_id == base_id, "Base asset inconsistent with start price"); + FC_ASSERT(ostart_price->quote.asset_id == quote_id, "Quote asset inconsistent with start price"); + } + + const auto& index_by_account = _db.get_index_type().indices().get(); + limit_order_multi_index_type::index::type::const_iterator lower_itr; + limit_order_multi_index_type::index::type::const_iterator upper_itr; + + // if both order_id and price are invalid, query the first page + if ( !ostart_id.valid() && !ostart_price.valid() ) + { + lower_itr = index_by_account.lower_bound(std::make_tuple(account->id, price::max(base_id, quote_id))); + } + else if ( ostart_id.valid() ) + { + // in case of the order been deleted during page querying + const limit_order_object *p_loo = _db.find(*ostart_id); + + if ( !p_loo ) + { + if ( ostart_price.valid() ) + { + lower_itr = index_by_account.lower_bound(std::make_tuple(account->id, *ostart_price, *ostart_id)); + } + else + { + // start order id been deleted, yet not provided price either + FC_THROW("Order id invalid (maybe just been canceled?), and start price not provided"); + } + } + else + { + const limit_order_object &loo = *p_loo; + + // in case of the order not belongs to specified account or market + FC_ASSERT(loo.sell_price.base.asset_id == base_id, "Order base asset inconsistent"); + FC_ASSERT(loo.sell_price.quote.asset_id == quote_id, "Order quote asset inconsistent with order"); + FC_ASSERT(loo.seller == account->get_id(), "Order not owned by specified account"); + + lower_itr = index_by_account.lower_bound(std::make_tuple(account->id, loo.sell_price, *ostart_id)); + } + } + else + { + // if reach here start_price must be valid + lower_itr = index_by_account.lower_bound(std::make_tuple(account->id, *ostart_price)); + } + + upper_itr = index_by_account.upper_bound(std::make_tuple(account->id, price::min(base_id, quote_id))); + + // Add the account's orders + for ( ; lower_itr != upper_itr && count < limit; ++lower_itr, ++count) + { + const limit_order_object &order = *lower_itr; + results.emplace_back(order); + } + + return results; +} + vector database_api::get_call_orders(const std::string& a, uint32_t limit)const { return my->get_call_orders( a, limit ); @@ -1547,14 +1135,18 @@ vector database_api_impl::get_settle_orders(const std:: return result; } -vector database_api::get_settle_orders_by_account(const std::string& account_name_or_id, - force_settlement_id_type start, uint32_t limit)const +vector database_api::get_settle_orders_by_account( + const std::string& account_name_or_id, + force_settlement_id_type start, + uint32_t limit )const { return my->get_settle_orders_by_account( account_name_or_id, start, limit); } -vector database_api_impl::get_settle_orders_by_account(const std::string& account_name_or_id, - force_settlement_id_type start, uint32_t limit)const +vector database_api_impl::get_settle_orders_by_account( + const std::string& account_name_or_id, + force_settlement_id_type start, + uint32_t limit )const { uint64_t api_limit_get_settle_orders = _app_options->api_limit_get_settle_orders; FC_ASSERT( limit <= api_limit_get_settle_orders ); @@ -1597,12 +1189,14 @@ vector database_api_impl::get_margin_positions( const std::st } FC_CAPTURE_AND_RETHROW( (account_id_or_name) ) } -vector database_api::get_collateral_bids(const std::string& asset, uint32_t limit, uint32_t start)const +vector database_api::get_collateral_bids( const std::string& asset, + uint32_t limit, uint32_t start )const { return my->get_collateral_bids( asset, limit, start ); } -vector database_api_impl::get_collateral_bids(const std::string& asset, uint32_t limit, uint32_t skip)const +vector database_api_impl::get_collateral_bids( const std::string& asset, + uint32_t limit, uint32_t skip )const { try { FC_ASSERT( limit <= 100 ); const asset_id_type asset_id = get_asset_from_string(asset)->id; @@ -1612,8 +1206,12 @@ vector database_api_impl::get_collateral_bids(const std:: const asset_object& back = bad.options.short_backing_asset(_db); const auto& idx = _db.get_index_type(); const auto& aidx = idx.indices().get(); - auto start = aidx.lower_bound( boost::make_tuple( asset_id, price::max(back.id, asset_id), collateral_bid_id_type() ) ); - auto end = aidx.lower_bound( boost::make_tuple( asset_id, price::min(back.id, asset_id), collateral_bid_id_type(GRAPHENE_DB_MAX_INSTANCE_ID) ) ); + auto start = aidx.lower_bound( boost::make_tuple( asset_id, + price::max(back.id, asset_id), + collateral_bid_id_type() ) ); + auto end = aidx.lower_bound( boost::make_tuple( asset_id, + price::min(back.id, asset_id), + collateral_bid_id_type(GRAPHENE_DB_MAX_INSTANCE_ID) ) ); vector result; while( skip-- > 0 && start != end ) { ++start; } while( start != end && limit-- > 0) @@ -1624,12 +1222,14 @@ vector database_api_impl::get_collateral_bids(const std:: return result; } FC_CAPTURE_AND_RETHROW( (asset)(limit)(skip) ) } -void database_api::subscribe_to_market(std::function callback, const std::string& a, const std::string& b) +void database_api::subscribe_to_market( std::function callback, + const std::string& a, const std::string& b ) { my->subscribe_to_market( callback, a, b ); } -void database_api_impl::subscribe_to_market(std::function callback, const std::string& a, const std::string& b) +void database_api_impl::subscribe_to_market( std::function callback, + const std::string& a, const std::string& b ) { auto asset_a_id = get_asset_from_string(a)->id; auto asset_b_id = get_asset_from_string(b)->id; @@ -1654,16 +1254,6 @@ void database_api_impl::unsubscribe_from_market(const std::string& a, const std: _market_subscriptions.erase(std::make_pair(asset_a_id,asset_b_id)); } -string database_api_impl::price_to_string( const price& _price, const asset_object& _base, const asset_object& _quote ) -{ try { - if( _price.base.asset_id == _base.id && _price.quote.asset_id == _quote.id ) - return graphene::app::price_to_string( _price, _base.precision, _quote.precision ); - else if( _price.base.asset_id == _quote.id && _price.quote.asset_id == _base.id ) - return graphene::app::price_to_string( ~_price, _base.precision, _quote.precision ); - else - FC_ASSERT( !"bad parameters" ); -} FC_CAPTURE_AND_RETHROW( (_price)(_base)(_quote) ) } - market_ticker database_api::get_ticker( const string& base, const string& quote )const { return my->get_ticker( base, quote ); @@ -1681,7 +1271,7 @@ market_ticker database_api_impl::get_ticker( const string& base, const string& q auto base_id = assets[0]->id; auto quote_id = assets[1]->id; if( base_id > quote_id ) std::swap( base_id, quote_id ); - const auto& ticker_idx = _db.get_index_type().indices().get(); + const auto& ticker_idx = _db.get_index_type().indices().get(); auto itr = ticker_idx.find( std::make_tuple( base_id, quote_id ) ); const fc::time_point_sec now = _db.head_block_time(); if( itr != ticker_idx.end() ) @@ -1777,7 +1367,7 @@ vector database_api_impl::get_top_markets(uint32_t limit)const FC_ASSERT( limit <= 100 ); - const auto& volume_idx = _db.get_index_type().indices().get(); + const auto& volume_idx = _db.get_index_type().indices().get(); auto itr = volume_idx.rbegin(); vector result; result.reserve(limit); @@ -1828,11 +1418,12 @@ vector database_api_impl::get_trade_history( const string& base, start = fc::time_point_sec( fc::time_point::now() ); uint32_t count = 0; - const auto& history_idx = _db.get_index_type().indices().get(); + const auto& history_idx = _db.get_index_type().indices().get(); auto itr = history_idx.lower_bound( std::make_tuple( base_id, quote_id, start ) ); vector result; - while( itr != history_idx.end() && count < limit && !( itr->key.base != base_id || itr->key.quote != quote_id || itr->time < stop ) ) + while( itr != history_idx.end() && count < limit + && !( itr->key.base != base_id || itr->key.quote != quote_id || itr->time < stop ) ) { { market_trade trade; @@ -1926,7 +1517,8 @@ vector database_api_impl::get_trade_history_by_sequence( auto itr = history_idx.lower_bound( hkey ); vector result; - while( itr != history_idx.end() && count < limit && !( itr->key.base != base_id || itr->key.quote != quote_id || itr->time < stop ) ) + while( itr != history_idx.end() && count < limit + && !( itr->key.base != base_id || itr->key.quote != quote_id || itr->time < stop ) ) { if( itr->key.sequence == start_seq ) // found the key, should skip this and the other direction if found { @@ -2028,12 +1620,14 @@ fc::optional database_api_impl::get_witness_by_account(const std return {}; } -map database_api::lookup_witness_accounts(const string& lower_bound_name, uint32_t limit)const +map database_api::lookup_witness_accounts( const string& lower_bound_name, + uint32_t limit )const { return my->lookup_witness_accounts( lower_bound_name, limit ); } -map database_api_impl::lookup_witness_accounts(const string& lower_bound_name, uint32_t limit)const +map database_api_impl::lookup_witness_accounts( const string& lower_bound_name, + uint32_t limit )const { FC_ASSERT( limit <= 1000 ); const auto& witnesses_by_id = _db.get_index_type().indices().get(); @@ -2072,12 +1666,14 @@ uint64_t database_api_impl::get_witness_count()const // // ////////////////////////////////////////////////////////////////////// -vector> database_api::get_committee_members(const vector& committee_member_ids)const +vector> database_api::get_committee_members( + const vector& committee_member_ids )const { return my->get_committee_members( committee_member_ids ); } -vector> database_api_impl::get_committee_members(const vector& committee_member_ids)const +vector> database_api_impl::get_committee_members( + const vector& committee_member_ids )const { vector> result; result.reserve(committee_member_ids.size()); std::transform(committee_member_ids.begin(), committee_member_ids.end(), std::back_inserter(result), @@ -2089,12 +1685,14 @@ vector> database_api_impl::get_committee_membe return result; } -fc::optional database_api::get_committee_member_by_account(const std::string account_id_or_name)const +fc::optional database_api::get_committee_member_by_account( + const std::string account_id_or_name )const { return my->get_committee_member_by_account( account_id_or_name ); } -fc::optional database_api_impl::get_committee_member_by_account(const std::string account_id_or_name) const +fc::optional database_api_impl::get_committee_member_by_account( + const std::string account_id_or_name )const { const auto& idx = _db.get_index_type().indices().get(); const account_id_type account = get_account_from_string(account_id_or_name)->id; @@ -2104,12 +1702,14 @@ fc::optional database_api_impl::get_committee_member_by return {}; } -map database_api::lookup_committee_member_accounts(const string& lower_bound_name, uint32_t limit)const +map database_api::lookup_committee_member_accounts( + const string& lower_bound_name, uint32_t limit )const { return my->lookup_committee_member_accounts( lower_bound_name, limit ); } -map database_api_impl::lookup_committee_member_accounts(const string& lower_bound_name, uint32_t limit)const +map database_api_impl::lookup_committee_member_accounts( + const string& lower_bound_name, uint32_t limit )const { FC_ASSERT( limit <= 1000 ); const auto& committee_members_by_id = _db.get_index_type().indices().get(); @@ -2226,7 +1826,7 @@ vector database_api_impl::lookup_vote_ids( const vector& { auto itr = committee_idx.find( id ); if( itr != committee_idx.end() ) - result.emplace_back( variant( *itr, 2 ) ); // Depth of committee_member_object is 1, add 1 here to be safe + result.emplace_back( variant( *itr, 2 ) ); // Depth of committee_member_object is 1, add 1 to be safe else result.emplace_back( variant() ); break; @@ -2297,12 +1897,14 @@ std::string database_api_impl::get_transaction_hex_without_sig( return fc::to_hex(fc::raw::pack(static_cast(trx))); } -set database_api::get_required_signatures( const signed_transaction& trx, const flat_set& available_keys )const +set database_api::get_required_signatures( const signed_transaction& trx, + const flat_set& available_keys )const { return my->get_required_signatures( trx, available_keys ); } -set database_api_impl::get_required_signatures( const signed_transaction& trx, const flat_set& available_keys )const +set database_api_impl::get_required_signatures( const signed_transaction& trx, + const flat_set& available_keys )const { bool allow_non_immediate_owner = ( _db.head_block_time() >= HARDFORK_CORE_584_TIME ); auto result = trx.get_required_signatures( _db.get_chain_id(), @@ -2401,7 +2003,8 @@ bool database_api_impl::verify_authority( const signed_transaction& trx )const return true; } -bool database_api::verify_account_authority( const string& account_name_or_id, const flat_set& signers )const +bool database_api::verify_account_authority( const string& account_name_or_id, + const flat_set& signers )const { return my->verify_account_authority( account_name_or_id, signers ); } @@ -2440,7 +2043,8 @@ processed_transaction database_api_impl::validate_transaction( const signed_tran return _db.validate_transaction(trx); } -vector< fc::variant > database_api::get_required_fees( const vector& ops, const std::string& asset_id_or_symbol )const +vector< fc::variant > database_api::get_required_fees( const vector& ops, + const std::string& asset_id_or_symbol )const { return my->get_required_fees( ops, asset_id_or_symbol ); } @@ -2501,7 +2105,8 @@ struct get_required_fees_helper uint32_t current_recursion = 0; }; -vector< fc::variant > database_api_impl::get_required_fees( const vector& ops, const std::string& asset_id_or_symbol )const +vector< fc::variant > database_api_impl::get_required_fees( const vector& ops, + const std::string& asset_id_or_symbol )const { vector< operation > _ops = ops; // @@ -2560,12 +2165,14 @@ vector database_api_impl::get_proposed_transactions( const std: // // ////////////////////////////////////////////////////////////////////// -vector database_api::get_blinded_balances( const flat_set& commitments )const +vector database_api::get_blinded_balances( + const flat_set& commitments )const { return my->get_blinded_balances( commitments ); } -vector database_api_impl::get_blinded_balances( const flat_set& commitments )const +vector database_api_impl::get_blinded_balances( + const flat_set& commitments )const { vector result; result.reserve(commitments.size()); const auto& bal_idx = _db.get_index_type(); @@ -2585,12 +2192,18 @@ vector database_api_impl::get_blinded_balances( const fl // // ////////////////////////////////////////////////////////////////////// -vector database_api::get_withdraw_permissions_by_giver(const std::string account_id_or_name, withdraw_permission_id_type start, uint32_t limit)const +vector database_api::get_withdraw_permissions_by_giver( + const std::string account_id_or_name, + withdraw_permission_id_type start, + uint32_t limit)const { return my->get_withdraw_permissions_by_giver( account_id_or_name, start, limit ); } -vector database_api_impl::get_withdraw_permissions_by_giver(const std::string account_id_or_name, withdraw_permission_id_type start, uint32_t limit)const +vector database_api_impl::get_withdraw_permissions_by_giver( + const std::string account_id_or_name, + withdraw_permission_id_type start, + uint32_t limit)const { FC_ASSERT( limit <= 101 ); vector result; @@ -2599,7 +2212,8 @@ vector database_api_impl::get_withdraw_permissions_b auto withdraw_index_end = withdraw_idx.end(); const account_id_type account = get_account_from_string(account_id_or_name)->id; auto withdraw_itr = withdraw_idx.lower_bound(boost::make_tuple(account, start)); - while(withdraw_itr != withdraw_index_end && withdraw_itr->withdraw_from_account == account && result.size() < limit) + while( withdraw_itr != withdraw_index_end && withdraw_itr->withdraw_from_account == account + && result.size() < limit ) { result.push_back(*withdraw_itr); ++withdraw_itr; @@ -2607,12 +2221,18 @@ vector database_api_impl::get_withdraw_permissions_b return result; } -vector database_api::get_withdraw_permissions_by_recipient(const std::string account_id_or_name, withdraw_permission_id_type start, uint32_t limit)const +vector database_api::get_withdraw_permissions_by_recipient( + const std::string account_id_or_name, + withdraw_permission_id_type start, + uint32_t limit)const { return my->get_withdraw_permissions_by_recipient( account_id_or_name, start, limit ); } -vector database_api_impl::get_withdraw_permissions_by_recipient(const std::string account_id_or_name, withdraw_permission_id_type start, uint32_t limit)const +vector database_api_impl::get_withdraw_permissions_by_recipient( + const std::string account_id_or_name, + withdraw_permission_id_type start, + uint32_t limit)const { FC_ASSERT( limit <= 101 ); vector result; @@ -2650,12 +2270,14 @@ fc::optional database_api_impl::get_htlc( htlc_id_type id, optional return fc::optional(); } -vector database_api::get_htlc_by_from(const std::string account_id_or_name, htlc_id_type start, uint32_t limit)const +vector database_api::get_htlc_by_from( const std::string account_id_or_name, + htlc_id_type start, uint32_t limit )const { return my->get_htlc_by_from(account_id_or_name, start, limit); } -vector database_api_impl::get_htlc_by_from(const std::string account_id_or_name, htlc_id_type start, uint32_t limit) const +vector database_api_impl::get_htlc_by_from( const std::string account_id_or_name, + htlc_id_type start, uint32_t limit ) const { FC_ASSERT( limit <= _app_options->api_limit_get_htlc_by ); vector result; @@ -2673,12 +2295,14 @@ vector database_api_impl::get_htlc_by_from(const std::string accoun return result; } -vector database_api::get_htlc_by_to(const std::string account_id_or_name, htlc_id_type start, uint32_t limit)const +vector database_api::get_htlc_by_to( const std::string account_id_or_name, + htlc_id_type start, uint32_t limit )const { return my->get_htlc_by_to(account_id_or_name, start, limit); } -vector database_api_impl::get_htlc_by_to(const std::string account_id_or_name, htlc_id_type start, uint32_t limit) const +vector database_api_impl::get_htlc_by_to( const std::string account_id_or_name, + htlc_id_type start, uint32_t limit ) const { FC_ASSERT( limit <= _app_options->api_limit_get_htlc_by ); @@ -2723,6 +2347,110 @@ vector database_api_impl::list_htlcs(const htlc_id_type start, uint // // ////////////////////////////////////////////////////////////////////// +const account_object* database_api_impl::get_account_from_string( const std::string& name_or_id, + bool throw_if_not_found ) const +{ + // TODO cache the result to avoid repeatly fetching from db + FC_ASSERT( name_or_id.size() > 0); + const account_object* account = nullptr; + if (std::isdigit(name_or_id[0])) + account = _db.find(fc::variant(name_or_id, 1).as(1)); + else + { + const auto& idx = _db.get_index_type().indices().get(); + auto itr = idx.find(name_or_id); + if (itr != idx.end()) + account = &*itr; + } + if(throw_if_not_found) + FC_ASSERT( account, "no such account" ); + return account; +} + +const asset_object* database_api_impl::get_asset_from_string( const std::string& symbol_or_id, + bool throw_if_not_found ) const +{ + // TODO cache the result to avoid repeatly fetching from db + FC_ASSERT( symbol_or_id.size() > 0); + const asset_object* asset = nullptr; + if (std::isdigit(symbol_or_id[0])) + asset = _db.find(fc::variant(symbol_or_id, 1).as(1)); + else + { + const auto& idx = _db.get_index_type().indices().get(); + auto itr = idx.find(symbol_or_id); + if (itr != idx.end()) + asset = &*itr; + } + if(throw_if_not_found) + FC_ASSERT( asset, "no such asset" ); + return asset; +} + +// helper function +vector> database_api_impl::get_assets( const vector& asset_ids, + optional subscribe )const +{ + bool to_subscribe = get_whether_to_subscribe( subscribe ); + vector> result; result.reserve(asset_ids.size()); + std::transform(asset_ids.begin(), asset_ids.end(), std::back_inserter(result), + [this,to_subscribe](asset_id_type id) -> optional { + if(auto o = _db.find(id)) + { + if( to_subscribe ) + subscribe_to_item( id ); + return extend_asset( *o ); + } + return {}; + }); + return result; +} + +// helper function +vector database_api_impl::get_limit_orders( const asset_id_type a, const asset_id_type b, + const uint32_t limit )const +{ + uint64_t api_limit_get_limit_orders=_app_options->api_limit_get_limit_orders; + FC_ASSERT( limit <= api_limit_get_limit_orders ); + + const auto& limit_order_idx = _db.get_index_type(); + const auto& limit_price_idx = limit_order_idx.indices().get(); + + vector result; + result.reserve(limit*2); + + uint32_t count = 0; + auto limit_itr = limit_price_idx.lower_bound(price::max(a,b)); + auto limit_end = limit_price_idx.upper_bound(price::min(a,b)); + while(limit_itr != limit_end && count < limit) + { + result.push_back(*limit_itr); + ++limit_itr; + ++count; + } + count = 0; + limit_itr = limit_price_idx.lower_bound(price::max(b,a)); + limit_end = limit_price_idx.upper_bound(price::min(b,a)); + while(limit_itr != limit_end && count < limit) + { + result.push_back(*limit_itr); + ++limit_itr; + ++count; + } + + return result; +} + +bool database_api_impl::is_impacted_account( const flat_set& accounts) +{ + if( !_subscribed_accounts.size() || !accounts.size() ) + return false; + + return std::any_of(accounts.begin(), accounts.end(), [this](const account_id_type& account) { + return _subscribed_accounts.find(account) != _subscribed_accounts.end(); + }); +} + void database_api_impl::broadcast_updates( const vector& updates ) { if( updates.size() && _subscribe_callback ) { @@ -2750,7 +2478,9 @@ void database_api_impl::broadcast_market_updates( const market_queue_type& queue } } -void database_api_impl::on_objects_removed( const vector& ids, const vector& objs, const flat_set& impacted_accounts) +void database_api_impl::on_objects_removed( const vector& ids, + const vector& objs, + const flat_set& impacted_accounts ) { handle_object_changed(_notify_remove_create, false, ids, impacted_accounts, [objs](object_id_type id) -> const object* { @@ -2766,21 +2496,27 @@ void database_api_impl::on_objects_removed( const vector& ids, c ); } -void database_api_impl::on_objects_new(const vector& ids, const flat_set& impacted_accounts) +void database_api_impl::on_objects_new( const vector& ids, + const flat_set& impacted_accounts ) { handle_object_changed(_notify_remove_create, true, ids, impacted_accounts, std::bind(&object_database::find_object, &_db, std::placeholders::_1) ); } -void database_api_impl::on_objects_changed(const vector& ids, const flat_set& impacted_accounts) +void database_api_impl::on_objects_changed( const vector& ids, + const flat_set& impacted_accounts ) { handle_object_changed(false, true, ids, impacted_accounts, std::bind(&object_database::find_object, &_db, std::placeholders::_1) ); } -void database_api_impl::handle_object_changed(bool force_notify, bool full_object, const vector& ids, const flat_set& impacted_accounts, std::function find_object) +void database_api_impl::handle_object_changed( bool force_notify, + bool full_object, + const vector& ids, + const flat_set& impacted_accounts, + std::function find_object ) { if( _subscribe_callback ) { @@ -2825,7 +2561,8 @@ void database_api_impl::handle_object_changed(bool force_notify, bool full_objec } else if( id.is() ) { - enqueue_if_subscribed_to_market( find_object(id), broadcast_queue, full_object ); + enqueue_if_subscribed_to_market( find_object(id), broadcast_queue, + full_object ); } } diff --git a/libraries/app/database_api_impl.hxx b/libraries/app/database_api_impl.hxx new file mode 100644 index 0000000000..e2e7c376a4 --- /dev/null +++ b/libraries/app/database_api_impl.hxx @@ -0,0 +1,366 @@ +/* + * Copyright (c) 2017 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 + +#define GET_REQUIRED_FEES_MAX_RECURSION 4 + +namespace graphene { namespace app { + +typedef std::map< std::pair, + std::vector > market_queue_type; + +class database_api_impl : public std::enable_shared_from_this +{ + public: + explicit database_api_impl( graphene::chain::database& db, const application_options* app_options ); + virtual ~database_api_impl(); + + // Objects + fc::variants get_objects( const vector& ids, optional subscribe )const; + + // Subscriptions + void set_subscribe_callback( std::function cb, bool notify_remove_create ); + void set_auto_subscription( bool enable ); + void set_pending_transaction_callback( std::function cb ); + void set_block_applied_callback( std::function cb ); + void cancel_all_subscriptions(bool reset_callback, bool reset_market_subscriptions); + + // Blocks and transactions + optional get_block_header(uint32_t block_num)const; + map> get_block_header_batch(const vector block_nums)const; + optional get_block(uint32_t block_num)const; + processed_transaction get_transaction( uint32_t block_num, uint32_t trx_in_block )const; + + // Globals + chain_property_object get_chain_properties()const; + global_property_object get_global_properties()const; + fc::variant_object get_config()const; + chain_id_type get_chain_id()const; + dynamic_global_property_object get_dynamic_global_properties()const; + + // Keys + vector> get_key_references( vector key )const; + bool is_public_key_registered(string public_key) const; + + // Accounts + account_id_type get_account_id_from_string(const std::string& name_or_id)const; + vector> get_accounts( const vector& account_names_or_ids, + optional subscribe )const; + std::map get_full_accounts( const vector& names_or_ids, + optional subscribe ); + optional get_account_by_name( string name )const; + vector get_account_references( const std::string account_id_or_name )const; + vector> lookup_account_names(const vector& account_names)const; + map lookup_accounts( const string& lower_bound_name, + uint32_t limit, + optional subscribe )const; + uint64_t get_account_count()const; + + // Balances + vector get_account_balances( const std::string& account_name_or_id, + const flat_set& assets )const; + vector get_named_account_balances(const std::string& name, const flat_set& assets)const; + vector get_balance_objects( const vector
& addrs )const; + vector get_vested_balances( const vector& objs )const; + vector get_vesting_balances( const std::string account_id_or_name )const; + + // Assets + uint64_t get_asset_count()const; + asset_id_type get_asset_id_from_string(const std::string& symbol_or_id)const; + vector> get_assets( const vector& asset_symbols_or_ids, + optional subscribe )const; + vector list_assets(const string& lower_bound_symbol, uint32_t limit)const; + vector> lookup_asset_symbols(const vector& symbols_or_ids)const; + vector get_assets_by_issuer(const std::string& issuer_name_or_id, + asset_id_type start, uint32_t limit)const; + + // Markets / feeds + vector get_limit_orders( const std::string& a, const std::string& b, + uint32_t limit)const; + vector get_account_limit_orders( const string& account_name_or_id, + const string &base, + const string "e, uint32_t limit, + optional ostart_id, + optional ostart_price ); + vector get_call_orders(const std::string& a, uint32_t limit)const; + vector get_call_orders_by_account(const std::string& account_name_or_id, + asset_id_type start, uint32_t limit)const; + vector get_settle_orders(const std::string& a, uint32_t limit)const; + vector get_settle_orders_by_account(const std::string& account_name_or_id, + force_settlement_id_type start, + uint32_t limit)const; + vector get_margin_positions( const std::string account_id_or_name )const; + vector get_collateral_bids( const std::string& asset, + uint32_t limit, uint32_t start)const; + + void subscribe_to_market( std::function callback, + const std::string& a, const std::string& b ); + void unsubscribe_from_market(const std::string& a, const std::string& b); + + market_ticker get_ticker( const string& base, const string& quote, + bool skip_order_book = false )const; + market_volume get_24_volume( const string& base, const string& quote )const; + order_book get_order_book( const string& base, const string& quote, + unsigned limit = 50 )const; + vector get_top_markets( uint32_t limit )const; + vector get_trade_history( const string& base, const string& quote, + fc::time_point_sec start, fc::time_point_sec stop, + unsigned limit = 100 )const; + vector get_trade_history_by_sequence( const string& base, const string& quote, + int64_t start, fc::time_point_sec stop, + unsigned limit = 100 )const; + + // Witnesses + vector> get_witnesses(const vector& witness_ids)const; + fc::optional get_witness_by_account(const std::string account_id_or_name)const; + map lookup_witness_accounts(const string& lower_bound_name, uint32_t limit)const; + uint64_t get_witness_count()const; + + // Committee members + vector> get_committee_members( + const vector& committee_member_ids )const; + fc::optional get_committee_member_by_account( + const std::string account_id_or_name )const; + map lookup_committee_member_accounts( + const string& lower_bound_name, uint32_t limit )const; + uint64_t get_committee_count()const; + + // Workers + vector get_all_workers()const; + vector> get_workers_by_account(const std::string account_id_or_name)const; + uint64_t get_worker_count()const; + + // Votes + vector lookup_vote_ids( const vector& votes )const; + + // Authority / validation + std::string get_transaction_hex(const signed_transaction& trx)const; + std::string get_transaction_hex_without_sig(const signed_transaction& trx)const; + + set get_required_signatures( const signed_transaction& trx, + const flat_set& available_keys )const; + set get_potential_signatures( const signed_transaction& trx )const; + set
get_potential_address_signatures( const signed_transaction& trx )const; + bool verify_authority( const signed_transaction& trx )const; + bool verify_account_authority( const string& account_name_or_id, + const flat_set& signers )const; + processed_transaction validate_transaction( const signed_transaction& trx )const; + vector< fc::variant > get_required_fees( const vector& ops, + const std::string& asset_id_or_symbol )const; + + // Proposed transactions + vector get_proposed_transactions( const std::string account_id_or_name )const; + + // Blinded balances + vector get_blinded_balances( const flat_set& commitments )const; + + // Withdrawals + vector get_withdraw_permissions_by_giver( const std::string account_id_or_name, + withdraw_permission_id_type start, + uint32_t limit )const; + vector get_withdraw_permissions_by_recipient( const std::string account_id_or_name, + withdraw_permission_id_type start, + uint32_t limit )const; + + // HTLC + optional get_htlc( htlc_id_type id, optional subscribe ) const; + vector get_htlc_by_from( const std::string account_id_or_name, + htlc_id_type start, uint32_t limit ) const; + vector get_htlc_by_to( const std::string account_id_or_name, + htlc_id_type start, uint32_t limit) const; + vector list_htlcs(const htlc_id_type lower_bound_id, uint32_t limit) const; + + //private: + + //////////////////////////////////////////////// + // Accounts + //////////////////////////////////////////////// + + const account_object* get_account_from_string( const std::string& name_or_id, + bool throw_if_not_found = true ) const; + + //////////////////////////////////////////////// + // Assets + //////////////////////////////////////////////// + + template + extended_asset_object extend_asset( ASSET&& a )const + { + asset_id_type id = a.id; + extended_asset_object result = extended_asset_object( std::forward( a ) ); + if( amount_in_collateral_index ) + { + result.total_in_collateral = amount_in_collateral_index->get_amount_in_collateral( id ); + if( result.bitasset_data_id.valid() ) + result.total_backing_collateral = amount_in_collateral_index->get_backing_collateral( id ); + } + return result; + } + + const asset_object* get_asset_from_string( const std::string& symbol_or_id, + bool throw_if_not_found = true ) const; + // helper function + vector> get_assets( const vector& asset_ids, + optional subscribe = optional() )const; + + //////////////////////////////////////////////// + // Markets + //////////////////////////////////////////////// + + // helper function + vector get_limit_orders( const asset_id_type a, const asset_id_type b, + const uint32_t limit )const; + + //////////////////////////////////////////////// + // Subscription + //////////////////////////////////////////////// + + // Decides whether to subscribe using member variables and given parameter + bool get_whether_to_subscribe( optional subscribe )const + { + if( !_subscribe_callback ) + return false; + if( subscribe.valid() ) + return *subscribe; + return _enabled_auto_subscription; + } + + // Note: + // Different type of object_id objects could become identical after packed. + // For example, both `account_id_type a=1.2.0` and `asset_id_type b=1.3.0` will become `0` after packed. + // In order to avoid collision, we don't use a template function here, instead, we implicitly convert all + // object IDs to `object_id_type` when subscribing. + // + // If need to subscribe to other data types, override this function with the types as parameter. + // For example, we had a `get_subscription_key( const public_key_type& item )` function here, which was + // removed lately since we no longer subscribe to public keys. + vector get_subscription_key( const object_id_type& item )const + { + return fc::raw::pack(item); + } + + template + void subscribe_to_item( const T& item )const + { + if( !_subscribe_callback ) + return; + + vector key = get_subscription_key( item ); + if( !_subscribe_filter.contains( key.data(), key.size() ) ) + { + _subscribe_filter.insert( key.data(), key.size() ); + } + } + + template + bool is_subscribed_to_item( const T& item )const + { + if( !_subscribe_callback ) + return false; + + vector key = get_subscription_key( item ); + return _subscribe_filter.contains( key.data(), key.size() ); + } + + // for full-account subscription + bool is_impacted_account( const flat_set& accounts ); + + // for market subscription + template + const std::pair get_order_market( const T& order ) + { + return order.get_market(); + } + + // for market subscription + const std::pair get_order_market( const force_settlement_object& order ) + { + // TODO cache the result to avoid repeatly fetching from db + asset_id_type backing_id = order.balance.asset_id( _db ).bitasset_data( _db ).options.short_backing_asset; + auto tmp = std::make_pair( order.balance.asset_id, backing_id ); + if( tmp.first > tmp.second ) std::swap( tmp.first, tmp.second ); + return tmp; + } + + template + void enqueue_if_subscribed_to_market(const object* obj, market_queue_type& queue, bool full_object=true) + { + const T* order = dynamic_cast(obj); + FC_ASSERT( order != nullptr); + + const auto& market = get_order_market( *order ); + + auto sub = _market_subscriptions.find( market ); + if( sub != _market_subscriptions.end() ) { + queue[market].emplace_back( full_object ? obj->to_variant() : fc::variant(obj->id, 1) ); + } + } + + void broadcast_updates( const vector& updates ); + void broadcast_market_updates( const market_queue_type& queue); + void handle_object_changed( bool force_notify, + bool full_object, + const vector& ids, + const flat_set& impacted_accounts, + std::function find_object ); + + /** called every time a block is applied to report the objects that were changed */ + void on_objects_new(const vector& ids, const flat_set& impacted_accounts); + void on_objects_changed(const vector& ids, const flat_set& impacted_accounts); + void on_objects_removed(const vector& ids, const vector& objs, + const flat_set& impacted_accounts); + void on_applied_block(); + + //////////////////////////////////////////////// + // Member variables + //////////////////////////////////////////////// + + bool _notify_remove_create = false; + bool _enabled_auto_subscription = true; + + mutable fc::bloom_filter _subscribe_filter; + std::set _subscribed_accounts; + + std::function _subscribe_callback; + std::function _pending_trx_callback; + std::function _block_applied_callback; + + boost::signals2::scoped_connection _new_connection; + boost::signals2::scoped_connection _change_connection; + boost::signals2::scoped_connection _removed_connection; + boost::signals2::scoped_connection _applied_block_connection; + boost::signals2::scoped_connection _pending_trx_connection; + + map< pair, std::function > _market_subscriptions; + + graphene::chain::database& _db; + const application_options* _app_options = nullptr; + + const graphene::api_helper_indexes::amount_in_collateral_index* amount_in_collateral_index; +}; + +} } // graphene::app diff --git a/libraries/app/include/graphene/app/full_account.hpp b/libraries/app/include/graphene/app/api_objects.hpp similarity index 54% rename from libraries/app/include/graphene/app/full_account.hpp rename to libraries/app/include/graphene/app/api_objects.hpp index 4618ef84a1..af7a96e1ab 100644 --- a/libraries/app/include/graphene/app/full_account.hpp +++ b/libraries/app/include/graphene/app/api_objects.hpp @@ -24,15 +24,21 @@ #pragma once #include +#include #include -#include #include #include #include #include +#include +#include + +#include + namespace graphene { namespace app { using namespace graphene::chain; + using namespace graphene::market_history; struct more_data { @@ -72,6 +78,74 @@ namespace graphene { namespace app { more_data more_data_available; }; + struct order + { + string price; + string quote; + string base; + }; + + struct order_book + { + string base; + string quote; + vector< order > bids; + vector< order > asks; + }; + + struct market_ticker + { + time_point_sec time; + string base; + string quote; + string latest; + string lowest_ask; + string highest_bid; + string percent_change; + string base_volume; + string quote_volume; + + market_ticker() {} + market_ticker(const market_ticker_object& mto, + const fc::time_point_sec& now, + const asset_object& asset_base, + const asset_object& asset_quote, + const order_book& orders); + market_ticker(const fc::time_point_sec& now, + const asset_object& asset_base, + const asset_object& asset_quote); + }; + + struct market_volume + { + time_point_sec time; + string base; + string quote; + string base_volume; + string quote_volume; + }; + + struct market_trade + { + int64_t sequence = 0; + fc::time_point_sec date; + string price; + string amount; + string value; + account_id_type side1_account_id = GRAPHENE_NULL_ACCOUNT; + account_id_type side2_account_id = GRAPHENE_NULL_ACCOUNT; + }; + + struct extended_asset_object : asset_object + { + extended_asset_object() {} + explicit extended_asset_object( const asset_object& a ) : asset_object( a ) {} + explicit extended_asset_object( asset_object&& a ) : asset_object( std::move(a) ) {} + + optional total_in_collateral; + optional total_backing_collateral; + }; + } } FC_REFLECT( graphene::app::more_data, @@ -100,3 +174,13 @@ FC_REFLECT( graphene::app::full_account, (htlcs_to) (more_data_available) ) + +FC_REFLECT( graphene::app::order, (price)(quote)(base) ); +FC_REFLECT( graphene::app::order_book, (base)(quote)(bids)(asks) ); +FC_REFLECT( graphene::app::market_ticker, + (time)(base)(quote)(latest)(lowest_ask)(highest_bid)(percent_change)(base_volume)(quote_volume) ); +FC_REFLECT( graphene::app::market_volume, (time)(base)(quote)(base_volume)(quote_volume) ); +FC_REFLECT( graphene::app::market_trade, (sequence)(date)(price)(amount)(value)(side1_account_id)(side2_account_id) ); + +FC_REFLECT_DERIVED( graphene::app::extended_asset_object, (graphene::chain::asset_object), + (total_in_collateral)(total_backing_collateral) ); diff --git a/libraries/app/include/graphene/app/database_api.hpp b/libraries/app/include/graphene/app/database_api.hpp index 519f641b5a..1960e64824 100644 --- a/libraries/app/include/graphene/app/database_api.hpp +++ b/libraries/app/include/graphene/app/database_api.hpp @@ -23,30 +23,21 @@ */ #pragma once -#include +#include #include #include -#include -#include #include #include #include #include -#include #include -#include #include #include -#include - -#include -#include #include -#include #include #include @@ -68,74 +59,6 @@ using std::map; class database_api_impl; -struct order -{ - string price; - string quote; - string base; -}; - -struct order_book -{ - string base; - string quote; - vector< order > bids; - vector< order > asks; -}; - -struct market_ticker -{ - time_point_sec time; - string base; - string quote; - string latest; - string lowest_ask; - string highest_bid; - string percent_change; - string base_volume; - string quote_volume; - - market_ticker() {} - market_ticker(const market_ticker_object& mto, - const fc::time_point_sec& now, - const asset_object& asset_base, - const asset_object& asset_quote, - const order_book& orders); - market_ticker(const fc::time_point_sec& now, - const asset_object& asset_base, - const asset_object& asset_quote); -}; - -struct market_volume -{ - time_point_sec time; - string base; - string quote; - string base_volume; - string quote_volume; -}; - -struct market_trade -{ - int64_t sequence = 0; - fc::time_point_sec date; - string price; - string amount; - string value; - account_id_type side1_account_id = GRAPHENE_NULL_ACCOUNT; - account_id_type side2_account_id = GRAPHENE_NULL_ACCOUNT; -}; - -struct extended_asset_object : asset_object -{ - extended_asset_object() {} - explicit extended_asset_object( const asset_object& a ) : asset_object( a ) {} - explicit extended_asset_object( asset_object&& a ) : asset_object( std::move(a) ) {} - - optional total_in_collateral; - optional total_backing_collateral; -}; - /** * @brief The database_api class implements the RPC API for the chain database. * @@ -972,16 +895,6 @@ class database_api extern template class fc::api; -FC_REFLECT( graphene::app::order, (price)(quote)(base) ); -FC_REFLECT( graphene::app::order_book, (base)(quote)(bids)(asks) ); -FC_REFLECT( graphene::app::market_ticker, - (time)(base)(quote)(latest)(lowest_ask)(highest_bid)(percent_change)(base_volume)(quote_volume) ); -FC_REFLECT( graphene::app::market_volume, (time)(base)(quote)(base_volume)(quote_volume) ); -FC_REFLECT( graphene::app::market_trade, (sequence)(date)(price)(amount)(value)(side1_account_id)(side2_account_id) ); - -FC_REFLECT_DERIVED( graphene::app::extended_asset_object, (graphene::chain::asset_object), - (total_in_collateral)(total_backing_collateral) ); - FC_API(graphene::app::database_api, // Objects (get_objects) diff --git a/libraries/app/include/graphene/app/util.hpp b/libraries/app/include/graphene/app/util.hpp index 1513be9d10..a92b51588f 100644 --- a/libraries/app/include/graphene/app/util.hpp +++ b/libraries/app/include/graphene/app/util.hpp @@ -29,9 +29,18 @@ namespace graphene { namespace protocol { struct price; } +namespace chain { + struct asset_object; +} namespace app { std::string uint128_amount_to_string( const fc::uint128_t& amount, const uint8_t precision ); - std::string price_to_string( const graphene::protocol::price& _price, const uint8_t base_precision, const uint8_t quote_precision); - std::string price_diff_percent_string( const graphene::protocol::price& old_price, const graphene::protocol::price& new_price ); + std::string price_to_string( const graphene::protocol::price& _price, + const uint8_t base_precision, + const uint8_t quote_precision ); + std::string price_to_string( const graphene::protocol::price& _price, + const graphene::chain::asset_object& _base, + const graphene::chain::asset_object& _quote ); + std::string price_diff_percent_string( const graphene::protocol::price& old_price, + const graphene::protocol::price& new_price ); } } diff --git a/libraries/app/util.cpp b/libraries/app/util.cpp index 542e69f177..006bdab500 100644 --- a/libraries/app/util.cpp +++ b/libraries/app/util.cpp @@ -26,6 +26,7 @@ #include #include +#include namespace graphene { namespace app { @@ -64,7 +65,9 @@ std::string uint128_amount_to_string( const fc::uint128_t& amount, const uint8_t return ss.str(); } FC_CAPTURE_AND_RETHROW( (amount)(precision) ) } -std::string price_to_string( const graphene::protocol::price& _price, const uint8_t base_precision, const uint8_t quote_precision ) +std::string price_to_string( const graphene::protocol::price& _price, + const uint8_t base_precision, + const uint8_t quote_precision ) { try { if( _price.base.amount == 0 ) return "0"; @@ -86,6 +89,18 @@ std::string price_to_string( const graphene::protocol::price& _price, const uint return uint128_amount_to_string( price128, 19 + base_precision - quote_precision ); } FC_CAPTURE_AND_RETHROW( (_price)(base_precision)(quote_precision) ) } +std::string price_to_string( const graphene::protocol::price& _price, + const graphene::chain::asset_object& _base, + const graphene::chain::asset_object& _quote ) +{ try { + if( _price.base.asset_id == _base.id && _price.quote.asset_id == _quote.id ) + return price_to_string( _price, _base.precision, _quote.precision ); + else if( _price.base.asset_id == _quote.id && _price.quote.asset_id == _base.id ) + return price_to_string( ~_price, _base.precision, _quote.precision ); + else + FC_ASSERT( !"bad parameters" ); +} FC_CAPTURE_AND_RETHROW( (_price)(_base)(_quote) ) } + std::string price_diff_percent_string( const graphene::protocol::price& old_price, const graphene::protocol::price& new_price ) { try {