diff --git a/libraries/chain/apply_context.cpp b/libraries/chain/apply_context.cpp index f11d4d6c8ce..1afba333342 100644 --- a/libraries/chain/apply_context.cpp +++ b/libraries/chain/apply_context.cpp @@ -57,6 +57,7 @@ void apply_context::exec_one() 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 ); @@ -143,7 +144,13 @@ void apply_context::exec_one() r.auth_sequence[auth.actor] = next_auth_sequence( auth.actor ); } - trx_context.executed_action_receipt_digests.emplace_back( r.digest() ); + uint32_t version = 0; + if( control.is_builtin_activated( builtin_protocol_feature_t::action_return_value ) ) { + version = set_field( version, builtin_protocol_feature_t::action_return_value, true ); + r.return_value.emplace( std::move( action_return_value ) ); + } + + trx_context.executed_action_receipt_digests.emplace_back( r.digest( version ) ); finalize_trace( trace, start ); diff --git a/libraries/chain/controller.cpp b/libraries/chain/controller.cpp index f56ba72e251..7773c667ed0 100644 --- a/libraries/chain/controller.cpp +++ b/libraries/chain/controller.cpp @@ -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); @@ -3312,6 +3313,13 @@ 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/action_receipt.hpp b/libraries/chain/include/eosio/chain/action_receipt.hpp index 78ef25c7c00..21720f6c232 100644 --- a/libraries/chain/include/eosio/chain/action_receipt.hpp +++ b/libraries/chain/include/eosio/chain/action_receipt.hpp @@ -1,6 +1,7 @@ #pragma once #include +#include namespace eosio { namespace chain { @@ -15,10 +16,28 @@ namespace eosio { namespace chain { flat_map auth_sequence; fc::unsigned_int code_sequence = 0; ///< total number of setcodes fc::unsigned_int abi_sequence = 0; ///< total number of setabis + fc::optional> return_value; ///< return value of the action - digest_type digest()const { return digest_type::hash(*this); } + /// @param version of digest to calculate + /// 0 for original version + /// set_field( version, builtin_protocol_feature_t::action_return_value, true ) for version of digest with return_value + digest_type digest(uint32_t version)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); + if( has_field( version, builtin_protocol_feature_t::action_return_value ) ) { + fc::raw::pack(e, return_value); + } + return e.result(); + } }; } } /// namespace eosio::chain -FC_REFLECT( eosio::chain::action_receipt, (receiver)(act_digest)(global_sequence)(recv_sequence)(auth_sequence)(code_sequence)(abi_sequence) ) +FC_REFLECT( eosio::chain::action_receipt, + (receiver)(act_digest)(global_sequence)(recv_sequence)(auth_sequence)(code_sequence)(abi_sequence)(return_value) ) diff --git a/libraries/chain/include/eosio/chain/apply_context.hpp b/libraries/chain/include/eosio/chain/apply_context.hpp index 3a5889248bf..c50c10daca5 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/protocol_feature_manager.hpp b/libraries/chain/include/eosio/chain/protocol_feature_manager.hpp index 81d22c15b0e..98a80443e9e 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/webassembly/eos-vm-oc/intrinsic_mapping.hpp b/libraries/chain/include/eosio/chain/webassembly/eos-vm-oc/intrinsic_mapping.hpp index 022df920918..54f0fbbb1cd 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 @@ -248,7 +248,8 @@ constexpr auto intrinsic_table = boost::hana::make_tuple( "eosio_injection._eosio_i32_to_f64"_s, "eosio_injection._eosio_i64_to_f64"_s, "eosio_injection._eosio_ui32_to_f64"_s, - "eosio_injection._eosio_ui64_to_f64"_s + "eosio_injection._eosio_ui64_to_f64"_s, + "env.set_action_return_value"_s ); }}} \ No newline at end of file diff --git a/libraries/chain/protocol_feature_manager.cpp b/libraries/chain/protocol_feature_manager.cpp index d61a7fe25c4..f43eb562661 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/wasm_interface.cpp b/libraries/chain/wasm_interface.cpp index c29bb77838c..7ad3032177b 100644 --- a/libraries/chain/wasm_interface.cpp +++ b/libraries/chain/wasm_interface.cpp @@ -1134,6 +1134,10 @@ class action_api : public context_aware_api { name current_receiver() { return context.get_receiver(); } + + void set_action_return_value( array_ptr packed_blob, uint32_t datalen ) { + context.action_return_value.assign( packed_blob.value, packed_blob.value + datalen ); + } }; class console_api : public context_aware_api { @@ -1964,6 +1968,7 @@ REGISTER_INTRINSICS(action_api, (read_action_data, int(int, int) ) (action_data_size, int() ) (current_receiver, int64_t() ) + (set_action_return_value,void(int, int) ) ); REGISTER_INTRINSICS(authorization_api, diff --git a/plugins/state_history_plugin/include/eosio/state_history_plugin/state_history_serialization.hpp b/plugins/state_history_plugin/include/eosio/state_history_plugin/state_history_serialization.hpp index c1fd3f509bc..df5832f066d 100644 --- a/plugins/state_history_plugin/include/eosio/state_history_plugin/state_history_serialization.hpp +++ b/plugins/state_history_plugin/include/eosio/state_history_plugin/state_history_serialization.hpp @@ -531,7 +531,11 @@ datastream& operator<<(datastream& ds, const history_serial_wrapper datastream& operator<<(datastream& ds, const history_serial_wrapper& obj) { - fc::raw::pack(ds, fc::unsigned_int(0)); + if (!obj.obj.return_value) { + fc::raw::pack( ds, fc::unsigned_int( 0 )); + } else { + fc::raw::pack( ds, fc::unsigned_int( 1 )); + } fc::raw::pack(ds, as_type(obj.obj.receiver.to_uint64_t())); fc::raw::pack(ds, as_type(obj.obj.act_digest)); fc::raw::pack(ds, as_type(obj.obj.global_sequence)); @@ -539,6 +543,9 @@ datastream& operator<<(datastream& ds, const history_serial_wrapper>(obj.obj.auth_sequence)); fc::raw::pack(ds, as_type(obj.obj.code_sequence)); fc::raw::pack(ds, as_type(obj.obj.abi_sequence)); + if (obj.obj.return_value) { + fc::raw::pack(ds, as_type(*obj.obj.return_value)); + } return ds; } diff --git a/plugins/state_history_plugin/state_history_plugin_abi.cpp b/plugins/state_history_plugin/state_history_plugin_abi.cpp index 766c747d3d1..a63b1b2fca7 100644 --- a/plugins/state_history_plugin/state_history_plugin_abi.cpp +++ b/plugins/state_history_plugin/state_history_plugin_abi.cpp @@ -85,6 +85,18 @@ extern const char* const state_history_plugin_abi = R"({ { "name": "abi_sequence", "type": "varuint32" } ] }, + { + "name": "action_receipt_v1", "fields": [ + { "name": "receiver", "type": "name" }, + { "name": "act_digest", "type": "checksum256" }, + { "name": "global_sequence", "type": "uint64" }, + { "name": "recv_sequence", "type": "uint64" }, + { "name": "auth_sequence", "type": "account_auth_sequence[]" }, + { "name": "code_sequence", "type": "varuint32" }, + { "name": "abi_sequence", "type": "varuint32" }, + { "name": "return_value", "type": "bytes" } + ] + }, { "name": "account_delta", "fields": [ { "name": "account", "type": "name" }, @@ -500,7 +512,7 @@ extern const char* const state_history_plugin_abi = R"({ { "name": "request", "types": ["get_status_request_v0", "get_blocks_request_v0", "get_blocks_ack_request_v0"] }, { "name": "result", "types": ["get_status_result_v0", "get_blocks_result_v0"] }, - { "name": "action_receipt", "types": ["action_receipt_v0"] }, + { "name": "action_receipt", "types": ["action_receipt_v0", "action_receipt_v1"] }, { "name": "action_trace", "types": ["action_trace_v0"] }, { "name": "partial_transaction", "types": ["partial_transaction_v0"] }, { "name": "transaction_trace", "types": ["transaction_trace_v0"] }, diff --git a/unittests/api_tests.cpp b/unittests/api_tests.cpp index 7f40f291ef2..096180ab013 100644 --- a/unittests/api_tests.cpp +++ b/unittests/api_tests.cpp @@ -3,7 +3,6 @@ #include #include #include -#include #include #include @@ -22,19 +21,13 @@ #include #include #include -#include #include #include -#include #include #include #include -#include -#include -#include -#include #include #include @@ -591,6 +584,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) ); @@ -2356,6 +2353,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(atrace[0].receipt->return_value.valid(), true); + BOOST_REQUIRE_EQUAL(fc::raw::unpack(*atrace[0].receipt->return_value), unsigned_int(1) ); int start_gseq = atrace[0].receipt->global_sequence; BOOST_REQUIRE_EQUAL((int)atrace[1].action_ordinal,2); @@ -2365,6 +2364,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(atrace[1].receipt->return_value.valid(), true); + BOOST_REQUIRE_EQUAL(fc::raw::unpack(*atrace[1].receipt->return_value), "bob" ); BOOST_REQUIRE_EQUAL(atrace[1].receipt->global_sequence, start_gseq + 1); BOOST_REQUIRE_EQUAL((int)atrace[2].action_ordinal, 3); @@ -2374,6 +2375,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(atrace[2].receipt->return_value.valid(), true); + BOOST_REQUIRE_EQUAL(fc::raw::unpack(*atrace[2].receipt->return_value), name("five") ); BOOST_REQUIRE_EQUAL(atrace[2].receipt->global_sequence, start_gseq + 4); BOOST_REQUIRE_EQUAL((int)atrace[3].action_ordinal, 4); @@ -2383,6 +2386,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(atrace[3].receipt->return_value.valid(), true); + BOOST_REQUIRE_EQUAL(fc::raw::unpack(*atrace[3].receipt->return_value), unsigned_int(9) ); BOOST_REQUIRE_EQUAL(atrace[3].receipt->global_sequence, start_gseq + 8); BOOST_REQUIRE_EQUAL((int)atrace[4].action_ordinal, 5); @@ -2392,6 +2397,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(atrace[4].receipt->return_value.valid(), true); + BOOST_REQUIRE_EQUAL(fc::raw::unpack(*atrace[4].receipt->return_value), "charlie" ); BOOST_REQUIRE_EQUAL(atrace[4].receipt->global_sequence, start_gseq + 2); BOOST_REQUIRE_EQUAL((int)atrace[5].action_ordinal, 6); @@ -2401,6 +2408,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(atrace[5].receipt->return_value.valid(), true); + BOOST_REQUIRE_EQUAL(fc::raw::unpack(*atrace[5].receipt->return_value), 13.23 ); BOOST_REQUIRE_EQUAL(atrace[5].receipt->global_sequence, start_gseq + 9); BOOST_REQUIRE_EQUAL((int)atrace[6].action_ordinal, 7); @@ -2410,6 +2419,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(atrace[6].receipt->return_value.valid(), true); + BOOST_REQUIRE_EQUAL(fc::raw::unpack(*atrace[6].receipt->return_value), "david" ); BOOST_REQUIRE_EQUAL(atrace[6].receipt->global_sequence, start_gseq + 3); BOOST_REQUIRE_EQUAL((int)atrace[7].action_ordinal, 8); @@ -2419,6 +2430,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(atrace[7].receipt->return_value.valid(), true); + BOOST_REQUIRE_EQUAL(fc::raw::unpack(*atrace[7].receipt->return_value), 11.42f ); BOOST_REQUIRE_EQUAL(atrace[7].receipt->global_sequence, start_gseq + 10); BOOST_REQUIRE_EQUAL((int)atrace[8].action_ordinal, 9); @@ -2428,6 +2441,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(atrace[8].receipt->return_value.valid(), true); + BOOST_REQUIRE_EQUAL(fc::raw::unpack(*atrace[8].receipt->return_value), true ); BOOST_REQUIRE_EQUAL(atrace[8].receipt->global_sequence, start_gseq + 5); BOOST_REQUIRE_EQUAL((int)atrace[9].action_ordinal, 10); @@ -2437,6 +2452,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(atrace[9].receipt->return_value.valid(), true); + BOOST_REQUIRE_EQUAL(fc::raw::unpack(*atrace[9].receipt->return_value), signed_int(7) ); BOOST_REQUIRE_EQUAL(atrace[9].receipt->global_sequence, start_gseq + 6); BOOST_REQUIRE_EQUAL((int)atrace[10].action_ordinal, 11); @@ -2446,6 +2463,8 @@ 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].receipt->return_value.valid(), true); // return value not set is still a return value, it is just empty + BOOST_REQUIRE_EQUAL(atrace[10].receipt->return_value->size(), 0 ); // state_history_plugin keys off presence of return_value for version of receipt BOOST_REQUIRE_EQUAL(atrace[10].receipt->global_sequence, start_gseq + 7); } FC_LOG_AND_RETHROW() } @@ -2561,6 +2580,8 @@ 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(atrace[0].receipt->return_value.valid(), true); + BOOST_REQUIRE_EQUAL(fc::raw::unpack(*atrace[0].receipt->return_value), unsigned_int(1) ); BOOST_REQUIRE_EQUAL(atrace[0].except.valid(), false); int start_gseq = atrace[0].receipt->global_sequence; @@ -2572,6 +2593,8 @@ 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(atrace[1].receipt->return_value.valid(), true); + BOOST_REQUIRE_EQUAL(fc::raw::unpack(*atrace[1].receipt->return_value), "bob" ); BOOST_REQUIRE_EQUAL(atrace[1].receipt->global_sequence, start_gseq + 1); // not executed @@ -2680,6 +2703,8 @@ 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(atrace[0].receipt->return_value.valid(), true); + BOOST_REQUIRE_EQUAL(fc::raw::unpack(*atrace[0].receipt->return_value), unsigned_int(1) ); BOOST_REQUIRE_EQUAL(atrace[0].except.valid(), false); int start_gseq = atrace[0].receipt->global_sequence; @@ -2691,6 +2716,8 @@ 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(atrace[1].receipt->return_value.valid(), true); + BOOST_REQUIRE_EQUAL(fc::raw::unpack(*atrace[1].receipt->return_value), "bob" ); BOOST_REQUIRE_EQUAL(atrace[1].receipt->global_sequence, start_gseq + 1); // executed @@ -2701,6 +2728,8 @@ 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(atrace[2].receipt->return_value.valid(), true); + BOOST_REQUIRE_EQUAL(fc::raw::unpack(*atrace[2].receipt->return_value), name("five") ); BOOST_REQUIRE_EQUAL(atrace[2].receipt->global_sequence, start_gseq + 4); // fails here @@ -2722,6 +2751,8 @@ 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(atrace[4].receipt->return_value.valid(), true); + BOOST_REQUIRE_EQUAL(fc::raw::unpack(*atrace[4].receipt->return_value), "charlie" ); BOOST_REQUIRE_EQUAL(atrace[4].receipt->global_sequence, start_gseq + 2); // not executed @@ -2742,6 +2773,8 @@ 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(atrace[6].receipt->return_value.valid(), true); + BOOST_REQUIRE_EQUAL(fc::raw::unpack(*atrace[6].receipt->return_value), "david" ); BOOST_REQUIRE_EQUAL(atrace[6].receipt->global_sequence, start_gseq + 3); // not executed @@ -2762,6 +2795,8 @@ 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(atrace[8].receipt->return_value.valid(), true); + BOOST_REQUIRE_EQUAL(fc::raw::unpack(*atrace[8].receipt->return_value), true ); BOOST_REQUIRE_EQUAL(atrace[8].receipt->global_sequence, start_gseq + 5); // executed @@ -2772,6 +2807,8 @@ 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(atrace[9].receipt->return_value.valid(), true); + BOOST_REQUIRE_EQUAL(fc::raw::unpack(*atrace[9].receipt->return_value), signed_int(7) ); BOOST_REQUIRE_EQUAL(atrace[9].receipt->global_sequence, start_gseq + 6); // executed @@ -2782,6 +2819,8 @@ 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].receipt->return_value.valid(), true); + BOOST_REQUIRE_EQUAL(atrace[10].receipt->return_value->size(), 0 ); BOOST_REQUIRE_EQUAL(atrace[10].receipt->global_sequence, start_gseq + 7); } FC_LOG_AND_RETHROW() } diff --git a/unittests/misc_tests.cpp b/unittests/misc_tests.cpp index 7414c8a104b..881abe1bd78 100644 --- a/unittests/misc_tests.cpp +++ b/unittests/misc_tests.cpp @@ -1110,6 +1110,22 @@ BOOST_AUTO_TEST_CASE(stable_priority_queue_test) { } FC_LOG_AND_RETHROW() } +BOOST_AUTO_TEST_CASE(action_receipt_digest) { + try { + action_receipt ar{ .receiver = eosio::name("hi"), .act_digest = fc::sha256("0101"), + .global_sequence = 3, .recv_sequence = 4, + .auth_sequence = {{eosio::name("name"), 13}}, + .code_sequence = 5, + .abi_sequence = 6 }; + auto d = digest_type::hash(ar); + uint32_t version = 0; + version = eosio::chain::set_field( version, builtin_protocol_feature_t::action_return_value, true ); + BOOST_REQUIRE_EQUAL( ar.digest(version), d ); + BOOST_REQUIRE_NE( ar.digest(0), d ); + + } FC_LOG_AND_RETHROW() +} + // test that std::bad_alloc is being thrown BOOST_AUTO_TEST_CASE(bad_alloc_test) { tester t; // force a controller to be constructed and set the new_handler diff --git a/unittests/protocol_feature_tests.cpp b/unittests/protocol_feature_tests.cpp index 4874d4e5354..8afede4ddad 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/test_api/test_action.cpp b/unittests/test-contracts/test_api/test_action.cpp index 1b20249fc0e..648f9780b02 100644 --- a/unittests/test-contracts/test_api/test_action.cpp +++ b/unittests/test-contracts/test_api/test_action.cpp @@ -10,6 +10,11 @@ #include "test_api.hpp" +extern "C" { + __attribute__((eosio_wasm_import)) + void set_action_return_value(const char*, size_t); +} + using namespace eosio; void test_action::read_action_normal() { @@ -276,6 +281,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 +291,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 +300,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 +323,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 +344,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.wasm b/unittests/test-contracts/test_api/test_api.wasm index 5fd5ff870c3..9f213362e17 100755 Binary files a/unittests/test-contracts/test_api/test_api.wasm and b/unittests/test-contracts/test_api/test_api.wasm differ