Merge branch 'develop' into GRPH-50-network_broadcast_api-fix-v2
This commit is contained in:
commit
97f9875918
12 changed files with 450 additions and 85 deletions
|
|
@ -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
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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) ) }
|
||||
|
|
|
|||
|
|
@ -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 >> >();
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
|
|||
Loading…
Reference in a new issue