From e2bca95567365fc5b25957831b628ed111cc5ecf Mon Sep 17 00:00:00 2001 From: arhag Date: Fri, 3 May 2019 21:22:53 -0400 Subject: [PATCH 1/5] change how replace existing deferred transaction is implemented to not violate the new (and now explicitly stated) requirements imposed on chainbase objects that should be respected in this code base; following those new requirements will automatically satisfy the critical preconditions on the chainbase modify function --- libraries/chain/apply_context.cpp | 15 ++++-- libraries/chain/eosio_contract.cpp | 1 - .../include/eosio/chain/account_object.hpp | 6 +-- .../chain/include/eosio/chain/code_object.hpp | 6 +-- .../eosio/chain/contract_table_objects.hpp | 16 +++--- .../chain/generated_transaction_object.hpp | 6 +-- .../eosio/chain/permission_link_object.hpp | 4 +- .../include/eosio/chain/permission_object.hpp | 4 +- .../include/eosio/chain/producer_object.hpp | 49 ------------------ .../eosio/chain/resource_limits_private.hpp | 6 +-- .../eosio/chain/reversible_block_object.hpp | 2 +- .../eosio/chain/transaction_object.hpp | 2 +- libraries/chain/include/eosio/chain/types.hpp | 50 ++++++++++++++++++- libraries/chainbase | 2 +- plugins/chain_plugin/chain_plugin.cpp | 1 - plugins/producer_plugin/producer_plugin.cpp | 1 - unittests/auth_tests.cpp | 1 - unittests/delay_tests.cpp | 3 +- 18 files changed, 87 insertions(+), 88 deletions(-) delete mode 100644 libraries/chain/include/eosio/chain/producer_object.hpp diff --git a/libraries/chain/apply_context.cpp b/libraries/chain/apply_context.cpp index 37ad5a3762f..dc7687cf05c 100644 --- a/libraries/chain/apply_context.cpp +++ b/libraries/chain/apply_context.cpp @@ -480,10 +480,17 @@ void apply_context::schedule_deferred_transaction( const uint128_t& sender_id, a control.add_to_ram_correction( ptr->payer, orig_trx_ram_bytes ); } - db.modify( *ptr, [&]( auto& gtx ) { - if( replace_deferred_activated ) { - gtx.trx_id = trx.id(); - } + transaction_id_type trx_id_for_new_obj; + if( replace_deferred_activated ) { + trx_id_for_new_obj = trx.id(); + } else { + trx_id_for_new_obj = ptr->trx_id; + } + + // Use remove and create rather than modify because mutating the trx_id field in a modifier is unsafe. + db.remove( *ptr ); + db.create( [&]( auto& gtx ) { + gtx.trx_id = trx_id_for_new_obj; gtx.sender = receiver; gtx.sender_id = sender_id; gtx.payer = payer; diff --git a/libraries/chain/eosio_contract.cpp b/libraries/chain/eosio_contract.cpp index 4a18406ee02..9bd9a022f22 100644 --- a/libraries/chain/eosio_contract.cpp +++ b/libraries/chain/eosio_contract.cpp @@ -17,7 +17,6 @@ #include #include #include -#include #include #include diff --git a/libraries/chain/include/eosio/chain/account_object.hpp b/libraries/chain/include/eosio/chain/account_object.hpp index 28b899ba238..235c3b91284 100644 --- a/libraries/chain/include/eosio/chain/account_object.hpp +++ b/libraries/chain/include/eosio/chain/account_object.hpp @@ -17,7 +17,7 @@ namespace eosio { namespace chain { OBJECT_CTOR(account_object,(abi)) id_type id; - account_name name; + account_name name; //< name should not be changed within a chainbase modifier lambda block_timestamp_type creation_date; shared_blob abi; @@ -56,7 +56,7 @@ namespace eosio { namespace chain { }; id_type id; - account_name name; + account_name name; //< name should not be changed within a chainbase modifier lambda uint64_t recv_sequence = 0; uint64_t auth_sequence = 0; uint64_t code_sequence = 0; @@ -88,7 +88,7 @@ namespace eosio { namespace chain { OBJECT_CTOR(account_ram_correction_object); id_type id; - account_name name; + account_name name; //< name should not be changed within a chainbase modifier lambda uint64_t ram_correction = 0; }; diff --git a/libraries/chain/include/eosio/chain/code_object.hpp b/libraries/chain/include/eosio/chain/code_object.hpp index e8789c1612b..3309704a335 100644 --- a/libraries/chain/include/eosio/chain/code_object.hpp +++ b/libraries/chain/include/eosio/chain/code_object.hpp @@ -14,12 +14,12 @@ namespace eosio { namespace chain { OBJECT_CTOR(code_object, (code)) id_type id; - digest_type code_hash; + digest_type code_hash; //< code_hash should not be changed within a chainbase modifier lambda shared_blob code; uint64_t code_ref_count; uint32_t first_block_used; - uint8_t vm_type = 0; - uint8_t vm_version = 0; + uint8_t vm_type = 0; //< vm_type should not be changed within a chainbase modifier lambda + uint8_t vm_version = 0; //< vm_version should not be changed within a chainbase modifier lambda }; struct by_code_hash; diff --git a/libraries/chain/include/eosio/chain/contract_table_objects.hpp b/libraries/chain/include/eosio/chain/contract_table_objects.hpp index dc6b25b0501..bc58cb3e6d9 100644 --- a/libraries/chain/include/eosio/chain/contract_table_objects.hpp +++ b/libraries/chain/include/eosio/chain/contract_table_objects.hpp @@ -20,9 +20,9 @@ namespace eosio { namespace chain { OBJECT_CTOR(table_id_object) id_type id; - account_name code; - scope_name scope; - table_name table; + account_name code; //< code should not be changed within a chainbase modifier lambda + scope_name scope; //< scope should not be changed within a chainbase modifier lambda + table_name table; //< table should not be changed within a chainbase modifier lambda account_name payer; uint32_t count = 0; /// the number of elements in the table }; @@ -59,8 +59,8 @@ namespace eosio { namespace chain { static const int number_of_keys = 1; id_type id; - table_id t_id; - uint64_t primary_key; + table_id t_id; //< t_id should not be changed within a chainbase modifier lambda + uint64_t primary_key; //< primary_key should not be changed within a chainbase modifier lambda account_name payer = 0; shared_blob value; }; @@ -90,10 +90,10 @@ namespace eosio { namespace chain { typedef SecondaryKey secondary_key_type; typename chainbase::object::id_type id; - table_id t_id; - uint64_t primary_key; + table_id t_id; //< t_id should not be changed within a chainbase modifier lambda + uint64_t primary_key; //< primary_key should not be changed within a chainbase modifier lambda account_name payer = 0; - SecondaryKey secondary_key; + SecondaryKey secondary_key; //< secondary_key should not be changed within a chainbase modifier lambda }; diff --git a/libraries/chain/include/eosio/chain/generated_transaction_object.hpp b/libraries/chain/include/eosio/chain/generated_transaction_object.hpp index 7c3da995e65..74e07a4d9e9 100644 --- a/libraries/chain/include/eosio/chain/generated_transaction_object.hpp +++ b/libraries/chain/include/eosio/chain/generated_transaction_object.hpp @@ -28,9 +28,9 @@ namespace eosio { namespace chain { OBJECT_CTOR(generated_transaction_object, (packed_trx) ) id_type id; - transaction_id_type trx_id; - account_name sender; - uint128_t sender_id = 0; /// ID given this transaction by the sender + transaction_id_type trx_id; //< trx_id should not be changed within a chainbase modifier lambda + account_name sender; //< sender should not be changed within a chainbase modifier lambda + uint128_t sender_id = 0; /// ID given this transaction by the sender (should not be changed within a chainbase modifier lambda) account_name payer; time_point delay_until; /// this generated transaction will not be applied until the specified time time_point expiration; /// this generated transaction will not be applied after this time diff --git a/libraries/chain/include/eosio/chain/permission_link_object.hpp b/libraries/chain/include/eosio/chain/permission_link_object.hpp index b7b4ea76f57..ee5b1287b18 100644 --- a/libraries/chain/include/eosio/chain/permission_link_object.hpp +++ b/libraries/chain/include/eosio/chain/permission_link_object.hpp @@ -39,6 +39,7 @@ namespace eosio { namespace chain { /// May be empty; if so, it sets a default @ref required_permission for all messages to @ref code action_name message_type; /// The permission level which @ref account requires for the specified message types + /// all of the above fields should not be changed within a chainbase modifier lambda permission_name required_permission; }; @@ -61,8 +62,7 @@ namespace eosio { namespace chain { composite_key > > diff --git a/libraries/chain/include/eosio/chain/permission_object.hpp b/libraries/chain/include/eosio/chain/permission_object.hpp index 7db580b74d8..335ce754907 100644 --- a/libraries/chain/include/eosio/chain/permission_object.hpp +++ b/libraries/chain/include/eosio/chain/permission_object.hpp @@ -32,8 +32,8 @@ namespace eosio { namespace chain { id_type id; permission_usage_object::id_type usage_id; id_type parent; ///< parent permission - account_name owner; ///< the account this permission belongs to - permission_name name; ///< human-readable name for the permission + account_name owner; ///< the account this permission belongs to (should not be changed within a chainbase modifier lambda) + permission_name name; ///< human-readable name for the permission (should not be changed within a chainbase modifier lambda) time_point last_updated; ///< the last time this authority was updated shared_authority auth; ///< authority required to execute this permission diff --git a/libraries/chain/include/eosio/chain/producer_object.hpp b/libraries/chain/include/eosio/chain/producer_object.hpp deleted file mode 100644 index 61d23870280..00000000000 --- a/libraries/chain/include/eosio/chain/producer_object.hpp +++ /dev/null @@ -1,49 +0,0 @@ -/** - * @file - * @copyright defined in eos/LICENSE - */ -#pragma once -#include -#include - -#include "multi_index_includes.hpp" - -namespace eosio { namespace chain { -class producer_object : public chainbase::object { - OBJECT_CTOR(producer_object) - - id_type id; - account_name owner; - uint64_t last_aslot = 0; - public_key_type signing_key; - int64_t total_missed = 0; - uint32_t last_confirmed_block_num = 0; - - - /// The blockchain configuration values this producer recommends - chain_config configuration; -}; - -struct by_key; -struct by_owner; -using producer_multi_index = chainbase::shared_multi_index_container< - producer_object, - indexed_by< - ordered_unique, member>, - ordered_unique, member>, - ordered_unique, - composite_key, - member - > - > - > ->; - -} } // eosio::chain - -CHAINBASE_SET_INDEX_TYPE(eosio::chain::producer_object, eosio::chain::producer_multi_index) - -FC_REFLECT(eosio::chain::producer_object::id_type, (_id)) -FC_REFLECT(eosio::chain::producer_object, (id)(owner)(last_aslot)(signing_key)(total_missed)(last_confirmed_block_num) - (configuration)) diff --git a/libraries/chain/include/eosio/chain/resource_limits_private.hpp b/libraries/chain/include/eosio/chain/resource_limits_private.hpp index dc5b26008bd..e3ac7346263 100644 --- a/libraries/chain/include/eosio/chain/resource_limits_private.hpp +++ b/libraries/chain/include/eosio/chain/resource_limits_private.hpp @@ -133,8 +133,8 @@ namespace eosio { namespace chain { namespace resource_limits { OBJECT_CTOR(resource_limits_object) id_type id; - account_name owner; - bool pending = false; + account_name owner; //< owner should not be changed within a chainbase modifier lambda + bool pending = false; //< pending should not be changed within a chainbase modifier lambda int64_t net_weight = -1; int64_t cpu_weight = -1; @@ -162,7 +162,7 @@ namespace eosio { namespace chain { namespace resource_limits { OBJECT_CTOR(resource_usage_object) id_type id; - account_name owner; + account_name owner; //< owner should not be changed within a chainbase modifier lambda usage_accumulator net_usage; usage_accumulator cpu_usage; diff --git a/libraries/chain/include/eosio/chain/reversible_block_object.hpp b/libraries/chain/include/eosio/chain/reversible_block_object.hpp index daaac00a71b..c493a4915e2 100644 --- a/libraries/chain/include/eosio/chain/reversible_block_object.hpp +++ b/libraries/chain/include/eosio/chain/reversible_block_object.hpp @@ -17,7 +17,7 @@ namespace eosio { namespace chain { OBJECT_CTOR(reversible_block_object,(packedblock) ) id_type id; - uint32_t blocknum = 0; + uint32_t blocknum = 0; //< blocknum should not be changed within a chainbase modifier lambda shared_string packedblock; void set_block( const signed_block_ptr& b ) { diff --git a/libraries/chain/include/eosio/chain/transaction_object.hpp b/libraries/chain/include/eosio/chain/transaction_object.hpp index 50a7eb62cdd..cf87e11b5e9 100644 --- a/libraries/chain/include/eosio/chain/transaction_object.hpp +++ b/libraries/chain/include/eosio/chain/transaction_object.hpp @@ -27,7 +27,7 @@ namespace eosio { namespace chain { id_type id; time_point_sec expiration; - transaction_id_type trx_id; + transaction_id_type trx_id; //< trx_id should not be changed within a chainbase modifier lambda }; struct by_expiration; diff --git a/libraries/chain/include/eosio/chain/types.hpp b/libraries/chain/include/eosio/chain/types.hpp index e701e92c4fb..d6323fbd23d 100644 --- a/libraries/chain/include/eosio/chain/types.hpp +++ b/libraries/chain/include/eosio/chain/types.hpp @@ -169,7 +169,7 @@ namespace eosio { namespace chain { block_summary_object_type, transaction_object_type, generated_transaction_object_type, - producer_object_type, + UNUSED_producer_object_type, UNUSED_chain_property_object_type, account_control_history_object_type, ///< Defined by history_plugin UNUSED_account_transaction_history_object_type, @@ -195,8 +195,54 @@ namespace eosio { namespace chain { OBJECT_TYPE_COUNT ///< Sentry value which contains the number of different object types }; + /** + * Important notes on using chainbase objects in EOSIO code: + * + * There are several constraints that need to be followed when using chainbase objects. + * Some of these constraints are due to the requirements imposed by the chainbase library, + * others are due to requirements to ensure determinism in the EOSIO chain library. + * + * Before listing the constraints, the "restricted field set" must be defined. + * + * Every chainbase object includes a field called id which has the type id_type. + * The id field is always included in the restricted field set. + * + * A field of a chainbase object is considered to be in the restricted field set if it is involved in the + * derivation of the key used for one of the indices in the chainbase multi-index unless its only involvement + * is through being included in composite_keys that end with the id field. + * + * So if the multi-index includes an index like the following + * ``` + * ordered_unique< tag, + * composite_key< generated_transaction_object, + * BOOST_MULTI_INDEX_MEMBER( generated_transaction_object, account_name, sender), + * BOOST_MULTI_INDEX_MEMBER( generated_transaction_object, uint128_t, sender_id) + * > + * > + * ``` + * both `sender` and `sender_id` fields are part of the restricted field set. + * + * On the other hand, an index like the following + * ``` + * ordered_unique< tag, + * composite_key< generated_transaction_object, + * BOOST_MULTI_INDEX_MEMBER( generated_transaction_object, time_point, expiration), + * BOOST_MULTI_INDEX_MEMBER( generated_transaction_object, generated_transaction_object::id_type, id) + * > + * > + * ``` + * would not by itself require the `expiration` field to be part of the restricted field set. + * + * The restrictions on usage of the chainbase objects within this code base are: + * + The chainbase object includes the id field discussed above. + * + The multi-index must include an ordered_unique index tagged with by_id that is based on the id field as the sole key. + * + No other types of indices other than ordered_unique are allowed. + * If an index is desired that does not enforce uniqueness, then use a composite key that ends with the id field. + * + When creating a chainbase object, the constructor lambda should never mutate the id field. + * + When modifying a chainbase object, the modifier lambda should never mutate any fields in the restricted field set. + */ + class account_object; - class producer_object; using block_id_type = fc::sha256; using checksum_type = fc::sha256; diff --git a/libraries/chainbase b/libraries/chainbase index 118c513436e..57b97a0a794 160000 --- a/libraries/chainbase +++ b/libraries/chainbase @@ -1 +1 @@ -Subproject commit 118c513436e1310d8e1395303c964430f26b0bb4 +Subproject commit 57b97a0a794f23f3eb7bae32388755e27528c930 diff --git a/plugins/chain_plugin/chain_plugin.cpp b/plugins/chain_plugin/chain_plugin.cpp index 4c0ef6acdb0..6b0247afd0d 100644 --- a/plugins/chain_plugin/chain_plugin.cpp +++ b/plugins/chain_plugin/chain_plugin.cpp @@ -7,7 +7,6 @@ #include #include #include -#include #include #include #include diff --git a/plugins/producer_plugin/producer_plugin.cpp b/plugins/producer_plugin/producer_plugin.cpp index e2586db8fb7..044caa2757e 100644 --- a/plugins/producer_plugin/producer_plugin.cpp +++ b/plugins/producer_plugin/producer_plugin.cpp @@ -3,7 +3,6 @@ * @copyright defined in eos/LICENSE */ #include -#include #include #include #include diff --git a/unittests/auth_tests.cpp b/unittests/auth_tests.cpp index 0422487aae8..b944b532423 100644 --- a/unittests/auth_tests.cpp +++ b/unittests/auth_tests.cpp @@ -8,7 +8,6 @@ #include #include -#include #ifdef NON_VALIDATING_TEST #define TESTER tester diff --git a/unittests/delay_tests.cpp b/unittests/delay_tests.cpp index 913f3395e8e..2d6f77c7ee9 100644 --- a/unittests/delay_tests.cpp +++ b/unittests/delay_tests.cpp @@ -4,7 +4,6 @@ */ #include #include -#include #include #include @@ -261,7 +260,7 @@ BOOST_AUTO_TEST_CASE(delete_auth_test) { try { expect_assert_message(e, "permission_query_exception: Permission Query Exception\nFailed to retrieve permission"); return true; }); - + // update auth chain.push_action(config::system_account_name, updateauth::get_name(), tester_account, fc::mutable_variant_object() ("account", "tester") From 87d9dd627299d86fbed3982a5221782ba923e300 Mon Sep 17 00:00:00 2001 From: arhag Date: Fri, 3 May 2019 23:14:09 -0400 Subject: [PATCH 2/5] add new unit test, api_tests/more_deferred_transaction_tests, to check that certain deferred transaction replacement schemes do not cause chainbase failure any more Modified the deferred_test test contract to support the new unit test. --- unittests/api_tests.cpp | 185 +++++++++++++++++- .../deferred_test/deferred_test.abi | 45 +++++ .../deferred_test/deferred_test.cpp | 20 ++ .../deferred_test/deferred_test.hpp | 7 + .../deferred_test/deferred_test.wasm | Bin 10152 -> 11274 bytes 5 files changed, 255 insertions(+), 2 deletions(-) diff --git a/unittests/api_tests.cpp b/unittests/api_tests.cpp index 206d3878ff2..3f8831051bc 100644 --- a/unittests/api_tests.cpp +++ b/unittests/api_tests.cpp @@ -24,6 +24,7 @@ #include #include #include +#include #include #include @@ -1090,7 +1091,7 @@ BOOST_FIXTURE_TEST_CASE(transaction_tests, TESTER) { try { transaction_trace_ptr trace; auto c = control->applied_transaction.connect([&](std::tuple x) { auto& t = std::get<0>(x); - if (t && t->receipt && t->receipt->status != transaction_receipt::executed) { trace = t; } + if (t && t->receipt && t->receipt->status != transaction_receipt::executed) { trace = t; } } ); // test error handling on deferred transaction failure @@ -1139,7 +1140,7 @@ BOOST_FIXTURE_TEST_CASE(deferred_transaction_tests, TESTER) { try { transaction_trace_ptr trace; auto c = control->applied_transaction.connect([&](std::tuple x) { auto& t = std::get<0>(x); - if (t->scheduled) { trace = t; } + if (t->scheduled) { trace = t; } } ); CALL_TEST_FUNCTION(*this, "test_transaction", "send_deferred_transaction", {} ); BOOST_CHECK(!trace); @@ -1317,6 +1318,186 @@ BOOST_FIXTURE_TEST_CASE(deferred_transaction_tests, TESTER) { try { BOOST_REQUIRE_EQUAL( validate(), true ); } FC_LOG_AND_RETHROW() } +BOOST_FIXTURE_TEST_CASE(more_deferred_transaction_tests, TESTER) { try { + auto cfg = validating_tester::default_config(); + cfg.contracts_console = true; + validating_tester chain( cfg ); + chain.execute_setup_policy( setup_policy::preactivate_feature_and_new_bios ); + + const auto& pfm = chain.control->get_protocol_feature_manager(); + auto d = pfm.get_builtin_digest( builtin_protocol_feature_t::replace_deferred ); + BOOST_REQUIRE( d ); + + chain.preactivate_protocol_features( {*d} ); + chain.produce_block(); + + const auto& index = chain.control->db().get_index(); + + auto print_deferred = [&index]() { + for( const auto& gto : index ) { + wlog("id = ${id}, trx_id = ${trx_id}", ("id", gto.id)("trx_id", gto.trx_id)); + } + }; + + const auto& contract_account = account_name("tester"); + const auto& test_account = account_name("alice"); + + chain.create_accounts( {contract_account, test_account} ); + chain.set_code( contract_account, contracts::deferred_test_wasm() ); + chain.set_abi( contract_account, contracts::deferred_test_abi().data() ); + chain.produce_block(); + + BOOST_REQUIRE_EQUAL(0, index.size()); + + chain.push_action( contract_account, N(delayedcall), test_account, fc::mutable_variant_object() + ("payer", test_account) + ("sender_id", 0) + ("contract", contract_account) + ("payload", 42) + ("delay_sec", 1000) + ("replace_existing", false) + ); + + BOOST_REQUIRE_EQUAL(1, index.size()); + print_deferred(); + + signed_transaction trx; + trx.actions.emplace_back( + chain.get_action( contract_account, N(delayedcall), + vector{{test_account, config::active_name}}, + fc::mutable_variant_object() + ("payer", test_account) + ("sender_id", 0) + ("contract", contract_account) + ("payload", 13) + ("delay_sec", 1000) + ("replace_existing", true) + ) + ); + trx.actions.emplace_back( + chain.get_action( contract_account, N(delayedcall), + vector{{test_account, config::active_name}}, + fc::mutable_variant_object() + ("payer", test_account) + ("sender_id", 1) + ("contract", contract_account) + ("payload", 42) + ("delay_sec", 1000) + ("replace_existing", false) + ) + ); + trx.actions.emplace_back( + chain.get_action( contract_account, N(fail), + vector{}, + fc::mutable_variant_object() + ) + ); + chain.set_transaction_headers(trx); + trx.sign( chain.get_private_key( test_account, "active" ), chain.control->get_chain_id() ); + BOOST_REQUIRE_EXCEPTION( + chain.push_transaction( trx ), + eosio_assert_message_exception, + eosio_assert_message_is("fail") + ); + + BOOST_REQUIRE_EQUAL(1, index.size()); + print_deferred(); + + chain.produce_blocks(2); + + chain.push_action( contract_account, N(delayedcall), test_account, fc::mutable_variant_object() + ("payer", test_account) + ("sender_id", 1) + ("contract", contract_account) + ("payload", 101) + ("delay_sec", 1000) + ("replace_existing", false) + ); + + chain.push_action( contract_account, N(delayedcall), test_account, fc::mutable_variant_object() + ("payer", test_account) + ("sender_id", 2) + ("contract", contract_account) + ("payload", 102) + ("delay_sec", 1000) + ("replace_existing", false) + ); + + BOOST_REQUIRE_EQUAL(3, index.size()); + print_deferred(); + + BOOST_REQUIRE_THROW( + chain.push_action( contract_account, N(delayedcall), test_account, fc::mutable_variant_object() + ("payer", test_account) + ("sender_id", 2) + ("contract", contract_account) + ("payload", 101) + ("delay_sec", 1000) + ("replace_existing", true) + ), + fc::exception + ); + + BOOST_REQUIRE_EQUAL(3, index.size()); + print_deferred(); + + signed_transaction trx2; + trx2.actions.emplace_back( + chain.get_action( contract_account, N(delayedcall), + vector{{test_account, config::active_name}}, + fc::mutable_variant_object() + ("payer", test_account) + ("sender_id", 1) + ("contract", contract_account) + ("payload", 100) + ("delay_sec", 1000) + ("replace_existing", true) + ) + ); + trx2.actions.emplace_back( + chain.get_action( contract_account, N(delayedcall), + vector{{test_account, config::active_name}}, + fc::mutable_variant_object() + ("payer", test_account) + ("sender_id", 2) + ("contract", contract_account) + ("payload", 101) + ("delay_sec", 1000) + ("replace_existing", true) + ) + ); + trx2.actions.emplace_back( + chain.get_action( contract_account, N(delayedcall), + vector{{test_account, config::active_name}}, + fc::mutable_variant_object() + ("payer", test_account) + ("sender_id", 1) + ("contract", contract_account) + ("payload", 102) + ("delay_sec", 1000) + ("replace_existing", true) + ) + ); + trx2.actions.emplace_back( + chain.get_action( contract_account, N(fail), + vector{}, + fc::mutable_variant_object() + ) + ); + chain.set_transaction_headers(trx2); + trx2.sign( chain.get_private_key( test_account, "active" ), chain.control->get_chain_id() ); + BOOST_REQUIRE_EXCEPTION( + chain.push_transaction( trx2 ), + eosio_assert_message_exception, + eosio_assert_message_is("fail") + ); + + BOOST_REQUIRE_EQUAL(3, index.size()); + print_deferred(); + + BOOST_REQUIRE_EQUAL( validate(), true ); +} FC_LOG_AND_RETHROW() } + template struct setprod_act { static account_name get_account() { diff --git a/unittests/test-contracts/deferred_test/deferred_test.abi b/unittests/test-contracts/deferred_test/deferred_test.abi index 37c9eff9492..857e725efec 100644 --- a/unittests/test-contracts/deferred_test/deferred_test.abi +++ b/unittests/test-contracts/deferred_test/deferred_test.abi @@ -35,6 +35,41 @@ } ] }, + { + "name": "delayedcall", + "base": "", + "fields": [ + { + "name": "payer", + "type": "name" + }, + { + "name": "sender_id", + "type": "uint64" + }, + { + "name": "contract", + "type": "name" + }, + { + "name": "payload", + "type": "uint64" + }, + { + "name": "delay_sec", + "type": "uint32" + }, + { + "name": "replace_existing", + "type": "bool" + } + ] + }, + { + "name": "fail", + "base": "", + "fields": [] + }, { "name": "inlinecall", "base": "", @@ -65,6 +100,16 @@ "type": "deferfunc", "ricardian_contract": "" }, + { + "name": "delayedcall", + "type": "delayedcall", + "ricardian_contract": "" + }, + { + "name": "fail", + "type": "fail", + "ricardian_contract": "" + }, { "name": "inlinecall", "type": "inlinecall", diff --git a/unittests/test-contracts/deferred_test/deferred_test.cpp b/unittests/test-contracts/deferred_test/deferred_test.cpp index b24096de146..1ff307d8426 100644 --- a/unittests/test-contracts/deferred_test/deferred_test.cpp +++ b/unittests/test-contracts/deferred_test/deferred_test.cpp @@ -40,6 +40,22 @@ void deferred_test::defercall( name payer, uint64_t sender_id, name contract, ui trx.send( (static_cast(payer.value) << 64) | sender_id, payer, replace_existing ); } +void deferred_test::delayedcall( name payer, uint64_t sender_id, name contract, + uint64_t payload, uint32_t delay_sec, bool replace_existing ) +{ + print( "delayedcall called on ", get_self(), "\n" ); + require_auth( payer ); + + print( "deferred send of deferfunc action (with delay of ", delay_sec, " sec) to ", contract, " by ", payer, + " with sender id ", sender_id, " and payload ", payload ); + transaction trx; + trx.delay_sec = delay_sec; + deferfunc_action a( contract, {get_self(), "active"_n} ); + trx.actions.emplace_back( a.to_action( payload ) ); + + trx.send( sender_id, payer, replace_existing ); +} + void deferred_test::deferfunc( uint64_t payload ) { print( "deferfunc called on ", get_self(), " with payload = ", payload, "\n" ); check( payload != 13, "value 13 not allowed in payload" ); @@ -50,6 +66,10 @@ void deferred_test::inlinecall( name contract, name authorizer, uint64_t payload a.send( payload ); } +void deferred_test::fail() { + check( false, "fail" ); +} + void deferred_test::on_error( uint128_t sender_id, ignore> sent_trx ) { print( "onerror called on ", get_self(), "\n" ); } diff --git a/unittests/test-contracts/deferred_test/deferred_test.hpp b/unittests/test-contracts/deferred_test/deferred_test.hpp index 1e5aa22681b..ba7b5c26730 100644 --- a/unittests/test-contracts/deferred_test/deferred_test.hpp +++ b/unittests/test-contracts/deferred_test/deferred_test.hpp @@ -14,6 +14,10 @@ class [[eosio::contract]] deferred_test : public eosio::contract { [[eosio::action]] void defercall( eosio::name payer, uint64_t sender_id, eosio::name contract, uint64_t payload ); + [[eosio::action]] + void delayedcall( eosio::name payer, uint64_t sender_id, eosio::name contract, + uint64_t payload, uint32_t delay_sec, bool replace_existing ); + [[eosio::action]] void deferfunc( uint64_t payload ); using deferfunc_action = eosio::action_wrapper<"deferfunc"_n, &deferred_test::deferfunc>; @@ -21,6 +25,9 @@ class [[eosio::contract]] deferred_test : public eosio::contract { [[eosio::action]] void inlinecall( eosio::name contract, eosio::name authorizer, uint64_t payload ); + [[eosio::action]] + void fail(); + [[eosio::on_notify("eosio::onerror")]] void on_error( uint128_t sender_id, eosio::ignore> sent_trx ); }; diff --git a/unittests/test-contracts/deferred_test/deferred_test.wasm b/unittests/test-contracts/deferred_test/deferred_test.wasm index 588e38fadf2ed8f3d8a7952114113f3bc43ebd61..416dad9fd5f79c5fea5d29361ffa0eceaffc40d2 100755 GIT binary patch delta 1488 zcmY*YUuc_E6u*DIKUu$a-m$JWX|kU0OWG#2vN$w}(ssF>TU+fcf{Z<=7@KtKXKeaU zwxTRSR_OL1)eFiP3}nK5nK0*km_GC$Oax_p7$OYcRK$u3`drUV+Sq*$zjN-n=imLE z^L;vc=~N@h&Av$qA+$mdyzflgcKJMh<(u`nw%19 zweT{Pm1W|TiM+H{7xo@%=BB2K^8_01pXfG-h&mf;ltb$gbD*-mvGT#sS2n&Sz+N=6 zK!A#@n&K^oBwki-ezA1>&fov+x*X7E{jGoh<1X`2b(P1|GLO?|Z~ewQ19a<_XxyS& z>=h0@rrhAGbW2r&BeY+A7c8OP7aBR;9|8*u8TUP7u!%U1t}m0mIPMz2r0`<|4CfkW zv`o)OiCm&@csAN>Fw%UH0!eV<2ITSz5flP~$#<(761-5p5ttQT44`zzJtXmmazR*{` zE_?kH8^SYB^JJ|vE`{jwOl=`h-X`w$SCt3peS9I92;O$JULR_j&Yi(r+6iHi z`r=8Hn*>kdDV7C0&cdE~tV#C>m_&DLiiA_U5<6GT|6dI|wa&3Ju-cQ606b8zM5Y#( z*@?qO>M5}TI*O-5s4P(&iMkQ`7pHiwZ2_m42oE97gu4SnIJ&Se2Gu9wakj9g8Y6Kn zQju;gUayJwBCqVZ&Z$|(Y2H(xg-2V5u(ToV=bMNma$$`K%K?rRRdWQ=_46{eYRZ7H zL%{VIGS&5PO$@fAz}?v$oKe$}wsJidDclZa$dzkV3%)Zg;h``FhVSC6WRH(24PA-J z*p7SbAzv;7+KE=%d7K1rop^l=99W9A!cmTFV2Tc03j!QynhO-zdjOp=o+tQ_hOCB+ zh9vMf>p?cipy)-Xd+L|SOmzDlCg!TWKfwd8B|oliGr^_*a#g*3Fv#w%st*q?YObNZ zM~fHQqgu?i&(l8jN4wiKLOJs8F_*Kf2zv&u56f2Bs2IbzN``P<)=1S);a~&u`72hi zfGY>i?Az6Zz*oynbpejx;Md_f#7Epd#1rl$;@7UW*xqTxl6OU8M@q@)In?e%lXOu1 z9{mXQzR)4{dCWuYe(ZA9tBClZBZauXGs_?Fed=WAO8cYvLNPaA$dBcUMbJ>lLunES z(Z9HJSl#XPPR7qp%$0i=|v%8<9ij zK)x_um>GX-a_r&k$m$baEt=Kgu8t<;!ZF8l6Ge>LRHd#UY@x+EJ2Np?KoipAm@|c3 M9(CdvOD-Y*0o;*UGXMYp delta 586 zcmYk3KWGzC9LL|gd$~q0kymsuN0Yw0TymF0NEbVqHWynhB1#3pp|J#7wIQ)dEygT1 z2&R&$`a>j&1i?Xy2&Hy$5EGnq5^?F|Bo2ZI&EisjFD`w<=lkXN-tQl;b>-DXofK}B zDItX3KK3wB-CD0IitE5%C9E;+C>{`MSiI~JeT{fhh7M6*p;9T;i8$Ok<1oUb`U}Xb zB0b^DH5!el{r=|XPedrQEGvkhd{TzD-frx?`?Gf-oP}N0e#GL`+jdyoPutrU0{c|T z5w6o5%!jVhHoOm=ry0U5Sc+{x8+hJk-qcM5jl%rVAey z_46S^s0r0?YTkz~;ZzW=c;5z2*38tMIKG2I}FRuxCdlH9!Zl6Q`v@^(f>?ZPYOh&%Pt|MpS&md{hT|CeB-tUFG|Rr#5nR-BCCE?0+3F2p`1Ey;1c?GTjVF5kSnQIdA4;_Z6*#H0l From bd327eb4a66d61dafc7d0e5079c430cc611b1795 Mon Sep 17 00:00:00 2001 From: arhag Date: Sat, 4 May 2019 02:14:35 -0400 Subject: [PATCH 3/5] no need for boost fixture test case --- unittests/api_tests.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/unittests/api_tests.cpp b/unittests/api_tests.cpp index 3f8831051bc..eb05533362b 100644 --- a/unittests/api_tests.cpp +++ b/unittests/api_tests.cpp @@ -1318,7 +1318,7 @@ BOOST_FIXTURE_TEST_CASE(deferred_transaction_tests, TESTER) { try { BOOST_REQUIRE_EQUAL( validate(), true ); } FC_LOG_AND_RETHROW() } -BOOST_FIXTURE_TEST_CASE(more_deferred_transaction_tests, TESTER) { try { +BOOST_AUTO_TEST_CASE(more_deferred_transaction_tests) { try { auto cfg = validating_tester::default_config(); cfg.contracts_console = true; validating_tester chain( cfg ); From 1e5b6cc9e1e73c4e7feaf710809abd3ca1e46c88 Mon Sep 17 00:00:00 2001 From: arhag Date: Sat, 4 May 2019 02:31:07 -0400 Subject: [PATCH 4/5] forgot to fix the call to validate() --- unittests/api_tests.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/unittests/api_tests.cpp b/unittests/api_tests.cpp index eb05533362b..e3b45d07718 100644 --- a/unittests/api_tests.cpp +++ b/unittests/api_tests.cpp @@ -1495,7 +1495,7 @@ BOOST_AUTO_TEST_CASE(more_deferred_transaction_tests) { try { BOOST_REQUIRE_EQUAL(3, index.size()); print_deferred(); - BOOST_REQUIRE_EQUAL( validate(), true ); + BOOST_REQUIRE_EQUAL( chain.validate(), true ); } FC_LOG_AND_RETHROW() } template From 0894e826c94cbadceda0aa77f406893c5833c6f7 Mon Sep 17 00:00:00 2001 From: arhag Date: Mon, 6 May 2019 10:55:22 -0400 Subject: [PATCH 5/5] update chainbase submodule --- libraries/chainbase | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/chainbase b/libraries/chainbase index 57b97a0a794..b769749d533 160000 --- a/libraries/chainbase +++ b/libraries/chainbase @@ -1 +1 @@ -Subproject commit 57b97a0a794f23f3eb7bae32388755e27528c930 +Subproject commit b769749d53303ec1a037d483c631d02268cbf012