Progress #166: Initial implementation of get_full_accounts API call

This commit is contained in:
Nathan Hourt 2015-07-16 17:02:08 -04:00
parent dc8849f23b
commit 723b11533b
5 changed files with 124 additions and 22 deletions

View file

@ -149,6 +149,87 @@ namespace graphene { namespace app {
return result; return result;
} }
std::map<std::string, fc::variant> database_api::get_full_accounts(std::function<void(const variant&)> callback,
const vector<std::string>& names_or_ids)
{
std::map<std::string, fc::variant> results;
std::set<object_id_type> ids_to_subscribe;
for (const std::string& account_name_or_id : names_or_ids)
{
const account_object* account = nullptr;
if (std::isdigit(account_name_or_id[0]))
account = _db.find(fc::variant(account_name_or_id).as<account_id_type>());
else
{
const auto& idx = _db.get_index_type<account_index>().indices().get<by_name>();
auto itr = idx.find(account_name_or_id);
if (itr != idx.end())
account = &*itr;
}
if (account == nullptr)
continue;
ids_to_subscribe.insert({account->id, account->statistics});
fc::mutable_variant_object full_account;
// Add the account itself, its statistics object, cashback balance, and referral account names
full_account("account", *account)("statistics", account->statistics(_db))
("registrar_name", account->registrar(_db).name)("referrer_name", account->referrer(_db).name)
("lifetime_referrer_name", account->lifetime_referrer(_db).name);
if (account->cashback_vb)
{
ids_to_subscribe.insert(*account->cashback_vb);
full_account("cashback_balance", account->cashback_balance(_db));
}
// Add the account's balances
auto balance_range = _db.get_index_type<account_balance_index>().indices().get<by_account>().equal_range(account->id);
vector<account_balance_object> balances;
std::for_each(balance_range.first, balance_range.second,
[&balances, &ids_to_subscribe](const account_balance_object& balance) {
balances.emplace_back(balance);
ids_to_subscribe.insert(balance.id);
});
idump((balances));
full_account("balances", balances);
// Add the account's vesting balances
auto vesting_range = _db.get_index_type<vesting_balance_index>().indices().get<by_account>().equal_range(account->id);
vector<vesting_balance_object> vesting_balances;
std::for_each(vesting_range.first, vesting_range.second,
[&vesting_balances, &ids_to_subscribe](const vesting_balance_object& balance) {
vesting_balances.emplace_back(balance);
ids_to_subscribe.insert(balance.id);
});
full_account("vesting_balances", vesting_balances);
// Add the account's orders
auto order_range = _db.get_index_type<limit_order_index>().indices().get<by_account>().equal_range(account->id);
vector<limit_order_object> orders;
std::for_each(order_range.first, order_range.second,
[&orders, &ids_to_subscribe] (const limit_order_object& order) {
orders.emplace_back(order);
ids_to_subscribe.insert(order.id);
});
auto call_range = _db.get_index_type<call_order_index>().indices().get<by_account>().equal_range(account->id);
vector<call_order_object> calls;
std::for_each(call_range.first, call_range.second,
[&calls, &ids_to_subscribe] (const call_order_object& call) {
calls.emplace_back(call);
ids_to_subscribe.insert(call.id);
});
full_account("limit_orders", orders)("call_orders", calls);
results[account_name_or_id] = full_account;
}
wdump((results));
subscribe_to_objects(callback, vector<object_id_type>(ids_to_subscribe.begin(), ids_to_subscribe.end()));
return results;
}
vector<asset> database_api::get_account_balances(account_id_type acnt, const flat_set<asset_id_type>& assets)const vector<asset> database_api::get_account_balances(account_id_type acnt, const flat_set<asset_id_type>& assets)const
{ {
vector<asset> result; vector<asset> result;
@ -278,12 +359,12 @@ namespace graphene { namespace app {
// we want to order witnesses by account name, but that name is in the account object // we want to order witnesses by account name, but that name is in the account object
// so the witness_index doesn't have a quick way to access it. // so the witness_index doesn't have a quick way to access it.
// get all the names and look them all up, sort them, then figure out what // get all the names and look them all up, sort them, then figure out what
// records to return. This could be optimized, but we expect the // records to return. This could be optimized, but we expect the
// number of witnesses to be few and the frequency of calls to be rare // number of witnesses to be few and the frequency of calls to be rare
std::map<std::string, witness_id_type> witnesses_by_account_name; std::map<std::string, witness_id_type> witnesses_by_account_name;
for (const witness_object& witness : witnesses_by_id) for (const witness_object& witness : witnesses_by_id)
if (auto account_iter = _db.find(witness.witness_account)) if (auto account_iter = _db.find(witness.witness_account))
if (account_iter->name >= lower_bound_name) // we can ignore anything below lower_bound_name if (account_iter->name >= lower_bound_name) // we can ignore anything below lower_bound_name
witnesses_by_account_name.insert(std::make_pair(account_iter->name, witness.id)); witnesses_by_account_name.insert(std::make_pair(account_iter->name, witness.id));
auto end_iter = witnesses_by_account_name.begin(); auto end_iter = witnesses_by_account_name.begin();
@ -292,7 +373,7 @@ namespace graphene { namespace app {
witnesses_by_account_name.erase(end_iter, witnesses_by_account_name.end()); witnesses_by_account_name.erase(end_iter, witnesses_by_account_name.end());
return witnesses_by_account_name; return witnesses_by_account_name;
} }
map<string, committee_member_id_type> database_api::lookup_committee_member_accounts(const string& lower_bound_name, uint32_t limit)const map<string, committee_member_id_type> database_api::lookup_committee_member_accounts(const string& lower_bound_name, uint32_t limit)const
{ {
FC_ASSERT( limit <= 1000 ); FC_ASSERT( limit <= 1000 );
@ -301,12 +382,12 @@ namespace graphene { namespace app {
// we want to order committee_members by account name, but that name is in the account object // we want to order committee_members by account name, but that name is in the account object
// so the committee_member_index doesn't have a quick way to access it. // so the committee_member_index doesn't have a quick way to access it.
// get all the names and look them all up, sort them, then figure out what // get all the names and look them all up, sort them, then figure out what
// records to return. This could be optimized, but we expect the // records to return. This could be optimized, but we expect the
// number of committee_members to be few and the frequency of calls to be rare // number of committee_members to be few and the frequency of calls to be rare
std::map<std::string, committee_member_id_type> committee_members_by_account_name; std::map<std::string, committee_member_id_type> committee_members_by_account_name;
for (const committee_member_object& committee_member : committee_members_by_id) for (const committee_member_object& committee_member : committee_members_by_id)
if (auto account_iter = _db.find(committee_member.committee_member_account)) if (auto account_iter = _db.find(committee_member.committee_member_account))
if (account_iter->name >= lower_bound_name) // we can ignore anything below lower_bound_name if (account_iter->name >= lower_bound_name) // we can ignore anything below lower_bound_name
committee_members_by_account_name.insert(std::make_pair(account_iter->name, committee_member.id)); committee_members_by_account_name.insert(std::make_pair(account_iter->name, committee_member.id));
auto end_iter = committee_members_by_account_name.begin(); auto end_iter = committee_members_by_account_name.begin();
@ -315,7 +396,7 @@ namespace graphene { namespace app {
committee_members_by_account_name.erase(end_iter, committee_members_by_account_name.end()); committee_members_by_account_name.erase(end_iter, committee_members_by_account_name.end());
return committee_members_by_account_name; return committee_members_by_account_name;
} }
vector<optional<witness_object>> database_api::get_witnesses(const vector<witness_id_type>& witness_ids)const vector<optional<witness_object>> database_api::get_witnesses(const vector<witness_id_type>& witness_ids)const
{ {
vector<optional<witness_object>> result; result.reserve(witness_ids.size()); vector<optional<witness_object>> result; result.reserve(witness_ids.size());
@ -605,7 +686,7 @@ namespace graphene { namespace app {
return hist->tracked_buckets(); return hist->tracked_buckets();
} }
vector<bucket_object> history_api::get_market_history( asset_id_type a, asset_id_type b, vector<bucket_object> history_api::get_market_history( asset_id_type a, asset_id_type b,
uint32_t bucket_seconds, fc::time_point_sec start, fc::time_point_sec end )const uint32_t bucket_seconds, fc::time_point_sec start, fc::time_point_sec end )const
{ try { { try {
FC_ASSERT(_app.chain_database()); FC_ASSERT(_app.chain_database());

View file

@ -57,29 +57,29 @@ namespace graphene { namespace app {
* *
* If any of the provided IDs does not map to an object, a null variant is returned in its position. * If any of the provided IDs does not map to an object, a null variant is returned in its position.
*/ */
fc::variants get_objects(const vector<object_id_type>& ids)const; fc::variants get_objects(const vector<object_id_type>& ids)const;
/** /**
* @brief Retrieve a block header * @brief Retrieve a block header
* @param block_num Height of the block whose header should be returned * @param block_num Height of the block whose header should be returned
* @return header of the referenced block, or null if no matching block was found * @return header of the referenced block, or null if no matching block was found
*/ */
optional<block_header> get_block_header(uint32_t block_num)const; optional<block_header> get_block_header(uint32_t block_num)const;
/** /**
* @brief Retrieve a full, signed block * @brief Retrieve a full, signed block
* @param block_num Height of the block to be returned * @param block_num Height of the block to be returned
* @return the referenced block, or null if no matching block was found * @return the referenced block, or null if no matching block was found
*/ */
optional<signed_block> get_block(uint32_t block_num)const; optional<signed_block> get_block(uint32_t block_num)const;
/** /**
* @brief used to fetch an individual transaction. * @brief used to fetch an individual transaction.
*/ */
processed_transaction get_transaction( uint32_t block_num, uint32_t trx_in_block )const; processed_transaction get_transaction( uint32_t block_num, uint32_t trx_in_block )const;
/** /**
* @brief Retrieve the current @ref global_property_object * @brief Retrieve the current @ref global_property_object
*/ */
global_property_object get_global_properties()const; global_property_object get_global_properties()const;
/** /**
* @brief Retrieve the current @ref dynamic_global_property_object * @brief Retrieve the current @ref dynamic_global_property_object
*/ */
@ -138,6 +138,21 @@ namespace graphene { namespace app {
*/ */
map<string,account_id_type> lookup_accounts(const string& lower_bound_name, uint32_t limit)const; map<string,account_id_type> lookup_accounts(const string& lower_bound_name, uint32_t limit)const;
/**
* @brief Fetch all objects relevant to the specified accounts and subscribe to updates
* @param callback Function to call with updates
* @param names_or_ids Each item must be the name or ID of an account to retrieve
* @return Map of string from @ref names_or_ids to the corresponding account
*
* This function fetches all relevant objects for the given accounts, and subscribes to updates to the given
* accounts. If any of the strings in @ref names_or_ids cannot be tied to an account, that input will be
* ignored. All other accounts will be retrieved and subscribed.
*
* TODO: Describe the return value and argument to callback in detail
*/
std::map<string,fc::variant> get_full_accounts(std::function<void(const variant&)> callback,
const vector<string>& names_or_ids);
/** /**
* @brief Get limit orders in a given market * @brief Get limit orders in a given market
* @param a ID of asset being sold * @param a ID of asset being sold
@ -324,9 +339,9 @@ namespace graphene { namespace app {
vector<bucket_object> get_market_history( asset_id_type a, asset_id_type b, uint32_t bucket_seconds, vector<bucket_object> get_market_history( asset_id_type a, asset_id_type b, uint32_t bucket_seconds,
fc::time_point_sec start, fc::time_point_sec end )const; fc::time_point_sec start, fc::time_point_sec end )const;
flat_set<uint32_t> get_market_history_buckets()const; flat_set<uint32_t> get_market_history_buckets()const;
private: private:
application& _app; application& _app;
}; };
/** /**
@ -403,7 +418,7 @@ namespace graphene { namespace app {
std::vector<net::peer_status> get_connected_peers() const; std::vector<net::peer_status> get_connected_peers() const;
private: private:
application& _app; application& _app;
}; };
/** /**
@ -440,7 +455,7 @@ namespace graphene { namespace app {
/// @brief Called to enable an API, not reflected. /// @brief Called to enable an API, not reflected.
void enable_api( const string& api_name ); void enable_api( const string& api_name );
application& _app; application& _app;
optional< fc::api<database_api> > _database_api; optional< fc::api<database_api> > _database_api;
optional< fc::api<network_broadcast_api> > _network_broadcast_api; optional< fc::api<network_broadcast_api> > _network_broadcast_api;
optional< fc::api<network_node_api> > _network_node_api; optional< fc::api<network_node_api> > _network_node_api;
@ -464,6 +479,7 @@ FC_API(graphene::app::database_api,
(lookup_account_names) (lookup_account_names)
(get_account_count) (get_account_count)
(lookup_accounts) (lookup_accounts)
(get_full_accounts)
(get_account_balances) (get_account_balances)
(get_named_account_balances) (get_named_account_balances)
(lookup_asset_symbols) (lookup_asset_symbols)

View file

@ -174,8 +174,7 @@ void database::initialize_indexes()
prop_index->add_secondary_index<required_approval_index>(); prop_index->add_secondary_index<required_approval_index>();
add_index< primary_index<withdraw_permission_index > >(); add_index< primary_index<withdraw_permission_index > >();
//add_index< primary_index<vesting_balance_index> >(); add_index< primary_index<vesting_balance_index> >();
add_index< primary_index<simple_index<vesting_balance_object>> >();
add_index< primary_index<worker_index> >(); add_index< primary_index<worker_index> >();
add_index< primary_index<balance_index> >(); add_index< primary_index<balance_index> >();
@ -214,7 +213,7 @@ void database::init_genesis(const genesis_state_type& genesis_state)
transaction_evaluation_state genesis_eval_state(this); transaction_evaluation_state genesis_eval_state(this);
flat_index<block_summary_object>& bsi = get_mutable_index_type< flat_index<block_summary_object> >(); flat_index<block_summary_object>& bsi = get_mutable_index_type< flat_index<block_summary_object> >();
bsi.resize(0xffff+1); bsi.resize(0xffff+1);
// Create blockchain accounts // Create blockchain accounts

View file

@ -34,6 +34,7 @@ namespace graphene { namespace chain {
struct by_id; struct by_id;
struct by_price; struct by_price;
struct by_expiration; struct by_expiration;
struct by_account;
typedef multi_index_container< typedef multi_index_container<
limit_order_object, limit_order_object,
indexed_by< indexed_by<
@ -46,7 +47,8 @@ namespace graphene { namespace chain {
member< object, object_id_type, &object::id> member< object, object_id_type, &object::id>
>, >,
composite_key_compare< std::greater<price>, std::less<object_id_type> > composite_key_compare< std::greater<price>, std::less<object_id_type> >
> >,
ordered_non_unique< tag<by_account>, member<limit_order_object, account_id_type, &limit_order_object::seller>>
> >
> limit_order_multi_index_type; > limit_order_multi_index_type;
@ -74,7 +76,7 @@ namespace graphene { namespace chain {
account_id_type borrower; account_id_type borrower;
share_type collateral; ///< call_price.base.asset_id, access via get_collateral share_type collateral; ///< call_price.base.asset_id, access via get_collateral
share_type debt; ///< call_price.quote.asset_id, access via get_collateral share_type debt; ///< call_price.quote.asset_id, access via get_collateral
price call_price; ///< Debt / Collateral price call_price; ///< Debt / Collateral
}; };
/** /**

View file

@ -159,10 +159,14 @@ namespace graphene { namespace chain {
/** /**
* @ingroup object_index * @ingroup object_index
*/ */
struct by_account;
typedef multi_index_container< typedef multi_index_container<
vesting_balance_object, vesting_balance_object,
indexed_by< indexed_by<
hashed_unique< tag<by_id>, member< object, object_id_type, &object::id > > hashed_unique< tag<by_id>, member< object, object_id_type, &object::id > >,
ordered_non_unique< tag<by_account>,
member<vesting_balance_object, account_id_type, &vesting_balance_object::owner>
>
> >
> vesting_balance_multi_index_type; > vesting_balance_multi_index_type;
/** /**