Skip to content

Commit

Permalink
Merge pull request #2365 from bitshares/lp-secondary-index
Browse files Browse the repository at this point in the history
Add database API to query for liquidity pools by any one asset in the pool
  • Loading branch information
abitmore authored Mar 3, 2021
2 parents f386832 + 5b4cb7d commit 1c49a4c
Show file tree
Hide file tree
Showing 5 changed files with 160 additions and 9 deletions.
61 changes: 61 additions & 0 deletions libraries/app/database_api.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,16 @@ database_api_impl::database_api_impl( graphene::chain::database& db, const appli
{
amount_in_collateral_index = nullptr;
}

try
{
asset_in_liquidity_pools_index = &_db.get_index_type< primary_index< liquidity_pool_index > >()
.get_secondary_index<graphene::api_helper_indexes::asset_in_liquidity_pools_index>();
}
catch( fc::assert_exception& e )
{
asset_in_liquidity_pools_index = nullptr;
}
}

database_api_impl::~database_api_impl()
Expand Down Expand Up @@ -1816,6 +1826,57 @@ vector<extended_liquidity_pool_object> database_api_impl::get_liquidity_pools_by
with_statistics );
}

vector<extended_liquidity_pool_object> database_api::get_liquidity_pools_by_one_asset(
std::string asset_symbol_or_id,
optional<uint32_t> limit,
optional<liquidity_pool_id_type> start_id,
optional<bool> with_statistics )const
{
return my->get_liquidity_pools_by_one_asset(
asset_symbol_or_id,
limit,
start_id,
with_statistics );
}

vector<extended_liquidity_pool_object> database_api_impl::get_liquidity_pools_by_one_asset(
std::string asset_symbol_or_id,
optional<uint32_t> olimit,
optional<liquidity_pool_id_type> ostart_id,
optional<bool> with_statistics )const
{
// api_helper_indexes plugin is required for accessing the secondary index
FC_ASSERT( _app_options && _app_options->has_api_helper_indexes_plugin,
"api_helper_indexes plugin is not enabled on this server." );

uint32_t limit = olimit.valid() ? *olimit : 101;
const auto configured_limit = _app_options->api_limit_get_liquidity_pools;
FC_ASSERT( limit <= configured_limit,
"limit can not be greater than ${configured_limit}",
("configured_limit", configured_limit) );

asset_id_type aid = get_asset_from_string(asset_symbol_or_id)->id;

FC_ASSERT( asset_in_liquidity_pools_index, "Internal error" );
const auto& pools = asset_in_liquidity_pools_index->get_liquidity_pools_by_asset( aid );

liquidity_pool_id_type start_id = ostart_id.valid() ? *ostart_id : liquidity_pool_id_type();

auto itr = pools.lower_bound( start_id );

bool with_stats = ( with_statistics.valid() && *with_statistics );

vector<extended_liquidity_pool_object> results;

results.reserve( limit );
for ( ; itr != pools.end() && results.size() < limit; ++itr )
{
results.emplace_back( extend_liquidity_pool( (*itr)(_db), with_stats ) );
}

return results;
}

vector<extended_liquidity_pool_object> database_api::get_liquidity_pools_by_both_assets(
std::string asset_symbol_or_id_a,
std::string asset_symbol_or_id_b,
Expand Down
6 changes: 6 additions & 0 deletions libraries/app/database_api_impl.hxx
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,11 @@ class database_api_impl : public std::enable_shared_from_this<database_api_impl>
optional<uint32_t> limit = 101,
optional<liquidity_pool_id_type> start_id = optional<liquidity_pool_id_type>(),
optional<bool> with_statistics = false )const;
vector<extended_liquidity_pool_object> get_liquidity_pools_by_one_asset(
std::string asset_symbol_or_id,
optional<uint32_t> limit = 101,
optional<liquidity_pool_id_type> start_id = optional<liquidity_pool_id_type>(),
optional<bool> with_statistics = false )const;
vector<extended_liquidity_pool_object> get_liquidity_pools_by_both_assets(
std::string asset_symbol_or_id_a,
std::string asset_symbol_or_id_b,
Expand Down Expand Up @@ -465,6 +470,7 @@ class database_api_impl : public std::enable_shared_from_this<database_api_impl>
const application_options* _app_options = nullptr;

const graphene::api_helper_indexes::amount_in_collateral_index* amount_in_collateral_index;
const graphene::api_helper_indexes::asset_in_liquidity_pools_index* asset_in_liquidity_pools_index;
};

} } // graphene::app
21 changes: 21 additions & 0 deletions libraries/app/include/graphene/app/database_api.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -691,6 +691,26 @@ class database_api
optional<liquidity_pool_id_type> start_id = optional<liquidity_pool_id_type>(),
optional<bool> with_statistics = false )const;

