From b66e1ea1e8a33dc461fe8c2935de1b388829f4c8 Mon Sep 17 00:00:00 2001 From: abitmore Date: Sat, 23 Jun 2018 10:29:46 -0400 Subject: [PATCH 1/3] Improve account maintenance performance #803 --- libraries/chain/account_evaluator.cpp | 5 +- libraries/chain/account_object.cpp | 2 + libraries/chain/db_balance.cpp | 6 ++ libraries/chain/db_debug.cpp | 2 +- libraries/chain/db_getter.cpp | 7 ++ libraries/chain/db_init.cpp | 54 ++++++++++----- libraries/chain/db_maint.cpp | 65 ++++++++++++------- .../include/graphene/chain/account_object.hpp | 52 ++++++++++++++- .../chain/include/graphene/chain/config.hpp | 2 +- .../chain/include/graphene/chain/database.hpp | 5 +- tests/common/database_fixture.cpp | 2 +- 11 files changed, 155 insertions(+), 47 deletions(-) diff --git a/libraries/chain/account_evaluator.cpp b/libraries/chain/account_evaluator.cpp index 843343398d..1e55e3696d 100644 --- a/libraries/chain/account_evaluator.cpp +++ b/libraries/chain/account_evaluator.cpp @@ -202,7 +202,10 @@ object_id_type account_create_evaluator::do_apply( const account_create_operatio obj.owner = o.owner; obj.active = o.active; obj.options = o.options; - obj.statistics = db().create([&](account_statistics_object& s){s.owner = obj.id;}).id; + obj.statistics = db().create([&obj](account_statistics_object& s){ + s.owner = obj.id; + s.name = obj.name; + }).id; if( o.extensions.value.owner_special_authority.valid() ) obj.owner_special_authority = *(o.extensions.value.owner_special_authority); diff --git a/libraries/chain/account_object.cpp b/libraries/chain/account_object.cpp index 90d97692a5..efade46fe1 100644 --- a/libraries/chain/account_object.cpp +++ b/libraries/chain/account_object.cpp @@ -46,6 +46,8 @@ void account_balance_object::adjust_balance(const asset& delta) { assert(delta.asset_id == asset_type); balance += delta.amount; + if( asset_type == asset_id_type() ) // CORE asset + maintenance_flag = true; } void account_statistics_object::process_fees(const account_object& a, database& d) const diff --git a/libraries/chain/db_balance.cpp b/libraries/chain/db_balance.cpp index a70f077bb6..6b4bc800d9 100644 --- a/libraries/chain/db_balance.cpp +++ b/libraries/chain/db_balance.cpp @@ -67,6 +67,8 @@ void database::adjust_balance(account_id_type account, asset delta ) b.owner = account; b.asset_type = delta.asset_id; b.balance = delta.amount.value; + if( b.asset_type == asset_id_type() ) // CORE asset + b.maintenance_flag = true; }); } else { if( delta.amount < 0 ) @@ -158,6 +160,10 @@ void database::deposit_cashback(const account_object& acct, share_type amount, b { _acct.cashback_vb = *new_vbid; } ); + modify( acct.statistics( *this ), [&]( account_statistics_object& aso ) + { + aso.has_cashback_vb = true; + } ); } return; diff --git a/libraries/chain/db_debug.cpp b/libraries/chain/db_debug.cpp index a3668752a7..e6ab40fb70 100644 --- a/libraries/chain/db_debug.cpp +++ b/libraries/chain/db_debug.cpp @@ -43,7 +43,7 @@ void database::debug_dump() const asset_dynamic_data_object& core_asset_data = db.get_core_asset().dynamic_asset_data_id(db); const auto& balance_index = db.get_index_type().indices(); - const simple_index& statistics_index = db.get_index_type>(); + const auto& statistics_index = db.get_index_type().indices(); const auto& bids = db.get_index_type().indices(); const auto& settle_index = db.get_index_type().indices(); map total_balances; diff --git a/libraries/chain/db_getter.cpp b/libraries/chain/db_getter.cpp index 4af2df3e10..50113ad69a 100644 --- a/libraries/chain/db_getter.cpp +++ b/libraries/chain/db_getter.cpp @@ -97,5 +97,12 @@ uint32_t database::last_non_undoable_block_num() const return head_block_num() - _undo_db.size(); } +const account_statistics_object& database::get_account_stats_by_owner( account_id_type owner )const +{ + auto& idx = get_index_type().indices().get(); + auto itr = idx.find( owner ); + FC_ASSERT( itr != idx.end(), "Can not find account statistics object for owner ${a}", ("a",owner) ); + return *itr; +} } } diff --git a/libraries/chain/db_init.cpp b/libraries/chain/db_init.cpp index 2d37ac7436..065220607e 100644 --- a/libraries/chain/db_init.cpp +++ b/libraries/chain/db_init.cpp @@ -209,7 +209,7 @@ void database::initialize_indexes() add_index< primary_index >(); add_index< primary_index> >(); add_index< primary_index> >(); - add_index< primary_index> >(); + add_index< primary_index >(); add_index< primary_index> >(); add_index< primary_index> >(); add_index< primary_index > >(); @@ -258,12 +258,19 @@ void database::init_genesis(const genesis_state_type& genesis_state) n.owner.weight_threshold = 1; n.active.weight_threshold = 1; n.name = "committee-account"; - n.statistics = create( [&](account_statistics_object& s){ s.owner = n.id; }).id; + n.statistics = create( [&n](account_statistics_object& s){ + s.owner = n.id; + s.name = n.name; + s.core_in_balance = GRAPHENE_MAX_SHARE_SUPPLY; + }).id; }); FC_ASSERT(committee_account.get_id() == GRAPHENE_COMMITTEE_ACCOUNT); FC_ASSERT(create([this](account_object& a) { a.name = "witness-account"; - a.statistics = create([&](account_statistics_object& s){s.owner = a.id;}).id; + a.statistics = create([&a](account_statistics_object& s){ + s.owner = a.id; + s.name = a.name; + }).id; a.owner.weight_threshold = 1; a.active.weight_threshold = 1; a.registrar = a.lifetime_referrer = a.referrer = GRAPHENE_WITNESS_ACCOUNT; @@ -273,7 +280,10 @@ void database::init_genesis(const genesis_state_type& genesis_state) }).get_id() == GRAPHENE_WITNESS_ACCOUNT); FC_ASSERT(create([this](account_object& a) { a.name = "relaxed-committee-account"; - a.statistics = create([&](account_statistics_object& s){s.owner = a.id;}).id; + a.statistics = create([&a](account_statistics_object& s){ + s.owner = a.id; + s.name = a.name; + }).id; a.owner.weight_threshold = 1; a.active.weight_threshold = 1; a.registrar = a.lifetime_referrer = a.referrer = GRAPHENE_RELAXED_COMMITTEE_ACCOUNT; @@ -283,7 +293,10 @@ void database::init_genesis(const genesis_state_type& genesis_state) }).get_id() == GRAPHENE_RELAXED_COMMITTEE_ACCOUNT); FC_ASSERT(create([this](account_object& a) { a.name = "null-account"; - a.statistics = create([&](account_statistics_object& s){s.owner = a.id;}).id; + a.statistics = create([&a](account_statistics_object& s){ + s.owner = a.id; + s.name = a.name; + }).id; a.owner.weight_threshold = 1; a.active.weight_threshold = 1; a.registrar = a.lifetime_referrer = a.referrer = GRAPHENE_NULL_ACCOUNT; @@ -293,7 +306,10 @@ void database::init_genesis(const genesis_state_type& genesis_state) }).get_id() == GRAPHENE_NULL_ACCOUNT); FC_ASSERT(create([this](account_object& a) { a.name = "temp-account"; - a.statistics = create([&](account_statistics_object& s){s.owner = a.id;}).id; + a.statistics = create([&a](account_statistics_object& s){ + s.owner = a.id; + s.name = a.name; + }).id; a.owner.weight_threshold = 0; a.active.weight_threshold = 0; a.registrar = a.lifetime_referrer = a.referrer = GRAPHENE_TEMP_ACCOUNT; @@ -303,7 +319,10 @@ void database::init_genesis(const genesis_state_type& genesis_state) }).get_id() == GRAPHENE_TEMP_ACCOUNT); FC_ASSERT(create([this](account_object& a) { a.name = "proxy-to-self"; - a.statistics = create([&](account_statistics_object& s){s.owner = a.id;}).id; + a.statistics = create([&a](account_statistics_object& s){ + s.owner = a.id; + s.name = a.name; + }).id; a.owner.weight_threshold = 1; a.active.weight_threshold = 1; a.registrar = a.lifetime_referrer = a.referrer = GRAPHENE_NULL_ACCOUNT; @@ -318,9 +337,12 @@ void database::init_genesis(const genesis_state_type& genesis_state) uint64_t id = get_index().get_next_id().instance(); if( id >= genesis_state.immutable_parameters.num_special_accounts ) break; - const account_object& acct = create([&](account_object& a) { + const account_object& acct = create([this,id](account_object& a) { a.name = "special-account-" + std::to_string(id); - a.statistics = create([&](account_statistics_object& s){s.owner = a.id;}).id; + a.statistics = create([&a](account_statistics_object& s){ + s.owner = a.id; + s.name = a.name; + }).id; a.owner.weight_threshold = 1; a.active.weight_threshold = 1; a.registrar = a.lifetime_referrer = a.referrer = account_id_type(id); @@ -334,11 +356,11 @@ void database::init_genesis(const genesis_state_type& genesis_state) // Create core asset const asset_dynamic_data_object& dyn_asset = - create([&](asset_dynamic_data_object& a) { + create([](asset_dynamic_data_object& a) { a.current_supply = GRAPHENE_MAX_SHARE_SUPPLY; }); const asset_object& core_asset = - create( [&]( asset_object& a ) { + create( [&genesis_state,&dyn_asset]( asset_object& a ) { a.symbol = GRAPHENE_SYMBOL; a.options.max_supply = genesis_state.max_core_supply; a.precision = GRAPHENE_BLOCKCHAIN_PRECISION_DIGITS; @@ -360,10 +382,10 @@ void database::init_genesis(const genesis_state_type& genesis_state) if( id >= genesis_state.immutable_parameters.num_special_assets ) break; const asset_dynamic_data_object& dyn_asset = - create([&](asset_dynamic_data_object& a) { + create([](asset_dynamic_data_object& a) { a.current_supply = 0; }); - const asset_object& asset_obj = create( [&]( asset_object& a ) { + const asset_object& asset_obj = create( [id,&dyn_asset]( asset_object& a ) { a.symbol = "SPECIAL" + std::to_string( id ); a.options.max_supply = 0; a.precision = GRAPHENE_BLOCKCHAIN_PRECISION_DIGITS; @@ -481,9 +503,9 @@ void database::init_genesis(const genesis_state_type& genesis_state) cop.active = cop.owner; account_id_type owner_account_id = apply_operation(genesis_eval_state, cop).get(); - modify( owner_account_id(*this).statistics(*this), [&]( account_statistics_object& o ) { - o.total_core_in_orders = collateral_rec.collateral; - }); + modify( owner_account_id(*this).statistics(*this), [&collateral_rec]( account_statistics_object& o ) { + o.total_core_in_orders = collateral_rec.collateral; + }); create([&](call_order_object& c) { c.borrower = owner_account_id; diff --git a/libraries/chain/db_maint.cpp b/libraries/chain/db_maint.cpp index 2d93ce4686..ea5f206716 100644 --- a/libraries/chain/db_maint.cpp +++ b/libraries/chain/db_maint.cpp @@ -72,12 +72,45 @@ vector> database::sort return refs; } -template -void database::perform_account_maintenance(std::tuple helpers) +template +void database::perform_account_maintenance(Type tally_helper) { - const auto& idx = get_index_type().indices().get(); - for( const account_object& a : idx ) - detail::for_each(helpers, a, detail::gen_seq()); + const auto& bal_idx = get_index_type< account_balance_index >().indices().get< by_maintenance_flag >(); + if( bal_idx.begin() != bal_idx.end() ) + { + auto bal_itr = bal_idx.rbegin(); + while( bal_itr->maintenance_flag ) + { + const account_balance_object& bal_obj = *bal_itr; + + modify( get_account_stats_by_owner( bal_obj.owner ), [&bal_obj](account_statistics_object& aso) { + aso.core_in_balance = bal_obj.balance; + }); + + modify( bal_obj, []( account_balance_object& abo ) { + abo.maintenance_flag = false; + }); + + bal_itr = bal_idx.rbegin(); + } + } + + const auto& stats_idx = get_index_type< account_stats_index >().indices().get< by_maintenance_seq >(); + auto stats_itr = stats_idx.lower_bound( true ); + + while( stats_itr != stats_idx.end() ) + { + const account_statistics_object& acc_stat = *stats_itr; + const account_object& acc_obj = acc_stat.owner( *this ); + ++stats_itr; + + if( acc_stat.has_some_core() ) + tally_helper( acc_obj, acc_stat ); + + if( acc_stat.has_pending_fees() ) + acc_stat.process_fees( acc_obj, *this ); + } + } /// @brief A visitor for @ref worker_type which calls pay_worker on the worker within @@ -1014,7 +1047,8 @@ void database::perform_chain_maintenance(const signed_block& next_block, const g d._total_voting_stake = 0; } - void operator()(const account_object& stake_account) { + void operator()( const account_object& stake_account, const account_statistics_object& stats ) + { if( props.parameters.count_non_member_votes || stake_account.is_member(d.head_block_time()) ) { // There may be a difference between the account whose stake is voting and the one specifying opinions. @@ -1025,10 +1059,9 @@ void database::perform_chain_maintenance(const signed_block& next_block, const g GRAPHENE_PROXY_TO_SELF_ACCOUNT)? stake_account : d.get(stake_account.options.voting_account); - const auto& stats = stake_account.statistics(d); uint64_t voting_stake = stats.total_core_in_orders.value + (stake_account.cashback_vb.valid() ? (*stake_account.cashback_vb)(d).balance.amount.value: 0) - + d.get_balance(stake_account.get_id(), asset_id_type()).amount.value; + + stats.core_in_balance.value; for( vote_id_type id : opinion_account.options.votes ) { @@ -1065,22 +1098,8 @@ void database::perform_chain_maintenance(const signed_block& next_block, const g } } } tally_helper(*this, gpo); - struct process_fees_helper { - database& d; - const global_property_object& props; - - process_fees_helper(database& d, const global_property_object& gpo) - : d(d), props(gpo) {} - - void operator()(const account_object& a) { - a.statistics(d).process_fees(a, d); - } - } fee_helper(*this, gpo); - perform_account_maintenance(std::tie( - tally_helper, - fee_helper - )); + perform_account_maintenance( tally_helper ); struct clear_canary { clear_canary(vector& target): target(target){} diff --git a/libraries/chain/include/graphene/chain/account_object.hpp b/libraries/chain/include/graphene/chain/account_object.hpp index 522cb7bc9c..e57d9b6e6a 100644 --- a/libraries/chain/include/graphene/chain/account_object.hpp +++ b/libraries/chain/include/graphene/chain/account_object.hpp @@ -46,6 +46,8 @@ namespace graphene { namespace chain { account_id_type owner; + string name; ///< redundantly store account name here for better maintenance performance + /** * Keep the most recent operation as a root pointer to a linked list of the transaction history. */ @@ -62,6 +64,13 @@ namespace graphene { namespace chain { */ share_type total_core_in_orders; + share_type core_in_balance = 0; ///< redundantly store core balance here for better maintenance performance + + bool has_cashback_vb = false; ///< redundantly store this for better maintenance performance + + /// Whether this account owns some CORE asset + inline bool has_some_core() const { return total_core_in_orders > 0 || core_in_balance > 0 || has_cashback_vb; } + /** * Tracks the total fees paid by this account for the purpose of calculating bulk discounts. */ @@ -82,6 +91,12 @@ namespace graphene { namespace chain { */ share_type pending_vested_fees; + /// Whether this account has pending fees, no matter vested or not + inline bool has_pending_fees() const { return pending_fees > 0 || pending_vested_fees > 0; } + + /// Whether need to process this account during the maintenance interval + inline bool need_maintenance() const { return has_some_core() || has_pending_fees(); } + /// @brief Split up and pay out @ref pending_fees and @ref pending_vested_fees void process_fees(const account_object& a, database& d) const; @@ -107,6 +122,7 @@ namespace graphene { namespace chain { account_id_type owner; asset_id_type asset_type; share_type balance; + bool maintenance_flag = false; ///< Whether need to process this balance object in maintenance interval asset get_balance()const { return asset(balance, asset_type); } void adjust_balance(const asset& delta); @@ -314,6 +330,7 @@ namespace graphene { namespace chain { struct by_account_asset; struct by_asset_balance; + struct by_maintenance_flag; /** * @ingroup object_index */ @@ -321,6 +338,8 @@ namespace graphene { namespace chain { account_balance_object, indexed_by< ordered_unique< tag, member< object, object_id_type, &object::id > >, + ordered_non_unique< tag, + member< account_balance_object, bool, &account_balance_object::maintenance_flag > >, ordered_unique< tag, composite_key< account_balance_object, @@ -367,6 +386,33 @@ namespace graphene { namespace chain { */ typedef generic_index account_index; + struct by_owner; + struct by_maintenance_seq; + + /** + * @ingroup object_index + */ + typedef multi_index_container< + account_statistics_object, + indexed_by< + ordered_unique< tag, member< object, object_id_type, &object::id > >, + ordered_unique< tag, + member< account_statistics_object, account_id_type, &account_statistics_object::owner > >, + ordered_unique< tag, + composite_key< + account_statistics_object, + const_mem_fun, + member + > + > + > + > account_stats_multi_index_type; + + /** + * @ingroup object_index + */ + typedef generic_index account_stats_index; + }} FC_REFLECT_DERIVED( graphene::chain::account_object, @@ -383,14 +429,16 @@ FC_REFLECT_DERIVED( graphene::chain::account_object, FC_REFLECT_DERIVED( graphene::chain::account_balance_object, (graphene::db::object), - (owner)(asset_type)(balance) ) + (owner)(asset_type)(balance)(maintenance_flag) ) FC_REFLECT_DERIVED( graphene::chain::account_statistics_object, (graphene::chain::object), - (owner) + (owner)(name) (most_recent_op) (total_ops)(removed_ops) (total_core_in_orders) + (core_in_balance) + (has_cashback_vb) (lifetime_fees_paid) (pending_fees)(pending_vested_fees) ) diff --git a/libraries/chain/include/graphene/chain/config.hpp b/libraries/chain/include/graphene/chain/config.hpp index 5c3ccde273..253ed6ab33 100644 --- a/libraries/chain/include/graphene/chain/config.hpp +++ b/libraries/chain/include/graphene/chain/config.hpp @@ -121,7 +121,7 @@ #define GRAPHENE_RECENTLY_MISSED_COUNT_INCREMENT 4 #define GRAPHENE_RECENTLY_MISSED_COUNT_DECREMENT 3 -#define GRAPHENE_CURRENT_DB_VERSION "BTS2.15" +#define GRAPHENE_CURRENT_DB_VERSION "BTS2.16" #define GRAPHENE_IRREVERSIBLE_THRESHOLD (70 * GRAPHENE_1_PERCENT) diff --git a/libraries/chain/include/graphene/chain/database.hpp b/libraries/chain/include/graphene/chain/database.hpp index 464c2cf988..c6f1507906 100644 --- a/libraries/chain/include/graphene/chain/database.hpp +++ b/libraries/chain/include/graphene/chain/database.hpp @@ -257,6 +257,7 @@ namespace graphene { namespace chain { const dynamic_global_property_object& get_dynamic_global_properties()const; const node_property_object& get_node_properties()const; const fee_schedule& current_fee_schedule()const; + const account_statistics_object& get_account_stats_by_owner( account_id_type owner )const; time_point_sec head_block_time()const; uint32_t head_block_num()const; @@ -470,8 +471,8 @@ namespace graphene { namespace chain { void process_bids( const asset_bitasset_data_object& bad ); void process_bitassets(); - template - void perform_account_maintenance(std::tuple helpers); + template + void perform_account_maintenance( Type tally_helper ); ///@} ///@} diff --git a/tests/common/database_fixture.cpp b/tests/common/database_fixture.cpp index 9b82c79aee..52ec2591b3 100644 --- a/tests/common/database_fixture.cpp +++ b/tests/common/database_fixture.cpp @@ -204,7 +204,7 @@ void database_fixture::verify_asset_supplies( const database& db ) const asset_dynamic_data_object& core_asset_data = db.get_core_asset().dynamic_asset_data_id(db); BOOST_CHECK(core_asset_data.fee_pool == 0); - const simple_index& statistics_index = db.get_index_type>(); + const auto& statistics_index = db.get_index_type().indices(); const auto& balance_index = db.get_index_type().indices(); const auto& settle_index = db.get_index_type().indices(); const auto& bids = db.get_index_type().indices(); From 5e424577f5cf449c2d363605b32ea6508ee3dfe1 Mon Sep 17 00:00:00 2001 From: abitmore Date: Sat, 23 Jun 2018 14:17:51 -0400 Subject: [PATCH 2/3] Capture fewer variables for lamdba --- libraries/chain/db_balance.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libraries/chain/db_balance.cpp b/libraries/chain/db_balance.cpp index 6b4bc800d9..bbb33f3ac1 100644 --- a/libraries/chain/db_balance.cpp +++ b/libraries/chain/db_balance.cpp @@ -156,11 +156,11 @@ void database::deposit_cashback(const account_object& acct, share_type amount, b if( new_vbid.valid() ) { - modify( acct, [&]( account_object& _acct ) + modify( acct, [&new_vbid]( account_object& _acct ) { _acct.cashback_vb = *new_vbid; } ); - modify( acct.statistics( *this ), [&]( account_statistics_object& aso ) + modify( acct.statistics( *this ), []( account_statistics_object& aso ) { aso.has_cashback_vb = true; } ); From 8f08ec98b63cce86c6c98ad446df2c07c1889477 Mon Sep 17 00:00:00 2001 From: abitmore Date: Sat, 23 Jun 2018 14:23:29 -0400 Subject: [PATCH 3/3] Track is_voting in account stats, and code cleanup --- libraries/chain/account_evaluator.cpp | 45 +++++++++++++------ libraries/chain/db_maint.cpp | 2 +- .../include/graphene/chain/account_object.hpp | 12 +++-- .../graphene/chain/protocol/account.hpp | 6 +++ 4 files changed, 47 insertions(+), 18 deletions(-) diff --git a/libraries/chain/account_evaluator.cpp b/libraries/chain/account_evaluator.cpp index 1e55e3696d..cc199bcb87 100644 --- a/libraries/chain/account_evaluator.cpp +++ b/libraries/chain/account_evaluator.cpp @@ -188,12 +188,15 @@ object_id_type account_create_evaluator::do_apply( const account_create_operatio referrer_percent = GRAPHENE_100_PERCENT; } - const auto& new_acnt_object = db().create( [&]( account_object& obj ){ + const auto& global_properties = d.get_global_properties(); + + const auto& new_acnt_object = d.create( [&o,&d,&global_properties,referrer_percent]( account_object& obj ) + { obj.registrar = o.registrar; obj.referrer = o.referrer; - obj.lifetime_referrer = o.referrer(db()).lifetime_referrer; + obj.lifetime_referrer = o.referrer(d).lifetime_referrer; - auto& params = db().get_global_properties().parameters; + const auto& params = global_properties.parameters; obj.network_fee_percentage = params.network_percent_of_fee; obj.lifetime_referrer_fee_percentage = params.lifetime_referrer_percent_of_fee; obj.referrer_rewards_percentage = referrer_percent; @@ -202,9 +205,10 @@ object_id_type account_create_evaluator::do_apply( const account_create_operatio obj.owner = o.owner; obj.active = o.active; obj.options = o.options; - obj.statistics = db().create([&obj](account_statistics_object& s){ + obj.statistics = d.create([&obj](account_statistics_object& s){ s.owner = obj.id; s.name = obj.name; + s.is_voting = obj.options.is_voting(); }).id; if( o.extensions.value.owner_special_authority.valid() ) @@ -229,17 +233,18 @@ object_id_type account_create_evaluator::do_apply( const account_create_operatio } */ - const auto& dynamic_properties = db().get_dynamic_global_properties(); - db().modify(dynamic_properties, [](dynamic_global_property_object& p) { + const auto& dynamic_properties = d.get_dynamic_global_properties(); + d.modify(dynamic_properties, [](dynamic_global_property_object& p) { ++p.accounts_registered_this_interval; }); - const auto& global_properties = db().get_global_properties(); - if( dynamic_properties.accounts_registered_this_interval % - global_properties.parameters.accounts_per_fee_scale == 0 ) - db().modify(global_properties, [](global_property_object& p) { + if( dynamic_properties.accounts_registered_this_interval % global_properties.parameters.accounts_per_fee_scale == 0 + && global_properties.parameters.account_fee_scale_bitshifts != 0 ) + { + d.modify(global_properties, [](global_property_object& p) { p.parameters.current_fees->get().basic_fee <<= p.parameters.account_fee_scale_bitshifts; }); + } if( o.extensions.value.owner_special_authority.valid() || o.extensions.value.active_special_authority.valid() ) @@ -308,8 +313,20 @@ void_result account_update_evaluator::do_evaluate( const account_update_operatio void_result account_update_evaluator::do_apply( const account_update_operation& o ) { try { database& d = db(); - bool sa_before, sa_after; - d.modify( *acnt, [&](account_object& a){ + + bool sa_before = acnt->has_special_authority(); + + // update account statistics + if( o.new_options.valid() && o.new_options->is_voting() != acnt->options.is_voting() ) + { + d.modify( acnt->statistics( d ), []( account_statistics_object& aso ) + { + aso.is_voting = !aso.is_voting; + } ); + } + + // update account object + d.modify( *acnt, [&o](account_object& a){ if( o.owner ) { a.owner = *o.owner; @@ -321,7 +338,6 @@ void_result account_update_evaluator::do_apply( const account_update_operation& a.top_n_control_flags = 0; } if( o.new_options ) a.options = *o.new_options; - sa_before = a.has_special_authority(); if( o.extensions.value.owner_special_authority.valid() ) { a.owner_special_authority = *(o.extensions.value.owner_special_authority); @@ -332,9 +348,10 @@ void_result account_update_evaluator::do_apply( const account_update_operation& a.active_special_authority = *(o.extensions.value.active_special_authority); a.top_n_control_flags = 0; } - sa_after = a.has_special_authority(); }); + bool sa_after = acnt->has_special_authority(); + if( sa_before && (!sa_after) ) { const auto& sa_idx = d.get_index_type< special_authority_index >().indices().get(); diff --git a/libraries/chain/db_maint.cpp b/libraries/chain/db_maint.cpp index ea5f206716..4a0aa84aaa 100644 --- a/libraries/chain/db_maint.cpp +++ b/libraries/chain/db_maint.cpp @@ -104,7 +104,7 @@ void database::perform_account_maintenance(Type tally_helper) const account_object& acc_obj = acc_stat.owner( *this ); ++stats_itr; - if( acc_stat.has_some_core() ) + if( acc_stat.has_some_core_voting() ) tally_helper( acc_obj, acc_stat ); if( acc_stat.has_pending_fees() ) diff --git a/libraries/chain/include/graphene/chain/account_object.hpp b/libraries/chain/include/graphene/chain/account_object.hpp index e57d9b6e6a..02cadc09cb 100644 --- a/libraries/chain/include/graphene/chain/account_object.hpp +++ b/libraries/chain/include/graphene/chain/account_object.hpp @@ -68,8 +68,13 @@ namespace graphene { namespace chain { bool has_cashback_vb = false; ///< redundantly store this for better maintenance performance - /// Whether this account owns some CORE asset - inline bool has_some_core() const { return total_core_in_orders > 0 || core_in_balance > 0 || has_cashback_vb; } + bool is_voting = false; ///< redundately store whether this account is voting for better maintenance performance + + /// Whether this account owns some CORE asset and is voting + inline bool has_some_core_voting() const + { + return is_voting && ( total_core_in_orders > 0 || core_in_balance > 0 || has_cashback_vb ); + } /** * Tracks the total fees paid by this account for the purpose of calculating bulk discounts. @@ -95,7 +100,7 @@ namespace graphene { namespace chain { inline bool has_pending_fees() const { return pending_fees > 0 || pending_vested_fees > 0; } /// Whether need to process this account during the maintenance interval - inline bool need_maintenance() const { return has_some_core() || has_pending_fees(); } + inline bool need_maintenance() const { return has_some_core_voting() || has_pending_fees(); } /// @brief Split up and pay out @ref pending_fees and @ref pending_vested_fees void process_fees(const account_object& a, database& d) const; @@ -439,6 +444,7 @@ FC_REFLECT_DERIVED( graphene::chain::account_statistics_object, (total_core_in_orders) (core_in_balance) (has_cashback_vb) + (is_voting) (lifetime_fees_paid) (pending_fees)(pending_vested_fees) ) diff --git a/libraries/chain/include/graphene/chain/protocol/account.hpp b/libraries/chain/include/graphene/chain/protocol/account.hpp index fe669f6cd8..f6178d3e24 100644 --- a/libraries/chain/include/graphene/chain/protocol/account.hpp +++ b/libraries/chain/include/graphene/chain/protocol/account.hpp @@ -57,6 +57,12 @@ namespace graphene { namespace chain { flat_set votes; extensions_type extensions; + /// Whether this account is voting + inline bool is_voting() const + { + return ( voting_account != GRAPHENE_PROXY_TO_SELF_ACCOUNT || !votes.empty() ); + } + void validate()const; };