diff --git a/libraries/app/api.cpp b/libraries/app/api.cpp index 1360303e..283e3a80 100644 --- a/libraries/app/api.cpp +++ b/libraries/app/api.cpp @@ -302,6 +302,29 @@ namespace graphene { namespace app { return witnesses_by_account_name; } + map database_api::lookup_delegate_accounts(const string& lower_bound_name, uint32_t limit)const + { + FC_ASSERT( limit <= 1000 ); + const auto& delegates_by_id = _db.get_index_type().indices().get(); + + // we want to order delegates by account name, but that name is in the account object + // so the delegate_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 + // records to return. This could be optimized, but we expect the + // number of delegates to be few and the frequency of calls to be rare + std::map delegates_by_account_name; + for (const delegate_object& delegate : delegates_by_id) + if (auto account_iter = _db.find(delegate.delegate_account)) + if (account_iter->name >= lower_bound_name) // we can ignore anything below lower_bound_name + delegates_by_account_name.insert(std::make_pair(account_iter->name, delegate.id)); + + auto end_iter = delegates_by_account_name.begin(); + while (end_iter != delegates_by_account_name.end() && limit--) + ++end_iter; + delegates_by_account_name.erase(end_iter, delegates_by_account_name.end()); + return delegates_by_account_name; + } + vector> database_api::get_witnesses(const vector& witness_ids)const { vector> result; result.reserve(witness_ids.size()); @@ -314,6 +337,18 @@ namespace graphene { namespace app { return result; } + vector> database_api::get_delegates(const vector& delegate_ids)const + { + vector> result; result.reserve(delegate_ids.size()); + std::transform(delegate_ids.begin(), delegate_ids.end(), std::back_inserter(result), + [this](delegate_id_type id) -> optional { + if(auto o = _db.find(id)) + return *o; + return {}; + }); + return result; + } + login_api::login_api(application& a) :_app(a) { diff --git a/libraries/app/include/graphene/app/api.hpp b/libraries/app/include/graphene/app/api.hpp index c9fff614..7a69cd8f 100644 --- a/libraries/app/include/graphene/app/api.hpp +++ b/libraries/app/include/graphene/app/api.hpp @@ -206,6 +206,14 @@ namespace graphene { namespace app { */ map lookup_witness_accounts(const string& lower_bound_name, uint32_t limit)const; + /** + * @brief Get names and IDs for registered delegates + * @param lower_bound_name Lower bound of the first name to return + * @param limit Maximum number of results to return -- must not exceed 1000 + * @return Map of delegate names to corresponding IDs + */ + map lookup_delegate_accounts(const string& lower_bound_name, uint32_t limit)const; + /** * @brief Get a list of witnesses by ID * @param witness_ids IDs of the witnesses to retrieve @@ -215,6 +223,15 @@ namespace graphene { namespace app { */ vector> get_witnesses(const vector& witness_ids)const; + /** + * @brief Get a list of delegates by ID + * @param delegate_ids IDs of the delegates to retrieve + * @return The delegates corresponding to the provided IDs + * + * This function has semantics identical to @ref get_objects + */ + vector> get_delegates(const vector& delegate_ids)const; + /** * @group Push Notification Methods * These methods may be used to get push notifications whenever an object or market is changed @@ -441,9 +458,11 @@ FC_API(graphene::app::database_api, (list_assets) (get_delegate_by_account) (get_witnesses) + (get_delegates) (get_witness_by_account) (get_witness_count) (lookup_witness_accounts) + (lookup_delegate_accounts) (subscribe_to_objects) (unsubscribe_from_objects) (subscribe_to_market) diff --git a/libraries/wallet/include/graphene/wallet/wallet.hpp b/libraries/wallet/include/graphene/wallet/wallet.hpp index f7180f59..8e88889a 100644 --- a/libraries/wallet/include/graphene/wallet/wallet.hpp +++ b/libraries/wallet/include/graphene/wallet/wallet.hpp @@ -807,10 +807,13 @@ class wallet_api * An account can have at most one delegate object. * * @param owner_account the name or id of the account which is creating the delegate + * @param url a URL to include in the delegate record in the blockchain. Clients may + * display this when showing a list of delegates. May be blank. * @param broadcast true to broadcast the transaction on the network * @returns the signed transaction registering a delegate */ signed_transaction create_delegate(string owner_account, + string url, bool broadcast = false); /** Lists all witnesses registered in the blockchain. @@ -828,12 +831,33 @@ class wallet_api */ map list_witnesses(const string& lowerbound, uint32_t limit); + /** Lists all delegates registered in the blockchain. + * This returns a list of all account names that own delegates, and the associated delegate id, + * sorted by name. This lists delegates whether they are currently voted in or not. + * + * Use the \c lowerbound and limit parameters to page through the list. To retrieve all delegates, + * start by setting \c lowerbound to the empty string \c "", and then each iteration, pass + * the last delegate name returned as the \c lowerbound for the next \c list_delegates() call. + * + * @param lowerbound the name of the first delegate to return. If the named delegate does not exist, + * the list will start at the delegate that comes after \c lowerbound + * @param limit the maximum number of delegates to return (max: 1000) + * @returns a list of delegates mapping delegate names to delegate ids + */ + map list_delegates(const string& lowerbound, uint32_t limit); + /** Returns information about the given witness. - * @param witness_name_or_id the name or id of the witness account owner, or the id of the witness + * @param owner_account the name or id of the witness account owner, or the id of the witness * @returns the information about the witness stored in the block chain */ witness_object get_witness(string owner_account); + /** Returns information about the given delegate. + * @param owner_account the name or id of the delegate account owner, or the id of the delegate + * @returns the information about the delegate stored in the block chain + */ + delegate_object get_delegate(string owner_account); + /** Creates a witness object owned by the given account. * * An account can have at most one witness object. diff --git a/libraries/wallet/wallet.cpp b/libraries/wallet/wallet.cpp index 5701bc8a..c22d9414 100644 --- a/libraries/wallet/wallet.cpp +++ b/libraries/wallet/wallet.cpp @@ -279,12 +279,14 @@ private: } } } + void enable_umask_protection() { #ifdef __unix__ _old_umask = umask( S_IRWXG | S_IRWXO ); #endif } + void disable_umask_protection() { #ifdef __unix__ @@ -1169,12 +1171,13 @@ public: return sign_transaction( tx, broadcast ); } FC_CAPTURE_AND_RETHROW( (authorizing_account)(account_to_list)(new_listing_status)(broadcast) ) } - signed_transaction create_delegate(string owner_account, + signed_transaction create_delegate(string owner_account, string url, bool broadcast /* = false */) { try { delegate_create_operation delegate_create_op; delegate_create_op.delegate_account = get_account_id(owner_account); + delegate_create_op.url = url; if (_remote_db->get_delegate_by_account(delegate_create_op.delegate_account)) FC_THROW("Account ${owner_account} is already a delegate", ("owner_account", owner_account)); @@ -1221,6 +1224,41 @@ public: FC_CAPTURE_AND_RETHROW( (owner_account) ) } + delegate_object get_delegate(string owner_account) + { + try + { + fc::optional delegate_id = maybe_id(owner_account); + if (delegate_id) + { + std::vector ids_to_get; + ids_to_get.push_back(*delegate_id); + std::vector> delegate_objects = _remote_db->get_delegates(ids_to_get); + if (delegate_objects.front()) + return *delegate_objects.front(); + FC_THROW("No delegate is registered for id ${id}", ("id", owner_account)); + } + else + { + // then maybe it's the owner account + try + { + account_id_type owner_account_id = get_account_id(owner_account); + fc::optional delegate = _remote_db->get_delegate_by_account(owner_account_id); + if (delegate) + return *delegate; + else + FC_THROW("No delegate is registered for account ${account}", ("account", owner_account)); + } + catch (const fc::exception&) + { + FC_THROW("No account or delegate named ${account}", ("account", owner_account)); + } + } + } + FC_CAPTURE_AND_RETHROW( (owner_account) ) + } + signed_transaction create_witness(string owner_account, string url, bool broadcast /* = false */) @@ -2072,10 +2110,10 @@ signed_transaction wallet_api::whitelist_account(string authorizing_account, return my->whitelist_account(authorizing_account, account_to_list, new_listing_status, broadcast); } -signed_transaction wallet_api::create_delegate(string owner_account, +signed_transaction wallet_api::create_delegate(string owner_account, string url, bool broadcast /* = false */) { - return my->create_delegate(owner_account, broadcast); + return my->create_delegate(owner_account, url, broadcast); } map wallet_api::list_witnesses(const string& lowerbound, uint32_t limit) @@ -2083,6 +2121,11 @@ map wallet_api::list_witnesses(const string& lowerbound, return my->_remote_db->lookup_witness_accounts(lowerbound, limit); } +map wallet_api::list_delegates(const string& lowerbound, uint32_t limit) +{ + return my->_remote_db->lookup_delegate_accounts(lowerbound, limit); +} + witness_object wallet_api::get_witness(string owner_account) { return my->get_witness(owner_account);