Wrap exceptions thrown during dividend payouts to add extra info
for debugging
This commit is contained in:
parent
5b771c3a28
commit
ed9cdd96ec
1 changed files with 118 additions and 114 deletions
|
|
@ -737,7 +737,7 @@ void schedule_pending_dividend_balances(database& db,
|
|||
const vesting_balance_index& vesting_index,
|
||||
const total_distributed_dividend_balance_object_index& distributed_dividend_balance_index,
|
||||
const pending_dividend_payout_balance_for_holder_object_index& pending_payout_balance_index)
|
||||
{
|
||||
{ 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 current_distribution_account_balance_range =
|
||||
|
|
@ -1059,10 +1059,10 @@ void schedule_pending_dividend_balances(database& db,
|
|||
dividend_data_obj.last_distribution_time = current_head_block_time;
|
||||
});
|
||||
|
||||
}
|
||||
} FC_CAPTURE_AND_RETHROW() }
|
||||
|
||||
void process_dividend_assets(database& db)
|
||||
{
|
||||
{ try {
|
||||
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>();
|
||||
|
|
@ -1084,143 +1084,147 @@ void process_dividend_assets(database& db)
|
|||
if (dividend_data.options.next_payout_time &&
|
||||
db.head_block_time() >= *dividend_data.options.next_payout_time)
|
||||
{
|
||||
dlog("Dividend payout time has arrived for asset ${holder_asset}",
|
||||
("holder_asset", dividend_holder_asset_obj.symbol));
|
||||
try
|
||||
{
|
||||
dlog("Dividend payout time has arrived for asset ${holder_asset}",
|
||||
("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)));
|
||||
// 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)));
|
||||
#endif
|
||||
|
||||
// when we do the payouts, we first increase the balances in all of the receiving accounts
|
||||
// and use this map to keep track of the total amount of each asset paid out.
|
||||
// Afterwards, we decrease the distribution account's balance by the total amount paid out,
|
||||
// and modify the distributed_balances accordingly
|
||||
std::map<asset_id_type, share_type> amounts_paid_out_by_asset;
|
||||
// when we do the payouts, we first increase the balances in all of the receiving accounts
|
||||
// and use this map to keep track of the total amount of each asset paid out.
|
||||
// Afterwards, we decrease the distribution account's balance by the total amount paid out,
|
||||
// and modify the distributed_balances accordingly
|
||||
std::map<asset_id_type, share_type> amounts_paid_out_by_asset;
|
||||
|
||||
auto pending_payouts_range =
|
||||
pending_payout_balance_index.indices().get<by_dividend_account_payout>().equal_range(boost::make_tuple(dividend_holder_asset_obj.id));
|
||||
// the pending_payouts_range is all payouts for this dividend asset, sorted by the holder's account
|
||||
// we iterate in this order so we can build up a list of payouts for each account to put in the
|
||||
// virtual op
|
||||
flat_set<asset> payouts_for_this_holder;
|
||||
fc::optional<account_id_type> last_holder_account_id;
|
||||
auto pending_payouts_range =
|
||||
pending_payout_balance_index.indices().get<by_dividend_account_payout>().equal_range(boost::make_tuple(dividend_holder_asset_obj.id));
|
||||
// the pending_payouts_range is all payouts for this dividend asset, sorted by the holder's account
|
||||
// we iterate in this order so we can build up a list of payouts for each account to put in the
|
||||
// virtual op
|
||||
flat_set<asset> payouts_for_this_holder;
|
||||
fc::optional<account_id_type> last_holder_account_id;
|
||||
|
||||
// cache the assets the distribution account is approved to send, we will be asking
|
||||
// for these often
|
||||
flat_map<asset_id_type, bool> approved_assets; // assets that the dividend distribution account is authorized to send/receive
|
||||
auto is_asset_approved_for_distribution_account = [&](const asset_id_type& asset_id) {
|
||||
auto approved_assets_iter = approved_assets.find(asset_id);
|
||||
if (approved_assets_iter != approved_assets.end())
|
||||
return approved_assets_iter->second;
|
||||
bool is_approved = is_authorized_asset(db, dividend_distribution_account_object,
|
||||
asset_id(db));
|
||||
approved_assets[asset_id] = is_approved;
|
||||
return is_approved;
|
||||
};
|
||||
// cache the assets the distribution account is approved to send, we will be asking
|
||||
// for these often
|
||||
flat_map<asset_id_type, bool> approved_assets; // assets that the dividend distribution account is authorized to send/receive
|
||||
auto is_asset_approved_for_distribution_account = [&](const asset_id_type& asset_id) {
|
||||
auto approved_assets_iter = approved_assets.find(asset_id);
|
||||
if (approved_assets_iter != approved_assets.end())
|
||||
return approved_assets_iter->second;
|
||||
bool is_approved = is_authorized_asset(db, dividend_distribution_account_object,
|
||||
asset_id(db));
|
||||
approved_assets[asset_id] = is_approved;
|
||||
return is_approved;
|
||||
};
|
||||
|
||||
for (auto pending_balance_object_iter = pending_payouts_range.first; pending_balance_object_iter != pending_payouts_range.second; )
|
||||
{
|
||||
const pending_dividend_payout_balance_for_holder_object& pending_balance_object = *pending_balance_object_iter;
|
||||
for (auto pending_balance_object_iter = pending_payouts_range.first; pending_balance_object_iter != pending_payouts_range.second; )
|
||||
{
|
||||
const pending_dividend_payout_balance_for_holder_object& pending_balance_object = *pending_balance_object_iter;
|
||||
|
||||
if (last_holder_account_id && *last_holder_account_id != pending_balance_object.owner && payouts_for_this_holder.size())
|
||||
if (last_holder_account_id && *last_holder_account_id != pending_balance_object.owner && payouts_for_this_holder.size())
|
||||
{
|
||||
// we've moved on to a new account, generate the dividend payment virtual op for the previous one
|
||||
db.push_applied_operation(asset_dividend_distribution_operation(dividend_holder_asset_obj.id,
|
||||
*last_holder_account_id,
|
||||
payouts_for_this_holder));
|
||||
dlog("Just pushed virtual op for payout to ${account}", ("account", (*last_holder_account_id)(db).name));
|
||||
payouts_for_this_holder.clear();
|
||||
last_holder_account_id.reset();
|
||||
}
|
||||
|
||||
|
||||
if (pending_balance_object.pending_balance.value &&
|
||||
is_authorized_asset(db, pending_balance_object.owner(db), pending_balance_object.dividend_payout_asset_type(db)) &&
|
||||
is_asset_approved_for_distribution_account(pending_balance_object.dividend_payout_asset_type))
|
||||
{
|
||||
dlog("Processing payout of ${asset} to account ${account}",
|
||||
("asset", asset(pending_balance_object.pending_balance, pending_balance_object.dividend_payout_asset_type))
|
||||
("account", pending_balance_object.owner(db).name));
|
||||
|
||||
db.adjust_balance(pending_balance_object.owner,
|
||||
asset(pending_balance_object.pending_balance,
|
||||
pending_balance_object.dividend_payout_asset_type));
|
||||
payouts_for_this_holder.insert(asset(pending_balance_object.pending_balance,
|
||||
pending_balance_object.dividend_payout_asset_type));
|
||||
last_holder_account_id = pending_balance_object.owner;
|
||||
amounts_paid_out_by_asset[pending_balance_object.dividend_payout_asset_type] += pending_balance_object.pending_balance;
|
||||
|
||||
db.modify(pending_balance_object, [&]( pending_dividend_payout_balance_for_holder_object& pending_balance ){
|
||||
pending_balance.pending_balance = 0;
|
||||
});
|
||||
}
|
||||
|
||||
++pending_balance_object_iter;
|
||||
}
|
||||
// we will always be left with the last holder's data, generate the virtual op for it now.
|
||||
if (last_holder_account_id && payouts_for_this_holder.size())
|
||||
{
|
||||
// we've moved on to a new account, generate the dividend payment virtual op for the previous one
|
||||
db.push_applied_operation(asset_dividend_distribution_operation(dividend_holder_asset_obj.id,
|
||||
*last_holder_account_id,
|
||||
payouts_for_this_holder));
|
||||
dlog("Just pushed virtual op for payout to ${account}", ("account", (*last_holder_account_id)(db).name));
|
||||
payouts_for_this_holder.clear();
|
||||
last_holder_account_id.reset();
|
||||
}
|
||||
|
||||
// now debit the total amount of dividends paid out from the distribution account
|
||||
// and reduce the distributed_balances accordingly
|
||||
|
||||
if (pending_balance_object.pending_balance.value &&
|
||||
is_authorized_asset(db, pending_balance_object.owner(db), pending_balance_object.dividend_payout_asset_type(db)) &&
|
||||
is_asset_approved_for_distribution_account(pending_balance_object.dividend_payout_asset_type))
|
||||
for (const auto& value : amounts_paid_out_by_asset)
|
||||
{
|
||||
dlog("Processing payout of ${asset} to account ${account}",
|
||||
("asset", asset(pending_balance_object.pending_balance, pending_balance_object.dividend_payout_asset_type))
|
||||
("account", pending_balance_object.owner(db).name));
|
||||
const asset_id_type& asset_paid_out = value.first;
|
||||
const share_type& amount_paid_out = value.second;
|
||||
|
||||
db.adjust_balance(pending_balance_object.owner,
|
||||
asset(pending_balance_object.pending_balance,
|
||||
pending_balance_object.dividend_payout_asset_type));
|
||||
payouts_for_this_holder.insert(asset(pending_balance_object.pending_balance,
|
||||
pending_balance_object.dividend_payout_asset_type));
|
||||
last_holder_account_id = pending_balance_object.owner;
|
||||
amounts_paid_out_by_asset[pending_balance_object.dividend_payout_asset_type] += pending_balance_object.pending_balance;
|
||||
db.adjust_balance(dividend_data.dividend_distribution_account,
|
||||
asset(-amount_paid_out,
|
||||
asset_paid_out));
|
||||
auto distributed_balance_iter =
|
||||
distributed_dividend_balance_index.indices().get<by_dividend_payout_asset>().find(boost::make_tuple(dividend_holder_asset_obj.id,
|
||||
asset_paid_out));
|
||||
assert(distributed_balance_iter != distributed_dividend_balance_index.indices().get<by_dividend_payout_asset>().end());
|
||||
if (distributed_balance_iter != distributed_dividend_balance_index.indices().get<by_dividend_payout_asset>().end())
|
||||
db.modify(*distributed_balance_iter, [&]( total_distributed_dividend_balance_object& obj ){
|
||||
obj.balance_at_last_maintenance_interval -= amount_paid_out; // now they've been paid out, reset to zero
|
||||
});
|
||||
|
||||
db.modify(pending_balance_object, [&]( pending_dividend_payout_balance_for_holder_object& pending_balance ){
|
||||
pending_balance.pending_balance = 0;
|
||||
});
|
||||
}
|
||||
|
||||
++pending_balance_object_iter;
|
||||
}
|
||||
// we will always be left with the last holder's data, generate the virtual op for it now.
|
||||
if (last_holder_account_id && payouts_for_this_holder.size())
|
||||
{
|
||||
// we've moved on to a new account, generate the dividend payment virtual op for the previous one
|
||||
db.push_applied_operation(asset_dividend_distribution_operation(dividend_holder_asset_obj.id,
|
||||
*last_holder_account_id,
|
||||
payouts_for_this_holder));
|
||||
dlog("Just pushed virtual op for payout to ${account}", ("account", (*last_holder_account_id)(db).name));
|
||||
}
|
||||
// now schedule the next payout time
|
||||
db.modify(dividend_data, [current_head_block_time](asset_dividend_data_object& dividend_data_obj) {
|
||||
dividend_data_obj.last_scheduled_payout_time = dividend_data_obj.options.next_payout_time;
|
||||
dividend_data_obj.last_payout_time = current_head_block_time;
|
||||
fc::optional<fc::time_point_sec> next_payout_time;
|
||||
if (dividend_data_obj.options.payout_interval)
|
||||
{
|
||||
// if there was a previous payout, make our next payment one interval
|
||||
uint32_t current_time_sec = current_head_block_time.sec_since_epoch();
|
||||
fc::time_point_sec reference_time = *dividend_data_obj.last_scheduled_payout_time;
|
||||
uint32_t next_possible_time_sec = dividend_data_obj.last_scheduled_payout_time->sec_since_epoch();
|
||||
do
|
||||
next_possible_time_sec += *dividend_data_obj.options.payout_interval;
|
||||
while (next_possible_time_sec <= current_time_sec);
|
||||
|
||||
// now debit the total amount of dividends paid out from the distribution account
|
||||
// and reduce the distributed_balances accordingly
|
||||
|
||||
for (const auto& value : amounts_paid_out_by_asset)
|
||||
{
|
||||
const asset_id_type& asset_paid_out = value.first;
|
||||
const share_type& amount_paid_out = value.second;
|
||||
|
||||
db.adjust_balance(dividend_data.dividend_distribution_account,
|
||||
asset(-amount_paid_out,
|
||||
asset_paid_out));
|
||||
auto distributed_balance_iter =
|
||||
distributed_dividend_balance_index.indices().get<by_dividend_payout_asset>().find(boost::make_tuple(dividend_holder_asset_obj.id,
|
||||
asset_paid_out));
|
||||
assert(distributed_balance_iter != distributed_dividend_balance_index.indices().get<by_dividend_payout_asset>().end());
|
||||
if (distributed_balance_iter != distributed_dividend_balance_index.indices().get<by_dividend_payout_asset>().end())
|
||||
db.modify(*distributed_balance_iter, [&]( total_distributed_dividend_balance_object& obj ){
|
||||
obj.balance_at_last_maintenance_interval -= amount_paid_out; // now they've been paid out, reset to zero
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
// now schedule the next payout time
|
||||
db.modify(dividend_data, [current_head_block_time](asset_dividend_data_object& dividend_data_obj) {
|
||||
dividend_data_obj.last_scheduled_payout_time = dividend_data_obj.options.next_payout_time;
|
||||
dividend_data_obj.last_payout_time = current_head_block_time;
|
||||
fc::optional<fc::time_point_sec> next_payout_time;
|
||||
if (dividend_data_obj.options.payout_interval)
|
||||
{
|
||||
// if there was a previous payout, make our next payment one interval
|
||||
uint32_t current_time_sec = current_head_block_time.sec_since_epoch();
|
||||
fc::time_point_sec reference_time = *dividend_data_obj.last_scheduled_payout_time;
|
||||
uint32_t next_possible_time_sec = dividend_data_obj.last_scheduled_payout_time->sec_since_epoch();
|
||||
do
|
||||
next_possible_time_sec += *dividend_data_obj.options.payout_interval;
|
||||
while (next_possible_time_sec <= current_time_sec);
|
||||
|
||||
next_payout_time = next_possible_time_sec;
|
||||
}
|
||||
dividend_data_obj.options.next_payout_time = next_payout_time;
|
||||
idump((dividend_data_obj.last_scheduled_payout_time)
|
||||
(dividend_data_obj.last_payout_time)
|
||||
(dividend_data_obj.options.next_payout_time));
|
||||
});
|
||||
next_payout_time = next_possible_time_sec;
|
||||
}
|
||||
dividend_data_obj.options.next_payout_time = next_payout_time;
|
||||
idump((dividend_data_obj.last_scheduled_payout_time)
|
||||
(dividend_data_obj.last_payout_time)
|
||||
(dividend_data_obj.options.next_payout_time));
|
||||
});
|
||||
}
|
||||
FC_RETHROW_EXCEPTIONS(error, "Error while paying out dividends for holder asset ${holder_asset}", ("holder_asset", dividend_holder_asset_obj.symbol))
|
||||
}
|
||||
}
|
||||
}
|
||||
} FC_CAPTURE_AND_RETHROW() }
|
||||
|
||||
void database::perform_chain_maintenance(const signed_block& next_block, const global_property_object& global_props)
|
||||
{
|
||||
{ try {
|
||||
const auto& gpo = get_global_properties();
|
||||
|
||||
distribute_fba_balances(*this);
|
||||
|
|
@ -1416,6 +1420,6 @@ void database::perform_chain_maintenance(const signed_block& next_block, const g
|
|||
// process_budget needs to run at the bottom because
|
||||
// it needs to know the next_maintenance_time
|
||||
process_budget();
|
||||
}
|
||||
} FC_CAPTURE_AND_RETHROW() }
|
||||
|
||||
} }
|
||||
|
|
|
|||
Loading…
Reference in a new issue