From 7da37b6bc41a63a9eaef5e79ff7aaf2aea854826 Mon Sep 17 00:00:00 2001 From: Todd Fleming Date: Thu, 13 Jan 2022 11:46:22 -0500 Subject: [PATCH 1/4] backport 2.1 ACTION_RETURN_VALUE --- libraries/chain/abi_serializer.cpp | 16 ++ libraries/chain/apply_context.cpp | 29 +++- libraries/chain/chain_config.cpp | 25 ++- libraries/chain/controller.cpp | 37 +++-- .../chain/include/eosio/chain/abi_def.hpp | 32 ++-- .../include/eosio/chain/abi_serializer.hpp | 51 +++++++ .../chain/include/eosio/chain/action.hpp | 71 +++++++-- .../include/eosio/chain/action_receipt.hpp | 12 +- .../include/eosio/chain/apply_context.hpp | 1 + .../include/eosio/chain/chain_config.hpp | 143 +++++++++++++++--- .../chain/include/eosio/chain/config.hpp | 4 + .../chain/include/eosio/chain/exceptions.hpp | 4 + .../include/eosio/chain/genesis_state.hpp | 4 +- .../eosio/chain/protocol_feature_manager.hpp | 1 + libraries/chain/include/eosio/chain/trace.hpp | 4 +- .../eosio/chain/transaction_context.hpp | 2 +- .../eos-vm-oc/intrinsic_mapping.hpp | 3 +- .../eosio/chain/webassembly/interface.hpp | 8 + libraries/chain/protocol_feature_manager.cpp | 11 ++ libraries/chain/transaction_context.cpp | 1 - libraries/chain/webassembly/action.cpp | 9 ++ libraries/chain/webassembly/privileged.cpp | 6 +- .../chain/webassembly/runtimes/eos-vm.cpp | 1 + libraries/state_history/abi.cpp | 42 ++++- .../eosio/state_history/serialization.hpp | 6 +- programs/cleos/main.cpp | 25 ++- 26 files changed, 456 insertions(+), 92 deletions(-) diff --git a/libraries/chain/abi_serializer.cpp b/libraries/chain/abi_serializer.cpp index 7f72e0f66c..4c86b31c92 100644 --- a/libraries/chain/abi_serializer.cpp +++ b/libraries/chain/abi_serializer.cpp @@ -141,6 +141,7 @@ namespace eosio { namespace chain { tables.clear(); error_messages.clear(); variants.clear(); + action_results.clear(); for( const auto& st : abi.structs ) structs[st.name] = st; @@ -163,6 +164,9 @@ namespace eosio { namespace chain { for( const auto& v : abi.variants.value ) variants[v.name] = v; + for( const auto& r : abi.action_results.value ) + action_results[r.name] = r.result_type; + /** * The ABI vector may contain duplicates which would make it * an invalid ABI @@ -173,6 +177,7 @@ namespace eosio { namespace chain { EOS_ASSERT( tables.size() == abi.tables.size(), duplicate_abi_table_def_exception, "duplicate table definition detected" ); EOS_ASSERT( error_messages.size() == abi.error_messages.size(), duplicate_abi_err_msg_def_exception, "duplicate error message definition detected" ); EOS_ASSERT( variants.size() == abi.variants.value.size(), duplicate_abi_variant_def_exception, "duplicate variant definition detected" ); + EOS_ASSERT( action_results.size() == abi.action_results.value.size(), duplicate_abi_action_results_def_exception, "duplicate action results definition detected" ); validate(ctx); } @@ -301,6 +306,11 @@ namespace eosio { namespace chain { ctx.check_deadline(); EOS_ASSERT(_is_type(t.second, ctx), invalid_type_inside_abi, "${type}", ("type",impl::limit_size(t.second)) ); } FC_CAPTURE_AND_RETHROW( (t) ) } + + for( const auto& r : action_results ) { try { + ctx.check_deadline(); + EOS_ASSERT(_is_type(r.second, ctx), invalid_type_inside_abi, "${type}", ("type",impl::limit_size(r.second)) ); + } FC_CAPTURE_AND_RETHROW( (r) ) } } std::string_view abi_serializer::resolve_type(const std::string_view& type)const { @@ -590,6 +600,12 @@ namespace eosio { namespace chain { return type_name(); } + type_name abi_serializer::get_action_result_type(name action_result)const { + auto itr = action_results.find(action_result); + if( itr != action_results.end() ) return itr->second; + return type_name(); + } + optional abi_serializer::get_error_message( uint64_t error_code )const { auto itr = error_messages.find( error_code ); if( itr == error_messages.end() ) diff --git a/libraries/chain/apply_context.cpp b/libraries/chain/apply_context.cpp index df192fa0fa..d5991dc0ff 100644 --- a/libraries/chain/apply_context.cpp +++ b/libraries/chain/apply_context.cpp @@ -53,14 +53,13 @@ void apply_context::exec_one() { auto start = fc::time_point::now(); - action_receipt r; - r.receiver = receiver; - r.act_digest = digest_type::hash(*act); + digest_type act_digest; const auto& cfg = control.get_global_properties().configuration; const account_metadata_object* receiver_account = nullptr; try { try { + action_return_value.clear(); receiver_account = &db.get( receiver ); privileged = receiver_account->is_privileged(); auto native = control.find_apply_handler( receiver, act->account, act->name ); @@ -111,6 +110,18 @@ void apply_context::exec_one() } } } FC_RETHROW_EXCEPTIONS( warn, "pending console output: ${console}", ("console", _pending_console_output) ) + + if( control.is_builtin_activated( builtin_protocol_feature_t::action_return_value ) ) { + act_digest = generate_action_digest( + [this](const char* data, uint32_t datalen) { + return trx_context.hash_with_checktime(data, datalen); + }, + *act, + action_return_value + ); + } else { + act_digest = digest_type::hash(*act); + } } catch( const fc::exception& e ) { action_trace& trace = trx_context.get_action_trace( action_ordinal ); trace.error_code = controller::convert_exception_to_error_code( e ); @@ -124,6 +135,13 @@ void apply_context::exec_one() // * a pointer to an object in a chainbase index is not invalidated if the fields of that object are modified; // * and, the *receiver_account object itself cannot be removed because accounts cannot be deleted in EOSIO. + action_trace& trace = trx_context.get_action_trace( action_ordinal ); + trace.return_value = std::move(action_return_value); + trace.receipt.emplace(); + + action_receipt& r = *trace.receipt; + r.receiver = receiver; + r.act_digest = act_digest; r.global_sequence = next_global_sequence(); r.recv_sequence = next_recv_sequence( *receiver_account ); @@ -141,10 +159,7 @@ void apply_context::exec_one() r.auth_sequence[auth.actor] = next_auth_sequence( auth.actor ); } - action_trace& trace = trx_context.get_action_trace( action_ordinal ); - trace.receipt = r; - - trx_context.executed.emplace_back( std::move(r) ); + trx_context.executed_action_receipt_digests.emplace_back( r.digest() ); finalize_trace( trace, start ); diff --git a/libraries/chain/chain_config.cpp b/libraries/chain/chain_config.cpp index 5aa72c2b9b..db7d52d3b2 100644 --- a/libraries/chain/chain_config.cpp +++ b/libraries/chain/chain_config.cpp @@ -1,9 +1,10 @@ #include #include +#include namespace eosio { namespace chain { - void chain_config::validate()const { + void chain_config_v0::validate() const { EOS_ASSERT( target_block_net_usage_pct <= config::percent_100, action_validate_exception, "target block net usage percentage cannot exceed 100%" ); EOS_ASSERT( target_block_net_usage_pct >= config::percent_1/10, action_validate_exception, @@ -38,4 +39,26 @@ namespace eosio { namespace chain { "max authority depth should be at least 1" ); } +void chain_config_v1::validate() const { + chain_config_v0::validate(); + EOS_ASSERT( max_action_return_value_size <= MAX_SIZE_OF_BYTE_ARRAYS, action_validate_exception, + "max action return value size should be less than MAX_SIZE_OF_BYTE_ARRAYS" ); +} + +bool config_entry_validator::operator()(uint32_t id) const { + bool allowed = true; + switch(id){ + case chain_config_v1::max_action_return_value_size_id: + { + allowed = control.is_builtin_activated(builtin_protocol_feature_t::action_return_value); + if (!allowed){ + wlog("action_return_value protocol feature is not active, max_action_return_value_size config is not allowed"); + } + } + break; + } + + return allowed; +} + } } // namespace eosio::chain diff --git a/libraries/chain/controller.cpp b/libraries/chain/controller.cpp index 4db2e63217..9d6355f6e3 100644 --- a/libraries/chain/controller.cpp +++ b/libraries/chain/controller.cpp @@ -123,7 +123,7 @@ struct building_block { size_t _num_new_protocol_features_that_have_activated = 0; vector _pending_trx_metas; vector _pending_trx_receipts; - vector _actions; + vector _action_receipt_digests; optional _transaction_mroot; }; @@ -328,6 +328,7 @@ struct controller_impl { set_activation_handler(); set_activation_handler(); set_activation_handler(); + set_activation_handler(); self.irreversible_block.connect([this](const block_state_ptr& bsp) { wasmif.current_lib(bsp->block_num); @@ -1089,17 +1090,17 @@ struct controller_impl { auto& bb = pending->_block_stage.get(); auto orig_block_transactions_size = bb._pending_trx_receipts.size(); auto orig_state_transactions_size = bb._pending_trx_metas.size(); - auto orig_state_actions_size = bb._actions.size(); + auto orig_action_receipt_digests_size = bb._action_receipt_digests.size(); std::function callback = [this, orig_block_transactions_size, orig_state_transactions_size, - orig_state_actions_size]() + orig_action_receipt_digests_size]() { auto& bb = pending->_block_stage.get(); bb._pending_trx_receipts.resize(orig_block_transactions_size); bb._pending_trx_metas.resize(orig_state_transactions_size); - bb._actions.resize(orig_state_actions_size); + bb._action_receipt_digests.resize(orig_action_receipt_digests_size); }; return fc::make_scoped_exit( std::move(callback) ); @@ -1143,7 +1144,8 @@ struct controller_impl { auto restore = make_block_restore_point(); trace->receipt = push_receipt( gtrx.trx_id, transaction_receipt::soft_fail, trx_context.billed_cpu_time_us, trace->net_usage ); - fc::move_append( pending->_block_stage.get()._actions, move(trx_context.executed) ); + fc::move_append( pending->_block_stage.get()._action_receipt_digests, + std::move(trx_context.executed_action_receipt_digests) ); trx_context.squash(); restore.cancel(); @@ -1286,7 +1288,8 @@ struct controller_impl { trx_context.billed_cpu_time_us, trace->net_usage ); - fc::move_append( pending->_block_stage.get()._actions, move(trx_context.executed) ); + fc::move_append( pending->_block_stage.get()._action_receipt_digests, + std::move(trx_context.executed_action_receipt_digests) ); trace->account_ram_delta = account_delta( gtrx.payer, trx_removal_ram_delta ); @@ -1478,7 +1481,8 @@ struct controller_impl { trace->receipt = r; } - fc::move_append(pending->_block_stage.get()._actions, move(trx_context.executed)); + fc::move_append( pending->_block_stage.get()._action_receipt_digests, + std::move(trx_context.executed_action_receipt_digests) ); // call the accept signal but only once for this transaction if (!trx->accepted) { @@ -1693,7 +1697,7 @@ struct controller_impl { // Create (unsigned) block: auto block_ptr = std::make_shared( pbhs.make_block_header( bb._transaction_mroot ? *bb._transaction_mroot : calculate_trx_merkle( bb._pending_trx_receipts ), - calculate_action_merkle(), + merkle( std::move( pending->_block_stage.get()._action_receipt_digests ) ), bb._new_pending_producer_schedule, std::move( bb._new_protocol_feature_activations ), protocol_features.get_protocol_feature_set() @@ -2165,16 +2169,6 @@ struct controller_impl { return applied_trxs; } - checksum256_type calculate_action_merkle() { - vector action_digests; - const auto& actions = pending->_block_stage.get()._actions; - action_digests.reserve( actions.size() ); - for( const auto& a : actions ) - action_digests.emplace_back( a.digest() ); - - return merkle( move(action_digests) ); - } - static checksum256_type calculate_trx_merkle( const vector& trxs ) { vector trx_digests; trx_digests.reserve( trxs.size() ); @@ -3367,7 +3361,12 @@ void controller_impl::on_activation +void controller_impl::on_activation() { + db.modify( db.get(), [&]( auto& ps ) { + add_intrinsic_to_whitelist( ps.whitelisted_intrinsics, "set_action_return_value" ); + } ); +} /// End of protocol feature activation handlers diff --git a/libraries/chain/include/eosio/chain/abi_def.hpp b/libraries/chain/include/eosio/chain/abi_def.hpp index dcace00552..cab5069ff1 100644 --- a/libraries/chain/include/eosio/chain/abi_def.hpp +++ b/libraries/chain/include/eosio/chain/abi_def.hpp @@ -95,6 +95,16 @@ struct variant_def { vector types; }; +struct action_result_def { + action_result_def() = default; + action_result_def(const action_name& name, const type_name& result_type) + :name(name), result_type(result_type) + {} + + action_name name; + type_name result_type; +}; + template struct may_not_exist { T value{}; @@ -111,15 +121,16 @@ struct abi_def { ,error_messages(error_msgs) {} - string version = ""; - vector types; - vector structs; - vector actions; - vector tables; - vector ricardian_clauses; - vector error_messages; - extensions_type abi_extensions; - may_not_exist> variants; + string version = ""; + vector types; + vector structs; + vector actions; + vector tables; + vector ricardian_clauses; + vector error_messages; + extensions_type abi_extensions; + may_not_exist> variants; + may_not_exist> action_results; }; abi_def eosio_contract_abi(const abi_def& eosio_system_abi); @@ -164,5 +175,6 @@ FC_REFLECT( eosio::chain::table_def , (name)(index_type)( FC_REFLECT( eosio::chain::clause_pair , (id)(body) ) FC_REFLECT( eosio::chain::error_message , (error_code)(error_msg) ) FC_REFLECT( eosio::chain::variant_def , (name)(types) ) +FC_REFLECT( eosio::chain::action_result_def , (name)(result_type) ) FC_REFLECT( eosio::chain::abi_def , (version)(types)(structs)(actions)(tables) - (ricardian_clauses)(error_messages)(abi_extensions)(variants) ) + (ricardian_clauses)(error_messages)(abi_extensions)(variants)(action_results) ) diff --git a/libraries/chain/include/eosio/chain/abi_serializer.hpp b/libraries/chain/include/eosio/chain/abi_serializer.hpp index 1be1095c94..4446e715c5 100644 --- a/libraries/chain/include/eosio/chain/abi_serializer.hpp +++ b/libraries/chain/include/eosio/chain/abi_serializer.hpp @@ -60,6 +60,7 @@ struct abi_serializer { type_name get_action_type(name action)const; type_name get_table_type(name action)const; + type_name get_action_result_type(name action_result)const; optional get_error_message( uint64_t error_code )const; @@ -139,6 +140,7 @@ struct abi_serializer { map tables; map error_messages; map> variants; + map action_results; map, std::less<>> built_in_types; void configure_built_in_types(); @@ -443,6 +445,55 @@ namespace impl { out(name, std::move(mvo)); } + /** + * overload of to_variant_object for action_trace + * + * This matches the FC_REFLECT for this type, but this is provided to extract the contents of action_trace.return_value + * @tparam Resolver + * @param action_trace + * @param resolver + * @return + */ + template + static void add( mutable_variant_object& out, const char* name, const action_trace& act_trace, Resolver resolver, abi_traverse_context& ctx ) + { + static_assert(fc::reflector::total_member_count == 17); + auto h = ctx.enter_scope(); + mutable_variant_object mvo; + + mvo("action_ordinal", act_trace.action_ordinal); + mvo("creator_action_ordinal", act_trace.creator_action_ordinal); + mvo("closest_unnotified_ancestor_action_ordinal", act_trace.closest_unnotified_ancestor_action_ordinal); + mvo("receipt", act_trace.receipt); + mvo("receiver", act_trace.receiver); + add(mvo, "act", act_trace.act, resolver, ctx); + mvo("context_free", act_trace.context_free); + mvo("elapsed", act_trace.elapsed); + mvo("console", act_trace.console); + mvo("trx_id", act_trace.trx_id); + mvo("block_num", act_trace.block_num); + mvo("block_time", act_trace.block_time); + mvo("producer_block_id", act_trace.producer_block_id); + mvo("account_ram_deltas", act_trace.account_ram_deltas); + mvo("except", act_trace.except); + mvo("error_code", act_trace.error_code); + + mvo("return_value_hex_data", act_trace.return_value); + auto act = act_trace.act; + try { + auto abi = resolver(act.account); + if (abi) { + auto type = abi->get_action_result_type(act.name); + if (!type.empty()) { + binary_to_variant_context _ctx(*abi, ctx, type); + _ctx.short_path = true; // Just to be safe while avoiding the complexity of threading an override boolean all over the place + mvo( "return_value_data", abi->_binary_to_variant( type, act_trace.return_value, _ctx )); + } + } + } catch(...) {} + out(name, std::move(mvo)); + } + /** * overload of to_variant_object for packed_transaction * diff --git a/libraries/chain/include/eosio/chain/action.hpp b/libraries/chain/include/eosio/chain/action.hpp index 1e59344795..a3c6ed604a 100644 --- a/libraries/chain/include/eosio/chain/action.hpp +++ b/libraries/chain/include/eosio/chain/action.hpp @@ -53,32 +53,38 @@ namespace eosio { namespace chain { * application code. An application code will check to see if the required authorization * were properly declared when it executes. */ - struct action { - account_name account; - action_name name; - vector authorization; + struct action_base { + account_name account; + action_name name; + vector authorization; + + action_base() = default; + + action_base( account_name acnt, action_name act, const vector& auth ) + : account(acnt), name(act), authorization(auth) {} + action_base( account_name acnt, action_name act, vector&& auth ) + : account(acnt), name(act), authorization(std::move(auth)) {} + }; + + struct action : public action_base { bytes data; - action(){} + action() = default; template::value, int> = 1> - action( vector auth, const T& value ) { - account = T::get_account(); - name = T::get_name(); - authorization = move(auth); + action( vector auth, const T& value ) + : action_base( T::get_account(), T::get_name(), std::move(auth) ) { data.assign(value.data(), value.data() + value.size()); } template::value, int> = 1> - action( vector auth, const T& value ) { - account = T::get_account(); - name = T::get_name(); - authorization = move(auth); - data = fc::raw::pack(value); + action( vector auth, const T& value ) + : action_base( T::get_account(), T::get_name(), std::move(auth) ) { + data = fc::raw::pack(value); } action( vector auth, account_name account, action_name name, const bytes& data ) - : account(account), name(name), authorization(move(auth)), data(data) { + : action_base(account, name, std::move(auth)), data(data) { } template @@ -89,6 +95,38 @@ namespace eosio { namespace chain { } }; + template + auto generate_action_digest(Hasher&& hash, const action& act, const vector& action_output) { + using hash_type = decltype(hash(nullptr, 0)); + hash_type hashes[2]; + const action_base* base = &act; + const auto action_base_size = fc::raw::pack_size(*base); + const auto action_input_size = fc::raw::pack_size(act.data); + const auto action_output_size = fc::raw::pack_size(action_output); + const auto rhs_size = action_input_size + action_output_size; + std::vector buff; + buff.reserve(std::max(action_base_size, rhs_size)); + { + buff.resize(action_base_size); + fc::datastream ds(buff.data(), action_base_size); + fc::raw::pack(ds, *base); + hashes[0] = hash(buff.data(), action_base_size); + } + { + buff.resize(rhs_size); + fc::datastream ds(buff.data(), rhs_size); + fc::raw::pack(ds, act.data); + fc::raw::pack(ds, action_output); + hashes[1] = hash(buff.data(), rhs_size); + } + auto hashes_size = fc::raw::pack_size(hashes[0]) + fc::raw::pack_size(hashes[1]); + buff.resize(hashes_size); // may cause reallocation but in practice will be unlikely + fc::datastream ds(buff.data(), hashes_size); + fc::raw::pack(ds, hashes[0]); + fc::raw::pack(ds, hashes[1]); + return hash(buff.data(), hashes_size); + } + struct action_notice : public action { account_name receiver; }; @@ -96,4 +134,5 @@ namespace eosio { namespace chain { } } /// namespace eosio::chain FC_REFLECT( eosio::chain::permission_level, (actor)(permission) ) -FC_REFLECT( eosio::chain::action, (account)(name)(authorization)(data) ) +FC_REFLECT( eosio::chain::action_base, (account)(name)(authorization) ) +FC_REFLECT_DERIVED( eosio::chain::action, (eosio::chain::action_base), (data) ) diff --git a/libraries/chain/include/eosio/chain/action_receipt.hpp b/libraries/chain/include/eosio/chain/action_receipt.hpp index 78ef25c7c0..4afe35371d 100644 --- a/libraries/chain/include/eosio/chain/action_receipt.hpp +++ b/libraries/chain/include/eosio/chain/action_receipt.hpp @@ -16,7 +16,17 @@ namespace eosio { namespace chain { fc::unsigned_int code_sequence = 0; ///< total number of setcodes fc::unsigned_int abi_sequence = 0; ///< total number of setabis - digest_type digest()const { return digest_type::hash(*this); } + digest_type digest()const { + digest_type::encoder e; + fc::raw::pack(e, receiver); + fc::raw::pack(e, act_digest); + fc::raw::pack(e, global_sequence); + fc::raw::pack(e, recv_sequence); + fc::raw::pack(e, auth_sequence); + fc::raw::pack(e, code_sequence); + fc::raw::pack(e, abi_sequence); + return e.result(); + } }; } } /// namespace eosio::chain diff --git a/libraries/chain/include/eosio/chain/apply_context.hpp b/libraries/chain/include/eosio/chain/apply_context.hpp index 352c5405f4..e8fbc403f2 100644 --- a/libraries/chain/include/eosio/chain/apply_context.hpp +++ b/libraries/chain/include/eosio/chain/apply_context.hpp @@ -571,6 +571,7 @@ class apply_context { bool context_free = false; public: + std::vector action_return_value; generic_index idx64; generic_index idx128; generic_index idx256; diff --git a/libraries/chain/include/eosio/chain/chain_config.hpp b/libraries/chain/include/eosio/chain/chain_config.hpp index 9e83b09779..b6038d6f33 100644 --- a/libraries/chain/include/eosio/chain/chain_config.hpp +++ b/libraries/chain/include/eosio/chain/chain_config.hpp @@ -12,7 +12,30 @@ namespace eosio { namespace chain { * their preference for each of the parameters in this object, and the blockchain runs according to the median of the * values specified by the producers. */ -struct chain_config { +struct chain_config_v0 { + + //order must match parameters as ids are used in serialization + enum { + max_block_net_usage_id, + target_block_net_usage_pct_id, + max_transaction_net_usage_id, + base_per_transaction_net_usage_id, + net_usage_leeway_id, + context_free_discount_net_usage_num_id, + context_free_discount_net_usage_den_id, + max_block_cpu_usage_id, + target_block_cpu_usage_pct_id, + max_transaction_cpu_usage_id, + min_transaction_cpu_usage_id, + max_transaction_lifetime_id, + deferred_trx_expiration_window_id, + max_transaction_delay_id, + max_inline_action_size_id, + max_inline_action_depth_id, + max_authority_depth_id, + PARAMS_COUNT + }; + uint64_t max_block_net_usage; ///< the maxiumum net usage in instructions for a block uint32_t target_block_net_usage_pct; ///< the target percent (1% == 100, 100%= 10,000) of maximum net usage; exceeding this triggers congestion handling uint32_t max_transaction_net_usage; ///< the maximum objectively measured net usage that the chain will allow regardless of account limits @@ -35,29 +58,16 @@ struct chain_config { void validate()const; + inline const chain_config_v0& v0() const { + return *this; + } + template - friend Stream& operator << ( Stream& out, const chain_config& c ) { - return out << "Max Block Net Usage: " << c.max_block_net_usage << ", " - << "Target Block Net Usage Percent: " << ((double)c.target_block_net_usage_pct / (double)config::percent_1) << "%, " - << "Max Transaction Net Usage: " << c.max_transaction_net_usage << ", " - << "Base Per-Transaction Net Usage: " << c.base_per_transaction_net_usage << ", " - << "Net Usage Leeway: " << c.net_usage_leeway << ", " - << "Context-Free Data Net Usage Discount: " << (double)c.context_free_discount_net_usage_num * 100.0 / (double)c.context_free_discount_net_usage_den << "% , " - - << "Max Block CPU Usage: " << c.max_block_cpu_usage << ", " - << "Target Block CPU Usage Percent: " << ((double)c.target_block_cpu_usage_pct / (double)config::percent_1) << "%, " - << "Max Transaction CPU Usage: " << c.max_transaction_cpu_usage << ", " - << "Min Transaction CPU Usage: " << c.min_transaction_cpu_usage << ", " - - << "Max Transaction Lifetime: " << c.max_transaction_lifetime << ", " - << "Deferred Transaction Expiration Window: " << c.deferred_trx_expiration_window << ", " - << "Max Transaction Delay: " << c.max_transaction_delay << ", " - << "Max Inline Action Size: " << c.max_inline_action_size << ", " - << "Max Inline Action Depth: " << c.max_inline_action_depth << ", " - << "Max Authority Depth: " << c.max_authority_depth << "\n"; + friend Stream& operator << ( Stream& out, const chain_config_v0& c ) { + return c.log(out) << "\n"; } - friend inline bool operator ==( const chain_config& lhs, const chain_config& rhs ) { + friend inline bool operator ==( const chain_config_v0& lhs, const chain_config_v0& rhs ) { return std::tie( lhs.max_block_net_usage, lhs.target_block_net_usage_pct, lhs.max_transaction_net_usage, @@ -97,13 +107,96 @@ struct chain_config { ); }; - friend inline bool operator !=( const chain_config& lhs, const chain_config& rhs ) { return !(lhs == rhs); } + friend inline bool operator !=( const chain_config_v0& lhs, const chain_config_v0& rhs ) { return !(lhs == rhs); } +protected: + template + Stream& log(Stream& out) const{ + return out << "Max Block Net Usage: " << max_block_net_usage << ", " + << "Target Block Net Usage Percent: " << ((double)target_block_net_usage_pct / (double)config::percent_1) << "%, " + << "Max Transaction Net Usage: " << max_transaction_net_usage << ", " + << "Base Per-Transaction Net Usage: " << base_per_transaction_net_usage << ", " + << "Net Usage Leeway: " << net_usage_leeway << ", " + << "Context-Free Data Net Usage Discount: " << (double)context_free_discount_net_usage_num * 100.0 / (double)context_free_discount_net_usage_den << "% , " + + << "Max Block CPU Usage: " << max_block_cpu_usage << ", " + << "Target Block CPU Usage Percent: " << ((double)target_block_cpu_usage_pct / (double)config::percent_1) << "%, " + << "Max Transaction CPU Usage: " << max_transaction_cpu_usage << ", " + << "Min Transaction CPU Usage: " << min_transaction_cpu_usage << ", " + + << "Max Transaction Lifetime: " << max_transaction_lifetime << ", " + << "Deferred Transaction Expiration Window: " << deferred_trx_expiration_window << ", " + << "Max Transaction Delay: " << max_transaction_delay << ", " + << "Max Inline Action Size: " << max_inline_action_size << ", " + << "Max Inline Action Depth: " << max_inline_action_depth << ", " + << "Max Authority Depth: " << max_authority_depth; + } }; +/** + * @brief v1 Producer-voted blockchain configuration parameters + * + * If Adding new parameters create chain_config_v[n] class instead of adding + * new parameters to v1 or v0. This is needed for snapshots backward compatibility + */ +struct chain_config_v1 : chain_config_v0 { + using Base = chain_config_v0; + + uint32_t max_action_return_value_size = config::default_max_action_return_value_size; ///< size limit for action return value + + //order must match parameters as ids are used in serialization + enum { + max_action_return_value_size_id = Base::PARAMS_COUNT, + PARAMS_COUNT + }; + + inline const Base& base() const { + return static_cast(*this); + } + + void validate() const; + + template + friend Stream& operator << ( Stream& out, const chain_config_v1& c ) { + return c.log(out) << "\n"; + } + + friend inline bool operator == ( const chain_config_v1& lhs, const chain_config_v1& rhs ) { + //add v1 parameters comarison here + return std::tie(lhs.max_action_return_value_size) == std::tie(rhs.max_action_return_value_size) + && lhs.base() == rhs.base(); + } + + friend inline bool operator != ( const chain_config_v1& lhs, const chain_config_v1& rhs ) { + return !(lhs == rhs); + } + + inline chain_config_v1& operator= (const Base& b) { + Base::operator= (b); + return *this; + } + +protected: + template + Stream& log(Stream& out) const{ + return base().log(out) << ", Max Action Return Value Size: " << max_action_return_value_size; + } +}; + +class controller; + +struct config_entry_validator{ + const controller& control; + + bool operator()(uint32_t id) const; +}; + +//after adding 1st value to chain_config_v1 change this using to point to v1 +using chain_config = chain_config_v1; + } } // namespace eosio::chain -FC_REFLECT(eosio::chain::chain_config, +FC_REFLECT(eosio::chain::chain_config_v0, (max_block_net_usage)(target_block_net_usage_pct) (max_transaction_net_usage)(base_per_transaction_net_usage)(net_usage_leeway) (context_free_discount_net_usage_num)(context_free_discount_net_usage_den) @@ -115,3 +208,7 @@ FC_REFLECT(eosio::chain::chain_config, (max_inline_action_size)(max_inline_action_depth)(max_authority_depth) ) + +FC_REFLECT_DERIVED(eosio::chain::chain_config_v1, (eosio::chain::chain_config_v0), + (max_action_return_value_size) +) diff --git a/libraries/chain/include/eosio/chain/config.hpp b/libraries/chain/include/eosio/chain/config.hpp index 6461f7c836..15b41e8189 100644 --- a/libraries/chain/include/eosio/chain/config.hpp +++ b/libraries/chain/include/eosio/chain/config.hpp @@ -1,6 +1,7 @@ #pragma once #include #include +#include #pragma GCC diagnostic ignored "-Wunused-variable" @@ -83,6 +84,9 @@ const static uint32_t default_block_cpu_effort_pct = 80 * perc const static uint16_t default_controller_thread_pool_size = 2; const static uint32_t default_max_variable_signature_length = 16384u; const static uint32_t default_max_nonprivileged_inline_action_size = 4 * 1024; // 4 KB +const static uint32_t default_max_action_return_value_size = 256; + +static_assert(MAX_SIZE_OF_BYTE_ARRAYS == 20*1024*1024, "Changing MAX_SIZE_OF_BYTE_ARRAYS breaks consensus. Make sure this is expected"); const static uint32_t min_net_usage_delta_between_base_and_max_for_trx = 10*1024; // Should be large enough to allow recovery from badly set blockchain parameters without a hard fork diff --git a/libraries/chain/include/eosio/chain/exceptions.hpp b/libraries/chain/include/eosio/chain/exceptions.hpp index c19cc97858..c60427054e 100644 --- a/libraries/chain/include/eosio/chain/exceptions.hpp +++ b/libraries/chain/include/eosio/chain/exceptions.hpp @@ -306,6 +306,8 @@ namespace eosio { namespace chain { 3050011, "eosio_assert_code assertion failure uses restricted error code value" ) FC_DECLARE_DERIVED_EXCEPTION( inline_action_too_big_nonprivileged, action_validate_exception, 3050012, "Inline action exceeds maximum size limit for a non-privileged account" ) + FC_DECLARE_DERIVED_EXCEPTION( action_return_value_exception, action_validate_exception, + 3050014, "action return value size too big" ) FC_DECLARE_DERIVED_EXCEPTION( database_exception, chain_exception, 3060000, "Database exception" ) @@ -518,6 +520,8 @@ namespace eosio { namespace chain { 3015015, "Duplicate variant definition in the ABI" ) FC_DECLARE_DERIVED_EXCEPTION( unsupported_abi_version_exception, abi_exception, 3015016, "ABI has an unsupported version" ) + FC_DECLARE_DERIVED_EXCEPTION( duplicate_abi_action_results_def_exception, abi_exception, + 3015017, "Duplicate action results definition in the ABI" ) FC_DECLARE_DERIVED_EXCEPTION( contract_exception, chain_exception, 3160000, "Contract exception" ) diff --git a/libraries/chain/include/eosio/chain/genesis_state.hpp b/libraries/chain/include/eosio/chain/genesis_state.hpp index 5e5b643a1b..889cbf5c0e 100644 --- a/libraries/chain/include/eosio/chain/genesis_state.hpp +++ b/libraries/chain/include/eosio/chain/genesis_state.hpp @@ -15,7 +15,7 @@ struct genesis_state { static const string eosio_root_key; - chain_config initial_configuration = { + chain_config_v0 initial_configuration = { .max_block_net_usage = config::default_max_block_net_usage, .target_block_net_usage_pct = config::default_target_block_net_usage_pct, .max_transaction_net_usage = config::default_max_transaction_net_usage, @@ -58,6 +58,6 @@ struct genesis_state { } } // namespace eosio::chain - +// @swap initial_timestamp initial_key initial_configuration FC_REFLECT(eosio::chain::genesis_state, (initial_timestamp)(initial_key)(initial_configuration)) diff --git a/libraries/chain/include/eosio/chain/protocol_feature_manager.hpp b/libraries/chain/include/eosio/chain/protocol_feature_manager.hpp index 81d22c15b0..98a80443e9 100644 --- a/libraries/chain/include/eosio/chain/protocol_feature_manager.hpp +++ b/libraries/chain/include/eosio/chain/protocol_feature_manager.hpp @@ -23,6 +23,7 @@ enum class builtin_protocol_feature_t : uint32_t { ram_restrictions, webauthn_key, wtmsig_block_signatures, + action_return_value, }; struct protocol_feature_subjective_restrictions { diff --git a/libraries/chain/include/eosio/chain/trace.hpp b/libraries/chain/include/eosio/chain/trace.hpp index b617baf68b..29a25c0d49 100644 --- a/libraries/chain/include/eosio/chain/trace.hpp +++ b/libraries/chain/include/eosio/chain/trace.hpp @@ -44,6 +44,7 @@ namespace eosio { namespace chain { flat_set account_ram_deltas; fc::optional except; fc::optional error_code; + std::vector return_value; }; struct transaction_trace { @@ -87,8 +88,9 @@ FC_REFLECT( eosio::chain::account_delta, FC_REFLECT( eosio::chain::action_trace, (action_ordinal)(creator_action_ordinal)(closest_unnotified_ancestor_action_ordinal)(receipt) (receiver)(act)(context_free)(elapsed)(console)(trx_id)(block_num)(block_time) - (producer_block_id)(account_ram_deltas)(except)(error_code) ) + (producer_block_id)(account_ram_deltas)(except)(error_code)(return_value) ) +// @ignore except_ptr FC_REFLECT( eosio::chain::transaction_trace, (id)(block_num)(block_time)(producer_block_id) (receipt)(elapsed)(net_usage)(scheduled) (action_traces)(account_ram_delta)(failed_dtrx_trace)(except)(error_code) ) diff --git a/libraries/chain/include/eosio/chain/transaction_context.hpp b/libraries/chain/include/eosio/chain/transaction_context.hpp index bb60c3d36f..7d656eaaa3 100644 --- a/libraries/chain/include/eosio/chain/transaction_context.hpp +++ b/libraries/chain/include/eosio/chain/transaction_context.hpp @@ -129,7 +129,7 @@ namespace eosio { namespace chain { fc::time_point published; - vector executed; + vector executed_action_receipt_digests; flat_set bill_to_accounts; flat_set validate_ram_usage; diff --git a/libraries/chain/include/eosio/chain/webassembly/eos-vm-oc/intrinsic_mapping.hpp b/libraries/chain/include/eosio/chain/webassembly/eos-vm-oc/intrinsic_mapping.hpp index 73d344caf8..92796e6aa6 100644 --- a/libraries/chain/include/eosio/chain/webassembly/eos-vm-oc/intrinsic_mapping.hpp +++ b/libraries/chain/include/eosio/chain/webassembly/eos-vm-oc/intrinsic_mapping.hpp @@ -253,7 +253,8 @@ inline constexpr auto get_intrinsic_table() { "eosio_injection._eosio_i32_to_f64", "eosio_injection._eosio_i64_to_f64", "eosio_injection._eosio_ui32_to_f64", - "eosio_injection._eosio_ui64_to_f64" + "eosio_injection._eosio_ui64_to_f64", + "env.set_action_return_value" ); } inline constexpr std::size_t find_intrinsic_index(std::string_view hf) { diff --git a/libraries/chain/include/eosio/chain/webassembly/interface.hpp b/libraries/chain/include/eosio/chain/webassembly/interface.hpp index f20e5541a3..816a061f2a 100644 --- a/libraries/chain/include/eosio/chain/webassembly/interface.hpp +++ b/libraries/chain/include/eosio/chain/webassembly/interface.hpp @@ -516,6 +516,14 @@ namespace webassembly { */ name current_receiver() const; + /** + * Sets a value (packed blob char array) to be included in the action receipt. + * + * @ingroup action + * @param packed_blob - the packed blob + */ + void set_action_return_value(span packed_blob); + /** * Print a string. * diff --git a/libraries/chain/protocol_feature_manager.cpp b/libraries/chain/protocol_feature_manager.cpp index d61a7fe25c..f43eb56266 100644 --- a/libraries/chain/protocol_feature_manager.cpp +++ b/libraries/chain/protocol_feature_manager.cpp @@ -183,6 +183,17 @@ and may have additional signatures in a block extension with ID `2`. Privileged Contracts: may continue to use `set_proposed_producers` as they have; may use a new `set_proposed_producers_ex` intrinsic to access extended features. +*/ + {} + } ) + ( builtin_protocol_feature_t::action_return_value, builtin_protocol_feature_spec{ + "ACTION_RETURN_VALUE", + fc::variant("69b064c5178e2738e144ed6caa9349a3995370d78db29e494b3126ebd9111966").as(), + // SHA256 hash of the raw message below within the comment delimiters (do not modify message below). +/* +Builtin protocol feature: ACTION_RETURN_VALUE + +Enables new `set_action_return_value` intrinsic which sets a value that is included in action_receipt. */ {} } ) diff --git a/libraries/chain/transaction_context.cpp b/libraries/chain/transaction_context.cpp index 63a6a78ea6..3d936f19ab 100644 --- a/libraries/chain/transaction_context.cpp +++ b/libraries/chain/transaction_context.cpp @@ -65,7 +65,6 @@ namespace eosio { namespace chain { trace->block_num = c.head_block_num() + 1; trace->block_time = c.pending_block_time(); trace->producer_block_id = c.pending_producer_block_id(); - executed.reserve( trx.total_actions() ); } void transaction_context::disallow_transaction_extensions( const char* error_msg )const { diff --git a/libraries/chain/webassembly/action.cpp b/libraries/chain/webassembly/action.cpp index 97a4dec9f3..20a914556e 100644 --- a/libraries/chain/webassembly/action.cpp +++ b/libraries/chain/webassembly/action.cpp @@ -20,4 +20,13 @@ namespace eosio { namespace chain { namespace webassembly { name interface::current_receiver() const { return context.get_receiver(); } + + void interface::set_action_return_value( span packed_blob ) { + auto max_action_return_value_size = + context.control.get_global_properties().configuration.max_action_return_value_size; + EOS_ASSERT(packed_blob.size() <= max_action_return_value_size, + action_return_value_exception, + "action return value size must be less or equal to ${s} bytes", ("s", max_action_return_value_size)); + context.action_return_value.assign( packed_blob.data(), packed_blob.data() + packed_blob.size() ); + } }}} // ns eosio::chain::webassembly diff --git a/libraries/chain/webassembly/privileged.cpp b/libraries/chain/webassembly/privileged.cpp index 1bd040f49c..925464d123 100644 --- a/libraries/chain/webassembly/privileged.cpp +++ b/libraries/chain/webassembly/privileged.cpp @@ -115,12 +115,12 @@ namespace eosio { namespace chain { namespace webassembly { uint32_t interface::get_blockchain_parameters_packed( legacy_span packed_blockchain_parameters ) const { auto& gpo = context.control.get_global_properties(); - auto s = fc::raw::pack_size( gpo.configuration ); + auto s = fc::raw::pack_size( gpo.configuration.v0() ); if( packed_blockchain_parameters.size() == 0 ) return s; if ( s <= packed_blockchain_parameters.size() ) { datastream ds( packed_blockchain_parameters.data(), s ); - fc::raw::pack(ds, gpo.configuration); + fc::raw::pack(ds, gpo.configuration.v0()); return s; } return 0; @@ -128,7 +128,7 @@ namespace eosio { namespace chain { namespace webassembly { void interface::set_blockchain_parameters_packed( legacy_span packed_blockchain_parameters ) { datastream ds( packed_blockchain_parameters.data(), packed_blockchain_parameters.size() ); - chain::chain_config cfg; + chain::chain_config_v0 cfg; fc::raw::unpack(ds, cfg); cfg.validate(); context.db.modify( context.control.get_global_properties(), diff --git a/libraries/chain/webassembly/runtimes/eos-vm.cpp b/libraries/chain/webassembly/runtimes/eos-vm.cpp index 6febf97922..5ae3edc2e8 100644 --- a/libraries/chain/webassembly/runtimes/eos-vm.cpp +++ b/libraries/chain/webassembly/runtimes/eos-vm.cpp @@ -342,6 +342,7 @@ REGISTER_CF_HOST_FUNCTION(eosio_exit) REGISTER_LEGACY_CF_HOST_FUNCTION(read_action_data); REGISTER_CF_HOST_FUNCTION(action_data_size); REGISTER_CF_HOST_FUNCTION(current_receiver); +REGISTER_HOST_FUNCTION(set_action_return_value); // console api REGISTER_LEGACY_CF_HOST_FUNCTION(prints); diff --git a/libraries/state_history/abi.cpp b/libraries/state_history/abi.cpp index 628836bae7..ae8ad2e6c1 100644 --- a/libraries/state_history/abi.cpp +++ b/libraries/state_history/abi.cpp @@ -107,6 +107,22 @@ extern const char* const state_history_plugin_abi = R"({ { "name": "error_code", "type": "uint64?" } ] }, + { + "name": "action_trace_v1", "fields": [ + { "name": "action_ordinal", "type": "varuint32" }, + { "name": "creator_action_ordinal", "type": "varuint32" }, + { "name": "receipt", "type": "action_receipt?" }, + { "name": "receiver", "type": "name" }, + { "name": "act", "type": "action" }, + { "name": "context_free", "type": "bool" }, + { "name": "elapsed", "type": "int64" }, + { "name": "console", "type": "string" }, + { "name": "account_ram_deltas", "type": "account_delta[]" }, + { "name": "except", "type": "string?" }, + { "name": "error_code", "type": "uint64?" }, + { "name": "return_value", "type": "bytes"} + ] + }, { "name": "partial_transaction_v0", "fields": [ { "name": "expiration", "type": "time_point_sec" }, @@ -351,6 +367,28 @@ extern const char* const state_history_plugin_abi = R"({ { "type": "uint16", "name": "max_authority_depth" } ] }, + { + "name": "chain_config_v1", "fields": [ + { "type": "uint64", "name": "max_block_net_usage" }, + { "type": "uint32", "name": "target_block_net_usage_pct" }, + { "type": "uint32", "name": "max_transaction_net_usage" }, + { "type": "uint32", "name": "base_per_transaction_net_usage" }, + { "type": "uint32", "name": "net_usage_leeway" }, + { "type": "uint32", "name": "context_free_discount_net_usage_num" }, + { "type": "uint32", "name": "context_free_discount_net_usage_den" }, + { "type": "uint32", "name": "max_block_cpu_usage" }, + { "type": "uint32", "name": "target_block_cpu_usage_pct" }, + { "type": "uint32", "name": "max_transaction_cpu_usage" }, + { "type": "uint32", "name": "min_transaction_cpu_usage" }, + { "type": "uint32", "name": "max_transaction_lifetime" }, + { "type": "uint32", "name": "deferred_trx_expiration_window" }, + { "type": "uint32", "name": "max_transaction_delay" }, + { "type": "uint32", "name": "max_inline_action_size" }, + { "type": "uint16", "name": "max_inline_action_depth" }, + { "type": "uint16", "name": "max_authority_depth" }, + { "type": "uint32", "name": "max_action_return_value_size" } + ] + }, { "name": "global_property_v0", "fields": [ { "type": "uint32?", "name": "proposed_schedule_block_num" }, @@ -502,7 +540,7 @@ extern const char* const state_history_plugin_abi = R"({ { "name": "result", "types": ["get_status_result_v0", "get_blocks_result_v0"] }, { "name": "action_receipt", "types": ["action_receipt_v0"] }, - { "name": "action_trace", "types": ["action_trace_v0"] }, + { "name": "action_trace", "types": ["action_trace_v0", "action_trace_v1"] }, { "name": "partial_transaction", "types": ["partial_transaction_v0"] }, { "name": "transaction_trace", "types": ["transaction_trace_v0"] }, { "name": "transaction_variant", "types": ["transaction_id", "packed_transaction"] }, @@ -518,7 +556,7 @@ extern const char* const state_history_plugin_abi = R"({ { "name": "contract_index256", "types": ["contract_index256_v0"] }, { "name": "contract_index_double", "types": ["contract_index_double_v0"] }, { "name": "contract_index_long_double", "types": ["contract_index_long_double_v0"] }, - { "name": "chain_config", "types": ["chain_config_v0"] }, + { "name": "chain_config", "types": ["chain_config_v0", "chain_config_v1"] }, { "name": "global_property", "types": ["global_property_v0", "global_property_v1"] }, { "name": "generated_transaction", "types": ["generated_transaction_v0"] }, { "name": "activated_protocol_feature", "types": ["activated_protocol_feature_v0"] }, diff --git a/libraries/state_history/include/eosio/state_history/serialization.hpp b/libraries/state_history/include/eosio/state_history/serialization.hpp index b36a9d72ad..2c5ca89df4 100644 --- a/libraries/state_history/include/eosio/state_history/serialization.hpp +++ b/libraries/state_history/include/eosio/state_history/serialization.hpp @@ -291,7 +291,7 @@ datastream& operator<<(datastream& template datastream& operator<<(datastream& ds, const history_serial_wrapper& obj) { - fc::raw::pack(ds, fc::unsigned_int(0)); + fc::raw::pack(ds, fc::unsigned_int(1)); fc::raw::pack(ds, as_type(obj.obj.max_block_net_usage)); fc::raw::pack(ds, as_type(obj.obj.target_block_net_usage_pct)); fc::raw::pack(ds, as_type(obj.obj.max_transaction_net_usage)); @@ -309,6 +309,7 @@ datastream& operator<<(datastream& ds, const history_serial_wrapper(obj.obj.max_inline_action_size)); fc::raw::pack(ds, as_type(obj.obj.max_inline_action_depth)); fc::raw::pack(ds, as_type(obj.obj.max_authority_depth)); + fc::raw::pack(ds, as_type(obj.obj.max_action_return_value_size)); return ds; } @@ -570,7 +571,7 @@ inline fc::optional cap_error_code(const fc::optional& error template datastream& operator<<(datastream& ds, const history_context_wrapper& obj) { bool debug_mode = obj.context; - fc::raw::pack(ds, fc::unsigned_int(0)); + fc::raw::pack(ds, fc::unsigned_int(1)); fc::raw::pack(ds, as_type(obj.obj.action_ordinal)); fc::raw::pack(ds, as_type(obj.obj.creator_action_ordinal)); fc::raw::pack(ds, bool(obj.obj.receipt)); @@ -597,6 +598,7 @@ datastream& operator<<(datastream& ds, const history_context_wrapper>(e)); fc::raw::pack(ds, as_type>(debug_mode ? obj.obj.error_code : cap_error_code(obj.obj.error_code))); + fc::raw::pack(ds, as_type(obj.obj.return_value)); return ds; } diff --git a/programs/cleos/main.cpp b/programs/cleos/main.cpp index 2b5c6650e1..859768ac65 100644 --- a/programs/cleos/main.cpp +++ b/programs/cleos/main.cpp @@ -368,9 +368,29 @@ fc::variant push_actions(std::vector&& actions, packed_transactio return push_transaction(trx, compression); } +void print_return_value( const fc::variant& at ) { + std::string return_value, return_value_prefix{"return value: "}; + const auto & iter_value = at.get_object().find("return_value_data"); + const auto & iter_hex = at.get_object().find("return_value_hex_data"); + + if( iter_value != at.get_object().end() ) { + return_value = fc::json::to_string(iter_value->value(), fc::time_point::maximum()); + } + else if( iter_hex != at.get_object().end() ) { + return_value = iter_hex->value().as_string(); + return_value_prefix = "return value (hex): "; + } + + if( !return_value.empty() ) { + if( return_value.size() > 100 ) { + return_value = return_value.substr(0, 100) + "..."; + } + cout << "=>" << std::setw(46) << std::right << return_value_prefix << return_value << "\n"; + } +} + void print_action( const fc::variant& at ) { - const auto& receipt = at["receipt"]; - auto receiver = receipt["receiver"].as_string(); + auto receiver = at["receiver"].as_string(); const auto& act = at["act"].get_object(); auto code = act["account"].as_string(); auto func = act["name"].as_string(); @@ -385,6 +405,7 @@ void print_action( const fc::variant& at ) { */ if( args.size() > 100 ) args = args.substr(0,100) + "..."; cout << "#" << std::setw(14) << right << receiver << " <= " << std::setw(28) << std::left << (code +"::" + func) << " " << args << "\n"; + print_return_value(at); if( console.size() ) { std::stringstream ss(console); string line; From 08f336c6a3e7018d3bd611fd712bc1f3e9556040 Mon Sep 17 00:00:00 2001 From: Todd Fleming Date: Thu, 13 Jan 2022 18:29:44 -0500 Subject: [PATCH 2/4] backport 2.1 ACTION_RETURN_VALUE tests --- libraries/testing/contracts.hpp.in | 1 + unittests/api_tests.cpp | 143 +++++++++++++++++- unittests/protocol_feature_tests.cpp | 42 +++++ unittests/test-contracts/CMakeLists.txt | 1 + .../action_results/CMakeLists.txt | 6 + .../action_results/action_results.abi | 90 +++++++++++ .../action_results/action_results.cpp | 70 +++++++++ .../test-contracts/test_api/test_action.cpp | 14 ++ .../test-contracts/test_api/test_api.hpp | 5 + 9 files changed, 371 insertions(+), 1 deletion(-) create mode 100644 unittests/test-contracts/action_results/CMakeLists.txt create mode 100644 unittests/test-contracts/action_results/action_results.abi create mode 100644 unittests/test-contracts/action_results/action_results.cpp diff --git a/libraries/testing/contracts.hpp.in b/libraries/testing/contracts.hpp.in index 8c835359de..15b115b4af 100644 --- a/libraries/testing/contracts.hpp.in +++ b/libraries/testing/contracts.hpp.in @@ -62,6 +62,7 @@ namespace eosio { MAKE_READ_WASM_ABI(test_api_db, test_api_db, test-contracts) MAKE_READ_WASM_ABI(test_api_multi_index, test_api_multi_index, test-contracts) MAKE_READ_WASM_ABI(test_ram_limit, test_ram_limit, test-contracts) + MAKE_READ_WASM_ABI(action_results, action_results, test-contracts) }; } /// eosio::testing } /// eosio diff --git a/unittests/api_tests.cpp b/unittests/api_tests.cpp index c7e6d6d006..eadabd1c4f 100644 --- a/unittests/api_tests.cpp +++ b/unittests/api_tests.cpp @@ -599,6 +599,10 @@ BOOST_AUTO_TEST_CASE(ram_billing_in_notify_tests) { try { fc::temp_directory tempdir; validating_tester chain( tempdir, true ); chain.execute_setup_policy( setup_policy::preactivate_feature_and_new_bios ); + const auto& pfm = chain.control->get_protocol_feature_manager(); + const auto& d = pfm.get_builtin_digest(builtin_protocol_feature_t::action_return_value); // testapi requires this + BOOST_REQUIRE(d); + chain.preactivate_protocol_features( {*d} ); chain.produce_blocks(2); chain.create_account( N(testapi) ); @@ -694,7 +698,7 @@ BOOST_FIXTURE_TEST_CASE(cf_action_tests, TESTER) { try { trx.actions.push_back(act1); // attempt to access non context free api - for (uint32_t i = 200; i <= 211; ++i) { + for (uint32_t i = 200; i <= 212; ++i) { trx.context_free_actions.clear(); trx.context_free_data.clear(); cfa.payload = i; @@ -2498,6 +2502,36 @@ BOOST_FIXTURE_TEST_CASE(action_ordinal_test, TESTER) { try { set_code( N(erin), contracts::test_api_wasm() ); produce_blocks(1); + // prove act digest + auto pad = [](const digest_type& expected_act_digest, const action& act, const vector& act_output) + { + std::vector buf1; + buf1.resize(64); + datastream ds(buf1.data(), buf1.size()); + + { + std::vector buf2; + const action_base* act_base = &act; + buf2.resize(fc::raw::pack_size(*act_base)); + datastream ds2(buf2.data(), buf2.size()); + fc::raw::pack(ds2, *act_base); + fc::raw::pack(ds, sha256::hash(buf2.data(), buf2.size())); + } + + { + std::vector buf2; + buf2.resize(fc::raw::pack_size(act.data) + fc::raw::pack_size(act_output)); + datastream ds2(buf2.data(), buf2.size()); + fc::raw::pack(ds2, act.data); + fc::raw::pack(ds2, act_output); + fc::raw::pack(ds, sha256::hash(buf2.data(), buf2.size())); + } + + digest_type computed_act_digest = sha256::hash(buf1.data(), ds.tellp()); + + return expected_act_digest == computed_act_digest; + }; + transaction_trace_ptr txn_trace = CALL_TEST_FUNCTION_SCOPE( *this, "test_action", "test_action_ordinal1", {}, vector{ N(testapi)}); @@ -2514,6 +2548,8 @@ BOOST_FIXTURE_TEST_CASE(action_ordinal_test, TESTER) { try { BOOST_REQUIRE_EQUAL(atrace[0].act.account, N(testapi)); BOOST_REQUIRE_EQUAL(atrace[0].act.name, TEST_METHOD("test_action", "test_action_ordinal1")); BOOST_REQUIRE_EQUAL(atrace[0].receipt.valid(), true); + BOOST_REQUIRE_EQUAL(fc::raw::unpack(atrace[0].return_value), unsigned_int(1) ); + BOOST_REQUIRE(pad(atrace[0].receipt->act_digest, atrace[0].act, atrace[0].return_value)); int start_gseq = atrace[0].receipt->global_sequence; BOOST_REQUIRE_EQUAL((int)atrace[1].action_ordinal,2); @@ -2523,6 +2559,8 @@ BOOST_FIXTURE_TEST_CASE(action_ordinal_test, TESTER) { try { BOOST_REQUIRE_EQUAL(atrace[1].act.account, N(testapi)); BOOST_REQUIRE_EQUAL(atrace[1].act.name, TEST_METHOD("test_action", "test_action_ordinal1")); BOOST_REQUIRE_EQUAL(atrace[1].receipt.valid(), true); + BOOST_REQUIRE_EQUAL(fc::raw::unpack(atrace[1].return_value), "bob" ); + BOOST_REQUIRE(pad(atrace[1].receipt->act_digest, atrace[1].act, atrace[1].return_value)); BOOST_REQUIRE_EQUAL(atrace[1].receipt->global_sequence, start_gseq + 1); BOOST_REQUIRE_EQUAL((int)atrace[2].action_ordinal, 3); @@ -2532,6 +2570,8 @@ BOOST_FIXTURE_TEST_CASE(action_ordinal_test, TESTER) { try { BOOST_REQUIRE_EQUAL(atrace[2].act.account, N(testapi)); BOOST_REQUIRE_EQUAL(atrace[2].act.name, TEST_METHOD("test_action", "test_action_ordinal2")); BOOST_REQUIRE_EQUAL(atrace[2].receipt.valid(), true); + BOOST_REQUIRE_EQUAL(fc::raw::unpack(atrace[2].return_value), name("five") ); + BOOST_REQUIRE(pad(atrace[2].receipt->act_digest, atrace[2].act, atrace[2].return_value)); BOOST_REQUIRE_EQUAL(atrace[2].receipt->global_sequence, start_gseq + 4); BOOST_REQUIRE_EQUAL((int)atrace[3].action_ordinal, 4); @@ -2541,6 +2581,8 @@ BOOST_FIXTURE_TEST_CASE(action_ordinal_test, TESTER) { try { BOOST_REQUIRE_EQUAL(atrace[3].act.account, N(testapi)); BOOST_REQUIRE_EQUAL(atrace[3].act.name, TEST_METHOD("test_action", "test_action_ordinal3")); BOOST_REQUIRE_EQUAL(atrace[3].receipt.valid(), true); + BOOST_REQUIRE_EQUAL(fc::raw::unpack(atrace[3].return_value), unsigned_int(9) ); + BOOST_REQUIRE(pad(atrace[3].receipt->act_digest, atrace[3].act, atrace[3].return_value)); BOOST_REQUIRE_EQUAL(atrace[3].receipt->global_sequence, start_gseq + 8); BOOST_REQUIRE_EQUAL((int)atrace[4].action_ordinal, 5); @@ -2550,6 +2592,8 @@ BOOST_FIXTURE_TEST_CASE(action_ordinal_test, TESTER) { try { BOOST_REQUIRE_EQUAL(atrace[4].act.account, N(testapi)); BOOST_REQUIRE_EQUAL(atrace[4].act.name, TEST_METHOD("test_action", "test_action_ordinal1")); BOOST_REQUIRE_EQUAL(atrace[4].receipt.valid(), true); + BOOST_REQUIRE_EQUAL(fc::raw::unpack(atrace[4].return_value), "charlie" ); + BOOST_REQUIRE(pad(atrace[4].receipt->act_digest, atrace[4].act, atrace[4].return_value)); BOOST_REQUIRE_EQUAL(atrace[4].receipt->global_sequence, start_gseq + 2); BOOST_REQUIRE_EQUAL((int)atrace[5].action_ordinal, 6); @@ -2559,6 +2603,8 @@ BOOST_FIXTURE_TEST_CASE(action_ordinal_test, TESTER) { try { BOOST_REQUIRE_EQUAL(atrace[5].act.account, N(bob)); BOOST_REQUIRE_EQUAL(atrace[5].act.name, TEST_METHOD("test_action", "test_action_ordinal_foo")); BOOST_REQUIRE_EQUAL(atrace[5].receipt.valid(), true); + BOOST_REQUIRE_EQUAL(fc::raw::unpack(atrace[5].return_value), 13.23 ); + BOOST_REQUIRE(pad(atrace[5].receipt->act_digest, atrace[5].act, atrace[5].return_value)); BOOST_REQUIRE_EQUAL(atrace[5].receipt->global_sequence, start_gseq + 9); BOOST_REQUIRE_EQUAL((int)atrace[6].action_ordinal, 7); @@ -2568,6 +2614,8 @@ BOOST_FIXTURE_TEST_CASE(action_ordinal_test, TESTER) { try { BOOST_REQUIRE_EQUAL(atrace[6].act.account, N(testapi)); BOOST_REQUIRE_EQUAL(atrace[6].act.name, TEST_METHOD("test_action", "test_action_ordinal1")); BOOST_REQUIRE_EQUAL(atrace[6].receipt.valid(), true); + BOOST_REQUIRE_EQUAL(fc::raw::unpack(atrace[6].return_value), "david" ); + BOOST_REQUIRE(pad(atrace[6].receipt->act_digest, atrace[6].act, atrace[6].return_value)); BOOST_REQUIRE_EQUAL(atrace[6].receipt->global_sequence, start_gseq + 3); BOOST_REQUIRE_EQUAL((int)atrace[7].action_ordinal, 8); @@ -2577,6 +2625,8 @@ BOOST_FIXTURE_TEST_CASE(action_ordinal_test, TESTER) { try { BOOST_REQUIRE_EQUAL(atrace[7].act.account, N(charlie)); BOOST_REQUIRE_EQUAL(atrace[7].act.name, TEST_METHOD("test_action", "test_action_ordinal_bar")); BOOST_REQUIRE_EQUAL(atrace[7].receipt.valid(), true); + BOOST_REQUIRE_EQUAL(fc::raw::unpack(atrace[7].return_value), 11.42f ); + BOOST_REQUIRE(pad(atrace[7].receipt->act_digest, atrace[7].act, atrace[7].return_value)); BOOST_REQUIRE_EQUAL(atrace[7].receipt->global_sequence, start_gseq + 10); BOOST_REQUIRE_EQUAL((int)atrace[8].action_ordinal, 9); @@ -2586,6 +2636,8 @@ BOOST_FIXTURE_TEST_CASE(action_ordinal_test, TESTER) { try { BOOST_REQUIRE_EQUAL(atrace[8].act.account, N(testapi)); BOOST_REQUIRE_EQUAL(atrace[8].act.name, TEST_METHOD("test_action", "test_action_ordinal2")); BOOST_REQUIRE_EQUAL(atrace[8].receipt.valid(), true); + BOOST_REQUIRE_EQUAL(fc::raw::unpack(atrace[8].return_value), true ); + BOOST_REQUIRE(pad(atrace[8].receipt->act_digest, atrace[8].act, atrace[8].return_value)); BOOST_REQUIRE_EQUAL(atrace[8].receipt->global_sequence, start_gseq + 5); BOOST_REQUIRE_EQUAL((int)atrace[9].action_ordinal, 10); @@ -2595,6 +2647,8 @@ BOOST_FIXTURE_TEST_CASE(action_ordinal_test, TESTER) { try { BOOST_REQUIRE_EQUAL(atrace[9].act.account, N(testapi)); BOOST_REQUIRE_EQUAL(atrace[9].act.name, TEST_METHOD("test_action", "test_action_ordinal2")); BOOST_REQUIRE_EQUAL(atrace[9].receipt.valid(), true); + BOOST_REQUIRE_EQUAL(fc::raw::unpack(atrace[9].return_value), signed_int(7) ); + BOOST_REQUIRE(pad(atrace[9].receipt->act_digest, atrace[9].act, atrace[9].return_value)); BOOST_REQUIRE_EQUAL(atrace[9].receipt->global_sequence, start_gseq + 6); BOOST_REQUIRE_EQUAL((int)atrace[10].action_ordinal, 11); @@ -2604,6 +2658,7 @@ BOOST_FIXTURE_TEST_CASE(action_ordinal_test, TESTER) { try { BOOST_REQUIRE_EQUAL(atrace[10].act.account, N(testapi)); BOOST_REQUIRE_EQUAL(atrace[10].act.name, TEST_METHOD("test_action", "test_action_ordinal4")); BOOST_REQUIRE_EQUAL(atrace[10].receipt.valid(), true); + BOOST_REQUIRE_EQUAL(atrace[10].return_value.size(), 0 ); BOOST_REQUIRE_EQUAL(atrace[10].receipt->global_sequence, start_gseq + 7); } FC_LOG_AND_RETHROW() } @@ -2719,6 +2774,7 @@ BOOST_FIXTURE_TEST_CASE(action_ordinal_failtest2, TESTER) { try { BOOST_REQUIRE_EQUAL(atrace[0].act.account, N(testapi)); BOOST_REQUIRE_EQUAL(atrace[0].act.name, TEST_METHOD("test_action", "test_action_ordinal1")); BOOST_REQUIRE_EQUAL(atrace[0].receipt.valid(), true); + BOOST_REQUIRE_EQUAL(fc::raw::unpack(atrace[0].return_value), unsigned_int(1) ); BOOST_REQUIRE_EQUAL(atrace[0].except.valid(), false); int start_gseq = atrace[0].receipt->global_sequence; @@ -2730,6 +2786,7 @@ BOOST_FIXTURE_TEST_CASE(action_ordinal_failtest2, TESTER) { try { BOOST_REQUIRE_EQUAL(atrace[1].act.account, N(testapi)); BOOST_REQUIRE_EQUAL(atrace[1].act.name, TEST_METHOD("test_action", "test_action_ordinal1")); BOOST_REQUIRE_EQUAL(atrace[1].receipt.valid(), true); + BOOST_REQUIRE_EQUAL(fc::raw::unpack(atrace[1].return_value), "bob" ); BOOST_REQUIRE_EQUAL(atrace[1].receipt->global_sequence, start_gseq + 1); // not executed @@ -2838,6 +2895,7 @@ BOOST_FIXTURE_TEST_CASE(action_ordinal_failtest3, TESTER) { try { BOOST_REQUIRE_EQUAL(atrace[0].act.account, N(testapi)); BOOST_REQUIRE_EQUAL(atrace[0].act.name, TEST_METHOD("test_action", "test_action_ordinal1")); BOOST_REQUIRE_EQUAL(atrace[0].receipt.valid(), true); + BOOST_REQUIRE_EQUAL(fc::raw::unpack(atrace[0].return_value), unsigned_int(1) ); BOOST_REQUIRE_EQUAL(atrace[0].except.valid(), false); int start_gseq = atrace[0].receipt->global_sequence; @@ -2849,6 +2907,7 @@ BOOST_FIXTURE_TEST_CASE(action_ordinal_failtest3, TESTER) { try { BOOST_REQUIRE_EQUAL(atrace[1].act.account, N(testapi)); BOOST_REQUIRE_EQUAL(atrace[1].act.name, TEST_METHOD("test_action", "test_action_ordinal1")); BOOST_REQUIRE_EQUAL(atrace[1].receipt.valid(), true); + BOOST_REQUIRE_EQUAL(fc::raw::unpack(atrace[1].return_value), "bob" ); BOOST_REQUIRE_EQUAL(atrace[1].receipt->global_sequence, start_gseq + 1); // executed @@ -2859,6 +2918,7 @@ BOOST_FIXTURE_TEST_CASE(action_ordinal_failtest3, TESTER) { try { BOOST_REQUIRE_EQUAL(atrace[2].act.account, N(testapi)); BOOST_REQUIRE_EQUAL(atrace[2].act.name, TEST_METHOD("test_action", "test_action_ordinal2")); BOOST_REQUIRE_EQUAL(atrace[2].receipt.valid(), true); + BOOST_REQUIRE_EQUAL(fc::raw::unpack(atrace[2].return_value), name("five") ); BOOST_REQUIRE_EQUAL(atrace[2].receipt->global_sequence, start_gseq + 4); // fails here @@ -2880,6 +2940,7 @@ BOOST_FIXTURE_TEST_CASE(action_ordinal_failtest3, TESTER) { try { BOOST_REQUIRE_EQUAL(atrace[4].act.account, N(testapi)); BOOST_REQUIRE_EQUAL(atrace[4].act.name, TEST_METHOD("test_action", "test_action_ordinal1")); BOOST_REQUIRE_EQUAL(atrace[4].receipt.valid(), true); + BOOST_REQUIRE_EQUAL(fc::raw::unpack(atrace[4].return_value), "charlie" ); BOOST_REQUIRE_EQUAL(atrace[4].receipt->global_sequence, start_gseq + 2); // not executed @@ -2900,6 +2961,7 @@ BOOST_FIXTURE_TEST_CASE(action_ordinal_failtest3, TESTER) { try { BOOST_REQUIRE_EQUAL(atrace[6].act.account, N(testapi)); BOOST_REQUIRE_EQUAL(atrace[6].act.name, TEST_METHOD("test_action", "test_action_ordinal1")); BOOST_REQUIRE_EQUAL(atrace[6].receipt.valid(), true); + BOOST_REQUIRE_EQUAL(fc::raw::unpack(atrace[6].return_value), "david" ); BOOST_REQUIRE_EQUAL(atrace[6].receipt->global_sequence, start_gseq + 3); // not executed @@ -2920,6 +2982,7 @@ BOOST_FIXTURE_TEST_CASE(action_ordinal_failtest3, TESTER) { try { BOOST_REQUIRE_EQUAL(atrace[8].act.account, N(testapi)); BOOST_REQUIRE_EQUAL(atrace[8].act.name, TEST_METHOD("test_action", "test_action_ordinal2")); BOOST_REQUIRE_EQUAL(atrace[8].receipt.valid(), true); + BOOST_REQUIRE_EQUAL(fc::raw::unpack(atrace[8].return_value), true ); BOOST_REQUIRE_EQUAL(atrace[8].receipt->global_sequence, start_gseq + 5); // executed @@ -2930,6 +2993,7 @@ BOOST_FIXTURE_TEST_CASE(action_ordinal_failtest3, TESTER) { try { BOOST_REQUIRE_EQUAL(atrace[9].act.account, N(testapi)); BOOST_REQUIRE_EQUAL(atrace[9].act.name, TEST_METHOD("test_action", "test_action_ordinal2")); BOOST_REQUIRE_EQUAL(atrace[9].receipt.valid(), true); + BOOST_REQUIRE_EQUAL(fc::raw::unpack(atrace[9].return_value), signed_int(7) ); BOOST_REQUIRE_EQUAL(atrace[9].receipt->global_sequence, start_gseq + 6); // executed @@ -2940,8 +3004,85 @@ BOOST_FIXTURE_TEST_CASE(action_ordinal_failtest3, TESTER) { try { BOOST_REQUIRE_EQUAL(atrace[10].act.account, N(testapi)); BOOST_REQUIRE_EQUAL(atrace[10].act.name, TEST_METHOD("test_action", "test_action_ordinal4")); BOOST_REQUIRE_EQUAL(atrace[10].receipt.valid(), true); + BOOST_REQUIRE_EQUAL(atrace[10].return_value.size(), 0 ); BOOST_REQUIRE_EQUAL(atrace[10].receipt->global_sequence, start_gseq + 7); } FC_LOG_AND_RETHROW() } +BOOST_AUTO_TEST_CASE(action_results_tests) { try { + TESTER t; + t.produce_blocks(2); + t.create_account( "test"_n ); + t.set_code( "test"_n, contracts::action_results_wasm() ); + t.produce_blocks(1); + + auto call_autoresret_and_check = [&]( account_name contract, account_name signer, action_name action, auto&& checker ) { + signed_transaction trx; + trx.actions.emplace_back( vector{{signer, config::active_name}}, contract, action, bytes{} ); + t.set_transaction_headers( trx, t.DEFAULT_EXPIRATION_DELTA ); + trx.sign( t.get_private_key(signer, "active"), t.control->get_chain_id() ); + auto res = t.push_transaction(trx); + checker( res ); + }; + + call_autoresret_and_check( "test"_n, "test"_n, "actionresret"_n, [&]( const transaction_trace_ptr& res ) { + BOOST_CHECK_EQUAL( res->receipt->status, transaction_receipt::executed ); + + auto &atrace = res->action_traces; + BOOST_REQUIRE_EQUAL( atrace[0].receipt.valid(), true ); + BOOST_REQUIRE_EQUAL( atrace[0].return_value.size(), 4 ); + BOOST_REQUIRE_EQUAL( fc::raw::unpack(atrace[0].return_value), 10 ); + } ); + + t.produce_blocks(1); + + call_autoresret_and_check( "test"_n, "test"_n, "retlim"_n, [&]( const transaction_trace_ptr& res ) { + BOOST_CHECK_EQUAL( res->receipt->status, transaction_receipt::executed ); + + auto &atrace = res->action_traces; + BOOST_REQUIRE_EQUAL( atrace[0].receipt.valid(), true ); + BOOST_REQUIRE_EQUAL( atrace[0].return_value.size(), 256 ); + vector expected_vec(256 - 2, '0');//2 bytes for size of type unsigned_int + vector ret_vec = fc::raw::unpack>(atrace[0].return_value); + + BOOST_REQUIRE_EQUAL_COLLECTIONS( ret_vec.begin(), + ret_vec.end(), + expected_vec.begin(), + expected_vec.end() ); + } ); + + t.produce_blocks(1); + BOOST_REQUIRE_THROW(call_autoresret_and_check( "test"_n, "test"_n, "retoverlim"_n, [&]( auto res ) {}), + action_return_value_exception); + t.produce_blocks(1); + BOOST_REQUIRE_THROW(call_autoresret_and_check( "test"_n, "test"_n, "ret1overlim"_n, [&]( auto res ) {}), + action_return_value_exception); + t.produce_blocks(1); + t.set_code( config::system_account_name, contracts::action_results_wasm() ); + t.produce_blocks(1); + call_autoresret_and_check( config::system_account_name, + config::system_account_name, + "retmaxlim"_n, + [&]( const transaction_trace_ptr& res ) { + BOOST_CHECK_EQUAL( res->receipt->status, transaction_receipt::executed ); + + auto &atrace = res->action_traces; + BOOST_REQUIRE_EQUAL( atrace[0].receipt.valid(), true ); + BOOST_REQUIRE_EQUAL( atrace[0].return_value.size(), 20*1024*1024 ); + vector expected_vec(20*1024*1024, '1'); + + BOOST_REQUIRE_EQUAL_COLLECTIONS( atrace[0].return_value.begin(), + atrace[0].return_value.end(), + expected_vec.begin(), + expected_vec.end() ); + } ); + t.produce_blocks(1); + BOOST_REQUIRE_THROW(call_autoresret_and_check( config::system_account_name, + config::system_account_name, + "setliminv"_n, + [&]( auto res ) {}), + action_validate_exception); + +} FC_LOG_AND_RETHROW() } + BOOST_AUTO_TEST_SUITE_END() diff --git a/unittests/protocol_feature_tests.cpp b/unittests/protocol_feature_tests.cpp index 4874d4e535..8afede4dda 100644 --- a/unittests/protocol_feature_tests.cpp +++ b/unittests/protocol_feature_tests.cpp @@ -1739,4 +1739,46 @@ BOOST_AUTO_TEST_CASE( wtmsig_block_signing_inflight_extension_test ) { try { } FC_LOG_AND_RETHROW() } +static const char import_set_action_return_value_wast[] = R"=====( +(module + (import "env" "set_action_return_value" (func $set_action_return_value (param i32 i32))) + (memory $0 1) + (export "apply" (func $apply)) + (func $apply (param $0 i64) (param $1 i64) (param $2 i64) + (call $set_action_return_value + (i32.const 0) + (i32.const 43) + ) + ) + (data (i32.const 8) "\01\00\00\00\00\00\85\5C\34\00\03\EB\CF\44\B4\5A\71\D4\F2\25\76\8F\60\2D\1E\2E\2B\25\EF\77\9E\E9\89\7F\E7\44\BF\1A\16\E8\54\23\D5") +) +)====="; + +BOOST_AUTO_TEST_CASE( set_action_return_value_test ) { try { + tester c( setup_policy::preactivate_feature_and_new_bios ); + + const auto& pfm = c.control->get_protocol_feature_manager(); + const auto& d = pfm.get_builtin_digest(builtin_protocol_feature_t::action_return_value); + BOOST_REQUIRE(d); + + const auto& alice_account = account_name("alice"); + c.create_accounts( {alice_account} ); + c.produce_block(); + + BOOST_CHECK_EXCEPTION( c.set_code( alice_account, import_set_action_return_value_wast ), + wasm_exception, + fc_exception_message_is( "env.set_action_return_value unresolveable" ) ); + + c.preactivate_protocol_features( {*d} ); + c.produce_block(); + + // ensure it now resolves + c.set_code( alice_account, import_set_action_return_value_wast ); + + // ensure it can be called + BOOST_REQUIRE_EQUAL(c.push_action(action({{ alice_account, permission_name("active") }}, alice_account, action_name(), {} ), alice_account.to_uint64_t()), c.success()); + + c.produce_block(); +} FC_LOG_AND_RETHROW() } + BOOST_AUTO_TEST_SUITE_END() diff --git a/unittests/test-contracts/CMakeLists.txt b/unittests/test-contracts/CMakeLists.txt index 7c48729a52..3ecb2e52f5 100644 --- a/unittests/test-contracts/CMakeLists.txt +++ b/unittests/test-contracts/CMakeLists.txt @@ -23,3 +23,4 @@ add_subdirectory( test_api ) add_subdirectory( test_api_db ) add_subdirectory( test_api_multi_index ) add_subdirectory( test_ram_limit ) +add_subdirectory( action_results ) diff --git a/unittests/test-contracts/action_results/CMakeLists.txt b/unittests/test-contracts/action_results/CMakeLists.txt new file mode 100644 index 0000000000..68dc588ca1 --- /dev/null +++ b/unittests/test-contracts/action_results/CMakeLists.txt @@ -0,0 +1,6 @@ +if( EOSIO_COMPILE_TEST_CONTRACTS ) + add_contract( action_results action_results action_results.cpp ) +else() + configure_file( ${CMAKE_CURRENT_SOURCE_DIR}/action_results.wasm ${CMAKE_CURRENT_BINARY_DIR}/action_results.wasm COPYONLY ) + configure_file( ${CMAKE_CURRENT_SOURCE_DIR}/action_results.abi ${CMAKE_CURRENT_BINARY_DIR}/action_results.abi COPYONLY ) +endif() diff --git a/unittests/test-contracts/action_results/action_results.abi b/unittests/test-contracts/action_results/action_results.abi new file mode 100644 index 0000000000..0bacd7cdbb --- /dev/null +++ b/unittests/test-contracts/action_results/action_results.abi @@ -0,0 +1,90 @@ +{ + "____comment": "This file was generated with eosio-abigen. DO NOT EDIT ", + "version": "eosio::abi/1.2", + "types": [], + "structs": [ + { + "name": "actionresret", + "base": "", + "fields": [] + }, + { + "name": "ret1overlim", + "base": "", + "fields": [] + }, + { + "name": "retlim", + "base": "", + "fields": [] + }, + { + "name": "retmaxlim", + "base": "", + "fields": [] + }, + { + "name": "retoverlim", + "base": "", + "fields": [] + }, + { + "name": "setliminv", + "base": "", + "fields": [] + } + ], + "actions": [ + { + "name": "actionresret", + "type": "actionresret", + "ricardian_contract": "" + }, + { + "name": "ret1overlim", + "type": "ret1overlim", + "ricardian_contract": "" + }, + { + "name": "retlim", + "type": "retlim", + "ricardian_contract": "" + }, + { + "name": "retmaxlim", + "type": "retmaxlim", + "ricardian_contract": "" + }, + { + "name": "retoverlim", + "type": "retoverlim", + "ricardian_contract": "" + }, + { + "name": "setliminv", + "type": "setliminv", + "ricardian_contract": "" + } + ], + "tables": [], + "ricardian_clauses": [], + "variants": [], + "action_results": [ + { + "name": "actionresret", + "result_type": "int32" + }, + { + "name": "ret1overlim", + "result_type": "bytes" + }, + { + "name": "retlim", + "result_type": "bytes" + }, + { + "name": "retoverlim", + "result_type": "bytes" + } + ] +} \ No newline at end of file diff --git a/unittests/test-contracts/action_results/action_results.cpp b/unittests/test-contracts/action_results/action_results.cpp new file mode 100644 index 0000000000..ad95e155af --- /dev/null +++ b/unittests/test-contracts/action_results/action_results.cpp @@ -0,0 +1,70 @@ +#include /* contract, datastream, unsigned_int */ +#include /* vector */ + +using namespace eosio; +using namespace std; + +extern "C" { +__attribute__((eosio_wasm_import)) +void set_parameters_packed( const char* params, uint32_t params_size ); +__attribute__((eosio_wasm_import)) +void set_action_return_value(void*, size_t); +}; + +class [[eosio::contract]] action_results : public contract { + public: + using contract::contract; + + [[eosio::action]] + int actionresret() { + return 10; + } + + [[eosio::action]] + vector retoverlim() { + return vector(512, '0'); + } + + [[eosio::action]] + vector retlim() { + //2 is for size of type unsigned_int + return vector(256 - 2, '0'); + } + + [[eosio::action]] + vector ret1overlim() { + //2 is for size of type unsigned_int + return vector(257 - 2, '0'); + } + + /** + * required to be called as system contract for priviledged host function call + */ + [[eosio::action]] + void retmaxlim() { + + char buffer[12]; + datastream ds((char*)&buffer, sizeof(buffer)); + //20mb is MAX_SIZE_OF_BYTE_ARRAYS that is defined in fc. + //we don't use fc in contracts so using hardcode here. + ds << unsigned_int(uint32_t(1)) << unsigned_int(uint32_t(17)) << uint32_t(20*1024*1024); + set_parameters_packed(buffer, ds.tellp()); + + vector ret_vec(20*1024*1024, '1'); + //return ret_vec; + set_action_return_value( ret_vec.data(), ret_vec.size() ); + } + + [[eosio::action]] + void setliminv() { + + char buffer[12]; + datastream ds((char*)&buffer, sizeof(buffer)); + + ds << unsigned_int(uint32_t(1)) << unsigned_int(uint32_t(17)) << uint32_t(20*1024*1024 + 1); + //trying to set limit too large + set_parameters_packed(buffer, ds.tellp()); + } + + private: +}; diff --git a/unittests/test-contracts/test_api/test_action.cpp b/unittests/test-contracts/test_api/test_action.cpp index 1b20249fc0..d3a027935b 100644 --- a/unittests/test-contracts/test_api/test_action.cpp +++ b/unittests/test-contracts/test_api/test_action.cpp @@ -152,6 +152,9 @@ void test_action::test_cf_action() { } else if ( cfa.payload == 211 ) { send_deferred( "testapi"_n.value, "testapi"_n.value, "hello", 6, 0 ); eosio_assert( false, "transaction_api should not be allowed" ); + } else if ( cfa.payload == 212 ) { + set_action_return_value("hi", 2); + eosio_assert( false, "set_action_return_value should not be allowed" ); } } @@ -276,6 +279,7 @@ void test_action::test_action_ordinal1(uint64_t receiver, uint64_t code, uint64_ std::tuple<>()); act2.send(); // -> exec 9 + set_action_return_value( &eosio::pack(unsigned_int(1))[0], eosio::pack_size(unsigned_int(1)) ); eosio::require_recipient( "charlie"_n ); // -> exec 3 which would then cause execution of 11 } else if (receiver == "bob"_n.value) { @@ -285,6 +289,7 @@ void test_action::test_action_ordinal1(uint64_t receiver, uint64_t code, uint64_ std::tuple<>()); act1.send(); // -> exec 10 + set_action_return_value( &eosio::pack(std::string("bob"))[0], eosio::pack_size(std::string("bob")) ); eosio::require_recipient( "david"_n ); // -> exec 4 } else if (receiver == "charlie"_n.value) { print("exec 3"); @@ -293,12 +298,14 @@ void test_action::test_action_ordinal1(uint64_t receiver, uint64_t code, uint64_ std::tuple<>()); // exec 11 act1.send(); + set_action_return_value( &eosio::pack(std::string("charlie"))[0], eosio::pack_size(std::string("charlie")) ); if (is_account("fail3"_n)) { eosio_assert(false, "fail at point 3"); } } else if (receiver == "david"_n.value) { print("exec 4"); + set_action_return_value( &eosio::pack(std::string("david"))[0], eosio::pack_size(std::string("david")) ); } else { eosio_assert(false, "assert failed at test_action::test_action_ordinal1"); } @@ -314,16 +321,20 @@ void test_action::test_action_ordinal2(uint64_t receiver, uint64_t code, uint64_ name(WASM_TEST_ACTION("test_action", "test_action_ordinal4")), std::tuple<>()); act1.send(); // -> exec 8 + set_action_return_value( &eosio::pack("five"_n)[0], eosio::pack_size("five"_n) ); } else if (receiver == "david"_n.value) { print("exec 6"); + set_action_return_value( &eosio::pack(true)[0], eosio::pack_size(true) ); } else if (receiver == "erin"_n.value) { print("exec 7"); + set_action_return_value( &eosio::pack(signed_int(7))[0], eosio::pack_size(signed_int(7)) ); } else { eosio_assert(false, "assert failed at test_action::test_action_ordinal2"); } } void test_action::test_action_ordinal4(uint64_t receiver, uint64_t code, uint64_t action) { print("exec 8"); + // no set_action_return_value } void test_action::test_action_ordinal3(uint64_t receiver, uint64_t code, uint64_t action) { print("exec 9"); @@ -331,10 +342,13 @@ void test_action::test_action_ordinal3(uint64_t receiver, uint64_t code, uint64_ if (is_account("failnine"_n)) { eosio_assert(false, "fail at point 9"); } + set_action_return_value( &eosio::pack(unsigned_int(9))[0], eosio::pack_size(unsigned_int(9)) ); } void test_action::test_action_ordinal_foo(uint64_t receiver, uint64_t code, uint64_t action) { print("exec 10"); + set_action_return_value( &eosio::pack(13.23)[0], eosio::pack_size(13.23) ); } void test_action::test_action_ordinal_bar(uint64_t receiver, uint64_t code, uint64_t action) { print("exec 11"); + set_action_return_value( &eosio::pack(11.42f)[0], eosio::pack_size(11.42f) ); } diff --git a/unittests/test-contracts/test_api/test_api.hpp b/unittests/test-contracts/test_api/test_api.hpp index 9e8e5e06be..8c36627112 100644 --- a/unittests/test-contracts/test_api/test_api.hpp +++ b/unittests/test-contracts/test_api/test_api.hpp @@ -27,6 +27,11 @@ namespace eosio { class transaction; } return; \ } +extern "C" { + __attribute__((eosio_wasm_import)) + void set_action_return_value(const char*, size_t); +} + struct test_types { static void types_size(); static void char_to_symbol(); From 81be25f92a4536d4d688f47b1239e6e38b633c6a Mon Sep 17 00:00:00 2001 From: Todd Fleming Date: Fri, 14 Jan 2022 12:15:13 -0500 Subject: [PATCH 3/4] Update test wasms --- unittests/api_tests.cpp | 2 ++ .../action_results/action_results.wasm | Bin 0 -> 2211 bytes .../test-contracts/test_api/test_api.wasm | Bin 72845 -> 75646 bytes 3 files changed, 2 insertions(+) create mode 100755 unittests/test-contracts/action_results/action_results.wasm diff --git a/unittests/api_tests.cpp b/unittests/api_tests.cpp index eadabd1c4f..6edbb9d2a6 100644 --- a/unittests/api_tests.cpp +++ b/unittests/api_tests.cpp @@ -3009,6 +3009,7 @@ BOOST_FIXTURE_TEST_CASE(action_ordinal_failtest3, TESTER) { try { } FC_LOG_AND_RETHROW() } +#if 0 // TODO: enable when set_parameters_packed is added BOOST_AUTO_TEST_CASE(action_results_tests) { try { TESTER t; t.produce_blocks(2); @@ -3084,5 +3085,6 @@ BOOST_AUTO_TEST_CASE(action_results_tests) { try { action_validate_exception); } FC_LOG_AND_RETHROW() } +#endif BOOST_AUTO_TEST_SUITE_END() diff --git a/unittests/test-contracts/action_results/action_results.wasm b/unittests/test-contracts/action_results/action_results.wasm new file mode 100755 index 0000000000000000000000000000000000000000..ccb4f24f1b7c20eab228193bf6af580d12e44c4a GIT binary patch literal 2211 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 delta 14174 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 From 768d61feb03d99daeec8a6df9bba5b8462d2a298 Mon Sep 17 00:00:00 2001 From: Todd Fleming Date: Sat, 15 Jan 2022 12:05:53 -0500 Subject: [PATCH 4/4] bump snapshot version --- libraries/chain/include/eosio/chain/chain_snapshot.hpp | 2 +- unittests/snapshot_tests.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/libraries/chain/include/eosio/chain/chain_snapshot.hpp b/libraries/chain/include/eosio/chain/chain_snapshot.hpp index 0e44b654ed..d2bd01492f 100644 --- a/libraries/chain/include/eosio/chain/chain_snapshot.hpp +++ b/libraries/chain/include/eosio/chain/chain_snapshot.hpp @@ -25,7 +25,7 @@ struct chain_snapshot_header { */ static constexpr uint32_t minimum_compatible_version = 2; - static constexpr uint32_t current_version = 4; + static constexpr uint32_t current_version = 5; uint32_t version = current_version; diff --git a/unittests/snapshot_tests.cpp b/unittests/snapshot_tests.cpp index bb3b54e356..54c7a5e172 100644 --- a/unittests/snapshot_tests.cpp +++ b/unittests/snapshot_tests.cpp @@ -395,7 +395,7 @@ BOOST_AUTO_TEST_CASE_TEMPLATE(test_compatible_versions, SNAPSHOT_SUITE, snapshot std::string current_version = "v5"; int ordinal = 0; - for(std::string version : {"v2", "v3", "v4" /*, "v5"*/}) + for(std::string version : {"v2", "v3", "v4" , "v5"}) { if(save_snapshot && version == current_version) continue; static_assert(chain_snapshot_header::minimum_compatible_version <= 2, "version 2 unit test is no longer needed. Please clean up data files");