From 38c05b842e8394662d1d1a119f84dbb10f1dd037 Mon Sep 17 00:00:00 2001 From: arhag Date: Tue, 1 May 2018 23:59:44 -0400 Subject: [PATCH] bill discounted cpu usage rates for context free actions --- contracts/eosio.system/eosio.system.abi | 10 ++--- contracts/eosio.system/voting.hpp | 12 ++--- libraries/chain/apply_context.cpp | 8 +++- .../include/eosio/chain/apply_context.hpp | 1 + .../include/eosio/chain/chain_config.hpp | 10 ++--- .../chain/include/eosio/chain/config.hpp | 8 ++-- .../eosio/chain/transaction_context.hpp | 5 ++- libraries/chain/transaction_context.cpp | 45 ++++++++++++++++++- 8 files changed, 74 insertions(+), 25 deletions(-) diff --git a/contracts/eosio.system/eosio.system.abi b/contracts/eosio.system/eosio.system.abi index 1b807381a..9d612bcf8 100644 --- a/contracts/eosio.system/eosio.system.abi +++ b/contracts/eosio.system/eosio.system.abi @@ -95,12 +95,12 @@ "name": "blockchain_parameters", "base": "", "fields": [ - {"name":"max_block_net_usage", "type": "uint64"}, + {"name":"max_block_net_usage", "type": "uint32"}, {"name":"target_block_net_usage_pct", "type": "uint32"}, {"name":"max_transaction_net_usage", "type":"uint32"}, {"name":"base_per_transaction_net_usage", "type":"uint32"}, - {"name":"context_free_discount_net_usage_num", "type":"uint64"}, - {"name":"context_free_discount_net_usage_den", "type":"uint64"}, + {"name":"context_free_discount_net_usage_num", "type":"uint32"}, + {"name":"context_free_discount_net_usage_den", "type":"uint32"}, {"name":"max_block_cpu_usage", "type": "uint64"}, {"name":"target_block_cpu_usage_pct", "type": "uint32"}, {"name":"max_transaction_cpu_usage", "type":"uint32"}, @@ -108,8 +108,8 @@ {"name":"base_per_action_cpu_usage", "type":"uint32"}, {"name":"base_setcode_cpu_usage", "type":"uint32"}, {"name":"per_signature_cpu_usage", "type":"uint32"}, - {"name":"context_free_discount_cpu_usage_num", "type":"uint64"}, - {"name":"context_free_discount_cpu_usage_den", "type":"uint64"}, + {"name":"context_free_discount_cpu_usage_num", "type":"uint32"}, + {"name":"context_free_discount_cpu_usage_den", "type":"uint32"}, {"name":"max_transaction_lifetime", "type":"uint32"}, {"name":"deferred_trx_expiration_window", "type":"uint32"}, {"name":"max_transaction_delay", "type":"uint32"}, diff --git a/contracts/eosio.system/voting.hpp b/contracts/eosio.system/voting.hpp index acd7fc183..a72d0567d 100644 --- a/contracts/eosio.system/voting.hpp +++ b/contracts/eosio.system/voting.hpp @@ -191,22 +191,22 @@ namespace eosiosystem { producers_table producers_tbl( SystemAccount, SystemAccount ); auto idx = producers_tbl.template get_index(); - std::array max_block_net_usage; + std::array max_block_net_usage; std::array target_block_net_usage_pct; std::array base_per_transaction_net_usage; std::array max_transaction_net_usage; - std::array context_free_discount_net_usage_num; - std::array context_free_discount_net_usage_den; + std::array context_free_discount_net_usage_num; + std::array context_free_discount_net_usage_den; - std::array max_block_cpu_usage; + std::array max_block_cpu_usage; std::array target_block_cpu_usage_pct; std::array max_transaction_cpu_usage; std::array base_per_transaction_cpu_usage; std::array base_per_action_cpu_usage; std::array base_setcode_cpu_usage; std::array per_signature_cpu_usage; - std::array context_free_discount_cpu_usage_num; - std::array context_free_discount_cpu_usage_den; + std::array context_free_discount_cpu_usage_num; + std::array context_free_discount_cpu_usage_den; std::array max_transaction_lifetime; std::array deferred_trx_expiration_window; diff --git a/libraries/chain/apply_context.cpp b/libraries/chain/apply_context.cpp index 9875fef0b..25e5d84d5 100644 --- a/libraries/chain/apply_context.cpp +++ b/libraries/chain/apply_context.cpp @@ -36,6 +36,7 @@ action_trace apply_context::exec_one() { auto start = fc::time_point::now(); cpu_usage = 0; + cpu_usage_limit = trx_context.get_action_cpu_usage_limit( context_free ); checktime( control.get_global_properties().configuration.base_per_action_cpu_usage ); try { const auto &a = control.get_account(receiver); @@ -63,6 +64,8 @@ action_trace apply_context::exec_one() r.auth_sequence[auth.actor] = next_auth_sequence( auth.actor ); } + cpu_usage = trx_context.add_action_cpu_usage( cpu_usage, context_free ); + action_trace t(r); t.trx_id = trx_context.id; t.act = act; @@ -101,7 +104,7 @@ void apply_context::exec() ncontext.context_free = true; ncontext.exec(); fc::move_append( executed, move(ncontext.executed) ); - total_cpu_usage += ncontext.total_cpu_usage; + total_cpu_usage += ncontext.trace.total_cpu_usage; trace.total_cpu_usage += ncontext.trace.total_cpu_usage; trace.inline_traces.emplace_back(ncontext.trace); } @@ -352,7 +355,8 @@ void apply_context::reset_console() { void apply_context::checktime(uint32_t instruction_count) { cpu_usage += instruction_count; - trx_context.add_cpu_usage_and_check_time( instruction_count ); + EOS_ASSERT( BOOST_LIKELY(cpu_usage <= cpu_usage_limit), tx_cpu_usage_exceeded, "action cpu usage exceeded" ); + trx_context.check_time(); } bytes apply_context::get_packed_transaction() { diff --git a/libraries/chain/include/eosio/chain/apply_context.hpp b/libraries/chain/include/eosio/chain/apply_context.hpp index 4c5b6dc3f..205b71bc2 100644 --- a/libraries/chain/include/eosio/chain/apply_context.hpp +++ b/libraries/chain/include/eosio/chain/apply_context.hpp @@ -611,6 +611,7 @@ class apply_context { uint64_t cpu_usage = 0; uint64_t total_cpu_usage = 0; + uint64_t cpu_usage_limit = 0; private: diff --git a/libraries/chain/include/eosio/chain/chain_config.hpp b/libraries/chain/include/eosio/chain/chain_config.hpp index fbfa4070b..163b44a29 100644 --- a/libraries/chain/include/eosio/chain/chain_config.hpp +++ b/libraries/chain/include/eosio/chain/chain_config.hpp @@ -21,18 +21,18 @@ struct chain_config { uint32_t target_block_net_usage_pct; ///< the target percent (1% == 100, 100%= 10,000) of maximum net usage; exceeding this triggers congestion handling uint32_t max_transaction_net_usage; ///< the maximum objectively measured net usage that the chain will allow regardless of account limits uint32_t base_per_transaction_net_usage; ///< the base amount of net usage billed for a transaction to cover incidentals - uint64_t context_free_discount_net_usage_num; ///< the numerator for the discount on net usage of context-free data - uint64_t context_free_discount_net_usage_den; ///< the denominator for the discount on net usage of context-free data + uint32_t context_free_discount_net_usage_num; ///< the numerator for the discount on net usage of context-free data + uint32_t context_free_discount_net_usage_den; ///< the denominator for the discount on net usage of context-free data - uint64_t max_block_cpu_usage; ///< the maxiumum cpu usage in instructions for a block + uint32_t max_block_cpu_usage; ///< the maxiumum cpu usage in instructions for a block uint32_t target_block_cpu_usage_pct; ///< the target percent (1% == 100, 100%= 10,000) of maximum cpu usage; exceeding this triggers congestion handling uint32_t max_transaction_cpu_usage; ///< the maximum objectively measured cpu usage that the chain will allow regardless of account limits uint32_t base_per_transaction_cpu_usage; ///< the base amount of cpu usage billed for a transaction to cover incidentals uint32_t base_per_action_cpu_usage; ///< the base amount of cpu usage billed for an action to cover incidentals uint32_t base_setcode_cpu_usage; ///< the base amount of cpu usage billed for a setcode action to cover compilation/etc uint32_t per_signature_cpu_usage; ///< the cpu usage billed for every signature on a transaction - uint64_t context_free_discount_cpu_usage_num; ///< the numerator for the discount on cpu usage of context-free actions - uint64_t context_free_discount_cpu_usage_den; ///< the denominator for the discount on cpu usage of context-free actions + uint32_t context_free_discount_cpu_usage_num; ///< the numerator for the discount on cpu usage of context-free actions + uint32_t context_free_discount_cpu_usage_den; ///< the denominator for the discount on cpu usage of context-free actions uint32_t max_transaction_lifetime; ///< the maximum number of seconds that an input transaction's expiration can be ahead of the time of the block in which it is first included uint32_t deferred_trx_expiration_window; ///< the number of seconds after the time a deferred transaction can first execute until it expires diff --git a/libraries/chain/include/eosio/chain/config.hpp b/libraries/chain/include/eosio/chain/config.hpp index a18747005..05f72be97 100644 --- a/libraries/chain/include/eosio/chain/config.hpp +++ b/libraries/chain/include/eosio/chain/config.hpp @@ -58,8 +58,8 @@ const static uint32_t default_max_block_net_usage = 1024 * 102 const static uint32_t default_target_block_net_usage_pct = 10 * percent_1; /// we target 1000 TPS const static uint32_t default_max_transaction_net_usage = default_max_block_net_usage / 10; const static uint32_t default_base_per_transaction_net_usage = 12; // 12 bytes (11 bytes for worst case of transaction_receipt_header + 1 byte for static_variant tag) -const static uint64_t default_context_free_discount_net_usage_num = 20; // TODO: is this reasonable? -const static uint64_t default_context_free_discount_net_usage_den = 100; +const static uint32_t default_context_free_discount_net_usage_num = 20; // TODO: is this reasonable? +const static uint32_t default_context_free_discount_net_usage_den = 100; const static uint32_t transaction_id_net_usage = 32; // 32 bytes for the size of a transaction id const static uint32_t default_max_block_cpu_usage = 100 * 1024 * 1024; /// at 500ms blocks and 20000instr trx, this enables ~10,000 TPS burst @@ -69,8 +69,8 @@ const static uint32_t default_base_per_transaction_cpu_usage = 512; const static uint32_t default_base_per_action_cpu_usage = 1024; const static uint32_t default_base_setcode_cpu_usage = 2 * 1024 * 1024; /// overbilling cpu usage for setcode to cover incidental const static uint32_t default_per_signature_cpu_usage = 100 * 1024; // TODO: is this reasonable? -const static uint64_t default_context_free_discount_cpu_usage_num = 20; -const static uint64_t default_context_free_discount_cpu_usage_den = 100; +const static uint32_t default_context_free_discount_cpu_usage_num = 20; +const static uint32_t default_context_free_discount_cpu_usage_den = 100; const static uint32_t default_max_trx_lifetime = 60*60; // 1 hour const static uint32_t default_deferred_trx_expiration_window = 10*60; // 10 minutes diff --git a/libraries/chain/include/eosio/chain/transaction_context.hpp b/libraries/chain/include/eosio/chain/transaction_context.hpp index 115e18297..d6d827d90 100644 --- a/libraries/chain/include/eosio/chain/transaction_context.hpp +++ b/libraries/chain/include/eosio/chain/transaction_context.hpp @@ -31,7 +31,10 @@ namespace eosio { namespace chain { inline void add_net_usage( uint64_t u ) { net_usage += u; check_net_usage(); } inline void add_cpu_usage( uint64_t u ) { cpu_usage += u; check_cpu_usage(); } - inline void add_cpu_usage_and_check_time( uint64_t u ) { check_time(); cpu_usage += u; check_cpu_usage(); } + + /// returns cpu usage amount that was actually added + uint64_t add_action_cpu_usage( uint64_t u, bool context_free ); + uint64_t get_action_cpu_usage_limit( bool context_free )const; void check_net_usage()const; void check_cpu_usage()const; diff --git a/libraries/chain/transaction_context.cpp b/libraries/chain/transaction_context.cpp index 7a239ba72..d0a094dc4 100644 --- a/libraries/chain/transaction_context.cpp +++ b/libraries/chain/transaction_context.cpp @@ -116,7 +116,8 @@ namespace eosio { namespace chain { && cfg.context_free_discount_net_usage_num < cfg.context_free_discount_net_usage_den ) { discounted_size_for_pruned_data *= cfg.context_free_discount_net_usage_num; - discounted_size_for_pruned_data /= cfg.context_free_discount_net_usage_den; + discounted_size_for_pruned_data = ( discounted_size_for_pruned_data + cfg.context_free_discount_net_usage_den - 1) + / cfg.context_free_discount_net_usage_den; // rounds up } uint64_t initial_net_usage = static_cast(cfg.base_per_transaction_net_usage) @@ -189,6 +190,38 @@ namespace eosio { namespace chain { undo_session.squash(); } + uint64_t transaction_context::add_action_cpu_usage( uint64_t u, bool context_free ) { + const auto& cfg = control.get_global_properties().configuration; + + uint64_t discounted_cpu_usage = u; + if( context_free && cfg.context_free_discount_cpu_usage_den > 0 + && cfg.context_free_discount_cpu_usage_num < cfg.context_free_discount_cpu_usage_den ) + { + discounted_cpu_usage *= cfg.context_free_discount_cpu_usage_num; + discounted_cpu_usage = ( discounted_cpu_usage + cfg.context_free_discount_cpu_usage_den - 1) + / cfg.context_free_discount_cpu_usage_den; // rounds up + } + + add_cpu_usage( discounted_cpu_usage ); + return discounted_cpu_usage; + } + + uint64_t transaction_context::get_action_cpu_usage_limit( bool context_free )const { + check_cpu_usage(); + uint64_t diff = max_cpu - cpu_usage; + if( !context_free ) return diff; + + const auto& cfg = control.get_global_properties().configuration; + uint64_t n = cfg.context_free_discount_cpu_usage_num; + uint64_t d = cfg.context_free_discount_cpu_usage_den; + + if( d == 0 || n >= d ) return diff; + + if( n == 0 ) return std::numeric_limits::max(); + + return (diff * d)/n; + } + void transaction_context::check_net_usage()const { if( BOOST_UNLIKELY(net_usage > max_net) ) { if( BOOST_UNLIKELY( net_limit_due_to_block ) ) { @@ -233,7 +266,15 @@ namespace eosio { namespace chain { apply_context acontext( control, *this, a ); acontext.context_free = context_free; acontext.receiver = receiver; - acontext.exec(); + + try { + acontext.exec(); + } catch( const tx_cpu_usage_exceeded& e ) { + add_action_cpu_usage( acontext.cpu_usage, context_free ); // Will update cpu_usage to latest value and throw appropriate exception + FC_ASSERT(false, "should not have reached here" ); + } catch( ... ) { + throw; + } fc::move_append(executed, move(acontext.executed) ); -- GitLab