Progress #61: Getting witnesses/delegates by owner

Support and API are now present for retrieving witnesses and delegates
by their owners.
This commit is contained in:
Nathan Hourt 2015-06-22 15:03:18 -04:00
parent db85d35b19
commit 3eaec849eb
11 changed files with 107 additions and 61 deletions

View file

@ -17,10 +17,8 @@
*/
#include <graphene/app/api.hpp>
#include <graphene/app/application.hpp>
#include <graphene/chain/asset_object.hpp>
#include <graphene/chain/database.hpp>
#include <graphene/utilities/key_conversion.hpp>
#include <graphene/chain/operation_history_object.hpp>
#include <fc/crypto/hex.hpp>
@ -167,7 +165,7 @@ namespace graphene { namespace app {
else
{
result.reserve(assets.size());
std::transform(assets.begin(), assets.end(), std::back_inserter(result),
[this, acnt](asset_id_type id) { return _db.get_balance(acnt, id); });
}
@ -242,7 +240,7 @@ namespace graphene { namespace app {
auto itr = assets_by_symbol.lower_bound(lower_bound_symbol);
if( lower_bound_symbol == "" )
if( lower_bound_symbol == "" )
itr = assets_by_symbol.begin();
while(limit-- && itr != assets_by_symbol.end())
@ -251,6 +249,24 @@ namespace graphene { namespace app {
return result;
}
fc::optional<delegate_object> database_api::get_delegate_by_account(account_id_type account) const
{
const auto& idx = _db.get_index_type<delegate_index>().indices().get<by_account>();
auto itr = idx.find(account);
if( itr != idx.end() )
return *itr;
return {};
}
fc::optional<witness_object> database_api::get_witness_by_account(account_id_type account) const
{
const auto& idx = _db.get_index_type<witness_index>().indices().get<by_account>();
auto itr = idx.find(account);
if( itr != idx.end() )
return *itr;
return {};
}
login_api::login_api(application& a)
:_app(a)
{

View file

@ -153,14 +153,14 @@ namespace detail {
secret_hash_type::encoder enc;
fc::raw::pack(enc, nathan_key);
fc::raw::pack(enc, secret_hash_type());
auto secret = secret_hash_type::hash(enc.result());
for( int i = 0; i < 10; ++i )
{
initial_state.allocation_targets.emplace_back("init"+fc::to_string(i), nathan_key.get_public_key(), 0, true);
initial_state.initial_committee.push_back({"init"+fc::to_string(i)});
auto name = "init"+fc::to_string(i);
initial_state.allocation_targets.emplace_back(name, nathan_key.get_public_key(), 0, true);
initial_state.initial_committee.push_back({name});
initial_state.initial_witnesses.push_back({name, nathan_key.get_public_key(), secret});
}
initial_state.initial_witnesses = vector<genesis_state_type::initial_witness_type>(10, {"committee-account",
nathan_key.get_public_key(),
secret_hash_type::hash(enc.result())});
initial_state.allocation_targets.emplace_back("nathan", address(public_key_type(nathan_key.get_public_key())), 1);
if( _options->count("genesis-json") )

View file

@ -24,7 +24,10 @@
#include <graphene/chain/limit_order_object.hpp>
#include <graphene/chain/call_order_object.hpp>
#include <graphene/chain/key_object.hpp>
#include <graphene/chain/delegate_object.hpp>
#include <graphene/chain/witness_object.hpp>
#include <graphene/net/node.hpp>
#include <fc/api.hpp>
namespace graphene { namespace app {
@ -165,6 +168,19 @@ namespace graphene { namespace app {
*/
vector<asset_object> list_assets(const string& lower_bound_symbol, uint32_t limit)const;
/**
* @brief Get the delegate owned by a given account
* @param account The ID of the account whose delegate should be retrieved
* @return The delegate object, or null if the account does not have a delegate
*/
fc::optional<delegate_object> get_delegate_by_account(account_id_type account)const;
/**
* @brief Get the witness owned by a given account
* @param account The ID of the account whose witness should be retrieved
* @return The witness object, or null if the account does not have a witness
*/
fc::optional<witness_object> get_witness_by_account(account_id_type account)const;
/**
* @group Push Notification Methods
* These methods may be used to get push notifications whenever an object or market is changed
@ -338,6 +354,8 @@ FC_API(graphene::app::database_api,
(get_call_orders)
(get_settle_orders)
(list_assets)
(get_delegate_by_account)
(get_witness_by_account)
(subscribe_to_objects)
(unsubscribe_from_objects)
(subscribe_to_market)

View file

@ -80,7 +80,7 @@ const signed_transaction& database::get_recent_transaction(const transaction_id_
*
* @return true if we switched forks as a result of this push.
*/
bool database::push_block( const signed_block& new_block, uint32_t skip )
bool database::push_block(const signed_block& new_block, uint32_t skip)
{
bool result;
with_skip_flags( skip, [&]()
@ -90,29 +90,20 @@ bool database::push_block( const signed_block& new_block, uint32_t skip )
return result;
}
bool database::_push_block( const signed_block& new_block )
bool database::_push_block(const signed_block& new_block)
{ try {
uint32_t skip = get_node_properties().skip_flags;
if( !(skip&skip_fork_db) )
{
auto new_head = _fork_db.push_block( new_block );
auto new_head = _fork_db.push_block(new_block);
//If the head block from the longest chain does not build off of the current head, we need to switch forks.
if( new_head->data.previous != head_block_id() )
{
//edump((new_head->data.previous));
//If the newly pushed block is the same height as head, we get head back in new_head
//Only switch forks if new_head is actually higher than head
if( new_head->data.block_num() > head_block_num() )
{
auto branches = _fork_db.fetch_branch_from( new_head->data.id(), _pending_block.previous );
for( auto item : branches.first )
{
// wdump( ("new")(item->id)(item->data.previous) );
}
for( auto item : branches.second )
{
// wdump( ("old")(item->id)(item->data.previous) );
}
auto branches = _fork_db.fetch_branch_from(new_head->data.id(), _pending_block.previous);
// pop blocks until we hit the forked block
while( head_block_id() != branches.second.back()->data.previous )
@ -131,9 +122,6 @@ bool database::_push_block( const signed_block& new_block )
catch ( const fc::exception& e ) { except = e; }
if( except )
{
//wdump((except->to_detail_string()));
// elog( "Encountered error when switching to a longer fork at id ${id}. Going back.",
// ("id", (*ritr)->id) );
// remove the rest of branches.first from the fork_db, those blocks are invalid
while( ritr != branches.first.rend() )
{
@ -170,8 +158,8 @@ bool database::_push_block( const signed_block& new_block )
try {
auto session = _undo_db.start_undo_session();
apply_block( new_block, skip );
_block_id_to_block.store( new_block.id(), new_block );
apply_block(new_block, skip);
_block_id_to_block.store(new_block.id(), new_block);
session.commit();
} catch ( const fc::exception& e ) {
elog("Failed to push new block:\n${e}", ("e", e.to_detail_string()));
@ -204,7 +192,6 @@ processed_transaction database::push_transaction( const signed_transaction& trx,
processed_transaction database::_push_transaction( const signed_transaction& trx )
{
uint32_t skip = get_node_properties().skip_flags;
//wdump((trx.digest())(trx.id()));
// If this is the first transaction pushed after applying a block, start a new undo session.
// This allows us to quickly rewind to the clean state of the head block, in case a new block arrives.
if( !_pending_block_session ) _pending_block_session = _undo_db.start_undo_session();
@ -239,9 +226,6 @@ processed_transaction database::push_proposal(const proposal_object& proposal)
return std::make_pair(id, authority::owner);
});
//ilog("Attempting to push proposal ${prop}", ("prop", proposal));
//idump((eval_state.approved_by));
eval_state.operation_results.reserve(proposal.proposed_transaction.operations.size());
processed_transaction ptrx(proposal.proposed_transaction);
eval_state._trx = &ptrx;

View file

@ -105,8 +105,8 @@ void database::initialize_indexes()
add_index< primary_index<force_settlement_index> >();
add_index< primary_index<account_index> >();
add_index< primary_index<simple_index<key_object>> >();
add_index< primary_index<simple_index<delegate_object>> >();
add_index< primary_index<simple_index<witness_object>> >();
add_index< primary_index<delegate_index> >();
add_index< primary_index<witness_index> >();
add_index< primary_index<limit_order_index > >();
add_index< primary_index<call_order_index > >();
add_index< primary_index<proposal_index > >();
@ -118,12 +118,12 @@ void database::initialize_indexes()
add_index< primary_index<transaction_index > >();
add_index< primary_index<account_balance_index > >();
add_index< primary_index<asset_bitasset_data_index > >();
add_index< primary_index<simple_index< global_property_object >> >();
add_index< primary_index<simple_index< dynamic_global_property_object >> >();
add_index< primary_index<simple_index< account_statistics_object >> >();
add_index< primary_index<simple_index< asset_dynamic_data_object >> >();
add_index< primary_index<flat_index< block_summary_object >> >();
add_index< primary_index< simple_index< witness_schedule_object > > >();
add_index< primary_index<simple_index<global_property_object >> >();
add_index< primary_index<simple_index<dynamic_global_property_object >> >();
add_index< primary_index<simple_index<account_statistics_object >> >();
add_index< primary_index<simple_index<asset_dynamic_data_object >> >();
add_index< primary_index<flat_index< block_summary_object >> >();
add_index< primary_index<simple_index<witness_schedule_object >> >();
}
void database::init_genesis(const genesis_state_type& genesis_state)
@ -356,6 +356,6 @@ void database::init_genesis(const genesis_state_type& genesis_state)
assert( wso.id == witness_schedule_id_type() );
_undo_db.enable();
} FC_LOG_AND_RETHROW() }
} FC_CAPTURE_AND_RETHROW((genesis_state)) }
} }

View file

@ -31,18 +31,19 @@
namespace graphene { namespace chain {
template<class ObjectType>
vector<std::reference_wrapper<const ObjectType>> database::sort_votable_objects(size_t count) const
template<class Index>
vector<std::reference_wrapper<const typename Index::object_type>> database::sort_votable_objects(size_t count) const
{
const auto& all_objects = dynamic_cast<const simple_index<ObjectType>&>(get_index<ObjectType>());
using ObjectType = typename Index::object_type;
const auto& all_objects = get_index_type<Index>().indices();
count = std::min(count, all_objects.size());
vector<std::reference_wrapper<const ObjectType>> refs;
refs.reserve(all_objects.size());
std::transform(all_objects.begin(), all_objects.end(),
std::back_inserter(refs),
[](const ObjectType& o) { return std::cref(o); });
std::partial_sort( refs.begin(), refs.begin() + count, refs.end(),
[this]( const ObjectType& a, const ObjectType& b )->bool {
std::partial_sort(refs.begin(), refs.begin() + count, refs.end(),
[this](const ObjectType& a, const ObjectType& b)->bool {
return _vote_tally_buffer[a.vote_id] > _vote_tally_buffer[b.vote_id];
});
@ -105,7 +106,7 @@ void database::update_active_witnesses()
&& (stake_tally <= stake_target) )
stake_tally += _witness_count_histogram_buffer[++witness_count];
auto wits = sort_votable_objects<witness_object>(std::max(witness_count*2+1, GRAPHENE_MIN_WITNESS_COUNT));
auto wits = sort_votable_objects<witness_index>(std::max(witness_count*2+1, GRAPHENE_MIN_WITNESS_COUNT));
const global_property_object& gpo = get_global_properties();
// Update witness authority
@ -171,12 +172,12 @@ void database::update_active_delegates()
&& (stake_tally <= stake_target) )
stake_tally += _committee_count_histogram_buffer[++delegate_count];
auto delegates = sort_votable_objects<delegate_object>(std::max(delegate_count*2+1, GRAPHENE_MIN_DELEGATE_COUNT));
auto delegates = sort_votable_objects<delegate_index>(std::max(delegate_count*2+1, GRAPHENE_MIN_DELEGATE_COUNT));
// Update genesis authorities
if( !delegates.empty() )
{
modify( get(GRAPHENE_COMMITTEE_ACCOUNT), [&]( account_object& a ) {
modify(get(GRAPHENE_COMMITTEE_ACCOUNT), [&](account_object& a) {
uint64_t total_votes = 0;
map<account_id_type, uint64_t> weights;
a.active.weight_threshold = 0;
@ -202,11 +203,11 @@ void database::update_active_delegates()
a.active.weight_threshold /= 2;
a.active.weight_threshold += 1;
});
modify( get(GRAPHENE_RELAXED_COMMITTEE_ACCOUNT), [&](account_object& a) {
modify(get(GRAPHENE_RELAXED_COMMITTEE_ACCOUNT), [&](account_object& a) {
a.active = get(GRAPHENE_COMMITTEE_ACCOUNT).active;
});
}
modify( get_global_properties(), [&]( global_property_object& gp ) {
modify(get_global_properties(), [&](global_property_object& gp) {
gp.active_delegates.clear();
std::transform(delegates.begin(), delegates.end(),
std::inserter(gp.active_delegates, gp.active_delegates.begin()),

View file

@ -399,8 +399,8 @@ namespace graphene { namespace chain {
optional<undo_database::session> _pending_block_session;
vector< unique_ptr<op_evaluator> > _operation_evaluators;
template<class ObjectType>
vector<std::reference_wrapper<const ObjectType>> sort_votable_objects(size_t count)const;
template<class Index>
vector<std::reference_wrapper<const typename Index::object_type>> sort_votable_objects(size_t count)const;
//////////////////// db_block.cpp ////////////////////

View file

@ -18,6 +18,7 @@
#pragma once
#include <graphene/chain/asset.hpp>
#include <graphene/db/object.hpp>
#include <graphene/db/generic_index.hpp>
namespace graphene { namespace chain {
using namespace graphene::db;
@ -33,7 +34,7 @@ namespace graphene { namespace chain {
* active delegates has control.
*
* Delegates were separated into a separate object to make iterating over
* the set of delegate easy.
* the set of delegate easy.
*/
class delegate_object : public abstract_object<delegate_object>
{
@ -45,6 +46,19 @@ namespace graphene { namespace chain {
vote_id_type vote_id;
};
struct by_account;
using delegate_multi_index_type = multi_index_container<
delegate_object,
indexed_by<
ordered_unique< tag<by_id>,
member<object, object_id_type, &object::id>
>,
hashed_unique< tag<by_account>,
member<delegate_object, account_id_type, &delegate_object::delegate_account>
>
>
>;
using delegate_index = generic_index<delegate_object, delegate_multi_index_type>;
} } // graphene::chain
FC_REFLECT_DERIVED( graphene::chain::delegate_object, (graphene::db::object),

View file

@ -18,6 +18,7 @@
#pragma once
#include <graphene/chain/asset.hpp>
#include <graphene/db/object.hpp>
#include <graphene/db/generic_index.hpp>
namespace graphene { namespace chain {
using namespace graphene::db;
@ -40,6 +41,19 @@ namespace graphene { namespace chain {
witness_object() : vote_id(vote_id_type::witness) {}
};
struct by_account;
using witness_multi_index_type = multi_index_container<
witness_object,
indexed_by<
hashed_unique< tag<by_id>,
member<object, object_id_type, &object::id>
>,
hashed_unique< tag<by_account>,
member<witness_object, account_id_type, &witness_object::witness_account>
>
>
>;
using witness_index = generic_index<witness_object, witness_multi_index_type>;
} } // graphene::chain
FC_REFLECT_DERIVED( graphene::chain::witness_object, (graphene::db::object),
@ -49,4 +63,3 @@ FC_REFLECT_DERIVED( graphene::chain::witness_object, (graphene::db::object),
(last_secret)
(accumulated_income)
(vote_id) )

View file

@ -155,7 +155,7 @@ void database_fixture::verify_asset_supplies( )const
total_balances[bad.options.short_backing_asset] += bad.settlement_fund;
}
}
for( const witness_object& witness_obj : db.get_index_type<simple_index<witness_object>>() )
for( const witness_object& witness_obj : db.get_index_type<witness_index>().indices() )
{
total_balances[asset_id_type()] += witness_obj.accumulated_income;
}

View file

@ -41,14 +41,14 @@ genesis_state_type make_genesis() {
secret_hash_type::encoder enc;
fc::raw::pack(enc, delegate_priv_key);
fc::raw::pack(enc, secret_hash_type());
auto secret = secret_hash_type::hash(enc.result());
for( int i = 0; i < 10; ++i )
{
genesis_state.allocation_targets.emplace_back("init"+fc::to_string(i), delegate_priv_key.get_public_key(), 0, true);
genesis_state.initial_committee.push_back({"init"+fc::to_string(i)});
auto name = "init"+fc::to_string(i);
genesis_state.allocation_targets.emplace_back(name, delegate_priv_key.get_public_key(), 0, true);
genesis_state.initial_committee.push_back({name});
genesis_state.initial_witnesses.push_back({name, delegate_priv_key.get_public_key(), secret});
}
genesis_state.initial_witnesses = vector<genesis_state_type::initial_witness_type>(10, {"committee-account",
delegate_priv_key.get_public_key(),
secret_hash_type::hash(enc.result())});
return genesis_state;
}
@ -70,7 +70,7 @@ BOOST_AUTO_TEST_CASE( block_database_test )
for( uint32_t i = 0; i < 5; ++i )
{
if( i > 0 ) b.previous = b.id();
b.witness = witness_id_type(i+1);
b.witness = witness_id_type(i+1);
bdb.store( b.id(), b );
auto fetch = bdb.fetch_by_number( b.block_num() );