From ed9cdd96ecde35e32e82f4098809ec2fd552c655 Mon Sep 17 00:00:00 2001 From: Eric Frias Date: Wed, 9 May 2018 19:27:49 -0400 Subject: [PATCH] Wrap exceptions thrown during dividend payouts to add extra info for debugging --- libraries/chain/db_maint.cpp | 232 ++++++++++++++++++----------------- 1 file changed, 118 insertions(+), 114 deletions(-) diff --git a/libraries/chain/db_maint.cpp b/libraries/chain/db_maint.cpp index 39fccf9c..26fcc772 100644 --- a/libraries/chain/db_maint.cpp +++ b/libraries/chain/db_maint.cpp @@ -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(); @@ -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().indices().get(); - 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().indices().get(); + 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 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 amounts_paid_out_by_asset; - auto pending_payouts_range = - pending_payout_balance_index.indices().get().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 payouts_for_this_holder; - fc::optional last_holder_account_id; + auto pending_payouts_range = + pending_payout_balance_index.indices().get().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 payouts_for_this_holder; + fc::optional last_holder_account_id; - // cache the assets the distribution account is approved to send, we will be asking - // for these often - flat_map 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 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().find(boost::make_tuple(dividend_holder_asset_obj.id, + asset_paid_out)); + assert(distributed_balance_iter != distributed_dividend_balance_index.indices().get().end()); + if (distributed_balance_iter != distributed_dividend_balance_index.indices().get().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 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().find(boost::make_tuple(dividend_holder_asset_obj.id, - asset_paid_out)); - assert(distributed_balance_iter != distributed_dividend_balance_index.indices().get().end()); - if (distributed_balance_iter != distributed_dividend_balance_index.indices().get().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 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() } } }