WIP, SON operations, cli_wallet commands and RPC

- create_son, update_son, delete_son, list_sons
- get_sons, get_son_by_account, lookup_son_accounts, get_son_count
This commit is contained in:
Srdjan Obucina 2019-10-02 01:47:24 +02:00
parent e2c579bb02
commit 3a65102e56
4 changed files with 292 additions and 10 deletions

View file

@ -137,7 +137,10 @@ class database_api_impl : public std::enable_shared_from_this<database_api_impl>
map<string, committee_member_id_type> lookup_committee_member_accounts(const string& lower_bound_name, uint32_t limit)const;
// SON members
fc::optional<son_object> get_son_member_by_account(account_id_type account)const;
vector<optional<son_object>> get_sons(const vector<son_id_type>& son_ids)const;
fc::optional<son_object> get_son_by_account(account_id_type account)const;
map<string, son_id_type> lookup_son_accounts(const string& lower_bound_name, uint32_t limit)const;
uint64_t get_son_count()const;
// Votes
vector<variant> lookup_vote_ids( const vector<vote_id_type>& votes )const;
@ -1586,12 +1589,29 @@ map<string, committee_member_id_type> database_api_impl::lookup_committee_member
// //
//////////////////////////////////////////////////////////////////////
fc::optional<son_object> database_api::get_son_member_by_account(account_id_type account)const
vector<optional<son_object>> database_api::get_sons(const vector<son_id_type>& son_ids)const
{
return my->get_son_member_by_account( account );
return my->get_sons( son_ids );
}
fc::optional<son_object> database_api_impl::get_son_member_by_account(account_id_type account) const
vector<optional<son_object>> database_api_impl::get_sons(const vector<son_id_type>& son_ids)const
{
vector<optional<son_object>> result; result.reserve(son_ids.size());
std::transform(son_ids.begin(), son_ids.end(), std::back_inserter(result),
[this](son_id_type id) -> optional<son_object> {
if(auto o = _db.find(id))
return *o;
return {};
});
return result;
}
fc::optional<son_object> database_api::get_son_by_account(account_id_type account)const
{
return my->get_son_by_account( account );
}
fc::optional<son_object> database_api_impl::get_son_by_account(account_id_type account) const
{
const auto& idx = _db.get_index_type<son_member_index>().indices().get<by_account>();
auto itr = idx.find(account);
@ -1600,6 +1620,44 @@ fc::optional<son_object> database_api_impl::get_son_member_by_account(account_id
return {};
}
map<string, son_id_type> database_api::lookup_son_accounts(const string& lower_bound_name, uint32_t limit)const
{
return my->lookup_son_accounts( lower_bound_name, limit );
}
map<string, son_id_type> 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<son_member_index>().indices().get<by_id>();
// 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<std::string, son_id_type> 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<son_member_index>().indices().size();
}
//////////////////////////////////////////////////////////////////////
// //
// Votes //

View file

@ -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<son_object> get_son_member_by_account(account_id_type account)const;
vector<optional<son_object>> get_sons(const vector<son_id_type>& 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<son_object> 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<string, son_id_type> 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)

View file

@ -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<string, son_id_type> 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)

View file

@ -1720,6 +1720,41 @@ public:
FC_CAPTURE_AND_RETHROW( (owner_account) )
}
son_object get_son(string owner_account)
{
try
{
fc::optional<son_id_type> son_id = maybe_id<son_id_type>(owner_account);
if (son_id)
{
std::vector<son_id_type> ids_to_get;
ids_to_get.push_back(*son_id);
std::vector<fc::optional<son_object>> 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_object> 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_object> son_member_obj = _remote_db->get_son_member_by_account(son_member_owner_account_id);
fc::optional<son_object> 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<string, son_id_type> 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 */)