/**
* @brief Get a list of liquidity pools by the symbol or ID of one asset in the pool
* @param asset_symbol_or_id symbol name or ID of the asset
* @param limit The limitation of items each query can fetch, not greater than a configured value
* @param start_id Start liquidity pool id, fetch pools whose IDs are greater than or equal to this ID
* @param with_statistics Whether to return statistics
* @return The liquidity pools
*
* @note
* 1. if @p asset_symbol_or_id cannot be tied to an asset, an error will be returned
* 2. @p limit can be omitted or be null, if so the default value 101 will be used
* 3. @p start_id can be omitted or be null, if so the api will return the "first page" of pools
* 4. can only omit one or more arguments in the end of the list, but not one or more in the middle
*/
vector<extended_liquidity_pool_object> get_liquidity_pools_by_one_asset(
std::string asset_symbol_or_id,
optional<uint32_t> limit = 101,
optional<liquidity_pool_id_type> start_id = optional<liquidity_pool_id_type>(),
optional<bool> with_statistics = false )const;

/**
* @brief Get a list of liquidity pools by the symbols or IDs of the two assets in the pool
* @param asset_symbol_or_id_a symbol name or ID of one asset
Expand Down Expand Up @@ -1179,6 +1199,7 @@ FC_API(graphene::app::database_api,
(list_liquidity_pools)
(get_liquidity_pools_by_asset_a)
(get_liquidity_pools_by_asset_b)
(get_liquidity_pools_by_one_asset)
(get_liquidity_pools_by_both_assets)
(get_liquidity_pools)
(get_liquidity_pools_by_share_asset)
Expand Down
56 changes: 48 additions & 8 deletions libraries/plugins/api_helper_indexes/api_helper_indexes.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
*/

#include <graphene/api_helper_indexes/api_helper_indexes.hpp>
#include <graphene/chain/liquidity_pool_object.hpp>
#include <graphene/chain/market_object.hpp>
#include <graphene/chain/proposal_object.hpp>

Expand Down Expand Up @@ -78,19 +79,51 @@ void amount_in_collateral_index::object_modified( const object& objct )
object_inserted( objct );
} FC_CAPTURE_AND_RETHROW( (objct) ); }

share_type amount_in_collateral_index::get_amount_in_collateral( const asset_id_type& asset )const
share_type amount_in_collateral_index::get_amount_in_collateral( const asset_id_type& asst )const
{ try {
auto itr = in_collateral.find( asset );
auto itr = in_collateral.find( asst );
if( itr == in_collateral.end() ) return 0;
return itr->second;
} FC_CAPTURE_AND_RETHROW( (asset) ); }
} FC_CAPTURE_AND_RETHROW( (asst) ); }

share_type amount_in_collateral_index::get_backing_collateral( const asset_id_type& asset )const
share_type amount_in_collateral_index::get_backing_collateral( const asset_id_type& asst )const
{ try {
auto itr = backing_collateral.find( asset );
auto itr = backing_collateral.find( asst );
if( itr == backing_collateral.end() ) return 0;
return itr->second;
} FC_CAPTURE_AND_RETHROW( (asset) ); }
} FC_CAPTURE_AND_RETHROW( (asst) ); }

void asset_in_liquidity_pools_index::object_inserted( const object& objct )
{ try {
const liquidity_pool_object& o = static_cast<const liquidity_pool_object&>( objct );
asset_in_pools_map[ o.asset_a ].insert( o.id ); // Note: [] operator will create an entry if not found
asset_in_pools_map[ o.asset_b ].insert( o.id );
} FC_CAPTURE_AND_RETHROW( (objct) ); }

