diff --git a/libraries/app/database_api.cpp b/libraries/app/database_api.cpp index 0711bd04..facf6d14 100644 --- a/libraries/app/database_api.cpp +++ b/libraries/app/database_api.cpp @@ -137,7 +137,10 @@ class database_api_impl : public std::enable_shared_from_this map lookup_committee_member_accounts(const string& lower_bound_name, uint32_t limit)const; // SON members - fc::optional get_son_member_by_account(account_id_type account)const; + vector> get_sons(const vector& son_ids)const; + fc::optional get_son_by_account(account_id_type account)const; + map lookup_son_accounts(const string& lower_bound_name, uint32_t limit)const; + uint64_t get_son_count()const; // Votes vector lookup_vote_ids( const vector& votes )const; @@ -1586,12 +1589,29 @@ map database_api_impl::lookup_committee_member // // ////////////////////////////////////////////////////////////////////// -fc::optional database_api::get_son_member_by_account(account_id_type account)const +vector> database_api::get_sons(const vector& son_ids)const { - return my->get_son_member_by_account( account ); + return my->get_sons( son_ids ); } -fc::optional database_api_impl::get_son_member_by_account(account_id_type account) const +vector> database_api_impl::get_sons(const vector& son_ids)const +{ + vector> result; result.reserve(son_ids.size()); + std::transform(son_ids.begin(), son_ids.end(), std::back_inserter(result), + [this](son_id_type id) -> optional { + if(auto o = _db.find(id)) + return *o; + return {}; + }); + return result; +} + +fc::optional database_api::get_son_by_account(account_id_type account)const +{ + return my->get_son_by_account( account ); +} + +fc::optional database_api_impl::get_son_by_account(account_id_type account) const { const auto& idx = _db.get_index_type().indices().get(); auto itr = idx.find(account); @@ -1600,6 +1620,44 @@ fc::optional database_api_impl::get_son_member_by_account(account_id return {}; } +map database_api::lookup_son_accounts(const string& lower_bound_name, uint32_t limit)const +{ + return my->lookup_son_accounts( lower_bound_name, limit ); +} + +map database_api_impl::lookup_son_accounts(const string& lower_bound_name, uint32_t limit)const +{ + FC_ASSERT( limit <= 1000 ); + const auto& sons_by_id = _db.get_index_type().indices().get(); + + // we want to order sons by account name, but that name is in the account object + // so the son_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 + // 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 + std::map sons_by_account_name; + for (const son_object& son : sons_by_id) + if (auto account_iter = _db.find(son.son_member_account)) + if (account_iter->name >= lower_bound_name) // we can ignore anything below lower_bound_name + sons_by_account_name.insert(std::make_pair(account_iter->name, son.id)); + + auto end_iter = sons_by_account_name.begin(); + while (end_iter != sons_by_account_name.end() && limit--) + ++end_iter; + sons_by_account_name.erase(end_iter, sons_by_account_name.end()); + return sons_by_account_name; +} + +uint64_t database_api::get_son_count()const +{ + return my->get_son_count(); +} + +uint64_t database_api_impl::get_son_count()const +{ + return _db.get_index_type().indices().size(); +} + ////////////////////////////////////////////////////////////////////// // // // Votes // diff --git a/libraries/app/include/graphene/app/database_api.hpp b/libraries/app/include/graphene/app/database_api.hpp index 7d239b20..44df27ac 100644 --- a/libraries/app/include/graphene/app/database_api.hpp +++ b/libraries/app/include/graphene/app/database_api.hpp @@ -552,12 +552,33 @@ class database_api ///////////////// /** - * @brief Get the son_member owned by a given account - * @param account The ID of the account whose son_member should be retrieved - * @return The son_member object, or null if the account does not have a son_member + * @brief Get a list of SONs by ID + * @param son_ids IDs of the SONs to retrieve + * @return The SONs corresponding to the provided IDs + * + * This function has semantics identical to @ref get_objects */ - fc::optional get_son_member_by_account(account_id_type account)const; + vector> get_sons(const vector& son_ids)const; + /** + * @brief Get the SON owned by a given account + * @param account The ID of the account whose SON should be retrieved + * @return The SON object, or null if the account does not have a SON + */ + fc::optional get_son_by_account(account_id_type account)const; + + /** + * @brief Get names and IDs for registered SONs + * @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 SON names to corresponding IDs + */ + map lookup_son_accounts(const string& lower_bound_name, uint32_t limit)const; + + /** + * @brief Get the total number of SONs registered with the blockchain + */ + uint64_t get_son_count()const; /// WORKERS @@ -770,7 +791,10 @@ FC_API(graphene::app::database_api, (lookup_committee_member_accounts) // SON members - (get_son_member_by_account) + (get_sons) + (get_son_by_account) + (lookup_son_accounts) + (get_son_count) // workers (get_workers_by_account) diff --git a/libraries/wallet/include/graphene/wallet/wallet.hpp b/libraries/wallet/include/graphene/wallet/wallet.hpp index c15c3954..741c1764 100644 --- a/libraries/wallet/include/graphene/wallet/wallet.hpp +++ b/libraries/wallet/include/graphene/wallet/wallet.hpp @@ -1271,6 +1271,63 @@ class wallet_api */ committee_member_object get_committee_member(string owner_account); + + /** Creates a SON object owned by the given account. + * + * An account can have at most one SON object. + * + * @param owner_account the name or id of the account which is creating the SON + * @param url a URL to include in the SON record in the blockchain. Clients may + * display this when showing a list of SONs. May be blank. + * @param broadcast true to broadcast the transaction on the network + * @returns the signed transaction registering a SON + */ + signed_transaction create_son(string owner_account, + string url, + bool broadcast = false); + + /** + * Update a SON object owned by the given account. + * + * @param witness The name of the SON's owner account. Also accepts the ID of the owner account or the ID of the SON. + * @param url Same as for create_son. The empty string makes it remain the same. + * @param block_signing_key The new block signing public key. The empty string makes it remain the same. + * @param broadcast true if you wish to broadcast the transaction. + */ + signed_transaction update_son(string owner_account, + string url, + string block_signing_key, + bool broadcast = false); + + + /** Deletes a SON object owned by the given account. + * + * An account can have at most one witness object. + * + * @param owner_account the name or id of the account which is creating the witness + * @param url a URL to include in the witness record in the blockchain. Clients may + * display this when showing a list of witnesses. May be blank. + * @param broadcast true to broadcast the transaction on the network + * @returns the signed transaction registering a witness + */ + signed_transaction delete_son(string owner_account, + bool broadcast = false); + + /** Lists all SONs in the blockchain. + * This returns a list of all account names that own SON, and the associated SON id, + * sorted by name. This lists SONs whether they are currently voted in or not. + * + * Use the \c lowerbound and limit parameters to page through the list. To retrieve all SONs, + * start by setting \c lowerbound to the empty string \c "", and then each iteration, pass + * the last SON name returned as the \c lowerbound for the next \c list_sons() call. + * + * @param lowerbound the name of the first SON to return. If the named SON does not exist, + * the list will start at the SON that comes after \c lowerbound + * @param limit the maximum number of SON to return (max: 1000) + * @returns a list of SON mapping SON names to SON ids + */ + map list_sons(const string& lowerbound, uint32_t limit); + /** Creates a witness object owned by the given account. * * An account can have at most one witness object. @@ -1980,6 +2037,10 @@ FC_API( graphene::wallet::wallet_api, (get_committee_member) (list_witnesses) (list_committee_members) + (create_son) + (update_son) + (delete_son) + (list_sons) (create_witness) (update_witness) (create_worker) diff --git a/libraries/wallet/wallet.cpp b/libraries/wallet/wallet.cpp index 630add0f..3f9c4eda 100644 --- a/libraries/wallet/wallet.cpp +++ b/libraries/wallet/wallet.cpp @@ -1720,6 +1720,41 @@ public: FC_CAPTURE_AND_RETHROW( (owner_account) ) } + son_object get_son(string owner_account) + { + try + { + fc::optional son_id = maybe_id(owner_account); + if (son_id) + { + std::vector ids_to_get; + ids_to_get.push_back(*son_id); + std::vector> son_objects = _remote_db->get_sons(ids_to_get); + if (son_objects.front()) + return *son_objects.front(); + FC_THROW("No SON 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 son = _remote_db->get_son_by_account(owner_account_id); + if (son) + return *son; + else + FC_THROW("No SON is registered for account ${account}", ("account", owner_account)); + } + catch (const fc::exception&) + { + FC_THROW("No account or SON named ${account}", ("account", owner_account)); + } + } + } + FC_CAPTURE_AND_RETHROW( (owner_account) ) + } + committee_member_object get_committee_member(string owner_account) { try @@ -1755,6 +1790,84 @@ public: FC_CAPTURE_AND_RETHROW( (owner_account) ) } + signed_transaction create_son(string owner_account, + string url, + bool broadcast /* = false */) + { try { + account_object son_account = get_account(owner_account); + fc::ecc::private_key active_private_key = get_private_key_for_account(son_account); + int son_key_index = find_first_unused_derived_key_index(active_private_key); + fc::ecc::private_key son_private_key = derive_private_key(key_to_wif(active_private_key), son_key_index); + graphene::chain::public_key_type son_public_key = son_private_key.get_public_key(); + + son_create_operation son_create_op; + son_create_op.owner_account = son_account.id; + son_create_op.signing_key = son_public_key; + son_create_op.url = url; + secret_hash_type::encoder enc; + fc::raw::pack(enc, son_private_key); + fc::raw::pack(enc, secret_hash_type()); + //son_create_op.initial_secret = secret_hash_type::hash(enc.result()); + + + if (_remote_db->get_son_by_account(son_create_op.owner_account)) + FC_THROW("Account ${owner_account} is already a SON", ("owner_account", owner_account)); + + signed_transaction tx; + tx.operations.push_back( son_create_op ); + set_operation_fees( tx, _remote_db->get_global_properties().parameters.current_fees); + tx.validate(); + + //_wallet.pending_witness_registrations[owner_account] = key_to_wif(son_private_key); + + return sign_transaction( tx, broadcast ); + } FC_CAPTURE_AND_RETHROW( (owner_account)(broadcast) ) } + + signed_transaction update_son(string owner_account, + string url, + string block_signing_key, + bool broadcast /* = false */) + { try { + son_object son = get_son(owner_account); + account_object son_account = get_account( son.son_member_account ); + fc::ecc::private_key active_private_key = get_private_key_for_account(son_account); + + son_update_operation son_update_op; + son_update_op.son_id = son.id; + son_update_op.owner_account = son_account.id; + if( url != "" ) + son_update_op.new_url = url; + if( block_signing_key != "" ) { + son_update_op.new_signing_key = public_key_type( block_signing_key ); + } + + signed_transaction tx; + tx.operations.push_back( son_update_op ); + set_operation_fees( tx, _remote_db->get_global_properties().parameters.current_fees ); + tx.validate(); + + return sign_transaction( tx, broadcast ); + } FC_CAPTURE_AND_RETHROW( (owner_account)(url)(block_signing_key)(broadcast) ) } + + signed_transaction delete_son(string owner_account, + bool broadcast /* = false */) + { try { + son_object son = get_son(owner_account); + account_object son_account = get_account( son.son_member_account ); + fc::ecc::private_key active_private_key = get_private_key_for_account(son_account); + + son_delete_operation son_delete_op; + son_delete_op.son_id = son.id; + son_delete_op.owner_account = son_account.id; + + signed_transaction tx; + tx.operations.push_back( son_delete_op ); + set_operation_fees( tx, _remote_db->get_global_properties().parameters.current_fees ); + tx.validate(); + + return sign_transaction( tx, broadcast ); + } FC_CAPTURE_AND_RETHROW( (owner_account)(broadcast) ) } + signed_transaction create_witness(string owner_account, string url, bool broadcast /* = false */) @@ -2031,7 +2144,7 @@ public: { try { account_object voting_account_object = get_account(voting_account); account_id_type son_member_owner_account_id = get_account_id(son_member); - fc::optional son_member_obj = _remote_db->get_son_member_by_account(son_member_owner_account_id); + fc::optional son_member_obj = _remote_db->get_son_by_account(son_member_owner_account_id); if (!son_member_obj) FC_THROW("Account ${son_member} is not registered as a son_member", ("son_member", son_member)); if (approve) @@ -4018,6 +4131,32 @@ committee_member_object wallet_api::get_committee_member(string owner_account) return my->get_committee_member(owner_account); } +signed_transaction wallet_api::create_son(string owner_account, + string url, + bool broadcast /* = false */) +{ + return my->create_son(owner_account, url, broadcast); +} + +signed_transaction wallet_api::update_son(string owner_account, + string url, + string block_signing_key, + bool broadcast /* = false */) +{ + return my->update_son(owner_account, url, block_signing_key, broadcast); +} + +signed_transaction wallet_api::delete_son(string owner_account, + bool broadcast /* = false */) +{ + my->delete_son(owner_account, broadcast); +} + +map wallet_api::list_sons(const string& lowerbound, uint32_t limit) +{ + my->_remote_db->lookup_son_accounts(lowerbound, limit); +} + signed_transaction wallet_api::create_witness(string owner_account, string url, bool broadcast /* = false */)