From 5b114ad976d20e6827bb2a5ad17ef945c5db5cc9 Mon Sep 17 00:00:00 2001 From: Jonathan LEI Date: Fri, 24 Aug 2018 00:10:01 +0800 Subject: [PATCH 01/23] Added contract build path to eosiocpp -g include paths --- tools/eosiocpp.in | 1 + 1 file changed, 1 insertion(+) diff --git a/tools/eosiocpp.in b/tools/eosiocpp.in index 1002a704c7b..f0b63f9c56b 100755 --- a/tools/eosiocpp.in +++ b/tools/eosiocpp.in @@ -112,6 +112,7 @@ function generate_abi { ${ABIGEN} -extra-arg=-c -extra-arg=--std=c++14 -extra-arg=--target=wasm32 \ -extra-arg=-nostdinc -extra-arg=-nostdinc++ -extra-arg=-DABIGEN \ -extra-arg=-I@CMAKE_SOURCE_DIR@/contracts \ + -extra-arg=-I@CMAKE_SOURCE_DIR@/build/contracts \ -extra-arg=-I@CMAKE_SOURCE_DIR@/contracts/libc++/upstream/include \ -extra-arg=-I@CMAKE_SOURCE_DIR@/contracts/musl/upstream/include \ -extra-arg=-I@CMAKE_SOURCE_DIR@/externals/magic_get/include \ From b9831d182a2aaae2e51abeae92058fb7a03b50a4 Mon Sep 17 00:00:00 2001 From: Jonathan Giszczak Date: Thu, 23 Aug 2018 12:24:46 -0500 Subject: [PATCH 02/23] Fix framework casing for case sensitive MacOS builds By @aaroncox --- plugins/wallet_plugin/CMakeLists.txt | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/plugins/wallet_plugin/CMakeLists.txt b/plugins/wallet_plugin/CMakeLists.txt index fc27ea76e7a..8b3a6d7d7b1 100644 --- a/plugins/wallet_plugin/CMakeLists.txt +++ b/plugins/wallet_plugin/CMakeLists.txt @@ -4,10 +4,10 @@ if(APPLE) set(SE_WALLET_SOURCES se_wallet.cpp macos_user_auth.m) set_source_files_properties(macos_user_presence.m PROPERTIES COMPILE_FLAGS "-x objective-c") - find_library(security_framework security) - find_library(localauthentication_framework localauthentication) - find_library(corefoundation_framework corefoundation) - find_library(cocoa_framework cocoa) + find_library(security_framework Security) + find_library(localauthentication_framework LocalAuthentication) + find_library(corefoundation_framework CoreFoundation) + find_library(cocoa_framework Cocoa) if(MAS_KEYCHAIN_GROUP) add_definitions(-DMAS_KEYCHAIN_GROUP=${MAS_KEYCHAIN_GROUP}) From 4655c8f708a23c560c6ee5f94905c2cf5016d6ec Mon Sep 17 00:00:00 2001 From: Bucky Kittinger Date: Thu, 23 Aug 2018 21:57:51 -0400 Subject: [PATCH 03/23] Slight change --- tools/eosiocpp.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/eosiocpp.in b/tools/eosiocpp.in index f0b63f9c56b..3e8f84e96d7 100755 --- a/tools/eosiocpp.in +++ b/tools/eosiocpp.in @@ -112,7 +112,7 @@ function generate_abi { ${ABIGEN} -extra-arg=-c -extra-arg=--std=c++14 -extra-arg=--target=wasm32 \ -extra-arg=-nostdinc -extra-arg=-nostdinc++ -extra-arg=-DABIGEN \ -extra-arg=-I@CMAKE_SOURCE_DIR@/contracts \ - -extra-arg=-I@CMAKE_SOURCE_DIR@/build/contracts \ + -extra-arg=-I@CMAKE_BINARY_DIR@/contracts \ -extra-arg=-I@CMAKE_SOURCE_DIR@/contracts/libc++/upstream/include \ -extra-arg=-I@CMAKE_SOURCE_DIR@/contracts/musl/upstream/include \ -extra-arg=-I@CMAKE_SOURCE_DIR@/externals/magic_get/include \ From efe5fdbbb35c4282dc342f7e8e74564972c56ab8 Mon Sep 17 00:00:00 2001 From: Bart Wyatt Date: Fri, 24 Aug 2018 09:59:51 -0400 Subject: [PATCH 04/23] do not apply transaction optimizations on light-nodes when speculating fixed an issue where block status was not taken into account when determining whether or not to skip some expensive checks. As a result light validation nodes would not apply those checks even when speculatively executing new transactions (which are not signed by producers). Also refactored common logic while maintaining the 2 config/parameters that control different sets of optimizations resolves EOSIO/eos#5408 --- libraries/chain/controller.cpp | 35 ++++++++++--------- .../chain/include/eosio/chain/controller.hpp | 1 + 2 files changed, 19 insertions(+), 17 deletions(-) diff --git a/libraries/chain/controller.cpp b/libraries/chain/controller.cpp index 0c582871a11..1a467ecbccb 100644 --- a/libraries/chain/controller.cpp +++ b/libraries/chain/controller.cpp @@ -1651,16 +1651,25 @@ optional controller::proposed_producers()const { return gpo.proposed_schedule; } -bool controller::skip_auth_check() const { - // replaying - bool consider_skipping = my->replaying; +bool controller::light_validation_allowed(bool replay_opts_disabled_by_policy) const { + if (!my->pending || my->in_trx_requiring_checks) { + return false; + } - // OR in light validation mode - consider_skipping = consider_skipping || my->conf.block_validation_mode == validation_mode::LIGHT; + auto pb_status = my->pending->_block_status; - return consider_skipping - && !my->conf.force_all_checks - && !my->in_trx_requiring_checks; + // in a pending irreversible or previously validated block and we have forcing all checks + bool consider_skipping_on_replay = (pb_status == block_status::irreversible || pb_status == block_status::validated) && !replay_opts_disabled_by_policy; + + // OR in a signed block and in light validation mode + bool consider_skipping_on_validate = (pb_status == block_status::complete && my->conf.block_validation_mode == validation_mode::LIGHT); + + return consider_skipping_on_replay || consider_skipping_on_validate; +} + + +bool controller::skip_auth_check() const { + return light_validation_allowed(my->conf.force_all_checks); } bool controller::skip_db_sessions( block_status bs ) const { @@ -1679,15 +1688,7 @@ bool controller::skip_db_sessions( ) const { } bool controller::skip_trx_checks() const { - // in a pending irreversible or previously validated block - bool consider_skipping = my->pending && ( my->pending->_block_status == block_status::irreversible || my->pending->_block_status == block_status::validated ); - - // OR in light validation mode - consider_skipping = consider_skipping || my->conf.block_validation_mode == validation_mode::LIGHT; - - return consider_skipping - && !my->conf.disable_replay_opts - && !my->in_trx_requiring_checks; + light_validation_allowed(my->conf.disable_replay_opts); } bool controller::contracts_console()const { diff --git a/libraries/chain/include/eosio/chain/controller.hpp b/libraries/chain/include/eosio/chain/controller.hpp index c457eb68cc9..956382f6ebd 100644 --- a/libraries/chain/include/eosio/chain/controller.hpp +++ b/libraries/chain/include/eosio/chain/controller.hpp @@ -219,6 +219,7 @@ namespace eosio { namespace chain { int64_t set_proposed_producers( vector producers ); + bool light_validation_allowed(bool replay_opts_disabled_by_policy) const; bool skip_auth_check()const; bool skip_db_sessions( )const; bool skip_db_sessions( block_status bs )const; From 9c711b4fee40271cf966e7cba159a522bacee21b Mon Sep 17 00:00:00 2001 From: Bart Wyatt Date: Fri, 24 Aug 2018 10:58:16 -0400 Subject: [PATCH 05/23] actually return the value... --- libraries/chain/controller.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/chain/controller.cpp b/libraries/chain/controller.cpp index 1a467ecbccb..ebc7830afc0 100644 --- a/libraries/chain/controller.cpp +++ b/libraries/chain/controller.cpp @@ -1688,7 +1688,7 @@ bool controller::skip_db_sessions( ) const { } bool controller::skip_trx_checks() const { - light_validation_allowed(my->conf.disable_replay_opts); + return light_validation_allowed(my->conf.disable_replay_opts); } bool controller::contracts_console()const { From be26f5f4ab24b90ec128129d7cf37aa9fda55b65 Mon Sep 17 00:00:00 2001 From: Kevin Heifner Date: Fri, 24 Aug 2018 11:37:31 -0500 Subject: [PATCH 06/23] Revert "on_accepted_block_header and on_accepted_block merged into one handler #4468" This reverts commit 8268af254e0d98d34ab97f9d38341de93afb1872. Processing of block_state before signature is problematic. --- plugins/bnet_plugin/bnet_plugin.cpp | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/plugins/bnet_plugin/bnet_plugin.cpp b/plugins/bnet_plugin/bnet_plugin.cpp index 80104a15ea1..c61274e4265 100644 --- a/plugins/bnet_plugin/bnet_plugin.cpp +++ b/plugins/bnet_plugin/bnet_plugin.cpp @@ -530,7 +530,10 @@ namespace eosio { _block_status.insert( block_status(id, false, false) ); } } + } + void on_accepted_block( const block_state_ptr& s ) { + verify_strand_in_this_thread(_strand, __func__, __LINE__); //idump((_block_status.size())(_transaction_status.size())); //ilog( "accepted block ${n}", ("n",s->block_num) ); @@ -1204,6 +1207,18 @@ namespace eosio { for_each_session( [s]( auto ses ){ ses->on_new_lib( s ); } ); } + /** + * Notify all active connections of the new accepted block so + * they can relay it. This method also pre-packages the block + * as a packed bnet_message so the connections can simply relay + * it on. + */ + void on_accepted_block( block_state_ptr s ) { + _ioc->post( [s,this] { /// post this to the thread pool because packing can be intensive + for_each_session( [s]( auto ses ){ ses->on_accepted_block( s ); } ); + }); + } + void on_accepted_block_header( block_state_ptr s ) { _ioc->post( [s,this] { /// post this to the thread pool because packing can be intensive for_each_session( [s]( auto ses ){ ses->on_accepted_block_header( s ); } ); @@ -1349,6 +1364,9 @@ namespace eosio { wlog( "bnet startup " ); + auto& chain = app().get_plugin().chain(); + FC_ASSERT ( chain.get_read_mode() != chain::db_read_mode::IRREVERSIBLE, "bnet is not compatible with \"irreversible\" read_mode"); + my->_on_appled_trx_handle = app().get_channel() .subscribe( [this]( transaction_metadata_ptr t ){ my->on_accepted_transaction(t); @@ -1359,6 +1377,11 @@ namespace eosio { my->on_irreversible_block(s); }); + my->_on_accepted_block_handle = app().get_channel() + .subscribe( [this]( block_state_ptr s ){ + my->on_accepted_block(s); + }); + my->_on_accepted_block_header_handle = app().get_channel() .subscribe( [this]( block_state_ptr s ){ my->on_accepted_block_header(s); From b41930d137d92a79e455e24244c1d947c20e4af1 Mon Sep 17 00:00:00 2001 From: Kevin Heifner Date: Fri, 24 Aug 2018 12:24:43 -0500 Subject: [PATCH 07/23] Add thread safe id() --- libraries/chain/include/eosio/chain/transaction.hpp | 3 ++- libraries/chain/transaction.cpp | 6 ++++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/libraries/chain/include/eosio/chain/transaction.hpp b/libraries/chain/include/eosio/chain/transaction.hpp index 1d325352d0d..895d3ee4fb8 100644 --- a/libraries/chain/include/eosio/chain/transaction.hpp +++ b/libraries/chain/include/eosio/chain/transaction.hpp @@ -132,7 +132,8 @@ namespace eosio { namespace chain { time_point_sec expiration()const; transaction_id_type id()const; - bytes get_raw_transaction()const; + transaction_id_type get_uncached_id()const; // thread safe + bytes get_raw_transaction()const; // thread safe vector get_context_free_data()const; transaction get_transaction()const; signed_transaction get_signed_transaction()const; diff --git a/libraries/chain/transaction.cpp b/libraries/chain/transaction.cpp index d81a35ff19b..c49f76ce771 100644 --- a/libraries/chain/transaction.cpp +++ b/libraries/chain/transaction.cpp @@ -298,6 +298,12 @@ transaction_id_type packed_transaction::id()const return get_transaction().id(); } +transaction_id_type packed_transaction::get_uncached_id()const +{ + const auto raw = get_raw_transaction(); + return fc::raw::unpack( raw ).id(); +} + void packed_transaction::local_unpack()const { if (!unpacked_trx) { From 43e6a3081ecf36e009ecc29644785e2e4dd2521c Mon Sep 17 00:00:00 2001 From: Kevin Heifner Date: Fri, 24 Aug 2018 12:25:39 -0500 Subject: [PATCH 08/23] Use thread safe get_uncached_id() --- plugins/bnet_plugin/bnet_plugin.cpp | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/plugins/bnet_plugin/bnet_plugin.cpp b/plugins/bnet_plugin/bnet_plugin.cpp index c61274e4265..1fcbea038a5 100644 --- a/plugins/bnet_plugin/bnet_plugin.cpp +++ b/plugins/bnet_plugin/bnet_plugin.cpp @@ -552,7 +552,9 @@ namespace eosio { */ for( const auto& receipt : s->block->transactions ) { if( receipt.trx.which() == 1 ) { - const auto tid = receipt.trx.get().id(); + const auto& pt = receipt.trx.get(); + // get id via get_uncached_id() as packed_transaction.id() mutates internal transaction state + const auto& tid = pt.get_uncached_id(); auto itr = _transaction_status.find( tid ); if( itr != _transaction_status.end() ) _transaction_status.erase(itr); @@ -1009,7 +1011,9 @@ namespace eosio { void mark_block_transactions_known_by_peer( const signed_block_ptr& b ) { for( const auto& receipt : b->transactions ) { if( receipt.trx.which() == 1 ) { - auto id = receipt.trx.get().id(); + const auto& pt = receipt.trx.get(); + // get id via get_uncached_id() as packed_transaction.id() mutates internal transaction state + const auto& id = pt.get_uncached_id(); mark_transaction_known_by_peer(id); } } @@ -1044,10 +1048,12 @@ namespace eosio { if( app().get_plugin().chain().get_read_mode() == chain::db_read_mode::READ_ONLY ) return; - auto id = p->id(); // ilog( "recv trx ${n}", ("n", id) ); if( p->expiration() < fc::time_point::now() ) return; + // get id via get_uncached_id() as packed_transaction.id() mutates internal transaction state + const auto& id = p->get_uncached_id(); + if( mark_transaction_known_by_peer( id ) ) return; From f2b49e8890f8d4eaf277903b6a48116769fd88cd Mon Sep 17 00:00:00 2001 From: Kevin Heifner Date: Fri, 24 Aug 2018 12:42:30 -0500 Subject: [PATCH 09/23] Add missing id variable --- plugins/bnet_plugin/bnet_plugin.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/plugins/bnet_plugin/bnet_plugin.cpp b/plugins/bnet_plugin/bnet_plugin.cpp index 1fcbea038a5..4052fce2b30 100644 --- a/plugins/bnet_plugin/bnet_plugin.cpp +++ b/plugins/bnet_plugin/bnet_plugin.cpp @@ -537,6 +537,8 @@ namespace eosio { //idump((_block_status.size())(_transaction_status.size())); //ilog( "accepted block ${n}", ("n",s->block_num) ); + const auto& id = s->id; + _local_head_block_id = id; _local_head_block_num = block_header::num_from_id(id); From c8f88db05357631a6d69fefca43ea296fcb2a154 Mon Sep 17 00:00:00 2001 From: Kevin Heifner Date: Sat, 25 Aug 2018 16:16:39 -0500 Subject: [PATCH 10/23] Change log to debug for exceptions as they are expected --- plugins/net_plugin/net_plugin.cpp | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/plugins/net_plugin/net_plugin.cpp b/plugins/net_plugin/net_plugin.cpp index eb120521e68..ef68ab08a63 100644 --- a/plugins/net_plugin/net_plugin.cpp +++ b/plugins/net_plugin/net_plugin.cpp @@ -2491,11 +2491,8 @@ namespace eosio { dispatcher->recv_transaction(c, tid); chain_plug->accept_transaction(msg, [=](const static_variant& result) { if (result.contains()) { - auto e_ptr = result.get(); - if (e_ptr->code() != tx_duplicate::code_value && e_ptr->code() != expired_tx_exception::code_value) { - elog("accept txn threw ${m}",("m",result.get()->to_detail_string())); - peer_elog(c, "bad packed_transaction : ${m}", ("m",result.get()->what())); - } + dlog("accept txn threw ${m}",("m",result.get()->to_detail_string())); + peer_dlog(c, "bad packed_transaction : ${m}", ("m",result.get()->what())); } else { auto trace = result.get(); if (!trace->except) { From 93bd82621710b63f9267e982c28db25282fcac8c Mon Sep 17 00:00:00 2001 From: Kevin Heifner Date: Mon, 27 Aug 2018 09:23:59 -0500 Subject: [PATCH 11/23] dlog is on by default, remove call for accept_transaction for exceptions since they are expected --- plugins/net_plugin/net_plugin.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/plugins/net_plugin/net_plugin.cpp b/plugins/net_plugin/net_plugin.cpp index ef68ab08a63..d907e7f7390 100644 --- a/plugins/net_plugin/net_plugin.cpp +++ b/plugins/net_plugin/net_plugin.cpp @@ -2491,7 +2491,6 @@ namespace eosio { dispatcher->recv_transaction(c, tid); chain_plug->accept_transaction(msg, [=](const static_variant& result) { if (result.contains()) { - dlog("accept txn threw ${m}",("m",result.get()->to_detail_string())); peer_dlog(c, "bad packed_transaction : ${m}", ("m",result.get()->what())); } else { auto trace = result.get(); From 044248513166bc99d7219f0e073a95f05279920d Mon Sep 17 00:00:00 2001 From: arhag Date: Mon, 27 Aug 2018 14:02:01 -0400 Subject: [PATCH 12/23] subjectively fail transaction if unprivileged code bills RAM to an account other than itself within a notify context --- libraries/chain/apply_context.cpp | 4 ++++ libraries/chain/controller.cpp | 4 ++++ libraries/chain/include/eosio/chain/controller.hpp | 3 +++ 3 files changed, 11 insertions(+) diff --git a/libraries/chain/apply_context.cpp b/libraries/chain/apply_context.cpp index a3399dab0d6..0bbfb8e3aec 100644 --- a/libraries/chain/apply_context.cpp +++ b/libraries/chain/apply_context.cpp @@ -301,6 +301,8 @@ void apply_context::schedule_deferred_transaction( const uint128_t& sender_id, a }); } + EOS_ASSERT( control.is_ram_billing_in_notify_allowed() || (receiver == act.account) || (receiver == payer) || privileged, + subjective_block_production_exception, "Cannot charge RAM to other accounts during notify." ); trx_context.add_ram_usage( payer, (config::billable_size_v + trx_size) ); } @@ -362,6 +364,8 @@ bytes apply_context::get_packed_transaction() { void apply_context::update_db_usage( const account_name& payer, int64_t delta ) { if( delta > 0 ) { if( !(privileged || payer == account_name(receiver)) ) { + EOS_ASSERT( control.is_ram_billing_in_notify_allowed() || (receiver == act.account), + subjective_block_production_exception, "Cannot charge RAM to other accounts during notify." ); require_authorization( payer ); } } diff --git a/libraries/chain/controller.cpp b/libraries/chain/controller.cpp index 0c582871a11..6b2cfdf9baf 100644 --- a/libraries/chain/controller.cpp +++ b/libraries/chain/controller.cpp @@ -1776,6 +1776,10 @@ bool controller::is_producing_block()const { return (my->pending->_block_status == block_status::incomplete); } +bool controller::is_ram_billing_in_notify_allowed()const { + return !is_producing_block() || my->conf.allow_ram_billing_in_notify; +} + void controller::validate_referenced_accounts( const transaction& trx )const { for( const auto& a : trx.context_free_actions ) { auto* code = my->db.find(a.account); diff --git a/libraries/chain/include/eosio/chain/controller.hpp b/libraries/chain/include/eosio/chain/controller.hpp index c457eb68cc9..7aee814e594 100644 --- a/libraries/chain/include/eosio/chain/controller.hpp +++ b/libraries/chain/include/eosio/chain/controller.hpp @@ -65,6 +65,7 @@ namespace eosio { namespace chain { bool force_all_checks = false; bool disable_replay_opts = false; bool contracts_console = false; + bool allow_ram_billing_in_notify = false; genesis_state genesis; wasm_interface::vm_type wasm_runtime = chain::config::default_wasm_runtime; @@ -204,6 +205,8 @@ namespace eosio { namespace chain { void check_key_list( const public_key_type& key )const; bool is_producing_block()const; + bool is_ram_billing_in_notify_allowed()const; + void add_resource_greylist(const account_name &name); void remove_resource_greylist(const account_name &name); bool is_resource_greylisted(const account_name &name) const; From 9a77456031413352ff833777b86b32eef28830bb Mon Sep 17 00:00:00 2001 From: arhag Date: Mon, 27 Aug 2018 16:06:56 -0400 Subject: [PATCH 13/23] Add support in config.ini to disable the RAM billing soft-fix in notification handlers. --- plugins/chain_plugin/chain_plugin.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/plugins/chain_plugin/chain_plugin.cpp b/plugins/chain_plugin/chain_plugin.cpp index 58295d52da8..67efb591611 100644 --- a/plugins/chain_plugin/chain_plugin.cpp +++ b/plugins/chain_plugin/chain_plugin.cpp @@ -244,6 +244,8 @@ void chain_plugin::set_program_options(options_description& cli, options_descrip "Chain validation mode (\"full\" or \"light\").\n" "In \"full\" mode all incoming blocks will be fully validated.\n" "In \"light\" mode all incoming blocks headers will be fully validated; transactions in those validated blocks will be trusted \n") + ("disable-ram-billing-notify-checks", bpo::bool_switch()->default_value(false), + "Disable the check which subjectively fails a transaction if a contract bills more RAM to another account within the context of a notification handler (i.e. when the receiver is not the code of the action).") ; // TODO: rate limiting @@ -405,6 +407,7 @@ void chain_plugin::plugin_initialize(const variables_map& options) { my->chain_config->force_all_checks = options.at( "force-all-checks" ).as(); my->chain_config->disable_replay_opts = options.at( "disable-replay-opts" ).as(); my->chain_config->contracts_console = options.at( "contracts-console" ).as(); + my->chain_config->allow_ram_billing_in_notify = options.at( "disable-ram-billing-notify-checks" ).as(); if( options.count( "extract-genesis-json" ) || options.at( "print-genesis-json" ).as()) { genesis_state gs; From a1dffae2f88ae52506261f9a7688775ee6f5c3d5 Mon Sep 17 00:00:00 2001 From: arhag Date: Mon, 27 Aug 2018 18:58:21 -0400 Subject: [PATCH 14/23] add unit test for RAM billing soft-fix in notify context --- contracts/test_api/test_action.cpp | 25 +++++++++++++++++++++++++ contracts/test_api/test_api.cpp | 1 + contracts/test_api/test_api.hpp | 2 +- unittests/api_tests.cpp | 21 +++++++++++++++++++++ 4 files changed, 48 insertions(+), 1 deletion(-) diff --git a/contracts/test_api/test_action.cpp b/contracts/test_api/test_action.cpp index adc517453fa..1bc54cb3d02 100644 --- a/contracts/test_api/test_action.cpp +++ b/contracts/test_api/test_action.cpp @@ -225,3 +225,28 @@ void test_action::test_assert_code() { eosio_assert( total == sizeof(uint64_t), "total == sizeof(uint64_t)"); eosio_assert_code( false, code ); } + +void test_action::test_ram_billing_in_notify(uint64_t receiver, uint64_t code, uint64_t action) { + uint128_t tmp = 0; + uint32_t total = read_action_data(&tmp, sizeof(uint128_t)); + eosio_assert( total == sizeof(uint128_t), "total == sizeof(uint128_t)"); + + uint64_t to_notify = tmp >> 64; + uint64_t payer = tmp & 0xFFFFFFFFFFFFFFFFULL; + + if( code == receiver ) { + eosio::require_recipient( to_notify ); + } else { + eosio_assert( to_notify == receiver, "notified recipient other than the one specified in to_notify" ); + + // Remove main table row if it already exists. + int itr = db_find_i64( receiver, N(notifytest), N(notifytest), N(notifytest) ); + if( itr >= 0 ) + db_remove_i64( itr ); + + // Create the main table row simply for the purpose of charging code more RAM. + if( payer != 0 ) + db_store_i64(N(notifytest), N(notifytest), payer, N(notifytest), &to_notify, sizeof(to_notify) ); + } + +} diff --git a/contracts/test_api/test_api.cpp b/contracts/test_api/test_api.cpp index 3bccd9a4a8c..29f47d75f64 100644 --- a/contracts/test_api/test_api.cpp +++ b/contracts/test_api/test_api.cpp @@ -79,6 +79,7 @@ extern "C" { WASM_TEST_HANDLER_EX(test_action, test_current_receiver); WASM_TEST_HANDLER(test_action, test_publication_time); WASM_TEST_HANDLER(test_action, test_assert_code); + WASM_TEST_HANDLER_EX(test_action, test_ram_billing_in_notify); // test named actions // We enforce action name matches action data type name, so name mangling will not work for these tests. diff --git a/contracts/test_api/test_api.hpp b/contracts/test_api/test_api.hpp index b54f12bff02..b026642175e 100644 --- a/contracts/test_api/test_api.hpp +++ b/contracts/test_api/test_api.hpp @@ -57,7 +57,6 @@ struct test_print { }; struct test_action { - static void read_action_normal(); static void read_action_to_0(); static void read_action_to_64k(); @@ -73,6 +72,7 @@ struct test_action { static void test_current_receiver(uint64_t receiver, uint64_t code, uint64_t action); static void test_publication_time(); static void test_assert_code(); + static void test_ram_billing_in_notify(uint64_t receiver, uint64_t code, uint64_t action); }; struct test_db { diff --git a/unittests/api_tests.cpp b/unittests/api_tests.cpp index 63eda3116b0..2ddf0b34253 100644 --- a/unittests/api_tests.cpp +++ b/unittests/api_tests.cpp @@ -437,6 +437,27 @@ BOOST_FIXTURE_TEST_CASE(action_tests, TESTER) { try { BOOST_REQUIRE_EQUAL( validate(), true ); } FC_LOG_AND_RETHROW() } +BOOST_FIXTURE_TEST_CASE(ram_billing_in_notify_tests, TESTER) { try { + produce_blocks(2); + create_account( N(testapi) ); + create_account( N(testapi2) ); + produce_blocks(10); + set_code( N(testapi), test_api_wast ); + produce_blocks(1); + set_code( N(testapi2), test_api_wast ); + produce_blocks(1); + + BOOST_CHECK_EXCEPTION( CALL_TEST_FUNCTION( *this, "test_action", "test_ram_billing_in_notify", fc::raw::pack( ((unsigned __int128)N(testapi2) << 64) | N(testapi) ) ), + subjective_block_production_exception, fc_exception_message_is("Cannot charge RAM to other accounts during notify.") ); + + + CALL_TEST_FUNCTION( *this, "test_action", "test_ram_billing_in_notify", fc::raw::pack( ((unsigned __int128)N(testapi2) << 64) | 0 ) ); + + CALL_TEST_FUNCTION( *this, "test_action", "test_ram_billing_in_notify", fc::raw::pack( ((unsigned __int128)N(testapi2) << 64) | N(testapi2) ) ); + + BOOST_REQUIRE_EQUAL( validate(), true ); +} FC_LOG_AND_RETHROW() } + /************************************************************************************* * context free action tests *************************************************************************************/ From 431367696f0820c3177abdd53d13b5f4039122dd Mon Sep 17 00:00:00 2001 From: Kayan Date: Tue, 28 Aug 2018 17:38:55 +0800 Subject: [PATCH 15/23] don't change transaction data in cleos push signed transaction --- programs/cleos/main.cpp | 24 ++++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/programs/cleos/main.cpp b/programs/cleos/main.cpp index e478f42a2bd..5b0cba91705 100644 --- a/programs/cleos/main.cpp +++ b/programs/cleos/main.cpp @@ -276,18 +276,22 @@ void sign_transaction(signed_transaction& trx, fc::variant& required_keys, const fc::variant push_transaction( signed_transaction& trx, int32_t extra_kcpu = 1000, packed_transaction::compression_type compression = packed_transaction::none ) { auto info = get_info(); - trx.expiration = info.head_block_time + tx_expiration; + if (trx.expiration == time_point_sec()) { + trx.expiration = info.head_block_time + tx_expiration; + } // Set tapos, default to last irreversible block if it's not specified by the user - block_id_type ref_block_id = info.last_irreversible_block_id; - try { - fc::variant ref_block; - if (!tx_ref_block_num_or_id.empty()) { - ref_block = call(get_block_func, fc::mutable_variant_object("block_num_or_id", tx_ref_block_num_or_id)); - ref_block_id = ref_block["id"].as(); - } - } EOS_RETHROW_EXCEPTIONS(invalid_ref_block_exception, "Invalid reference block num or id: ${block_num_or_id}", ("block_num_or_id", tx_ref_block_num_or_id)); - trx.set_reference_block(ref_block_id); + if (trx.ref_block_prefix == 0 && trx.ref_block_num == 0) { + block_id_type ref_block_id = info.last_irreversible_block_id; + try { + fc::variant ref_block; + if (!tx_ref_block_num_or_id.empty()) { + ref_block = call(get_block_func, fc::mutable_variant_object("block_num_or_id", tx_ref_block_num_or_id)); + ref_block_id = ref_block["id"].as(); + } + } EOS_RETHROW_EXCEPTIONS(invalid_ref_block_exception, "Invalid reference block num or id: ${block_num_or_id}", ("block_num_or_id", tx_ref_block_num_or_id)); + trx.set_reference_block(ref_block_id); + } if (tx_force_unique) { trx.context_free_actions.emplace_back( generate_nonce_action() ); From 5e019870096cd72e07ab94c568f5aa31f62f535f Mon Sep 17 00:00:00 2001 From: Kayan Date: Tue, 28 Aug 2018 17:56:27 +0800 Subject: [PATCH 16/23] should not change txn content if already signed --- programs/cleos/main.cpp | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/programs/cleos/main.cpp b/programs/cleos/main.cpp index 5b0cba91705..4ce7c2dbbd3 100644 --- a/programs/cleos/main.cpp +++ b/programs/cleos/main.cpp @@ -276,12 +276,11 @@ void sign_transaction(signed_transaction& trx, fc::variant& required_keys, const fc::variant push_transaction( signed_transaction& trx, int32_t extra_kcpu = 1000, packed_transaction::compression_type compression = packed_transaction::none ) { auto info = get_info(); - if (trx.expiration == time_point_sec()) { + + if (trx.signatures.size() == 0) { // #5445 can't change txn content if already signed trx.expiration = info.head_block_time + tx_expiration; - } - // Set tapos, default to last irreversible block if it's not specified by the user - if (trx.ref_block_prefix == 0 && trx.ref_block_num == 0) { + // Set tapos, default to last irreversible block if it's not specified by the user block_id_type ref_block_id = info.last_irreversible_block_id; try { fc::variant ref_block; @@ -291,14 +290,14 @@ fc::variant push_transaction( signed_transaction& trx, int32_t extra_kcpu = 1000 } } EOS_RETHROW_EXCEPTIONS(invalid_ref_block_exception, "Invalid reference block num or id: ${block_num_or_id}", ("block_num_or_id", tx_ref_block_num_or_id)); trx.set_reference_block(ref_block_id); - } - if (tx_force_unique) { - trx.context_free_actions.emplace_back( generate_nonce_action() ); - } + if (tx_force_unique) { + trx.context_free_actions.emplace_back( generate_nonce_action() ); + } - trx.max_cpu_usage_ms = tx_max_cpu_usage; - trx.max_net_usage_words = (tx_max_net_usage + 7)/8; + trx.max_cpu_usage_ms = tx_max_cpu_usage; + trx.max_net_usage_words = (tx_max_net_usage + 7)/8; + } if (!tx_skip_sign) { auto required_keys = determine_required_keys(trx); From e6633a5e9dd7dc600688167072ccd3b0d9135d43 Mon Sep 17 00:00:00 2001 From: Jonathan LEI Date: Tue, 28 Aug 2018 22:38:31 +0800 Subject: [PATCH 17/23] Fixed ignored action name in ABI generation --- libraries/abi_generator/abi_generator.cpp | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/libraries/abi_generator/abi_generator.cpp b/libraries/abi_generator/abi_generator.cpp index 42ae4ace9d0..fc021c0a3fd 100644 --- a/libraries/abi_generator/abi_generator.cpp +++ b/libraries/abi_generator/abi_generator.cpp @@ -112,14 +112,19 @@ bool abi_generator::inspect_type_methods_for_actions(const Decl* decl) { try { // Try to get "action" annotation from method comment bool raw_comment_is_action = false; + string action_name_from_comment; const RawComment* raw_comment = ast_context->getRawCommentForDeclNoCache(method); if(raw_comment != nullptr) { SourceManager& source_manager = ast_context->getSourceManager(); string raw_text = raw_comment->getRawText(source_manager); - regex r(R"(@abi (action)((?: [a-z0-9]+)*))"); + regex r(R"(@abi (action) ?((?:[a-z0-9]+)*))"); smatch smatch; regex_search(raw_text, smatch, r); raw_comment_is_action = smatch.size() == 3; + + if (raw_comment_is_action) { + action_name_from_comment = smatch[2]; + } } // Check if current method is listed the EOSIO_ABI macro @@ -156,7 +161,8 @@ bool abi_generator::inspect_type_methods_for_actions(const Decl* decl) { try { full_types[method_name] = method_name; - output->actions.push_back({method_name, method_name, rc[method_name]}); + string action_name = action_name_from_comment.empty() ? method_name : action_name_from_comment; + output->actions.push_back({action_name, method_name, rc[method_name]}); at_least_one_action = true; }; From 55667540e2caf13c57c262f668fdd55d23fefce9 Mon Sep 17 00:00:00 2001 From: Kayan Date: Mon, 27 Aug 2018 15:33:49 +0800 Subject: [PATCH 18/23] suppress greylist related exception loggings --- libraries/chain/controller.cpp | 3 +- .../chain/include/eosio/chain/exceptions.hpp | 2 + .../eosio/chain/transaction_context.hpp | 4 +- libraries/chain/transaction_context.cpp | 49 +++++++++++++------ plugins/http_plugin/http_plugin.cpp | 8 +-- plugins/net_plugin/net_plugin.cpp | 4 +- 6 files changed, 49 insertions(+), 21 deletions(-) diff --git a/libraries/chain/controller.cpp b/libraries/chain/controller.cpp index 0c582871a11..8bd97bd82af 100644 --- a/libraries/chain/controller.cpp +++ b/libraries/chain/controller.cpp @@ -574,6 +574,7 @@ struct controller_impl { || (code == block_net_usage_exceeded::code_value) || (code == greylist_net_usage_exceeded::code_value) || (code == block_cpu_usage_exceeded::code_value) + || (code == greylist_cpu_usage_exceeded::code_value) || (code == deadline_exception::code_value) || (code == leeway_deadline_exception::code_value) || (code == actor_whitelist_exception::code_value) @@ -714,7 +715,7 @@ struct controller_impl { auto& rl = self.get_mutable_resource_limits_manager(); rl.update_account_usage( trx_context.bill_to_accounts, block_timestamp_type(self.pending_block_time()).slot ); int64_t account_cpu_limit = 0; - std::tie( std::ignore, account_cpu_limit, std::ignore ) = trx_context.max_bandwidth_billed_accounts_can_pay( true ); + std::tie( std::ignore, account_cpu_limit, std::ignore, std::ignore ) = trx_context.max_bandwidth_billed_accounts_can_pay( true ); cpu_time_to_bill_us = static_cast( std::min( std::min( static_cast(cpu_time_to_bill_us), account_cpu_limit ), diff --git a/libraries/chain/include/eosio/chain/exceptions.hpp b/libraries/chain/include/eosio/chain/exceptions.hpp index 544596540f0..fd49e930af5 100644 --- a/libraries/chain/include/eosio/chain/exceptions.hpp +++ b/libraries/chain/include/eosio/chain/exceptions.hpp @@ -268,6 +268,8 @@ namespace eosio { namespace chain { 3080006, "Transaction took too long" ) FC_DECLARE_DERIVED_EXCEPTION( greylist_net_usage_exceeded, resource_exhausted_exception, 3080007, "Transaction exceeded the current greylisted account network usage limit" ) + FC_DECLARE_DERIVED_EXCEPTION( greylist_cpu_usage_exceeded, resource_exhausted_exception, + 3080008, "Transaction exceeded the current greylisted account CPU usage limit" ) FC_DECLARE_DERIVED_EXCEPTION( leeway_deadline_exception, deadline_exception, 3081001, "Transaction reached the deadline set due to leeway on account CPU limits" ) diff --git a/libraries/chain/include/eosio/chain/transaction_context.hpp b/libraries/chain/include/eosio/chain/transaction_context.hpp index e275ecf0025..22e8eae36d6 100644 --- a/libraries/chain/include/eosio/chain/transaction_context.hpp +++ b/libraries/chain/include/eosio/chain/transaction_context.hpp @@ -42,7 +42,7 @@ namespace eosio { namespace chain { uint32_t update_billed_cpu_time( fc::time_point now ); - std::tuple max_bandwidth_billed_accounts_can_pay( bool force_elastic_limits = false )const; + std::tuple max_bandwidth_billed_accounts_can_pay( bool force_elastic_limits = false )const; private: @@ -98,6 +98,8 @@ namespace eosio { namespace chain { uint64_t eager_net_limit = 0; uint64_t& net_usage; /// reference to trace->net_usage + bool cpu_limit_due_to_greylist = false; + fc::microseconds initial_objective_duration_limit; fc::microseconds objective_duration_limit; fc::time_point _deadline = fc::time_point::maximum(); diff --git a/libraries/chain/transaction_context.cpp b/libraries/chain/transaction_context.cpp index 0545c6337b8..cceb2835af6 100644 --- a/libraries/chain/transaction_context.cpp +++ b/libraries/chain/transaction_context.cpp @@ -92,9 +92,10 @@ namespace eosio { namespace chain { // Calculate the highest network usage and CPU time that all of the billed accounts can afford to be billed int64_t account_net_limit = 0; int64_t account_cpu_limit = 0; - bool greylisted = false; - std::tie( account_net_limit, account_cpu_limit, greylisted ) = max_bandwidth_billed_accounts_can_pay(); + bool greylisted = false, greylistedCPU = false; + std::tie( account_net_limit, account_cpu_limit, greylisted, greylistedCPU) = max_bandwidth_billed_accounts_can_pay(); net_limit_due_to_greylist |= greylisted; + cpu_limit_due_to_greylist |= greylistedCPU; eager_net_limit = net_limit; @@ -224,9 +225,10 @@ namespace eosio { namespace chain { // Calculate the new highest network usage and CPU time that all of the billed accounts can afford to be billed int64_t account_net_limit = 0; int64_t account_cpu_limit = 0; - bool greylisted = false; - std::tie( account_net_limit, account_cpu_limit, greylisted ) = max_bandwidth_billed_accounts_can_pay(); + bool greylisted = false, greylistedCPU = false; + std::tie( account_net_limit, account_cpu_limit, greylisted, greylistedCPU) = max_bandwidth_billed_accounts_can_pay(); net_limit_due_to_greylist |= greylisted; + cpu_limit_due_to_greylist |= greylistedCPU; // Possibly lower net_limit to what the billed accounts can pay if( static_cast(account_net_limit) <= net_limit ) { @@ -296,9 +298,15 @@ namespace eosio { namespace chain { "not enough time left in block to complete executing transaction", ("now", now)("deadline", _deadline)("start", start)("billing_timer", now - pseudo_start) ); } else if( deadline_exception_code == tx_cpu_usage_exceeded::code_value ) { - EOS_THROW( tx_cpu_usage_exceeded, - "transaction was executing for too long", - ("now", now)("deadline", _deadline)("start", start)("billing_timer", now - pseudo_start) ); + if (cpu_limit_due_to_greylist) { + EOS_THROW( greylist_cpu_usage_exceeded, + "transaction was executing for too long", + ("now", now)("deadline", _deadline)("start", start)("billing_timer", now - pseudo_start) ); + } else { + EOS_THROW( tx_cpu_usage_exceeded, + "transaction was executing for too long", + ("now", now)("deadline", _deadline)("start", start)("billing_timer", now - pseudo_start) ); + } } else if( deadline_exception_code == leeway_deadline_exception::code_value ) { EOS_THROW( leeway_deadline_exception, "the transaction was unable to complete by deadline, " @@ -350,11 +358,19 @@ namespace eosio { namespace chain { ("billed", billed_us)("billable", objective_duration_limit.count()) ); } else { - EOS_ASSERT( billed_us <= objective_duration_limit.count(), - tx_cpu_usage_exceeded, - "billed CPU time (${billed} us) is greater than the maximum billable CPU time for the transaction (${billable} us)", - ("billed", billed_us)("billable", objective_duration_limit.count()) - ); + if (cpu_limit_due_to_greylist) { + EOS_ASSERT( billed_us <= objective_duration_limit.count(), + greylist_cpu_usage_exceeded, + "billed CPU time (${billed} us) is greater than the maximum greylisted billable CPU time for the transaction (${billable} us)", + ("billed", billed_us)("billable", objective_duration_limit.count()) + ); + } else { + EOS_ASSERT( billed_us <= objective_duration_limit.count(), + tx_cpu_usage_exceeded, + "billed CPU time (${billed} us) is greater than the maximum billable CPU time for the transaction (${billable} us)", + ("billed", billed_us)("billable", objective_duration_limit.count()) + ); + } } } } @@ -376,7 +392,7 @@ namespace eosio { namespace chain { return static_cast(billed_cpu_time_us); } - std::tuple transaction_context::max_bandwidth_billed_accounts_can_pay( bool force_elastic_limits )const { + std::tuple transaction_context::max_bandwidth_billed_accounts_can_pay( bool force_elastic_limits ) const{ // Assumes rl.update_account_usage( bill_to_accounts, block_timestamp_type(control.pending_block_time()).slot ) was already called prior // Calculate the new highest network usage and CPU time that all of the billed accounts can afford to be billed @@ -385,6 +401,7 @@ namespace eosio { namespace chain { int64_t account_net_limit = large_number_no_overflow; int64_t account_cpu_limit = large_number_no_overflow; bool greylisted = false; + bool greylistedCPU = false; for( const auto& a : bill_to_accounts ) { bool elastic = force_elastic_limits || !(control.is_producing_block() && control.is_resource_greylisted(a)); auto net_limit = rl.get_account_net_limit(a, elastic); @@ -393,11 +410,13 @@ namespace eosio { namespace chain { if (!elastic) greylisted = true; } auto cpu_limit = rl.get_account_cpu_limit(a, elastic); - if( cpu_limit >= 0 ) + if( cpu_limit >= 0 ) { account_cpu_limit = std::min( account_cpu_limit, cpu_limit ); + if (!elastic) greylistedCPU = true; + } } - return std::make_tuple(account_net_limit, account_cpu_limit, greylisted); + return std::make_tuple(account_net_limit, account_cpu_limit, greylisted, greylistedCPU); } void transaction_context::dispatch_action( action_trace& trace, const action& a, account_name receiver, bool context_free, uint32_t recurse_depth ) { diff --git a/plugins/http_plugin/http_plugin.cpp b/plugins/http_plugin/http_plugin.cpp index 0a4984381ee..c971f9dd040 100644 --- a/plugins/http_plugin/http_plugin.cpp +++ b/plugins/http_plugin/http_plugin.cpp @@ -468,9 +468,11 @@ namespace eosio { } catch (fc::exception& e) { error_results results{500, "Internal Service Error", error_results::error_info(e, verbose_http_errors)}; cb( 500, fc::json::to_string( results )); - elog( "FC Exception encountered while processing ${api}.${call}", - ("api", api_name)( "call", call_name )); - dlog( "Exception Details: ${e}", ("e", e.to_detail_string())); + if (e.code() != chain::greylist_net_usage_exceeded::code_value && e.code() != chain::greylist_cpu_usage_exceeded::code_value) { + elog( "FC Exception encountered while processing ${api}.${call}", + ("api", api_name)( "call", call_name )); + dlog( "Exception Details: ${e}", ("e", e.to_detail_string())); + } } catch (std::exception& e) { error_results results{500, "Internal Service Error", error_results::error_info(fc::exception( FC_LOG_MESSAGE( error, e.what())), verbose_http_errors)}; cb( 500, fc::json::to_string( results )); diff --git a/plugins/net_plugin/net_plugin.cpp b/plugins/net_plugin/net_plugin.cpp index eb120521e68..d4764884bfb 100644 --- a/plugins/net_plugin/net_plugin.cpp +++ b/plugins/net_plugin/net_plugin.cpp @@ -2492,7 +2492,9 @@ namespace eosio { chain_plug->accept_transaction(msg, [=](const static_variant& result) { if (result.contains()) { auto e_ptr = result.get(); - if (e_ptr->code() != tx_duplicate::code_value && e_ptr->code() != expired_tx_exception::code_value) { + if (e_ptr->code() != tx_duplicate::code_value && e_ptr->code() != expired_tx_exception::code_value + && e_ptr->code() != greylist_net_usage_exceeded::code_value + && e_ptr->code() != greylist_cpu_usage_exceeded::code_value) { elog("accept txn threw ${m}",("m",result.get()->to_detail_string())); peer_elog(c, "bad packed_transaction : ${m}", ("m",result.get()->what())); } From 2732be066c39a8b24964fde85251970f670b6f67 Mon Sep 17 00:00:00 2001 From: arhag Date: Tue, 28 Aug 2018 11:57:03 -0400 Subject: [PATCH 19/23] cleanup of greylisting variables --- libraries/chain/transaction_context.cpp | 28 +++++++++++++------------ 1 file changed, 15 insertions(+), 13 deletions(-) diff --git a/libraries/chain/transaction_context.cpp b/libraries/chain/transaction_context.cpp index cceb2835af6..ff00ce8ecbe 100644 --- a/libraries/chain/transaction_context.cpp +++ b/libraries/chain/transaction_context.cpp @@ -92,10 +92,10 @@ namespace eosio { namespace chain { // Calculate the highest network usage and CPU time that all of the billed accounts can afford to be billed int64_t account_net_limit = 0; int64_t account_cpu_limit = 0; - bool greylisted = false, greylistedCPU = false; - std::tie( account_net_limit, account_cpu_limit, greylisted, greylistedCPU) = max_bandwidth_billed_accounts_can_pay(); - net_limit_due_to_greylist |= greylisted; - cpu_limit_due_to_greylist |= greylistedCPU; + bool greylisted_net = false, greylisted_cpu = false; + std::tie( account_net_limit, account_cpu_limit, greylisted_net, greylisted_cpu) = max_bandwidth_billed_accounts_can_pay(); + net_limit_due_to_greylist |= greylisted_net; + cpu_limit_due_to_greylist |= greylisted_cpu; eager_net_limit = net_limit; @@ -225,19 +225,21 @@ namespace eosio { namespace chain { // Calculate the new highest network usage and CPU time that all of the billed accounts can afford to be billed int64_t account_net_limit = 0; int64_t account_cpu_limit = 0; - bool greylisted = false, greylistedCPU = false; - std::tie( account_net_limit, account_cpu_limit, greylisted, greylistedCPU) = max_bandwidth_billed_accounts_can_pay(); - net_limit_due_to_greylist |= greylisted; - cpu_limit_due_to_greylist |= greylistedCPU; + bool greylisted_net = false, greylisted_cpu = false; + std::tie( account_net_limit, account_cpu_limit, greylisted_net, greylisted_cpu) = max_bandwidth_billed_accounts_can_pay(); + net_limit_due_to_greylist |= greylisted_net; + cpu_limit_due_to_greylist |= greylisted_cpu; // Possibly lower net_limit to what the billed accounts can pay if( static_cast(account_net_limit) <= net_limit ) { + // NOTE: net_limit may possibly not be objective anymore due to net greylisting, but it should still be no greater than the truly objective net_limit net_limit = static_cast(account_net_limit); net_limit_due_to_block = false; } // Possibly lower objective_duration_limit to what the billed accounts can pay if( account_cpu_limit <= objective_duration_limit.count() ) { + // NOTE: objective_duration_limit may possibly not be objective anymore due to cpu greylisting, but it should still be no greater than the truly objective objective_duration_limit objective_duration_limit = fc::microseconds(account_cpu_limit); billing_timer_exception_code = tx_cpu_usage_exceeded::code_value; } @@ -400,23 +402,23 @@ namespace eosio { namespace chain { const static int64_t large_number_no_overflow = std::numeric_limits::max()/2; int64_t account_net_limit = large_number_no_overflow; int64_t account_cpu_limit = large_number_no_overflow; - bool greylisted = false; - bool greylistedCPU = false; + bool greylisted_net = false; + bool greylisted_cpu = false; for( const auto& a : bill_to_accounts ) { bool elastic = force_elastic_limits || !(control.is_producing_block() && control.is_resource_greylisted(a)); auto net_limit = rl.get_account_net_limit(a, elastic); if( net_limit >= 0 ) { account_net_limit = std::min( account_net_limit, net_limit ); - if (!elastic) greylisted = true; + if (!elastic) greylisted_net = true; } auto cpu_limit = rl.get_account_cpu_limit(a, elastic); if( cpu_limit >= 0 ) { account_cpu_limit = std::min( account_cpu_limit, cpu_limit ); - if (!elastic) greylistedCPU = true; + if (!elastic) greylisted_cpu = true; } } - return std::make_tuple(account_net_limit, account_cpu_limit, greylisted, greylistedCPU); + return std::make_tuple(account_net_limit, account_cpu_limit, greylisted_net, greylisted_cpu); } void transaction_context::dispatch_action( action_trace& trace, const action& a, account_name receiver, bool context_free, uint32_t recurse_depth ) { From 083935ea0e4fa93e1a4a101164fe3e6182c8fcac Mon Sep 17 00:00:00 2001 From: arhag Date: Tue, 28 Aug 2018 15:07:04 -0400 Subject: [PATCH 20/23] added test case involving a deferred transaction canceling itself --- contracts/test_api/test_api.cpp | 1 + contracts/test_api/test_api.hpp | 1 + contracts/test_api/test_transaction.cpp | 20 ++++++++++++++++++++ unittests/api_tests.cpp | 20 ++++++++++++++++++++ 4 files changed, 42 insertions(+) diff --git a/contracts/test_api/test_api.cpp b/contracts/test_api/test_api.cpp index 29f47d75f64..8f1922f2d4f 100644 --- a/contracts/test_api/test_api.cpp +++ b/contracts/test_api/test_api.cpp @@ -148,6 +148,7 @@ extern "C" { WASM_TEST_HANDLER(test_transaction, context_free_api); WASM_TEST_HANDLER(test_transaction, new_feature); WASM_TEST_HANDLER(test_transaction, active_new_feature); + WASM_TEST_HANDLER_EX(test_transaction, repeat_deferred_transaction); //test chain WASM_TEST_HANDLER(test_chain, test_activeprods); diff --git a/contracts/test_api/test_api.hpp b/contracts/test_api/test_api.hpp index b026642175e..a8fa3f21d65 100644 --- a/contracts/test_api/test_api.hpp +++ b/contracts/test_api/test_api.hpp @@ -181,6 +181,7 @@ struct test_transaction { static void context_free_api(); static void new_feature(); static void active_new_feature(); + static void repeat_deferred_transaction(uint64_t receiver, uint64_t code, uint64_t action); }; struct test_chain { diff --git a/contracts/test_api/test_transaction.cpp b/contracts/test_api/test_transaction.cpp index b481e9335d4..95030419f58 100644 --- a/contracts/test_api/test_transaction.cpp +++ b/contracts/test_api/test_transaction.cpp @@ -317,3 +317,23 @@ void test_transaction::new_feature() { void test_transaction::active_new_feature() { activate_feature((int64_t)N(newfeature)); } + +void test_transaction::repeat_deferred_transaction(uint64_t receiver, uint64_t code, uint64_t action) { + using namespace eosio; + + uint128_t sender_id = 0; + + uint32_t payload = unpack_action_data(); + print( "repeat_deferred_transaction called: payload = ", payload ); + + bool res = cancel_deferred( sender_id ); + + print( "\nrepeat_deferred_transaction cancelled trx with sender_id = ", sender_id, ", result is ", res ); + + if( payload == 0 ) return; + + --payload; + transaction trx; + trx.actions.emplace_back( permission_level{receiver, N(active)}, code, action, payload ); + trx.send( sender_id, receiver ); +} diff --git a/unittests/api_tests.cpp b/unittests/api_tests.cpp index 2ddf0b34253..8b2be0ad065 100644 --- a/unittests/api_tests.cpp +++ b/unittests/api_tests.cpp @@ -1119,7 +1119,27 @@ BOOST_FIXTURE_TEST_CASE(deferred_transaction_tests, TESTER) { try { produce_blocks(10); + //repeated deferred transactions { + vector traces; + auto c = control->applied_transaction.connect([&]( const transaction_trace_ptr& t) { + if (t && t->scheduled) { + traces.push_back( t ); + } + } ); + + CALL_TEST_FUNCTION(*this, "test_transaction", "repeat_deferred_transaction", fc::raw::pack( (uint32_t)5 ) ); + + produce_block(); + + c.disconnect(); + + BOOST_CHECK_EQUAL( traces.size(), 5 ); + } + + produce_blocks(10); + +{ // Trigger a tx which in turn sends a deferred tx with payer != receiver // Payer is alice in this case, this tx should fail since we don't have the authorization of alice dtt_action dtt_act1; From 538322410404ba86eb60e5e8ab45b09a2e8bf3a0 Mon Sep 17 00:00:00 2001 From: Bucky Kittinger Date: Tue, 28 Aug 2018 15:15:10 -0400 Subject: [PATCH 21/23] changed exception message --- libraries/chain/transaction_context.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/chain/transaction_context.cpp b/libraries/chain/transaction_context.cpp index ff00ce8ecbe..fc790b2f496 100644 --- a/libraries/chain/transaction_context.cpp +++ b/libraries/chain/transaction_context.cpp @@ -302,7 +302,7 @@ namespace eosio { namespace chain { } else if( deadline_exception_code == tx_cpu_usage_exceeded::code_value ) { if (cpu_limit_due_to_greylist) { EOS_THROW( greylist_cpu_usage_exceeded, - "transaction was executing for too long", + "greylisted transaction was executing for too long", ("now", now)("deadline", _deadline)("start", start)("billing_timer", now - pseudo_start) ); } else { EOS_THROW( tx_cpu_usage_exceeded, From 1978a79bb892688815c56251935eafa95e60cdf5 Mon Sep 17 00:00:00 2001 From: arhag Date: Tue, 28 Aug 2018 15:15:38 -0400 Subject: [PATCH 22/23] distinguish exception messages of greylisted vs non-greylisted resource exhausted errors --- libraries/chain/transaction_context.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/libraries/chain/transaction_context.cpp b/libraries/chain/transaction_context.cpp index ff00ce8ecbe..64683ebc049 100644 --- a/libraries/chain/transaction_context.cpp +++ b/libraries/chain/transaction_context.cpp @@ -277,11 +277,11 @@ namespace eosio { namespace chain { ("net_usage", net_usage)("net_limit", eager_net_limit) ); } else if (net_limit_due_to_greylist) { EOS_THROW( greylist_net_usage_exceeded, - "net usage of transaction is too high: ${net_usage} > ${net_limit}", + "greylisted transaction net usage is too high: ${net_usage} > ${net_limit}", ("net_usage", net_usage)("net_limit", eager_net_limit) ); } else { EOS_THROW( tx_net_usage_exceeded, - "net usage of transaction is too high: ${net_usage} > ${net_limit}", + "transaction net usage is too high: ${net_usage} > ${net_limit}", ("net_usage", net_usage)("net_limit", eager_net_limit) ); } } @@ -302,7 +302,7 @@ namespace eosio { namespace chain { } else if( deadline_exception_code == tx_cpu_usage_exceeded::code_value ) { if (cpu_limit_due_to_greylist) { EOS_THROW( greylist_cpu_usage_exceeded, - "transaction was executing for too long", + "greylisted transaction was executing for too long", ("now", now)("deadline", _deadline)("start", start)("billing_timer", now - pseudo_start) ); } else { EOS_THROW( tx_cpu_usage_exceeded, From 281ab1a6b3cc5cda89212990206db1078f4397a5 Mon Sep 17 00:00:00 2001 From: arhag Date: Tue, 28 Aug 2018 16:46:03 -0400 Subject: [PATCH 23/23] bump version to 1.2.3 --- CMakeLists.txt | 2 +- Docker/README.md | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 76f7afa7a91..0f84b7a88bb 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -27,7 +27,7 @@ set( CXX_STANDARD_REQUIRED ON) set(VERSION_MAJOR 1) set(VERSION_MINOR 2) -set(VERSION_PATCH 2) +set(VERSION_PATCH 3) set( CLI_CLIENT_EXECUTABLE_NAME cleos ) set( NODE_EXECUTABLE_NAME nodeos ) diff --git a/Docker/README.md b/Docker/README.md index 4bd51558e36..cfeb4a9da4b 100644 --- a/Docker/README.md +++ b/Docker/README.md @@ -20,10 +20,10 @@ cd eos/Docker docker build . -t eosio/eos ``` -The above will build off the most recent commit to the master branch by default. If you would like to target a specific branch/tag, you may use a build argument. For example, if you wished to generate a docker image based off of the v1.2.2 tag, you could do the following: +The above will build off the most recent commit to the master branch by default. If you would like to target a specific branch/tag, you may use a build argument. For example, if you wished to generate a docker image based off of the v1.2.3 tag, you could do the following: ```bash -docker build -t eosio/eos:v1.2.2 --build-arg branch=v1.2.2 . +docker build -t eosio/eos:v1.2.3 --build-arg branch=v1.2.3 . ``` By default, the symbol in eosio.system is set to SYS. You can override this using the symbol argument while building the docker image.