void asset_in_liquidity_pools_index::object_removed( const object& objct )
{ try {
const liquidity_pool_object& o = static_cast<const liquidity_pool_object&>( objct );
asset_in_pools_map[ o.asset_a ].erase( o.id );
asset_in_pools_map[ o.asset_b ].erase( o.id );
// Note: do not erase entries with an empty set from the map in order to avoid read/write race conditions
} FC_CAPTURE_AND_RETHROW( (objct) ); }

void asset_in_liquidity_pools_index::about_to_modify( const object& objct )
{
}

void asset_in_liquidity_pools_index::object_modified( const object& objct )
{
}

const flat_set<liquidity_pool_id_type>& asset_in_liquidity_pools_index::get_liquidity_pools_by_asset(
const asset_id_type& a )const
{
auto itr = asset_in_pools_map.find( a );
if( itr != asset_in_pools_map.end() )
return itr->second;
return empty_set;
}

namespace detail
{
Expand Down Expand Up @@ -147,9 +180,10 @@ void api_helper_indexes::plugin_initialize(const boost::program_options::variabl
void api_helper_indexes::plugin_startup()
{
ilog("api_helper_indexes: plugin_startup() begin");
amount_in_collateral = database().add_secondary_index< primary_index<call_order_index>, amount_in_collateral_index >();
amount_in_collateral_idx = database().add_secondary_index< primary_index<call_order_index>,
amount_in_collateral_index >();
for( const auto& call : database().get_index_type<call_order_index>().indices() )
amount_in_collateral->object_inserted( call );
amount_in_collateral_idx->object_inserted( call );

auto& account_members = *database().add_secondary_index< primary_index<account_index>, account_member_index >();
for( const auto& account : database().get_index_type< account_index >().indices() )
Expand All @@ -158,6 +192,12 @@ void api_helper_indexes::plugin_startup()
auto& approvals = *database().add_secondary_index< primary_index<proposal_index>, required_approval_index >();
for( const auto& proposal : database().get_index_type< proposal_index >().indices() )
approvals.object_inserted( proposal );

asset_in_liquidity_pools_idx = database().add_secondary_index< primary_index<liquidity_pool_index>,
asset_in_liquidity_pools_index >();
for( const auto& pool : database().get_index_type<liquidity_pool_index>().indices() )
asset_in_liquidity_pools_idx->object_inserted( pool );

}

} }
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@ using namespace chain;
/**
* @brief This secondary index tracks how much of each asset is locked up as collateral for MPAs, and how much
* collateral is backing an MPA in total.
* @note This is implemented with \c flat_map considering there aren't too many MPAs and PMs in the system thus
* the performance would be acceptable.
*/
class amount_in_collateral_index : public secondary_index
{
Expand All @@ -49,6 +51,26 @@ class amount_in_collateral_index : public secondary_index
flat_map<asset_id_type, share_type> backing_collateral;
};

/**
* @brief This secondary index maintains a map to make it easier to find liquidity pools by any asset in the pool.
* @note This is implemented with \c flat_map and \c flat_set considering there aren't too many liquidity pools
* in the system thus the performance would be acceptable.
*/
class asset_in_liquidity_pools_index: public secondary_index
{
public:
virtual void object_inserted( const object& obj ) override;
virtual void object_removed( const object& obj ) override;
virtual void about_to_modify( const object& before ) override;
virtual void object_modified( const object& after ) override;

const flat_set<liquidity_pool_id_type>& get_liquidity_pools_by_asset( const asset_id_type& a )const;

private:
flat_set<liquidity_pool_id_type> empty_set;
flat_map<asset_id_type, flat_set<liquidity_pool_id_type>> asset_in_pools_map;
};

namespace detail
{
class api_helper_indexes_impl;
Expand All @@ -72,7 +94,8 @@ class api_helper_indexes : public graphene::app::plugin
std::unique_ptr<detail::api_helper_indexes_impl> my;

private:
amount_in_collateral_index* amount_in_collateral = nullptr;
amount_in_collateral_index* amount_in_collateral_idx = nullptr;
asset_in_liquidity_pools_index* asset_in_liquidity_pools_idx = nullptr;
};

} } //graphene::template

0 comments on commit 1c49a4c

Please sign in to comment.