From 324260dfc10d6d3f9ea3f18dd41e23d9c87582c7 Mon Sep 17 00:00:00 2001 From: arhag Date: Thu, 4 Apr 2019 18:44:34 -0400 Subject: [PATCH 01/10] implement NO_DUPLICATE_DEFERRED_ID protocol feature #6115 --- libraries/chain/apply_context.cpp | 36 ++++++++- libraries/chain/controller.cpp | 20 ++++- .../include/eosio/chain/block_header.hpp | 35 --------- .../chain/include/eosio/chain/exceptions.hpp | 4 + .../eosio/chain/protocol_feature_manager.hpp | 1 + .../chain/include/eosio/chain/transaction.hpp | 74 +++++++++---------- libraries/chain/include/eosio/chain/types.hpp | 37 ++++++++++ libraries/chain/protocol_feature_manager.cpp | 13 ++++ libraries/chain/transaction.cpp | 49 ++++++++++++ libraries/chain/transaction_context.cpp | 14 +++- 10 files changed, 202 insertions(+), 81 deletions(-) diff --git a/libraries/chain/apply_context.cpp b/libraries/chain/apply_context.cpp index 37c78ff7c7b..edffc87029c 100644 --- a/libraries/chain/apply_context.cpp +++ b/libraries/chain/apply_context.cpp @@ -288,13 +288,45 @@ void apply_context::execute_context_free_inline( action&& a ) { void apply_context::schedule_deferred_transaction( const uint128_t& sender_id, account_name payer, transaction&& trx, bool replace_existing ) { EOS_ASSERT( trx.context_free_actions.size() == 0, cfa_inside_generated_tx, "context free actions are not currently allowed in generated transactions" ); - trx.expiration = control.pending_block_time() + fc::microseconds(999'999); // Rounds up to nearest second (makes expiration check unnecessary) - trx.set_reference_block(control.head_block_id()); // No TaPoS check necessary bool enforce_actor_whitelist_blacklist = trx_context.enforce_whiteblacklist && control.is_producing_block() && !control.sender_avoids_whitelist_blacklist_enforcement( receiver ); trx_context.validate_referenced_accounts( trx, enforce_actor_whitelist_blacklist ); + if( control.is_builtin_activated( builtin_protocol_feature_t::no_duplicate_deferred_id ) ) { + auto exts = trx.validate_and_extract_extensions(); + if( exts.size() > 0 ) { + EOS_ASSERT( exts.size() == 1, invalid_transaction_extension, + "only one extension is currently supported for deferred transactions" + ); + const auto& context = exts.front().get(); + EOS_ASSERT( context.sender == receiver, ill_formed_deferred_transaction_generation_context, + "deferred transaction generaction context contains mismatching sender", + ("expected", receiver)("actual", context.sender) + ); + EOS_ASSERT( context.sender_id == sender_id, ill_formed_deferred_transaction_generation_context, + "deferred transaction generaction context contains mismatching sender_id", + ("expected", sender_id)("actual", context.sender_id) + ); + EOS_ASSERT( context.sender_trx_id == trx_context.id, ill_formed_deferred_transaction_generation_context, + "deferred transaction generaction context contains mismatching sender_trx_id", + ("expected", trx_context.id)("actual", context.sender_trx_id) + ); + } else { + FC_ASSERT( trx.transaction_extensions.size() == 0, "invariant failure" ); + trx.transaction_extensions.emplace_back( + deferred_transaction_generation_context::extension_id(), + fc::raw::pack( deferred_transaction_generation_context( trx_context.id, sender_id, receiver ) ) + ); + } + trx.expiration = {}; + trx.ref_block_num = 0; + trx.ref_block_prefix = 0; + } else { + trx.expiration = control.pending_block_time() + fc::microseconds(999'999); // Rounds up to nearest second (makes expiration check unnecessary) + trx.set_reference_block(control.head_block_id()); // No TaPoS check necessary + } + // Charge ahead of time for the additional net usage needed to retire the deferred transaction // whether that be by successfully executing, soft failure, hard failure, or expiration. const auto& cfg = control.get_global_properties().configuration; diff --git a/libraries/chain/controller.cpp b/libraries/chain/controller.cpp index 2e732c869dc..744ca4bf04b 100644 --- a/libraries/chain/controller.cpp +++ b/libraries/chain/controller.cpp @@ -954,8 +954,14 @@ struct controller_impl { // Deliver onerror action containing the failed deferred transaction directly back to the sender. etrx.actions.emplace_back( vector{{gtrx.sender, config::active_name}}, onerror( gtrx.sender_id, gtrx.packed_trx.data(), gtrx.packed_trx.size() ) ); - etrx.expiration = self.pending_block_time() + fc::microseconds(999'999); // Round up to avoid appearing expired - etrx.set_reference_block( self.head_block_id() ); + if( self.is_builtin_activated( builtin_protocol_feature_t::no_duplicate_deferred_id ) ) { + etrx.expiration = {}; + etrx.ref_block_num = 0; + etrx.ref_block_prefix = 0; + } else { + etrx.expiration = self.pending_block_time() + fc::microseconds(999'999); // Round up to nearest second to avoid appearing expired + etrx.set_reference_block( self.head_block_id() ); + } transaction_context trx_context( self, etrx, etrx.id(), start ); trx_context.deadline = deadline; @@ -2138,8 +2144,14 @@ struct controller_impl { signed_transaction trx; trx.actions.emplace_back(std::move(on_block_act)); - trx.set_reference_block(self.head_block_id()); - trx.expiration = self.pending_block_time() + fc::microseconds(999'999); // Round up to nearest second to avoid appearing expired + if( self.is_builtin_activated( builtin_protocol_feature_t::no_duplicate_deferred_id ) ) { + trx.expiration = {}; + trx.ref_block_num = 0; + trx.ref_block_prefix = 0; + } else { + trx.expiration = self.pending_block_time() + fc::microseconds(999'999); // Round up to nearest second to avoid appearing expired + trx.set_reference_block( self.head_block_id() ); + } return trx; } diff --git a/libraries/chain/include/eosio/chain/block_header.hpp b/libraries/chain/include/eosio/chain/block_header.hpp index adbdb7d3def..fc751826d95 100644 --- a/libraries/chain/include/eosio/chain/block_header.hpp +++ b/libraries/chain/include/eosio/chain/block_header.hpp @@ -8,41 +8,6 @@ namespace eosio { namespace chain { namespace detail { - struct extract_match { - bool enforce_unique = false; - }; - - template - struct decompose; - - template<> - struct decompose<> { - template - static auto extract( uint16_t id, const vector& data, ResultVariant& result ) - -> fc::optional - { - return {}; - } - }; - - template - struct decompose { - using head_t = T; - using tail_t = decompose< Rest... >; - - template - static auto extract( uint16_t id, const vector& data, ResultVariant& result ) - -> fc::optional - { - if( id == head_t::extension_id() ) { - result = fc::raw::unpack( data ); - return { extract_match{ head_t::enforce_unique() } }; - } - - return tail_t::template extract( id, data, result ); - } - }; - template struct block_header_extension_types { using block_header_extensions_t = fc::static_variant< Ts... >; diff --git a/libraries/chain/include/eosio/chain/exceptions.hpp b/libraries/chain/include/eosio/chain/exceptions.hpp index a80213e0425..3246fd2602e 100644 --- a/libraries/chain/include/eosio/chain/exceptions.hpp +++ b/libraries/chain/include/eosio/chain/exceptions.hpp @@ -196,6 +196,10 @@ namespace eosio { namespace chain { 3040013, "Transaction is too big" ) FC_DECLARE_DERIVED_EXCEPTION( unknown_transaction_compression, transaction_exception, 3040014, "Unknown transaction compression" ) + FC_DECLARE_DERIVED_EXCEPTION( invalid_transaction_extension, transaction_exception, + 3040015, "Invalid transaction extension" ) + FC_DECLARE_DERIVED_EXCEPTION( ill_formed_deferred_transaction_generation_context, transaction_exception, + 3040016, "Transaction includes an ill-formed deferred transaction generation context extension" ) FC_DECLARE_DERIVED_EXCEPTION( action_validate_exception, chain_exception, diff --git a/libraries/chain/include/eosio/chain/protocol_feature_manager.hpp b/libraries/chain/include/eosio/chain/protocol_feature_manager.hpp index f9f55dffb7a..987855c6f8b 100644 --- a/libraries/chain/include/eosio/chain/protocol_feature_manager.hpp +++ b/libraries/chain/include/eosio/chain/protocol_feature_manager.hpp @@ -17,6 +17,7 @@ enum class builtin_protocol_feature_t : uint32_t { preactivate_feature, only_link_to_existing_permission, replace_deferred, + no_duplicate_deferred_id, fix_linkauth_restriction, disallow_empty_producer_schedule }; diff --git a/libraries/chain/include/eosio/chain/transaction.hpp b/libraries/chain/include/eosio/chain/transaction.hpp index db61e5b17cb..d115c4a507a 100644 --- a/libraries/chain/include/eosio/chain/transaction.hpp +++ b/libraries/chain/include/eosio/chain/transaction.hpp @@ -9,6 +9,39 @@ namespace eosio { namespace chain { + struct deferred_transaction_generation_context : fc::reflect_init { + static constexpr uint16_t extension_id() { return 0; } + static constexpr bool enforce_unique() { return true; } + + deferred_transaction_generation_context() = default; + + deferred_transaction_generation_context( const transaction_id_type& sender_trx_id, uint128_t sender_id, account_name sender ) + :sender_trx_id( sender_trx_id ) + ,sender_id( sender_id ) + ,sender( sender ) + {} + + void reflector_init(); + + transaction_id_type sender_trx_id; + uint128_t sender_id; + account_name sender; + }; + + namespace detail { + template + struct transaction_extension_types { + using transaction_extensions_t = fc::static_variant< Ts... >; + using decompose_t = decompose< Ts... >; + }; + } + + using transaction_extension_types = detail::transaction_extension_types< + deferred_transaction_generation_context + >; + + using transaction_extensions = transaction_extension_types::transaction_extensions_t; + /** * The transaction header contains the fixed-sized data * associated with each transaction. It is separated from @@ -74,6 +107,7 @@ namespace eosio { namespace chain { return account_name(); } + vector validate_and_extract_extensions()const; }; struct signed_transaction : public transaction @@ -173,47 +207,11 @@ namespace eosio { namespace chain { using packed_transaction_ptr = std::shared_ptr; - /** - * When a transaction is generated it can be scheduled to occur - * in the future. It may also fail to execute for some reason in - * which case the sender needs to be notified. When the sender - * sends a transaction they will assign it an ID which will be - * passed back to the sender if the transaction fails for some - * reason. - */ - struct deferred_transaction : public signed_transaction - { - uint128_t sender_id; /// ID assigned by sender of generated, accessible via WASM api when executing normal or error - account_name sender; /// receives error handler callback - account_name payer; - time_point_sec execute_after; /// delayed execution - - deferred_transaction() = default; - - deferred_transaction(uint128_t sender_id, account_name sender, account_name payer,time_point_sec execute_after, - const signed_transaction& txn) - : signed_transaction(txn), - sender_id(sender_id), - sender(sender), - payer(payer), - execute_after(execute_after) - {} - }; - - struct deferred_reference { - deferred_reference(){} - deferred_reference( const account_name& sender, const uint128_t& sender_id) - :sender(sender),sender_id(sender_id) - {} - - account_name sender; - uint128_t sender_id; - }; - uint128_t transaction_id_to_sender_id( const transaction_id_type& tid ); } } /// namespace eosio::chain +FC_REFLECT(eosio::chain::deferred_transaction_generation_context, (sender_trx_id)(sender_id)(sender) ) FC_REFLECT( eosio::chain::transaction_header, (expiration)(ref_block_num)(ref_block_prefix) (max_net_usage_words)(max_cpu_usage_ms)(delay_sec) ) FC_REFLECT_DERIVED( eosio::chain::transaction, (eosio::chain::transaction_header), (context_free_actions)(actions)(transaction_extensions) ) @@ -221,5 +219,3 @@ FC_REFLECT_DERIVED( eosio::chain::signed_transaction, (eosio::chain::transaction FC_REFLECT_ENUM( eosio::chain::packed_transaction::compression_type, (none)(zlib)) // @ignore unpacked_trx FC_REFLECT( eosio::chain::packed_transaction, (signatures)(compression)(packed_context_free_data)(packed_trx) ) -FC_REFLECT_DERIVED( eosio::chain::deferred_transaction, (eosio::chain::signed_transaction), (sender_id)(sender)(payer)(execute_after) ) -FC_REFLECT( eosio::chain::deferred_reference, (sender)(sender_id) ) diff --git a/libraries/chain/include/eosio/chain/types.hpp b/libraries/chain/include/eosio/chain/types.hpp index 1cea911d9d9..d681c349844 100644 --- a/libraries/chain/include/eosio/chain/types.hpp +++ b/libraries/chain/include/eosio/chain/types.hpp @@ -269,6 +269,43 @@ namespace eosio { namespace chain { }; // enum_hash needed to support old gcc compiler of Ubuntu 16.04 + namespace detail { + struct extract_match { + bool enforce_unique = false; + }; + + template + struct decompose; + + template<> + struct decompose<> { + template + static auto extract( uint16_t id, const vector& data, ResultVariant& result ) + -> fc::optional + { + return {}; + } + }; + + template + struct decompose { + using head_t = T; + using tail_t = decompose< Rest... >; + + template + static auto extract( uint16_t id, const vector& data, ResultVariant& result ) + -> fc::optional + { + if( id == head_t::extension_id() ) { + result = fc::raw::unpack( data ); + return { extract_match{ head_t::enforce_unique() } }; + } + + return tail_t::template extract( id, data, result ); + } + }; + } + } } // eosio::chain FC_REFLECT( eosio::chain::void_t, ) diff --git a/libraries/chain/protocol_feature_manager.cpp b/libraries/chain/protocol_feature_manager.cpp index 78c84ff9410..66f2eaf183d 100644 --- a/libraries/chain/protocol_feature_manager.cpp +++ b/libraries/chain/protocol_feature_manager.cpp @@ -53,6 +53,19 @@ Also corrects the RAM usage of accounts affected by the replace deferred transac */ {} } ) + ( builtin_protocol_feature_t::no_duplicate_deferred_id, builtin_protocol_feature_spec{ + "NO_DUPLICATE_DEFERRED_ID", + fc::variant("45967387ee92da70171efd9fefd1ca8061b5efe6f124d269cd2468b47f1575a0").as(), + // SHA256 hash of the raw message below within the comment delimiters (do not modify message below). +/* +Builtin protocol feature: NO_DUPLICATE_DEFERRED_ID +Depends on: REPLACE_DEFERRED + +Ensures transactions generated by contracts for deferred execution are adjusted to avoid transaction ID conflicts. +Also allows a contract to send a deferred transaction in a manner that enables the contract to know the transaction ID ahead of time. +*/ + {builtin_protocol_feature_t::replace_deferred} + } ) ( builtin_protocol_feature_t::fix_linkauth_restriction, builtin_protocol_feature_spec{ "FIX_LINKAUTH_RESTRICTION", fc::variant("a98241c83511dc86c857221b9372b4aa7cea3aaebc567a48604e1d3db3557050").as(), diff --git a/libraries/chain/transaction.cpp b/libraries/chain/transaction.cpp index e1910ce02eb..1ebdfeccc01 100644 --- a/libraries/chain/transaction.cpp +++ b/libraries/chain/transaction.cpp @@ -50,6 +50,16 @@ typedef multi_index_container< > > recovery_cache_type; +void deferred_transaction_generation_context::reflector_init() { + static_assert( fc::raw::has_feature_reflector_init_on_unpacked_reflected_types, + "deferred_transaction_generation_context expects FC to support reflector_init" ); + + + EOS_ASSERT( sender != account_name(), ill_formed_deferred_transaction_generation_context, + "Deferred transaction generation context extension must have a non-empty sender account", + ); +} + void transaction_header::set_reference_block( const block_id_type& reference_block ) { ref_block_num = fc::endian_reverse_u32(reference_block._hash[0]); ref_block_prefix = reference_block._hash[1]; @@ -134,6 +144,45 @@ fc::microseconds transaction::get_signature_keys( const vector& return sig_cpu_usage; } FC_CAPTURE_AND_RETHROW() } +vector transaction::validate_and_extract_extensions()const { + using transaction_extensions_t = transaction_extension_types::transaction_extensions_t; + using decompose_t = transaction_extension_types::decompose_t; + + static_assert( std::is_same::value, + "transaction_extensions is not setup as expected" ); + + vector results; + + uint16_t id_type_lower_bound = 0; + + for( size_t i = 0; i < transaction_extensions.size(); ++i ) { + const auto& e = transaction_extensions[i]; + auto id = e.first; + + EOS_ASSERT( id >= id_type_lower_bound, invalid_transaction_extension, + "Transaction extensions are not in the correct order (ascending id types required)" + ); + + results.emplace_back(); + + auto match = decompose_t::extract( id, e.second, results.back() ); + EOS_ASSERT( match, invalid_transaction_extension, + "Transaction extension with id type ${id} is not supported", + ("id", id) + ); + + if( match->enforce_unique ) { + EOS_ASSERT( i == 0 || id > id_type_lower_bound, invalid_transaction_extension, + "Transaction extension with id type ${id} is not allowed to repeat", + ("id", id) + ); + } + + id_type_lower_bound = id; + } + + return results; +} const signature_type& signed_transaction::sign(const private_key_type& key, const chain_id_type& chain_id) { signatures.push_back(key.sign(sig_digest(chain_id, context_free_data))); diff --git a/libraries/chain/transaction_context.cpp b/libraries/chain/transaction_context.cpp index 226e6863a16..cc67b49eaec 100644 --- a/libraries/chain/transaction_context.cpp +++ b/libraries/chain/transaction_context.cpp @@ -166,7 +166,10 @@ namespace bacc = boost::accumulators; trace->block_time = c.pending_block_time(); trace->producer_block_id = c.pending_producer_block_id(); executed.reserve( trx.total_actions() ); - EOS_ASSERT( trx.transaction_extensions.size() == 0, unsupported_feature, "we don't support any extensions yet" ); + EOS_ASSERT( trx.transaction_extensions.size() == 0 + || control.is_builtin_activated( builtin_protocol_feature_t::no_duplicate_deferred_id ), + unsupported_feature, "we don't support any extensions yet" + ); // This assert may not be necessary. Consider removing. } void transaction_context::init(uint64_t initial_net_usage) @@ -278,6 +281,8 @@ namespace bacc = boost::accumulators; void transaction_context::init_for_implicit_trx( uint64_t initial_net_usage ) { + EOS_ASSERT( trx.transaction_extensions.size() == 0, unsupported_feature, + "no transaction extensions supported yet for implicit transactions" ); published = control.pending_block_time(); init( initial_net_usage); } @@ -286,6 +291,9 @@ namespace bacc = boost::accumulators; uint64_t packed_trx_prunable_size, bool skip_recording ) { + EOS_ASSERT( trx.transaction_extensions.size() == 0, unsupported_feature, + "no transaction extensions supported yet for input transactions" ); + const auto& cfg = control.get_global_properties().configuration; uint64_t discounted_size_for_pruned_data = packed_trx_prunable_size; @@ -322,6 +330,10 @@ namespace bacc = boost::accumulators; void transaction_context::init_for_deferred_trx( fc::time_point p ) { + EOS_ASSERT( (trx.expiration.sec_since_epoch() == 0) || (trx.transaction_extensions.size() == 0), unsupported_feature, + "no transaction extensions supported yet for deferred transactions" + ); + published = p; trace->scheduled = true; apply_context_free = false; From 363311937ab3396cb7f989d3cebb44619af7b0e9 Mon Sep 17 00:00:00 2001 From: arhag Date: Thu, 4 Apr 2019 19:24:14 -0400 Subject: [PATCH 02/10] fix proxy test contract to work even after NO_DUPLICATE_DEFERRED_ID activation #6115 --- unittests/test-contracts/README.md | 2 +- unittests/test-contracts/proxy/proxy.cpp | 1 + unittests/test-contracts/proxy/proxy.wasm | Bin 18398 -> 18481 bytes 3 files changed, 2 insertions(+), 1 deletion(-) diff --git a/unittests/test-contracts/README.md b/unittests/test-contracts/README.md index 157455c7202..aa9c0f8dee9 100644 --- a/unittests/test-contracts/README.md +++ b/unittests/test-contracts/README.md @@ -2,6 +2,6 @@ test_ram_limit contract was compiled with eosio.cdt v1.4.1 That contract was ported to compile with eosio.cdt v1.5.0, but the test that uses it is very sensitive to stdlib/eosiolib changes, compilation flags and linker flags. -deferred_test contract was compiled with eosio.cdt v1.6.1 +deferred_test and proxy contracts were compiled with eosio.cdt v1.6.1 The remaining contracts have been ported to compile with eosio.cdt v1.6.x. They were compiled with a patched version of eosio.cdt v1.6.0-rc1 (commit 1c9180ff5a1e431385180ce459e11e6a1255c1a4). diff --git a/unittests/test-contracts/proxy/proxy.cpp b/unittests/test-contracts/proxy/proxy.cpp index 1a199c4a5ba..c9fc324cad5 100644 --- a/unittests/test-contracts/proxy/proxy.cpp +++ b/unittests/test-contracts/proxy/proxy.cpp @@ -59,6 +59,7 @@ void proxy::on_error( uint128_t sender_id, eosio::ignore> ) { get_datastream() >> packed_trx_size; transaction trx; get_datastream() >> trx; + trx.transaction_extensions.clear(); trx.delay_sec = cfg.delay; trx.send( id, get_self() ); diff --git a/unittests/test-contracts/proxy/proxy.wasm b/unittests/test-contracts/proxy/proxy.wasm index c09311385bec5e82bc38b6306b33942f0dc9bee1..b40249782bac38ca92d735ae5363f1218e7f2496 100755 GIT binary patch delta 4312 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 Date: Thu, 4 Apr 2019 19:34:11 -0400 Subject: [PATCH 03/10] better way of rejecting disallowed transaction extensions #6115 Allows the `num_failed` tracker and blacklist of producer_plugin to work as intended. Preserves the current pattern of not retiring (except for case with expired status) deferred transaction with invalid extensions even after NO_DUPLICATE_DEFERRED_ID activation. --- libraries/chain/controller.cpp | 4 +++ .../chain/include/eosio/chain/exceptions.hpp | 2 ++ .../eosio/chain/transaction_context.hpp | 2 ++ libraries/chain/transaction_context.cpp | 33 ++++++++++++------- 4 files changed, 30 insertions(+), 11 deletions(-) diff --git a/libraries/chain/controller.cpp b/libraries/chain/controller.cpp index 744ca4bf04b..8d4adab795f 100644 --- a/libraries/chain/controller.cpp +++ b/libraries/chain/controller.cpp @@ -984,6 +984,8 @@ struct controller_impl { trx_context.squash(); restore.cancel(); return trace; + } catch( const disallowed_transaction_extensions_bad_block_exception& ) { + throw; } catch( const protocol_feature_bad_block_exception& ) { throw; } catch( const fc::exception& e ) { @@ -1118,6 +1120,8 @@ struct controller_impl { restore.cancel(); return trace; + } catch( const disallowed_transaction_extensions_bad_block_exception& ) { + throw; } catch( const protocol_feature_bad_block_exception& ) { throw; } catch( const fc::exception& e ) { diff --git a/libraries/chain/include/eosio/chain/exceptions.hpp b/libraries/chain/include/eosio/chain/exceptions.hpp index 3246fd2602e..2976dcbb7f1 100644 --- a/libraries/chain/include/eosio/chain/exceptions.hpp +++ b/libraries/chain/include/eosio/chain/exceptions.hpp @@ -200,6 +200,8 @@ namespace eosio { namespace chain { 3040015, "Invalid transaction extension" ) FC_DECLARE_DERIVED_EXCEPTION( ill_formed_deferred_transaction_generation_context, transaction_exception, 3040016, "Transaction includes an ill-formed deferred transaction generation context extension" ) + FC_DECLARE_DERIVED_EXCEPTION( disallowed_transaction_extensions_bad_block_exception, transaction_exception, + 3250002, "Transaction includes disallowed extensions (invalid block)" ) FC_DECLARE_DERIVED_EXCEPTION( action_validate_exception, chain_exception, diff --git a/libraries/chain/include/eosio/chain/transaction_context.hpp b/libraries/chain/include/eosio/chain/transaction_context.hpp index b0327dafb18..6238a6cedf6 100644 --- a/libraries/chain/include/eosio/chain/transaction_context.hpp +++ b/libraries/chain/include/eosio/chain/transaction_context.hpp @@ -73,6 +73,8 @@ namespace eosio { namespace chain { void validate_cpu_usage_to_bill( int64_t u, bool check_minimum = true )const; + void disallow_transaction_extensions( const char* error_msg )const; + /// Fields: public: diff --git a/libraries/chain/transaction_context.cpp b/libraries/chain/transaction_context.cpp index cc67b49eaec..beff789aeba 100644 --- a/libraries/chain/transaction_context.cpp +++ b/libraries/chain/transaction_context.cpp @@ -166,10 +166,14 @@ namespace bacc = boost::accumulators; trace->block_time = c.pending_block_time(); trace->producer_block_id = c.pending_producer_block_id(); executed.reserve( trx.total_actions() ); - EOS_ASSERT( trx.transaction_extensions.size() == 0 - || control.is_builtin_activated( builtin_protocol_feature_t::no_duplicate_deferred_id ), - unsupported_feature, "we don't support any extensions yet" - ); // This assert may not be necessary. Consider removing. + } + + void transaction_context::disallow_transaction_extensions( const char* error_msg )const { + if( control.is_producing_block() ) { + EOS_THROW( subjective_block_production_exception, error_msg ); + } else { + EOS_THROW( disallowed_transaction_extensions_bad_block_exception, error_msg ); + } } void transaction_context::init(uint64_t initial_net_usage) @@ -281,8 +285,10 @@ namespace bacc = boost::accumulators; void transaction_context::init_for_implicit_trx( uint64_t initial_net_usage ) { - EOS_ASSERT( trx.transaction_extensions.size() == 0, unsupported_feature, - "no transaction extensions supported yet for implicit transactions" ); + if( trx.transaction_extensions.size() > 0 ) { + disallow_transaction_extensions( "no transaction extensions supported yet for implicit transactions" ); + } + published = control.pending_block_time(); init( initial_net_usage); } @@ -291,8 +297,9 @@ namespace bacc = boost::accumulators; uint64_t packed_trx_prunable_size, bool skip_recording ) { - EOS_ASSERT( trx.transaction_extensions.size() == 0, unsupported_feature, - "no transaction extensions supported yet for input transactions" ); + if( trx.transaction_extensions.size() > 0 ) { + disallow_transaction_extensions( "no transaction extensions supported yet for input transactions" ); + } const auto& cfg = control.get_global_properties().configuration; @@ -330,9 +337,13 @@ namespace bacc = boost::accumulators; void transaction_context::init_for_deferred_trx( fc::time_point p ) { - EOS_ASSERT( (trx.expiration.sec_since_epoch() == 0) || (trx.transaction_extensions.size() == 0), unsupported_feature, - "no transaction extensions supported yet for deferred transactions" - ); + if( (trx.expiration.sec_since_epoch() != 0) && (trx.transaction_extensions.size() > 0) ) { + disallow_transaction_extensions( "no transaction extensions supported yet for deferred transactions" ); + } + // If (trx.expiration.sec_since_epoch() == 0) then it was created after NO_DUPLICATE_DEFERRED_ID activation, + // and so validation of its extensions was done either in: + // * apply_context::schedule_deferred_transaction for contract-generated transactions; + // * or transaction_context::init_for_input_trx for delayed input transactions. published = p; trace->scheduled = true; From 04e7b9010fcc7306b6a1862575daa7e086e7bec7 Mon Sep 17 00:00:00 2001 From: arhag Date: Thu, 4 Apr 2019 20:00:55 -0400 Subject: [PATCH 04/10] fix compilation errors on certain linux platforms #6115 --- libraries/chain/apply_context.cpp | 2 +- libraries/chain/controller.cpp | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/libraries/chain/apply_context.cpp b/libraries/chain/apply_context.cpp index edffc87029c..3dea34973be 100644 --- a/libraries/chain/apply_context.cpp +++ b/libraries/chain/apply_context.cpp @@ -319,7 +319,7 @@ void apply_context::schedule_deferred_transaction( const uint128_t& sender_id, a fc::raw::pack( deferred_transaction_generation_context( trx_context.id, sender_id, receiver ) ) ); } - trx.expiration = {}; + trx.expiration = time_point_sec(); trx.ref_block_num = 0; trx.ref_block_prefix = 0; } else { diff --git a/libraries/chain/controller.cpp b/libraries/chain/controller.cpp index 8d4adab795f..47bc6ad7e7f 100644 --- a/libraries/chain/controller.cpp +++ b/libraries/chain/controller.cpp @@ -955,7 +955,7 @@ struct controller_impl { etrx.actions.emplace_back( vector{{gtrx.sender, config::active_name}}, onerror( gtrx.sender_id, gtrx.packed_trx.data(), gtrx.packed_trx.size() ) ); if( self.is_builtin_activated( builtin_protocol_feature_t::no_duplicate_deferred_id ) ) { - etrx.expiration = {}; + etrx.expiration = time_point_sec(); etrx.ref_block_num = 0; etrx.ref_block_prefix = 0; } else { @@ -2149,7 +2149,7 @@ struct controller_impl { signed_transaction trx; trx.actions.emplace_back(std::move(on_block_act)); if( self.is_builtin_activated( builtin_protocol_feature_t::no_duplicate_deferred_id ) ) { - trx.expiration = {}; + trx.expiration = time_point_sec(); trx.ref_block_num = 0; trx.ref_block_prefix = 0; } else { From dd782de9941c4ec174438cd2856665c3b1b0699c Mon Sep 17 00:00:00 2001 From: arhag Date: Thu, 4 Apr 2019 21:21:30 -0400 Subject: [PATCH 05/10] log provided feature digests in bios_boot.sh and try a different way of iterating through the provided list #6115 --- testnet.template | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/testnet.template b/testnet.template index ab9051f0601..e36e8ba4f80 100644 --- a/testnet.template +++ b/testnet.template @@ -17,8 +17,6 @@ if [ -z "$bioscontractpath" ]; then bioscontractpath="unittests/contracts/eosio.bios" fi -featuredigests=($FEATURE_DIGESTS) - wddir=eosio-ignition-wd wdaddr=localhost:8899 wdurl=http://$wdaddr @@ -42,6 +40,8 @@ mkdir $wddir step=1 echo Initializing ignition sequence at $(date) | tee $logfile +echo "FEATURE_DIGESTS: $FEATURE_DIGESTS" >> $logfile + echo "http-server-address = $wdaddr" > $wddir/config.ini programs/keosd/keosd --config-dir $wddir --data-dir $wddir 2> $wddir/wdlog.txt & @@ -85,7 +85,7 @@ wcmd create --to-console -n ignition ecmd set contract eosio $bioscontractpath eosio.bios.wasm eosio.bios.abi # Preactivate all digests -for digest in "${featuredigests[@]}"; +for digest in $FEATURE_DIGESTS; do ecmd push action eosio preactivate "{\"feature_digest\":\"$digest\"}" -p eosio done From 9c47d56aedcbcea76b04d5b4c8235a3f1c853337 Mon Sep 17 00:00:00 2001 From: arhag Date: Thu, 4 Apr 2019 21:56:32 -0400 Subject: [PATCH 06/10] log what the FEATURE_DIGESTS environment variable is set to as well before calling bios_boot.sh #6115 --- tests/Cluster.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/Cluster.py b/tests/Cluster.py index 3476bf9de03..cb24bfac2d0 100644 --- a/tests/Cluster.py +++ b/tests/Cluster.py @@ -929,6 +929,7 @@ def bios_bootstrap(self, biosNode, totalNodes, pfSetupPolicy, silent=False): if pfSetupPolicy == PFSetupPolicy.FULL: allBuiltinProtocolFeatureDigests = biosNode.getAllBuiltinFeatureDigestsToPreactivate() env["FEATURE_DIGESTS"] = " ".join(allBuiltinProtocolFeatureDigests) + Utils.Print("Set FEATURE_DIGESTS to: %s" % env["FEATURE_DIGESTS"]) if 0 != subprocess.call(cmd.split(), stdout=Utils.FNull, env=env): if not silent: Utils.Print("Launcher failed to shut down eos cluster.") From 8ec809873002b9ee9872f06ad54662e29539c98e Mon Sep 17 00:00:00 2001 From: arhag Date: Thu, 4 Apr 2019 22:12:08 -0400 Subject: [PATCH 07/10] another attempt to prevent the shuffling of FEATURE_DIGESTS on certain platforms #6115 --- testnet.template | 3 +++ tests/Cluster.py | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/testnet.template b/testnet.template index e36e8ba4f80..432e04a11b5 100644 --- a/testnet.template +++ b/testnet.template @@ -85,10 +85,13 @@ wcmd create --to-console -n ignition ecmd set contract eosio $bioscontractpath eosio.bios.wasm eosio.bios.abi # Preactivate all digests +old_IFS=${IFS} +IFS=";" for digest in $FEATURE_DIGESTS; do ecmd push action eosio preactivate "{\"feature_digest\":\"$digest\"}" -p eosio done +IFS=${old_IFS} # Create required system accounts ecmd create key --to-console diff --git a/tests/Cluster.py b/tests/Cluster.py index cb24bfac2d0..d8356b6d5c2 100644 --- a/tests/Cluster.py +++ b/tests/Cluster.py @@ -928,7 +928,7 @@ def bios_bootstrap(self, biosNode, totalNodes, pfSetupPolicy, silent=False): if pfSetupPolicy == PFSetupPolicy.FULL: allBuiltinProtocolFeatureDigests = biosNode.getAllBuiltinFeatureDigestsToPreactivate() - env["FEATURE_DIGESTS"] = " ".join(allBuiltinProtocolFeatureDigests) + env["FEATURE_DIGESTS"] = ";".join(allBuiltinProtocolFeatureDigests) Utils.Print("Set FEATURE_DIGESTS to: %s" % env["FEATURE_DIGESTS"]) if 0 != subprocess.call(cmd.split(), stdout=Utils.FNull, env=env): From 063bb595dd37afae0d4766a8fed4622abbafe373 Mon Sep 17 00:00:00 2001 From: arhag Date: Thu, 4 Apr 2019 22:51:21 -0400 Subject: [PATCH 08/10] revert previous commit (the issue was not in the bash script) and fix the getAllBuiltinFeatureDigestsToPreactivate function in Node.py #6115 --- testnet.template | 3 --- tests/Cluster.py | 2 +- tests/Node.py | 16 ++++++++++------ 3 files changed, 11 insertions(+), 10 deletions(-) diff --git a/testnet.template b/testnet.template index 432e04a11b5..e36e8ba4f80 100644 --- a/testnet.template +++ b/testnet.template @@ -85,13 +85,10 @@ wcmd create --to-console -n ignition ecmd set contract eosio $bioscontractpath eosio.bios.wasm eosio.bios.abi # Preactivate all digests -old_IFS=${IFS} -IFS=";" for digest in $FEATURE_DIGESTS; do ecmd push action eosio preactivate "{\"feature_digest\":\"$digest\"}" -p eosio done -IFS=${old_IFS} # Create required system accounts ecmd create key --to-console diff --git a/tests/Cluster.py b/tests/Cluster.py index d8356b6d5c2..cb24bfac2d0 100644 --- a/tests/Cluster.py +++ b/tests/Cluster.py @@ -928,7 +928,7 @@ def bios_bootstrap(self, biosNode, totalNodes, pfSetupPolicy, silent=False): if pfSetupPolicy == PFSetupPolicy.FULL: allBuiltinProtocolFeatureDigests = biosNode.getAllBuiltinFeatureDigestsToPreactivate() - env["FEATURE_DIGESTS"] = ";".join(allBuiltinProtocolFeatureDigests) + env["FEATURE_DIGESTS"] = " ".join(allBuiltinProtocolFeatureDigests) Utils.Print("Set FEATURE_DIGESTS to: %s" % env["FEATURE_DIGESTS"]) if 0 != subprocess.call(cmd.split(), stdout=Utils.FNull, env=env): diff --git a/tests/Node.py b/tests/Node.py index 9621186c9f9..67407531d38 100644 --- a/tests/Node.py +++ b/tests/Node.py @@ -1486,15 +1486,19 @@ def activatePreactivateFeature(self): # Wait for the next block to be produced so the scheduled protocol feature is activated self.waitForHeadToAdvance() - # Return an array of feature digests to be preactivated + # Return an array of feature digests to be preactivated in a correct order respecting dependencies # Require producer_api_plugin def getAllBuiltinFeatureDigestsToPreactivate(self): protocolFeatures = [] - protocolFeatureDict = self.getSupportedProtocolFeatureDict() - for k, v in protocolFeatureDict.items(): - # Filter out "PREACTIVATE_FEATURE" - if k != "PREACTIVATE_FEATURE": - protocolFeatures.append(v["feature_digest"]) + supportedProtocolFeatures = self.getSupportedProtocolFeatures() + for protocolFeature in supportedProtocolFeatures: + for spec in protocolFeature["specification"]: + if (spec["name"] == "builtin_feature_codename"): + codename = spec["value"] + # Filter out "PREACTIVATE_FEATURE" + if codename != "PREACTIVATE_FEATURE": + protocolFeatures.append(protocolFeature["feature_digest"]) + break return protocolFeatures # Require PREACTIVATE_FEATURE to be activated and require eosio.bios with preactivate_feature From 4e8ba132cfbec03621c21f0eec845cd331f53054 Mon Sep 17 00:00:00 2001 From: arhag Date: Mon, 8 Apr 2019 18:55:14 -0400 Subject: [PATCH 09/10] Added protocol_feature_tests/no_duplicate_deferred_id_test unit test to test the NO_DUPLICATE_DEFERRED_ID protocol feature. #6115 Updated the deferred_test test contract to support testing requirements of new test. --- unittests/protocol_feature_tests.cpp | 132 ++++++++++++++++++ .../deferred_test/deferred_test.cpp | 21 +++ .../deferred_test/deferred_test.wasm | Bin 8216 -> 10152 bytes 3 files changed, 153 insertions(+) diff --git a/unittests/protocol_feature_tests.cpp b/unittests/protocol_feature_tests.cpp index c510904864a..ec994592fcb 100644 --- a/unittests/protocol_feature_tests.cpp +++ b/unittests/protocol_feature_tests.cpp @@ -4,6 +4,7 @@ */ #include #include +#include #include #include @@ -510,6 +511,137 @@ BOOST_AUTO_TEST_CASE( replace_deferred_test ) try { } FC_LOG_AND_RETHROW() +BOOST_AUTO_TEST_CASE( no_duplicate_deferred_id_test ) try { + tester c( setup_policy::preactivate_feature_and_new_bios ); + tester c2( setup_policy::none ); + + c.create_accounts( {N(alice), N(test)} ); + c.set_code( N(test), contracts::deferred_test_wasm() ); + c.set_abi( N(test), contracts::deferred_test_abi().data() ); + c.produce_block(); + + push_blocks( c, c2 ); + + c2.push_action( N(test), N(defercall), N(alice), fc::mutable_variant_object() + ("payer", "alice") + ("sender_id", 1) + ("contract", "test") + ("payload", 50) + ); + + c2.finish_block(); + + BOOST_CHECK_EXCEPTION( + c2.produce_block(), + fc::exception, + fc_exception_message_is( "no transaction extensions supported yet for deferred transactions" ) + ); + + c2.produce_empty_block( fc::minutes(10) ); + + transaction_trace_ptr trace0; + auto h = c2.control->applied_transaction.connect( [&]( const transaction_trace_ptr& t) { + if( t && t->receipt && t->receipt->status == transaction_receipt::expired) { + trace0 = t; + } + } ); + + c2.produce_block(); + + h.disconnect(); + + BOOST_REQUIRE( trace0 ); + + c.produce_block(); + + const auto& pfm = c.control->get_protocol_feature_manager(); + + auto d1 = pfm.get_builtin_digest( builtin_protocol_feature_t::replace_deferred ); + BOOST_REQUIRE( d1 ); + auto d2 = pfm.get_builtin_digest( builtin_protocol_feature_t::no_duplicate_deferred_id ); + BOOST_REQUIRE( d2 ); + + c.preactivate_protocol_features( {*d1, *d2} ); + c.produce_block(); + + auto& index = c.control->db().get_index(); + + auto check_generation_context = []( auto&& data, + const transaction_id_type& sender_trx_id, + unsigned __int128 sender_id, + account_name sender ) + { + transaction trx; + fc::datastream ds1( data.data(), data.size() ); + fc::raw::unpack( ds1, trx ); + BOOST_REQUIRE_EQUAL( trx.transaction_extensions.size(), 1 ); + BOOST_REQUIRE_EQUAL( trx.transaction_extensions.back().first, 0 ); + + fc::datastream ds2( trx.transaction_extensions.back().second.data(), + trx.transaction_extensions.back().second.size() ); + + transaction_id_type actual_sender_trx_id; + fc::raw::unpack( ds2, actual_sender_trx_id ); + BOOST_CHECK_EQUAL( actual_sender_trx_id, sender_trx_id ); + + unsigned __int128 actual_sender_id; + fc::raw::unpack( ds2, actual_sender_id ); + BOOST_CHECK( actual_sender_id == sender_id ); + + uint64_t actual_sender; + fc::raw::unpack( ds2, actual_sender ); + BOOST_CHECK_EQUAL( account_name(actual_sender), sender ); + }; + + BOOST_CHECK_EXCEPTION( + c.push_action( N(test), N(defercall), N(alice), fc::mutable_variant_object() + ("payer", "alice") + ("sender_id", 1) + ("contract", "test") + ("payload", 77 ) + ), + ill_formed_deferred_transaction_generation_context, + fc_exception_message_is( "deferred transaction generaction context contains mismatching sender" ) + ); + + BOOST_REQUIRE_EQUAL(0, index.size()); + + auto trace1 = c.push_action( N(test), N(defercall), N(alice), fc::mutable_variant_object() + ("payer", "alice") + ("sender_id", 1) + ("contract", "test") + ("payload", 40) + ); + + BOOST_REQUIRE_EQUAL(1, index.size()); + + check_generation_context( index.begin()->packed_trx, + trace1->id, + ((static_cast(N(alice)) << 64) | 1), + N(test) ); + + c.produce_block(); + + BOOST_REQUIRE_EQUAL(0, index.size()); + + auto trace2 = c.push_action( N(test), N(defercall), N(alice), fc::mutable_variant_object() + ("payer", "alice") + ("sender_id", 1) + ("contract", "test") + ("payload", 50) + ); + + BOOST_REQUIRE_EQUAL(1, index.size()); + + check_generation_context( index.begin()->packed_trx, + trace2->id, + ((static_cast(N(alice)) << 64) | 1), + N(test) ); + + c.produce_block(); + +} FC_LOG_AND_RETHROW() + BOOST_AUTO_TEST_CASE( fix_linkauth_restriction ) { try { tester chain( setup_policy::preactivate_feature_and_new_bios ); diff --git a/unittests/test-contracts/deferred_test/deferred_test.cpp b/unittests/test-contracts/deferred_test/deferred_test.cpp index 4ee7465537c..b24096de146 100644 --- a/unittests/test-contracts/deferred_test/deferred_test.cpp +++ b/unittests/test-contracts/deferred_test/deferred_test.cpp @@ -4,6 +4,8 @@ */ #include "deferred_test.hpp" #include +#include +#include using namespace eosio; @@ -16,6 +18,25 @@ void deferred_test::defercall( name payer, uint64_t sender_id, name contract, ui deferfunc_action a( contract, {get_self(), "active"_n} ); trx.actions.emplace_back( a.to_action( payload ) ); bool replace_existing = (payload >= 100); + + if( (50 <= payload && payload < 100) || payload >= 150 ) { + size_t tx_size = transaction_size(); + char* buffer = (char*)malloc( tx_size ); + read_transaction( buffer, tx_size ); + auto tx_id = sha256( buffer, tx_size ); + char context_buffer[56]; + trx.transaction_extensions.emplace_back( (uint16_t)0, std::vector() ); + auto& context_vector = std::get<1>( trx.transaction_extensions.back() ); + context_vector.resize(56); + datastream ds( context_vector.data(), 56 ); + ds << tx_id.extract_as_byte_array(); + ds << ((static_cast(payer.value) << 64) | sender_id); + if( payload != 77 ) + ds << get_self(); + else + ds << payer; + } + trx.send( (static_cast(payer.value) << 64) | sender_id, payer, replace_existing ); } diff --git a/unittests/test-contracts/deferred_test/deferred_test.wasm b/unittests/test-contracts/deferred_test/deferred_test.wasm index fbfdaf14f0841a352371938b55345e21362908ff..588e38fadf2ed8f3d8a7952114113f3bc43ebd61 100755 GIT binary patch delta 3707 zcmZu!YiwLc6`q-U_wKImuJ`)Aer!DV#))?m60aSXbhRD}KRw#%cM7M>eifE7`HL8#rR8=zKAvYj?6d@r{5eOksfr2PPDoFj?@SRyZ zF(tNq&zW=1oY$OlX8g+Jou_P8T)M~^V|=;wx;>mRf*@e$&0uvEAc92@@L&sJ*N=6k z%pf3^SY2h}A7<|l=lAfysa1=WrQ&q0yil21Du1QK;zp^m>{Ls|nW-H-W)V4ATv{qs zYg0=XibEr#tbu6uBidk{vO{WQaHvjmwxp`%#nQ!@!O?u399TP5#iyo<2+JaT%2EFvf-Pj z`c3BSw!hDr(;I$9^rVkP3Tta?uYUB=8^3y&N%6EfDVgNX@o-hN^U3gK(aU#*Z;O3V zW!s85L*ae#PwA#?r%$zy3Z_`f7L)WVnF@T*l-zgR@ZZK&?u8Ze+<`qFKbhpDQ%k#s zEe+b870e#zBG4Pb@Af$M{oTy;yB<54WVS7tZ86E5e!G#86o^=PcAc6F_eb{gz2UPF z7vGi0-p>7>l#4+gp&SS|A|t^yVemk#@?bYp`8m(%14{~ND93kN3(Pf@<8sAZ0X!lm zjY-Xt!gnng=SrM``A8vlO`6ggWNh6t3*#@YyAkjL-{sN-&o!k`-1mf%r;|J(kQ%W0 zsU!;&Q}1jt$3j>#GG@d8wiJySjBF!+j_)#Zgj!&`yJZNrp`527tXFhP3Cw*ha23*k^)W6^` z0&|4=+B<}C$587UGeCP|izd{Y8WWD`JQ9E z5~YQY7e&2+^f%^9paBJGjf(Ri<^8%SzIRcr!+@e_ly7K5jTe{dPd=8{tR{-g(t5ThMr4u+i46u%W=IZ)TFw!B}w=oP$M&T#or(3=HidsbVrlsu%jldl zCW-FG$wX9*qf%u|#??5Phfx_IHF=L0Q9W;K&j2vkNR(H5p(AMX&*)B#j%_~eVX!PAE&e4`}pn(^g zQ|z2RmZ;)RvLl;*dUqCD98m@y9ahiEO`s2pANSN4oEPU^Gb z{C4kATR-f}`Ud9->a5N1tI;XmAO0!2$;ZN*u`Y4rUbqq4+nl2TC@}z2gkb6qgdfJr zZT+smoJQKvf_1fH%ucE@YN`;tonVIMY}NS24hZ{ zmpqg58BsKmI2u(wz>hu+xDK(HG;r-eP<{O1!EtIG%r#|PLVF`uXSyjejzihnk9*Qm zX3gn$qY1;t_2F9hi^5lOkl9WH`ba>nYe%}ClE}9HtdjOc&?|X5{2e_ULaTz0i zoF<1+pAlrDUOc#aFyF{kW%^!%!b;Yo6Lft^6Stg9szqN4M7uoaHsbb7&bcW{8|p2N zs}j`&gE&Q+{g%4wBVIj>xJ(k;Bz6SO5+C;$NpM&qUo28>;x&3`;*81BKG9sWEyk21?Q9_It$V&k_(E)`z32ip&K=`5OA z;nZuDM(SAjGy4!f68^_Nhw~W+} z1TTa?ZF-){In#WW9}UaR3$&1#gJG;C(|HsfjRa_A!0Ifz+oTV8}$)m--M`{q6%Rz0K-*#0tOHx|baJ zW$S4IskU6~Buw}C&WUicEz{gnPpqld%rmHP6VJSbHkW6@>uqh~wR_>6wk(0)w(TSE zaUJYy|2#hwuC`|%w|%2Mi|y~Xw;X+qMwXre+bF7oFm9iJlbLC00nxyI)!r|y|dT@pNJBKNs#m+MXe$Y8V z;DgSK1isXziMP9EX_e@HRfAn4;h(#&5c+J-06!6az302MkeSaCsAgWNOU)5j*)>Ao z?yloc{!S#Ay0gjb=VpuL`O=K6Ey&{h{K9mxR+5XwFPE0srAq0_VrjYtW~o|TsLFCh z&X%u~W~N?Psg;)G^g?B+R=qS`Td0Ojy=fcoMO!p5hUa29+`FINigrZ4up&Wv6L^!n zT&`V^^iNI+%4Ud;L$ljvO0%Wv?4`=IoQ4Z9w@@M8d*Q9#4t_t}=-n%>-3$NHo0hn( ZZ0mBhTq}W(MA{MhUl2I6&8*AVe*wVT$p`=d delta 1860 zcmZuyTWl0n82-;?rb}nro|fIRJDs*?rh6^46zEOl($k1gF&Kmx9|S3M!EOsQEe1&p zTNM*b6pP18HO43pa(OX`JP6UW2}FbXBq4@qOw>0dYT$toWBkvoT!PFd=fBSX{g?C4 z9(!!M&w;^-odf{L=Gwzf=iyC>I4hM(u!WaO`;pN7`+?r1btey`iJqn#N|h{=1}92k zj%`Jwl-f4uG(xMYc}^UHaTJJ@{)1hyVl|GKr+?-yiRqjCc@o!qtw-Au0a;Z=3|cn@ z1wL~mGmbe5PA{P)ijWHJ4FM4`{ioHKjRml(3LHm*V}k_YI@MsjP>E99DQi95Y0oD~ zy~XzNe9_J%o9^|Cpg-m-^{m}hLbyW=@mfGlk&(bkfwUy=VFwXA&%ozub_N8ITu^l> z@C}QhGF|e_- zs99E&WlkbsFNQKscpM}}kumtc1BxPxy|i-F3%iV3iBU`d5S~*uiCd=FT_$Peu~$CB zxFpO#Qj?dZzBW-Mk*xn$ctYgLA$o6xz@)r%j*7rG1}<&Us<+0LkOlhfSUW~J zAKQqh8_(nUP`nS%m*XisPlTF_cWIW^=Q-km{;_Tp3mcfbk_7sdxl_!maZc^1|D2K~ z+D~q#Skep0wWM8tm)vcH74F((yDM$URjwHDFj0vY7pEsW^$|BqGWvr1K55lE8Xjy# zPnx400WM^Yc1jo#)mTAUWh$Vi->An5zin{6aHeQYlm|kUcW0)WdKr^ItGC*a2`}H% zjj&!(bUEiJvCOW_z(_!G>NJHrSpJEcfQP+OlY9VIo8#Vo@A(N_RJ;Vx!+K3iRmIwH zfY)Yqv@u7KbmN?nY%P;RjT=Z>|I(PNyH7Z;DYss@IO#~y#}zTL2##4@!_~8Kq>`$L zs6dB2i}<#BGu%kPEj=D`w&3L*#zgG?s>j1b&gi$A+U^H-v1wtW0W+y55HoQvrk2eM zOy=vFTMU_KUX0`YxcT8k8^&ijnhU7u2WDmVh+^lt%-#=axlFeiq_C!%|JNk2CUyCC zF_&ia>$1~~=DZv*MEJ`LS?BLGKOiowChcoq&||F=irynp_KsmA=KwG^UYV# zvE@yebZX_^;XTjDiNda-!nhnBGJ2eb6CWyUFN|-0cGot!ZE$q7FeG>HGPdKo qBRh|r(QC39dKr_>CgfFgxA%?@? Date: Wed, 10 Apr 2019 18:18:12 -0400 Subject: [PATCH 10/10] also propagate bad block exceptions out of push_transaction for consistency --- libraries/chain/controller.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/libraries/chain/controller.cpp b/libraries/chain/controller.cpp index 47bc6ad7e7f..5300e686e74 100644 --- a/libraries/chain/controller.cpp +++ b/libraries/chain/controller.cpp @@ -1312,6 +1312,10 @@ struct controller_impl { unapplied_transactions.erase( trx->signed_id ); } return trace; + } catch( const disallowed_transaction_extensions_bad_block_exception& ) { + throw; + } catch( const protocol_feature_bad_block_exception& ) { + throw; } catch (const fc::exception& e) { trace->except = e; trace->except_ptr = std::current_exception();