Merge branch 'develop' into GRPH-50-network_broadcast_api-fix-v2

This commit is contained in:
Miha Čančula 2019-09-21 17:17:19 +02:00
commit 97f9875918
No known key found for this signature in database
GPG key ID: 4FC9D4BD4FBAB9B9
12 changed files with 450 additions and 85 deletions

View file

@ -521,6 +521,11 @@ vector<vector<account_id_type>> database_api::get_key_references( vector<public_
vector<vector<account_id_type>> database_api_impl::get_key_references( vector<public_key_type> keys )const
{
wdump( (keys) );
const auto& idx = _db.get_index_type<account_index>();
const auto& aidx = dynamic_cast<const base_primary_index&>(idx);
const auto& refs = aidx.get_secondary_index<graphene::chain::account_member_index>();
vector< vector<account_id_type> > final_result;
final_result.reserve(keys.size());
@ -540,10 +545,6 @@ vector<vector<account_id_type>> database_api_impl::get_key_references( vector<pu
subscribe_to_item( a4 );
subscribe_to_item( a5 );
const auto& idx = _db.get_index_type<account_index>();
const auto& aidx = dynamic_cast<const primary_index<account_index>&>(idx);
const auto& refs = aidx.get_secondary_index<graphene::chain::account_member_index>();
auto itr = refs.account_to_key_memberships.find(key);
vector<account_id_type> result;
for( auto& a : {a1,a2,a3,a4,a5} )
@ -560,6 +561,7 @@ vector<vector<account_id_type>> database_api_impl::get_key_references( vector<pu
}
}
auto itr = refs.account_to_key_memberships.find(key);
if( itr != refs.account_to_key_memberships.end() )
{
result.reserve( result.size() + itr->second.size() );
@ -595,7 +597,7 @@ bool database_api_impl::is_public_key_registered(string public_key) const
return false;
}
const auto& idx = _db.get_index_type<account_index>();
const auto& aidx = dynamic_cast<const primary_index<account_index>&>(idx);
const auto& aidx = dynamic_cast<const base_primary_index&>(idx);
const auto& refs = aidx.get_secondary_index<graphene::chain::account_member_index>();
auto itr = refs.account_to_key_memberships.find(key);
bool is_known = itr != refs.account_to_key_memberships.end();
@ -636,6 +638,10 @@ std::map<string,full_account> database_api::get_full_accounts( const vector<stri
std::map<std::string, full_account> database_api_impl::get_full_accounts( const vector<std::string>& names_or_ids, bool subscribe)
{
const auto& proposal_idx = _db.get_index_type<proposal_index>();
const auto& pidx = dynamic_cast<const base_primary_index&>(proposal_idx);
const auto& proposals_by_account = pidx.get_secondary_index<graphene::chain::required_approval_index>();
std::map<std::string, full_account> results;
for (const std::string& account_name_or_id : names_or_ids)
@ -673,9 +679,6 @@ std::map<std::string, full_account> database_api_impl::get_full_accounts( const
acnt.cashback_balance = account->cashback_balance(_db);
}
// Add the account's proposals
const auto& proposal_idx = _db.get_index_type<proposal_index>();
const auto& pidx = dynamic_cast<const primary_index<proposal_index>&>(proposal_idx);
const auto& proposals_by_account = pidx.get_secondary_index<graphene::chain::required_approval_index>();
auto required_approvals_itr = proposals_by_account._account_to_proposals.find( account->id );
if( required_approvals_itr != proposals_by_account._account_to_proposals.end() )
{
@ -686,11 +689,16 @@ std::map<std::string, full_account> database_api_impl::get_full_accounts( const
// Add the account's balances
/* This Branch
auto balance_range = _db.get_index_type<account_balance_index>().indices().get<by_account_asset>().equal_range(boost::make_tuple(account->id));
std::for_each(balance_range.first, balance_range.second,
[&acnt](const account_balance_object& balance) {
acnt.balances.emplace_back(balance);
});
*/
const auto& balances = _db.get_index_type< primary_index< account_balance_index > >().get_secondary_index< balances_by_account_index >().get_account_balances( account->id );
for( const auto balance : balances )
acnt.balances.emplace_back( *balance.second );
// Add the account's vesting balances
auto vesting_range = _db.get_index_type<vesting_balance_index>().indices().get<by_account>().equal_range(account->id);
@ -762,7 +770,7 @@ vector<account_id_type> database_api::get_account_references( account_id_type ac
vector<account_id_type> database_api_impl::get_account_references( account_id_type account_id )const
{
const auto& idx = _db.get_index_type<account_index>();
const auto& aidx = dynamic_cast<const primary_index<account_index>&>(idx);
const auto& aidx = dynamic_cast<const base_primary_index&>(idx);
const auto& refs = aidx.get_secondary_index<graphene::chain::account_member_index>();
auto itr = refs.account_to_account_memberships.find(account_id);
vector<account_id_type> result;
@ -843,10 +851,10 @@ vector<asset> database_api_impl::get_account_balances(account_id_type acnt, cons
if (assets.empty())
{
// if the caller passes in an empty list of assets, return balances for all assets the account owns
const account_balance_index& balance_index = _db.get_index_type<account_balance_index>();
auto range = balance_index.indices().get<by_account_asset>().equal_range(boost::make_tuple(acnt));
for (const account_balance_object& balance : boost::make_iterator_range(range.first, range.second))
result.push_back(asset(balance.get_balance()));
const auto& balance_index = _db.get_index_type< primary_index< account_balance_index > >();
const auto& balances = balance_index.get_secondary_index< balances_by_account_index >().get_account_balances( acnt );
for( const auto balance : balances )
result.push_back( balance.second->get_balance() );
}
else
{

View file

@ -267,4 +267,54 @@ void account_referrer_index::object_modified( const object& after )
{
}
const uint8_t balances_by_account_index::bits = 20;
const uint64_t balances_by_account_index::mask = (1ULL << balances_by_account_index::bits) - 1;
void balances_by_account_index::object_inserted( const object& obj )
{
const auto& abo = dynamic_cast< const account_balance_object& >( obj );
while( balances.size() < (abo.owner.instance.value >> bits) + 1 )
{
balances.reserve( (abo.owner.instance.value >> bits) + 1 );
balances.resize( balances.size() + 1 );
balances.back().resize( 1ULL << bits );
}
balances[abo.owner.instance.value >> bits][abo.owner.instance.value & mask][abo.asset_type] = &abo;
}
void balances_by_account_index::object_removed( const object& obj )
{
const auto& abo = dynamic_cast< const account_balance_object& >( obj );
if( balances.size() < (abo.owner.instance.value >> bits) + 1 ) return;
balances[abo.owner.instance.value >> bits][abo.owner.instance.value & mask].erase( abo.asset_type );
}
void balances_by_account_index::about_to_modify( const object& before )
{
ids_being_modified.emplace( before.id );
}
void balances_by_account_index::object_modified( const object& after )
{
FC_ASSERT( ids_being_modified.top() == after.id, "Modification of ID is not supported!");
ids_being_modified.pop();
}
const map< asset_id_type, const account_balance_object* >& balances_by_account_index::get_account_balances( const account_id_type& acct )const
{
static const map< asset_id_type, const account_balance_object* > _empty;
if( balances.size() < (acct.instance.value >> bits) + 1 ) return _empty;
return balances[acct.instance.value >> bits][acct.instance.value & mask];
}
const account_balance_object* balances_by_account_index::get_account_balance( const account_id_type& acct, const asset_id_type& asset )const
{
if( balances.size() < (acct.instance.value >> bits) + 1 ) return nullptr;
const auto& mine = balances[acct.instance.value >> bits][acct.instance.value & mask];
const auto itr = mine.find( asset );
if( mine.end() == itr ) return nullptr;
return itr->second;
}
} } // graphene::chain

View file

@ -34,11 +34,11 @@ namespace graphene { namespace chain {
asset database::get_balance(account_id_type owner, asset_id_type asset_id) const
{
auto& index = get_index_type<account_balance_index>().indices().get<by_account_asset>();
auto itr = index.find(boost::make_tuple(owner, asset_id));
if( itr == index.end() )
auto& index = get_index_type< primary_index< account_balance_index > >().get_secondary_index<balances_by_account_index>();
auto abo = index.get_account_balance( owner, asset_id );
if( !abo )
return asset(0, asset_id);
return itr->get_balance();
return abo->get_balance();
}
asset database::get_balance(const account_object& owner, const asset_object& asset_obj) const
@ -65,9 +65,9 @@ void database::adjust_balance(account_id_type account, asset delta )
if( delta.amount == 0 )
return;
auto& index = get_index_type<account_balance_index>().indices().get<by_account_asset>();
auto itr = index.find(boost::make_tuple(account, delta.asset_id));
if(itr == index.end())
auto& index = get_index_type< primary_index< account_balance_index > >().get_secondary_index<balances_by_account_index>();
auto abo = index.get_account_balance( account, delta.asset_id );
if( !abo )
{
FC_ASSERT( delta.amount > 0, "Insufficient Balance: ${a}'s balance of ${b} is less than required ${r}",
("a",account(*this).name)
@ -80,8 +80,9 @@ void database::adjust_balance(account_id_type account, asset delta )
});
} else {
if( delta.amount < 0 )
FC_ASSERT( itr->get_balance() >= -delta, "Insufficient Balance: ${a}'s balance of ${b} is less than required ${r}", ("a",account(*this).name)("b",to_pretty_string(itr->get_balance()))("r",to_pretty_string(-delta)));
modify(*itr, [delta](account_balance_object& b) {
FC_ASSERT( abo->get_balance() >= -delta, "Insufficient Balance: ${a}'s balance of ${b} is less than required ${r}",
("a",account(*this).name)("b",to_pretty_string(abo->get_balance()))("r",to_pretty_string(-delta)));
modify(*abo, [delta](account_balance_object& b) {
b.adjust_balance(delta);
});
}

View file

@ -742,9 +742,9 @@ processed_transaction database::_apply_transaction(const signed_transaction& trx
ptrx.operation_results = std::move(eval_state.operation_results);
//Make sure the temp account has no non-zero balances
const auto& index = get_index_type<account_balance_index>().indices().get<by_account_asset>();
auto range = index.equal_range( boost::make_tuple( GRAPHENE_TEMP_ACCOUNT ) );
std::for_each(range.first, range.second, [](const account_balance_object& b) { FC_ASSERT(b.balance == 0); });
const auto& balances = get_index_type< primary_index< account_balance_index > >().get_secondary_index< balances_by_account_index >().get_account_balances( GRAPHENE_TEMP_ACCOUNT );
for( const auto b : balances )
FC_ASSERT(b.second->balance == 0);
return ptrx;
} FC_CAPTURE_AND_RETHROW( (trx) ) }

View file

@ -251,15 +251,15 @@ void database::initialize_indexes()
_undo_db.set_max_size( GRAPHENE_MIN_UNDO_HISTORY );
//Protocol object indexes
add_index< primary_index<asset_index> >();
add_index< primary_index<asset_index, 13> >(); // 8192 assets per chunk
add_index< primary_index<force_settlement_index> >();
auto acnt_index = add_index< primary_index<account_index> >();
auto acnt_index = add_index< primary_index<account_index, 20> >(); // ~1 million accounts per chunk
acnt_index->add_secondary_index<account_member_index>();
acnt_index->add_secondary_index<account_referrer_index>();
add_index< primary_index<committee_member_index> >();
add_index< primary_index<witness_index> >();
add_index< primary_index<committee_member_index, 8> >(); // 256 members per chunk
add_index< primary_index<witness_index, 10> >(); // 1024 witnesses per chunk
add_index< primary_index<limit_order_index > >();
add_index< primary_index<call_order_index > >();
@ -287,8 +287,11 @@ void database::initialize_indexes()
//Implementation object indexes
add_index< primary_index<transaction_index > >();
add_index< primary_index<account_balance_index > >();
add_index< primary_index<asset_bitasset_data_index > >();
auto bal_idx = add_index< primary_index<account_balance_index > >();
bal_idx->add_secondary_index<balances_by_account_index>();
add_index< primary_index<asset_bitasset_data_index, 13 > >(); // 8192
add_index< primary_index<asset_dividend_data_object_index > >();
add_index< primary_index<simple_index<global_property_object >> >();
add_index< primary_index<simple_index<dynamic_global_property_object >> >();

View file

@ -621,7 +621,7 @@ void distribute_fba_balances( database& db )
void create_buyback_orders( database& db )
{
const auto& bbo_idx = db.get_index_type< buyback_index >().indices().get<by_id>();
const auto& bal_idx = db.get_index_type< account_balance_index >().indices().get< by_account_asset >();
const auto& bal_idx = db.get_index_type< primary_index< account_balance_index > >().get_secondary_index< balances_by_account_index >();
for( const buyback_object& bbo : bbo_idx )
{
@ -629,7 +629,6 @@ void create_buyback_orders( database& db )
assert( asset_to_buy.buyback_account.valid() );
const account_object& buyback_account = (*(asset_to_buy.buyback_account))(db);
asset_id_type next_asset = asset_id_type();
if( !buyback_account.allowed_assets.valid() )
{
@ -637,16 +636,11 @@ void create_buyback_orders( database& db )
continue;
}
while( true )
for( const auto& entry : bal_idx.get_account_balances( buyback_account.id ) )
{
auto it = bal_idx.lower_bound( boost::make_tuple( buyback_account.id, next_asset ) );
if( it == bal_idx.end() )
break;
if( it->owner != buyback_account.id )
break;
const auto* it = entry.second;
asset_id_type asset_to_sell = it->asset_type;
share_type amount_to_sell = it->balance;
next_asset = asset_to_sell + 1;
if( asset_to_sell == asset_to_buy.id )
continue;
if( amount_to_sell == 0 )
@ -740,8 +734,10 @@ void schedule_pending_dividend_balances(database& db,
{ try {
dlog("Processing dividend payments for dividend holder asset type ${holder_asset} at time ${t}",
("holder_asset", dividend_holder_asset_obj.symbol)("t", db.head_block_time()));
auto balance_by_acc_index = db.get_index_type< primary_index< account_balance_index > >().get_secondary_index< balances_by_account_index >();
auto current_distribution_account_balance_range =
balance_index.indices().get<by_account_asset>().equal_range(boost::make_tuple(dividend_data.dividend_distribution_account));
//balance_index.indices().get<by_account_asset>().equal_range(boost::make_tuple(dividend_data.dividend_distribution_account));
balance_by_acc_index.get_account_balances(dividend_data.dividend_distribution_account);
auto previous_distribution_account_balance_range =
distributed_dividend_balance_index.indices().get<by_dividend_payout_asset>().equal_range(boost::make_tuple(dividend_holder_asset_obj.id));
// the current range is now all current balances for the distribution account, sorted by asset_type
@ -789,10 +785,10 @@ void schedule_pending_dividend_balances(database& db,
}
#endif
auto current_distribution_account_balance_iter = current_distribution_account_balance_range.first;
auto current_distribution_account_balance_iter = current_distribution_account_balance_range.begin();
auto previous_distribution_account_balance_iter = previous_distribution_account_balance_range.first;
dlog("Current balances in distribution account: ${current}, Previous balances: ${previous}",
("current", (int64_t)std::distance(current_distribution_account_balance_range.first, current_distribution_account_balance_range.second))
("current", (int64_t)std::distance(current_distribution_account_balance_range.begin(), current_distribution_account_balance_range.end()))
("previous", (int64_t)std::distance(previous_distribution_account_balance_range.first, previous_distribution_account_balance_range.second)));
// when we pay out the dividends to the holders, we need to know the total balance of the dividend asset in all
@ -808,7 +804,7 @@ void schedule_pending_dividend_balances(database& db,
total_balance_of_dividend_asset += itr->second;
}
// loop through all of the assets currently or previously held in the distribution account
while (current_distribution_account_balance_iter != current_distribution_account_balance_range.second ||
while (current_distribution_account_balance_iter != current_distribution_account_balance_range.end() ||
previous_distribution_account_balance_iter != previous_distribution_account_balance_range.second)
{
try
@ -819,15 +815,15 @@ void schedule_pending_dividend_balances(database& db,
asset_id_type payout_asset_type;
if (previous_distribution_account_balance_iter == previous_distribution_account_balance_range.second ||
current_distribution_account_balance_iter->asset_type < previous_distribution_account_balance_iter->dividend_payout_asset_type)
current_distribution_account_balance_iter->second->asset_type < previous_distribution_account_balance_iter->dividend_payout_asset_type)
{
// there are no more previous balances or there is no previous balance for this particular asset type
payout_asset_type = current_distribution_account_balance_iter->asset_type;
current_balance = current_distribution_account_balance_iter->balance;
payout_asset_type = current_distribution_account_balance_iter->second->asset_type;
current_balance = current_distribution_account_balance_iter->second->balance;
idump((payout_asset_type)(current_balance));
}
else if (current_distribution_account_balance_iter == current_distribution_account_balance_range.second ||
previous_distribution_account_balance_iter->dividend_payout_asset_type < current_distribution_account_balance_iter->asset_type)
else if (current_distribution_account_balance_iter == current_distribution_account_balance_range.end() ||
previous_distribution_account_balance_iter->dividend_payout_asset_type < current_distribution_account_balance_iter->second->asset_type)
{
// there are no more current balances or there is no current balance for this particular previous asset type
payout_asset_type = previous_distribution_account_balance_iter->dividend_payout_asset_type;
@ -837,8 +833,8 @@ void schedule_pending_dividend_balances(database& db,
else
{
// we have both a previous and a current balance for this asset type
payout_asset_type = current_distribution_account_balance_iter->asset_type;
current_balance = current_distribution_account_balance_iter->balance;
payout_asset_type = current_distribution_account_balance_iter->second->asset_type;
current_balance = current_distribution_account_balance_iter->second->balance;
previous_balance = previous_distribution_account_balance_iter->balance_at_last_maintenance_interval;
idump((payout_asset_type)(current_balance)(previous_balance));
}
@ -1043,10 +1039,10 @@ void schedule_pending_dividend_balances(database& db,
// iterate
if (previous_distribution_account_balance_iter == previous_distribution_account_balance_range.second ||
current_distribution_account_balance_iter->asset_type < previous_distribution_account_balance_iter->dividend_payout_asset_type)
current_distribution_account_balance_iter->second->asset_type < previous_distribution_account_balance_iter->dividend_payout_asset_type)
++current_distribution_account_balance_iter;
else if (current_distribution_account_balance_iter == current_distribution_account_balance_range.second ||
previous_distribution_account_balance_iter->dividend_payout_asset_type < current_distribution_account_balance_iter->asset_type)
else if (current_distribution_account_balance_iter == current_distribution_account_balance_range.end() ||
previous_distribution_account_balance_iter->dividend_payout_asset_type < current_distribution_account_balance_iter->second->asset_type)
++previous_distribution_account_balance_iter;
else
{
@ -1066,6 +1062,7 @@ void process_dividend_assets(database& db)
ilog("In process_dividend_assets time ${time}", ("time", db.head_block_time()));
const account_balance_index& balance_index = db.get_index_type<account_balance_index>();
//const auto& balance_index = db.get_index_type< primary_index< account_balance_index > >().get_secondary_index< balances_by_account_index >();
const vesting_balance_index& vbalance_index = db.get_index_type<vesting_balance_index>();
const total_distributed_dividend_balance_object_index& distributed_dividend_balance_index = db.get_index_type<total_distributed_dividend_balance_object_index>();
const pending_dividend_payout_balance_for_holder_object_index& pending_payout_balance_index = db.get_index_type<pending_dividend_payout_balance_for_holder_object_index>();
@ -1090,10 +1087,10 @@ void process_dividend_assets(database& db)
("holder_asset", dividend_holder_asset_obj.symbol));
#ifndef NDEBUG
// dump balances before the payouts for debugging
const auto& balance_idx = db.get_index_type<account_balance_index>().indices().get<by_account_asset>();
auto holder_account_balance_range = balance_idx.equal_range(boost::make_tuple(dividend_data.dividend_distribution_account));
for (const account_balance_object& holder_balance_object : boost::make_iterator_range(holder_account_balance_range.first, holder_account_balance_range.second))
ilog(" Current balance: ${asset}", ("asset", asset(holder_balance_object.balance, holder_balance_object.asset_type)));
const auto& balance_index = db.get_index_type< primary_index< account_balance_index > >();
const auto& balances = balance_index.get_secondary_index< balances_by_account_index >().get_account_balances( dividend_data.dividend_distribution_account );
for( const auto balance : balances )
ilog(" Current balance: ${asset}", ("asset", asset(balance.second->balance, balance.second->asset_type)));
#endif
// when we do the payouts, we first increase the balances in all of the receiving accounts

View file

@ -363,7 +363,30 @@ namespace graphene { namespace chain {
};
struct by_account_asset;
/**
* @brief This secondary index will allow fast access to the balance objects
* that belonging to an account.
*/
class balances_by_account_index : public secondary_index
{
public:
virtual void object_inserted( const object& obj ) override;
virtual void object_removed( const object& obj ) override;
virtual void about_to_modify( const object& before ) override;
virtual void object_modified( const object& after ) override;
const map< asset_id_type, const account_balance_object* >& get_account_balances( const account_id_type& acct )const;
const account_balance_object* get_account_balance( const account_id_type& acct, const asset_id_type& asset )const;
private:
static const uint8_t bits;
static const uint64_t mask;
/** Maps each account to its balance objects */
vector< vector< map< asset_id_type, const account_balance_object* > > > balances;
std::stack< object_id_type > ids_being_modified;
};
struct by_asset_balance;
/**
* @ingroup object_index
@ -372,13 +395,6 @@ namespace graphene { namespace chain {
account_balance_object,
indexed_by<
ordered_unique< tag<by_id>, member< object, object_id_type, &object::id > >,
ordered_unique< tag<by_account_asset>,
composite_key<
account_balance_object,
member<account_balance_object, account_id_type, &account_balance_object::owner>,
member<account_balance_object, asset_id_type, &account_balance_object::asset_type>
>
>,
ordered_unique< tag<by_asset_balance>,
composite_key<
account_balance_object,
@ -418,6 +434,26 @@ namespace graphene { namespace chain {
*/
typedef generic_index<account_object, account_multi_index_type> account_index;
struct by_owner;
struct by_maintenance_seq;
/**
* @ingroup object_index
*/
typedef multi_index_container<
account_statistics_object,
indexed_by<
ordered_unique< tag<by_id>, member< object, object_id_type, &object::id > >,
ordered_unique< tag<by_owner>,
member< account_statistics_object, account_id_type, &account_statistics_object::owner > >
>
> account_stats_multi_index_type;
/**
* @ingroup object_index
*/
typedef generic_index<account_statistics_object, account_stats_multi_index_type> account_stats_index;
struct by_dividend_payout_account{}; // use when calculating pending payouts
struct by_dividend_account_payout{}; // use when doing actual payouts
struct by_account_dividend_payout{}; // use in get_full_accounts()

View file

@ -23,11 +23,13 @@
*/
#pragma once
#include <graphene/db/object.hpp>
#include <fc/interprocess/file_mapping.hpp>
#include <fc/io/raw.hpp>
#include <fc/io/json.hpp>
#include <fc/crypto/sha256.hpp>
#include <fstream>
#include <stack>
namespace graphene { namespace db {
class object_database;
@ -190,7 +192,112 @@ namespace graphene { namespace db {
object_database& _db;
};
/** @class direct_index
* @brief A secondary index that tracks objects in vectors indexed by object
* id. It is meant for fully (or almost fully) populated indexes only (will
* fail when loading an object_database with large gaps).
*
* WARNING! If any of the methods called on insertion, removal or
* modification throws, subsequent behaviour is undefined! Such exceptions
* indicate that this index type is not appropriate for the use-case.
*/
template<typename Object, uint8_t chunkbits>
class direct_index : public secondary_index
{
static_assert( chunkbits < 64, "Do you really want arrays with more than 2^63 elements???" );
// private
static const size_t MAX_HOLE = 100;
static const size_t _mask = ((1 << chunkbits) - 1);
size_t next = 0;
vector< vector< const Object* > > content;
std::stack< object_id_type > ids_being_modified;
public:
direct_index() {
FC_ASSERT( (1ULL << chunkbits) > MAX_HOLE, "Small chunkbits is inefficient." );
}
virtual ~direct_index(){}
virtual void object_inserted( const object& obj )
{
uint64_t instance = obj.id.instance();
if( instance == next )
{
if( !(next & _mask) )
{
content.resize((next >> chunkbits) + 1);
content[next >> chunkbits].resize( 1 << chunkbits, nullptr );
}
next++;
}
else if( instance < next )
FC_ASSERT( !content[instance >> chunkbits][instance & _mask], "Overwriting insert at {id}!", ("id",obj.id) );
else // instance > next, allow small "holes"
{
FC_ASSERT( instance <= next + MAX_HOLE, "Out-of-order insert: {id} > {next}!", ("id",obj.id)("next",next) );
if( !(next & _mask) || (next & (~_mask)) != (instance & (~_mask)) )
{
content.resize((instance >> chunkbits) + 1);
content[instance >> chunkbits].resize( 1 << chunkbits, nullptr );
}
while( next <= instance )
{
content[next >> chunkbits][next & _mask] = nullptr;
next++;
}
}
FC_ASSERT( nullptr != dynamic_cast<const Object*>(&obj), "Wrong object type!" );
content[instance >> chunkbits][instance & _mask] = static_cast<const Object*>( &obj );
}
virtual void object_removed( const object& obj )
{
FC_ASSERT( nullptr != dynamic_cast<const Object*>(&obj), "Wrong object type!" );
uint64_t instance = obj.id.instance();
FC_ASSERT( instance < next, "Removing out-of-range object: {id} > {next}!", ("id",obj.id)("next",next) );
FC_ASSERT( content[instance >> chunkbits][instance & _mask], "Removing non-existent object {id}!", ("id",obj.id) );
content[instance >> chunkbits][instance & _mask] = nullptr;
}
virtual void about_to_modify( const object& before )
{
ids_being_modified.emplace( before.id );
}
virtual void object_modified( const object& after )
{
FC_ASSERT( ids_being_modified.top() == after.id, "Modification of ID is not supported!");
ids_being_modified.pop();
}
template< typename object_id >
const Object* find( const object_id& id )const
{
static_assert( object_id::space_id == Object::space_id, "Space ID mismatch!" );
static_assert( object_id::type_id == Object::type_id, "Type_ID mismatch!" );
if( id.instance >= next ) return nullptr;
return content[id.instance.value >> chunkbits][id.instance.value & _mask];
};
template< typename object_id >
const Object& get( const object_id& id )const
{
const Object* ptr = find( id );
FC_ASSERT( ptr != nullptr, "Object not found!" );
return *ptr;
};
const Object* find( const object_id_type& id )const
{
FC_ASSERT( id.space() == Object::space_id, "Space ID mismatch!" );
FC_ASSERT( id.type() == Object::type_id, "Type_ID mismatch!" );
if( id.instance() >= next ) return nullptr;
return content[id.instance() >> chunkbits][id.instance() & ((1 << chunkbits) - 1)];
};
};
/**
* @class primary_index
* @brief Wraps a derived index to intercept calls to create, modify, and remove so that
@ -198,14 +305,18 @@ namespace graphene { namespace db {
*
* @see http://en.wikipedia.org/wiki/Curiously_recurring_template_pattern
*/
template<typename DerivedIndex>
template<typename DerivedIndex, uint8_t DirectBits = 0>
class primary_index : public DerivedIndex, public base_primary_index
{
public:
typedef typename DerivedIndex::object_type object_type;
primary_index( object_database& db )
:base_primary_index(db),_next_id(object_type::space_id,object_type::type_id,0) {}
:base_primary_index(db),_next_id(object_type::space_id,object_type::type_id,0)
{
if( DirectBits > 0 )
_direct_by_id = add_secondary_index< direct_index< object_type, DirectBits > >();
}
virtual uint8_t object_space_id()const override
{ return object_type::space_id; }
@ -216,7 +327,15 @@ namespace graphene { namespace db {
virtual object_id_type get_next_id()const override { return _next_id; }
virtual void use_next_id()override { ++_next_id.number; }
virtual void set_next_id( object_id_type id )override { _next_id = id; }
/** @return the object with id or nullptr if not found */
virtual const object* find( object_id_type id )const override
{
if( DirectBits > 0 )
return _direct_by_id->find( id );
return DerivedIndex::find( id );
}
fc::sha256 get_object_version()const
{
std::string desc = "1.0";//get_type_description<object_type>();
@ -318,7 +437,8 @@ namespace graphene { namespace db {
}
private:
object_id_type _next_id;
object_id_type _next_id;
const direct_index< object_type, DirectBits >* _direct_by_id = nullptr;
};
} } // graphene::db

View file

@ -3444,30 +3444,54 @@ asset wallet_api::get_lottery_balance( asset_id_type lottery_id )const
return my->_remote_db->get_lottery_balance( lottery_id );
}
vector<operation_detail> wallet_api::get_account_history(string name, int limit)const
vector<operation_detail> wallet_api::get_account_history(string name, int limit) const
{
vector<operation_detail> result;
auto account_id = get_account(name).get_id();
while( limit > 0 )
while (limit > 0)
{
bool skip_first_row = false;
operation_history_id_type start;
if( result.size() )
if (result.size())
{
start = result.back().op.id;
start = start + 1;
if (start == operation_history_id_type()) // no more data
break;
start = start + (-1);
if (start == operation_history_id_type()) // will return most recent history if directly call remote API with this
{
start = start + 1;
skip_first_row = true;
}
}
int page_limit = skip_first_row ? std::min(100, limit + 1) : std::min(100, limit);
vector<operation_history_object> current = my->_remote_hist->get_account_history(account_id, operation_history_id_type(), std::min(100,limit), start);
for( auto& o : current ) {
vector<operation_history_object> current = my->_remote_hist->get_account_history(account_id, operation_history_id_type(),
page_limit, start);
bool first_row = true;
for (auto &o : current)
{
if (first_row)
{
first_row = false;
if (skip_first_row)
{
continue;
}
}
std::stringstream ss;
auto memo = o.op.visit(detail::operation_printer(ss, *my, o.result));
result.push_back( operation_detail{ memo, ss.str(), o } );
result.push_back(operation_detail{memo, ss.str(), o});
}
if( (int)current.size() < std::min(100,limit) )
if (int(current.size()) < page_limit)
break;
limit -= current.size();
if (skip_first_row)
++limit;
}
return result;

View file

@ -27,6 +27,7 @@
#include <graphene/utilities/tempdir.hpp>
#include <graphene/bookie/bookie_plugin.hpp>
#include <graphene/account_history/account_history_plugin.hpp>
#include <graphene/egenesis/egenesis.hpp>
#include <graphene/wallet/wallet.hpp>
#include <graphene/chain/config.hpp>
@ -118,6 +119,7 @@ std::shared_ptr<graphene::app::application> start_application(fc::temp_directory
std::shared_ptr<graphene::app::application> app1(new graphene::app::application{});
app1->register_plugin< graphene::bookie::bookie_plugin>();
app1->register_plugin<graphene::account_history::account_history_plugin>();
app1->startup_plugins();
boost::program_options::variables_map cfg;
#ifdef _WIN32
@ -439,3 +441,42 @@ BOOST_FIXTURE_TEST_CASE( cli_vote_for_2_witnesses, cli_fixture )
throw;
}
}
///////////////////////
// Check account history pagination
///////////////////////
BOOST_FIXTURE_TEST_CASE( account_history_pagination, cli_fixture )
{
try
{
INVOKE(create_new_account);
// attempt to give jmjatlanta some peerplay
BOOST_TEST_MESSAGE("Transferring peerplay from Nathan to jmjatlanta");
for(int i = 1; i <= 199; i++)
{
signed_transaction transfer_tx = con.wallet_api_ptr->transfer("nathan", "jmjatlanta", std::to_string(i),
"1.3.0", "Here are some CORE token for your new account", true);
}
BOOST_CHECK(generate_block(app1));
// now get account history and make sure everything is there (and no duplicates)
std::vector<graphene::wallet::operation_detail> history = con.wallet_api_ptr->get_account_history("jmjatlanta", 300);
BOOST_CHECK_EQUAL(201u, history.size() );
std::set<object_id_type> operation_ids;
for(auto& op : history)
{
if( operation_ids.find(op.op.id) != operation_ids.end() )
{
BOOST_FAIL("Duplicate found");
}
operation_ids.insert(op.op.id);
}
} catch( fc::exception& e ) {
edump((e.to_detail_string()));
throw;
}
}

View file

@ -402,16 +402,16 @@ BOOST_AUTO_TEST_CASE( affiliate_payout_helper_test )
{
// Fix total supply
auto& index = db.get_index_type<account_balance_index>().indices().get<by_account_asset>();
auto itr = index.find( boost::make_tuple( account_id_type(), asset_id_type() ) );
BOOST_CHECK( itr != index.end() );
db.modify( *itr, [&ath]( account_balance_object& bal ) {
auto& index = db.get_index_type< primary_index< account_balance_index > >().get_secondary_index<balances_by_account_index>();
auto abo = index.get_account_balance( account_id_type(), asset_id_type() );
BOOST_CHECK( abo != nullptr );
db.modify( *abo, [&ath]( account_balance_object& bal ) {
bal.balance -= ath.alice_ppy + ath.ann_ppy + ath.audrey_ppy;
});
itr = index.find( boost::make_tuple( irene_id, btc_id ) );
BOOST_CHECK( itr != index.end() );
db.modify( *itr, [alice_btc,ann_btc,audrey_btc]( account_balance_object& bal ) {
abo = index.get_account_balance( irene_id, btc_id );
BOOST_CHECK( abo != nullptr );
db.modify( *abo, [alice_btc,ann_btc,audrey_btc]( account_balance_object& bal ) {
bal.balance -= alice_btc + ann_btc + audrey_btc;
});
}

View file

@ -110,4 +110,89 @@ BOOST_AUTO_TEST_CASE( flat_index_test )
FC_ASSERT( !(*bitusd.bitasset_data_id)(db).current_feed.settlement_price.is_null() );
}
BOOST_AUTO_TEST_CASE( direct_index_test )
{ try {
try {
const graphene::db::primary_index< account_index, 6 > small_chunkbits( db );
BOOST_FAIL( "Expected assertion failure!" );
} catch( const fc::assert_exception& expected ) {}
graphene::db::primary_index< account_index, 8 > my_accounts( db );
const auto& direct = my_accounts.get_secondary_index<graphene::db::direct_index< account_object, 8 >>();
BOOST_CHECK_EQUAL( 0, my_accounts.indices().size() );
BOOST_CHECK( nullptr == direct.find( account_id_type( 1 ) ) );
// BOOST_CHECK_THROW( direct.find( asset_id_type( 1 ) ), fc::assert_exception ); // compile-time error
BOOST_CHECK_THROW( direct.find( object_id_type( asset_id_type( 1 ) ) ), fc::assert_exception );
BOOST_CHECK_THROW( direct.get( account_id_type( 1 ) ), fc::assert_exception );
account_object test_account;
test_account.id = account_id_type(1);
test_account.name = "account1";
my_accounts.load( fc::raw::pack( test_account ) );
BOOST_CHECK_EQUAL( 1, my_accounts.indices().size() );
BOOST_CHECK( nullptr == direct.find( account_id_type( 0 ) ) );
BOOST_CHECK( nullptr == direct.find( account_id_type( 2 ) ) );
BOOST_CHECK( nullptr != direct.find( account_id_type( 1 ) ) );
BOOST_CHECK_EQUAL( test_account.name, direct.get( test_account.id ).name );
// The following assumes that MAX_HOLE = 100
test_account.id = account_id_type(102);
test_account.name = "account102";
// highest insert was 1, direct.next is 2 => 102 is highest allowed instance
my_accounts.load( fc::raw::pack( test_account ) );
BOOST_CHECK_EQUAL( test_account.name, direct.get( test_account.id ).name );
// direct.next is now 103, but index sequence counter is 0
my_accounts.create( [] ( object& o ) {
account_object& acct = dynamic_cast< account_object& >( o );
BOOST_CHECK_EQUAL( 0, acct.id.instance() );
acct.name = "account0";
} );
test_account.id = account_id_type(50);
test_account.name = "account50";
my_accounts.load( fc::raw::pack( test_account ) );
// can handle nested modification
my_accounts.modify( direct.get( account_id_type(0) ), [&direct,&my_accounts] ( object& outer ) {
account_object& _outer = dynamic_cast< account_object& >( outer );
my_accounts.modify( direct.get( account_id_type(50) ), [] ( object& inner ) {
account_object& _inner = dynamic_cast< account_object& >( inner );
_inner.referrer = account_id_type(102);
});
_outer.options.voting_account = GRAPHENE_PROXY_TO_SELF_ACCOUNT;
});
// direct.next is still 103, so 204 is not allowed
test_account.id = account_id_type(204);
test_account.name = "account204";
GRAPHENE_REQUIRE_THROW( my_accounts.load( fc::raw::pack( test_account ) ), fc::assert_exception );
// This is actually undefined behaviour. The object has been inserted into
// the primary index, but the secondary has refused to insert it!
BOOST_CHECK_EQUAL( 5, my_accounts.indices().size() );
uint32_t count = 0;
for( uint32_t i = 0; i < 250; i++ )
{
const account_object* aptr = dynamic_cast< const account_object* >( my_accounts.find( account_id_type( i ) ) );
if( aptr )
{
count++;
BOOST_CHECK( aptr->id.instance() == 0 || aptr->id.instance() == 1
|| aptr->id.instance() == 50 || aptr->id.instance() == 102 );
BOOST_CHECK_EQUAL( i, aptr->id.instance() );
BOOST_CHECK_EQUAL( "account" + std::to_string( i ), aptr->name );
}
}
BOOST_CHECK_EQUAL( count, my_accounts.indices().size() - 1 );
GRAPHENE_REQUIRE_THROW( my_accounts.modify( direct.get( account_id_type( 1 ) ), [] ( object& acct ) {
acct.id = account_id_type(2);
}), fc::assert_exception );
// This is actually undefined behaviour. The object has been modified, but
// but the secondary has not updated its representation
} FC_LOG_AND_RETHROW() }
BOOST_AUTO_TEST_SUITE_END()