diff --git a/docs/01_nodeos/03_plugins/chain_plugin/index.md b/docs/01_nodeos/03_plugins/chain_plugin/index.md index 0b932e78ac..f417101b5e 100644 --- a/docs/01_nodeos/03_plugins/chain_plugin/index.md +++ b/docs/01_nodeos/03_plugins/chain_plugin/index.md @@ -190,10 +190,6 @@ Config Options for eosio::chain_plugin: --enable-account-queries arg (=0) enable queries to find accounts by various metadata. - --max-nonprivileged-inline-action-size arg (=4096) - maximum allowed size (in bytes) of an - inline action for a nonprivileged - account --transaction-retry-max-storage-size-gb arg Maximum size (in GiB) allowed to be allocated for the Transaction Retry diff --git a/libraries/chain/apply_context.cpp b/libraries/chain/apply_context.cpp index d4a067feb5..2c8c3e85e1 100644 --- a/libraries/chain/apply_context.cpp +++ b/libraries/chain/apply_context.cpp @@ -357,12 +357,6 @@ void apply_context::execute_inline( action&& a ) { control.check_actor_list( actors ); } - if( !privileged && control.is_speculative_block() ) { - const auto& chain_config = control.get_global_properties().configuration; - EOS_ASSERT( a.data.size() < std::min(chain_config.max_inline_action_size, control.get_max_nonprivileged_inline_action_size()), - inline_action_too_big_nonprivileged, - "inline action too big for nonprivileged account ${account}", ("account", a.account)); - } // No need to check authorization if replaying irreversible blocks or contract is privileged if( !control.skip_auth_check() && !privileged && !trx_context.is_read_only() ) { try { @@ -417,13 +411,6 @@ void apply_context::execute_context_free_inline( action&& a ) { EOS_ASSERT( a.authorization.size() == 0, action_validate_exception, "context-free actions cannot have authorizations" ); - if( !privileged && control.is_speculative_block() ) { - const auto& chain_config = control.get_global_properties().configuration; - EOS_ASSERT( a.data.size() < std::min(chain_config.max_inline_action_size, control.get_max_nonprivileged_inline_action_size()), - inline_action_too_big_nonprivileged, - "inline action too big for nonprivileged account ${account}", ("account", a.account)); - } - auto inline_receiver = a.account; _cfa_inline_actions.emplace_back( schedule_action( std::move(a), inline_receiver, true ) diff --git a/libraries/chain/controller.cpp b/libraries/chain/controller.cpp index 04e6bb8f01..045f4a8691 100644 --- a/libraries/chain/controller.cpp +++ b/libraries/chain/controller.cpp @@ -1258,8 +1258,7 @@ struct controller_impl { || (code == contract_blacklist_exception::code_value) || (code == action_blacklist_exception::code_value) || (code == key_blacklist_exception::code_value) - || (code == sig_variable_size_limit_exception::code_value) - || (code == inline_action_too_big_nonprivileged::code_value); + || (code == sig_variable_size_limit_exception::code_value); } bool scheduled_failure_is_subjective( const fc::exception& e ) const { @@ -2747,11 +2746,6 @@ subjective_billing& controller::get_mutable_subjective_billing() { } -uint32_t controller::get_max_nonprivileged_inline_action_size()const -{ - return my->conf.max_nonprivileged_inline_action_size; -} - controller::controller( const controller::config& cfg, const chain_id_type& chain_id ) :my( new controller_impl( cfg, *this, protocol_feature_set{}, chain_id ) ) { diff --git a/libraries/chain/include/eosio/chain/config.hpp b/libraries/chain/include/eosio/chain/config.hpp index a1cf326c89..6d81272944 100644 --- a/libraries/chain/include/eosio/chain/config.hpp +++ b/libraries/chain/include/eosio/chain/config.hpp @@ -79,7 +79,6 @@ const static uint32_t default_sig_cpu_bill_pct = 50 * perc const static uint32_t default_block_cpu_effort_pct = 90 * percent_1; // percentage of block time used for producing block 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; const static uint32_t default_max_transaction_finality_status_success_duration_sec = 180; diff --git a/libraries/chain/include/eosio/chain/controller.hpp b/libraries/chain/include/eosio/chain/controller.hpp index 965f96628a..e25a201050 100644 --- a/libraries/chain/include/eosio/chain/controller.hpp +++ b/libraries/chain/include/eosio/chain/controller.hpp @@ -74,7 +74,6 @@ namespace eosio { namespace chain { uint64_t state_guard_size = chain::config::default_state_guard_size; uint32_t sig_cpu_bill_pct = chain::config::default_sig_cpu_bill_pct; uint16_t thread_pool_size = chain::config::default_controller_thread_pool_size; - uint32_t max_nonprivileged_inline_action_size = chain::config::default_max_nonprivileged_inline_action_size; bool read_only = false; bool force_all_checks = false; bool disable_replay_opts = false; @@ -198,7 +197,6 @@ namespace eosio { namespace chain { const protocol_feature_manager& get_protocol_feature_manager()const; const subjective_billing& get_subjective_billing()const; subjective_billing& get_mutable_subjective_billing(); - uint32_t get_max_nonprivileged_inline_action_size()const; const flat_set& get_actor_whitelist() const; const flat_set& get_actor_blacklist() const; diff --git a/libraries/chain/include/eosio/chain/exceptions.hpp b/libraries/chain/include/eosio/chain/exceptions.hpp index 151021a284..50c322ff00 100644 --- a/libraries/chain/include/eosio/chain/exceptions.hpp +++ b/libraries/chain/include/eosio/chain/exceptions.hpp @@ -323,8 +323,7 @@ namespace eosio { namespace chain { 3050010, "Action attempts to increase RAM usage of account without authorization" ) FC_DECLARE_DERIVED_EXCEPTION( restricted_error_code_exception, action_validate_exception, 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" ) + // Removed 3050012 - inline_action_too_big_nonprivileged, no longer needed FC_DECLARE_DERIVED_EXCEPTION( action_return_value_exception, action_validate_exception, 3050014, "action return value size too big" ) diff --git a/libraries/testing/include/eosio/testing/tester.hpp b/libraries/testing/include/eosio/testing/tester.hpp index 0037bc3127..a85ed7f70d 100644 --- a/libraries/testing/include/eosio/testing/tester.hpp +++ b/libraries/testing/include/eosio/testing/tester.hpp @@ -156,7 +156,7 @@ namespace eosio { namespace testing { virtual ~base_tester() {}; - void init(const setup_policy policy = setup_policy::full, db_read_mode read_mode = db_read_mode::HEAD, std::optional genesis_max_inline_action_size = std::optional{}, std::optional config_max_nonprivileged_inline_action_size = std::optional{}); + void init(const setup_policy policy = setup_policy::full, db_read_mode read_mode = db_read_mode::HEAD, std::optional genesis_max_inline_action_size = std::optional{}); void init(controller::config config, const snapshot_reader_ptr& snapshot); void init(controller::config config, const genesis_state& genesis); void init(controller::config config); @@ -395,7 +395,7 @@ namespace eosio { namespace testing { return genesis; } - static std::pair default_config(const fc::temp_directory& tempdir, std::optional genesis_max_inline_action_size = std::optional{}, std::optional config_max_nonprivileged_inline_action_size = std::optional{}) { + static std::pair default_config(const fc::temp_directory& tempdir, std::optional genesis_max_inline_action_size = std::optional{}) { controller::config cfg; cfg.blocks_dir = tempdir.path() / config::default_blocks_dir_name; cfg.state_dir = tempdir.path() / config::default_state_dir_name; @@ -419,9 +419,6 @@ namespace eosio { namespace testing { if (genesis_max_inline_action_size) { gen.initial_configuration.max_inline_action_size = *genesis_max_inline_action_size; } - if (config_max_nonprivileged_inline_action_size) { - cfg.max_nonprivileged_inline_action_size = *config_max_nonprivileged_inline_action_size; - } return {cfg, gen}; } @@ -452,8 +449,8 @@ namespace eosio { namespace testing { class tester : public base_tester { public: - tester(setup_policy policy = setup_policy::full, db_read_mode read_mode = db_read_mode::HEAD, std::optional genesis_max_inline_action_size = std::optional{}, std::optional config_max_nonprivileged_inline_action_size = std::optional{}) { - init(policy, read_mode, genesis_max_inline_action_size, config_max_nonprivileged_inline_action_size); + tester(setup_policy policy = setup_policy::full, db_read_mode read_mode = db_read_mode::HEAD, std::optional genesis_max_inline_action_size = std::optional{}) { + init(policy, read_mode, genesis_max_inline_action_size); } tester(controller::config config, const genesis_state& genesis) { diff --git a/libraries/testing/tester.cpp b/libraries/testing/tester.cpp index 6bcc9d1b8d..bdd0d71f67 100644 --- a/libraries/testing/tester.cpp +++ b/libraries/testing/tester.cpp @@ -179,8 +179,8 @@ namespace eosio { namespace testing { return control->head_block_id() == other.control->head_block_id(); } - void base_tester::init(const setup_policy policy, db_read_mode read_mode, std::optional genesis_max_inline_action_size, std::optional config_max_nonprivileged_inline_action_size) { - auto def_conf = default_config(tempdir, genesis_max_inline_action_size, config_max_nonprivileged_inline_action_size); + void base_tester::init(const setup_policy policy, db_read_mode read_mode, std::optional genesis_max_inline_action_size) { + auto def_conf = default_config(tempdir, genesis_max_inline_action_size); def_conf.first.read_mode = read_mode; cfg = def_conf.first; diff --git a/plugins/chain_plugin/chain_plugin.cpp b/plugins/chain_plugin/chain_plugin.cpp index 7ac5d5400a..e4beb113da 100644 --- a/plugins/chain_plugin/chain_plugin.cpp +++ b/plugins/chain_plugin/chain_plugin.cpp @@ -368,7 +368,6 @@ void chain_plugin::set_program_options(options_description& cli, options_descrip "'none' - EOS VM OC tier-up is completely disabled.\n") #endif ("enable-account-queries", bpo::value()->default_value(false), "enable queries to find accounts by various metadata.") - ("max-nonprivileged-inline-action-size", bpo::value()->default_value(config::default_max_nonprivileged_inline_action_size), "maximum allowed size (in bytes) of an inline action for a nonprivileged account") ("transaction-retry-max-storage-size-gb", bpo::value(), "Maximum size (in GiB) allowed to be allocated for the Transaction Retry feature. Setting above 0 enables this feature.") ("transaction-retry-interval-sec", bpo::value()->default_value(20), @@ -633,9 +632,6 @@ void chain_plugin_impl::plugin_initialize(const variables_map& options) { if( options.count( "chain-state-db-guard-size-mb" )) chain_config->state_guard_size = options.at( "chain-state-db-guard-size-mb" ).as() * 1024 * 1024; - if( options.count( "max-nonprivileged-inline-action-size" )) - chain_config->max_nonprivileged_inline_action_size = options.at( "max-nonprivileged-inline-action-size" ).as(); - if( options.count( "transaction-finality-status-max-storage-size-gb" )) { const uint64_t max_storage_size = options.at( "transaction-finality-status-max-storage-size-gb" ).as() * 1024 * 1024 * 1024; if (max_storage_size > 0) { diff --git a/tests/PerformanceHarness/README.md b/tests/PerformanceHarness/README.md index 3178aa9e51..29c84f5536 100644 --- a/tests/PerformanceHarness/README.md +++ b/tests/PerformanceHarness/README.md @@ -1365,8 +1365,6 @@ Finally, the full detail test report for each of the determined max TPS throughp "_enableAccountQueriesNodeosDefault": 0, "_enableAccountQueriesNodeosArg": "--enable-account-queries", "maxNonprivilegedInlineActionSize": null, - "_maxNonprivilegedInlineActionSizeNodeosDefault": 4096, - "_maxNonprivilegedInlineActionSizeNodeosArg": "--max-nonprivileged-inline-action-size", "transactionRetryMaxStorageSizeGb": null, "_transactionRetryMaxStorageSizeGbNodeosDefault": null, "_transactionRetryMaxStorageSizeGbNodeosArg": "--transaction-retry-max-storage-size-gb", @@ -2033,8 +2031,6 @@ The Performance Test Basic generates, by default, a report that details results "_enableAccountQueriesNodeosDefault": 0, "_enableAccountQueriesNodeosArg": "--enable-account-queries", "maxNonprivilegedInlineActionSize": null, - "_maxNonprivilegedInlineActionSizeNodeosDefault": 4096, - "_maxNonprivilegedInlineActionSizeNodeosArg": "--max-nonprivileged-inline-action-size", "transactionRetryMaxStorageSizeGb": null, "_transactionRetryMaxStorageSizeGbNodeosDefault": null, "_transactionRetryMaxStorageSizeGbNodeosArg": "--transaction-retry-max-storage-size-gb", diff --git a/unittests/api_tests.cpp b/unittests/api_tests.cpp index 3d3d773c0f..673995ec18 100644 --- a/unittests/api_tests.cpp +++ b/unittests/api_tests.cpp @@ -1435,10 +1435,16 @@ BOOST_FIXTURE_TEST_CASE(transaction_tests, validating_tester) { try { // test send_action_empty CALL_TEST_FUNCTION(*this, "test_transaction", "send_action_empty", {}); - // test send_action_large - BOOST_CHECK_EXCEPTION(CALL_TEST_FUNCTION(*this, "test_transaction", "send_action_large", {}), inline_action_too_big_nonprivileged, + // test send_action_large (512k) + CALL_TEST_FUNCTION( *this, "test_transaction", "send_action_512k", {}); + + // test send_many_actions_512k (512k) + CALL_TEST_FUNCTION( *this, "test_transaction", "send_many_actions_512k", {}); + + // test send_action_large (512k + 1) + BOOST_CHECK_EXCEPTION(CALL_TEST_FUNCTION(*this, "test_transaction", "send_action_large", {}), inline_action_too_big, [](const fc::exception& e) { - return expect_assert_message(e, "inline action too big for nonprivileged account"); + return expect_assert_message(e, "inline action too big"); } ); @@ -1559,12 +1565,12 @@ BOOST_FIXTURE_TEST_CASE(transaction_tests, validating_tester) { try { } FC_LOG_AND_RETHROW() } /************************************************************************************* - * verify subjective limit test case + * verify objective limit test case *************************************************************************************/ -BOOST_AUTO_TEST_CASE(inline_action_subjective_limit) { try { +BOOST_AUTO_TEST_CASE(inline_action_with_over_4k_limit) { try { const uint32_t _4k = 4 * 1024; - tester chain(setup_policy::full, db_read_mode::HEAD, {_4k + 100}, {_4k + 1}); - tester chain2(setup_policy::full, db_read_mode::HEAD, {_4k + 100}, {_4k}); + tester chain(setup_policy::full, db_read_mode::HEAD, {_4k + 100}); + tester chain2(setup_policy::full, db_read_mode::HEAD, {_4k + 100}); signed_block_ptr block; for (int n=0; n < 2; ++n) { block = chain.produce_block(); @@ -1591,7 +1597,7 @@ BOOST_AUTO_TEST_CASE(inline_action_subjective_limit) { try { *************************************************************************************/ BOOST_AUTO_TEST_CASE(inline_action_objective_limit) { try { const uint32_t _4k = 4 * 1024; - tester chain(setup_policy::full, db_read_mode::HEAD, {_4k}, {_4k - 1}); + tester chain(setup_policy::full, db_read_mode::HEAD, {_4k}); chain.produce_blocks(2); chain.create_account( "testapi"_n ); chain.produce_blocks(100); @@ -1611,42 +1617,10 @@ BOOST_AUTO_TEST_CASE(inline_action_objective_limit) { try { } FC_LOG_AND_RETHROW() } -BOOST_AUTO_TEST_CASE(deferred_inline_action_subjective_limit_failure) { try { - const uint32_t _4k = 4 * 1024; - tester chain(setup_policy::full, db_read_mode::HEAD, {_4k + 100}, {_4k}); - chain.produce_blocks(2); - chain.create_accounts( {"testapi"_n, "testapi2"_n, "alice"_n} ); - chain.set_code( "testapi"_n, test_contracts::test_api_wasm() ); - chain.set_code( "testapi2"_n, test_contracts::test_api_wasm() ); - chain.produce_block(); - - transaction_trace_ptr trace; - auto c = chain.control->applied_transaction.connect([&](std::tuple x) { - auto& t = std::get<0>(x); - if (t->scheduled) { trace = t; } - } ); - CALL_TEST_FUNCTION(chain, "test_transaction", "send_deferred_transaction_4k_action", {} ); - BOOST_CHECK(!trace); - BOOST_CHECK_EXCEPTION(chain.produce_block( fc::seconds(2) ), fc::exception, - [](const fc::exception& e) { - return expect_assert_message(e, "inline action too big for nonprivileged account"); - } - ); - - //check that it populates exception fields - BOOST_REQUIRE(trace); - BOOST_REQUIRE(trace->except); - BOOST_REQUIRE(trace->error_code); - - BOOST_REQUIRE_EQUAL(1, trace->action_traces.size()); - c.disconnect(); - -} FC_LOG_AND_RETHROW() } - -BOOST_AUTO_TEST_CASE(deferred_inline_action_subjective_limit) { try { +BOOST_AUTO_TEST_CASE(deferred_inline_action_limit) { try { const uint32_t _4k = 4 * 1024; - tester chain(setup_policy::full, db_read_mode::HEAD, {_4k + 100}, {_4k + 1}); - tester chain2(setup_policy::full, db_read_mode::HEAD, {_4k + 100}, {_4k}); + tester chain(setup_policy::full, db_read_mode::HEAD, {_4k + 100}); + tester chain2(setup_policy::full, db_read_mode::HEAD, {_4k + 100}); signed_block_ptr block; for (int n=0; n < 2; ++n) { block = chain.produce_block(); @@ -1674,7 +1648,7 @@ BOOST_AUTO_TEST_CASE(deferred_inline_action_subjective_limit) { try { //confirm printed message BOOST_TEST(!trace->action_traces.empty()); - BOOST_TEST(trace->action_traces.back().console == "exec 8"); + BOOST_TEST(trace->action_traces.back().console == "action size: 4096"); c.disconnect(); for (int n=0; n < 10; ++n) { diff --git a/unittests/test-contracts/test_api/test_action.cpp b/unittests/test-contracts/test_api/test_action.cpp index c390fb9198..98fc38fd1a 100644 --- a/unittests/test-contracts/test_api/test_action.cpp +++ b/unittests/test-contracts/test_api/test_action.cpp @@ -62,6 +62,12 @@ void test_action::test_dummy_action() { } } +void test_action::read_action() { + print("action size: " + std::to_string(action_data_size())); + void* p = malloc(action_data_size()); + read_action_data(p, action_data_size()); +} + void test_action::read_action_to_0() { read_action_data( (void *)0, action_data_size() ); } diff --git a/unittests/test-contracts/test_api/test_api.cpp b/unittests/test-contracts/test_api/test_api.cpp index d51d3dc71b..e318e63bdb 100644 --- a/unittests/test-contracts/test_api/test_api.cpp +++ b/unittests/test-contracts/test_api/test_api.cpp @@ -49,6 +49,7 @@ extern "C" { //test_action WASM_TEST_HANDLER ( test_action, read_action_normal ); + WASM_TEST_HANDLER ( test_action, read_action ); WASM_TEST_HANDLER ( test_action, read_action_to_0 ); WASM_TEST_HANDLER ( test_action, read_action_to_64k ); WASM_TEST_HANDLER_EX( test_action, require_notice ); @@ -118,6 +119,8 @@ extern "C" { WASM_TEST_HANDLER ( test_transaction, send_action_empty ); WASM_TEST_HANDLER ( test_transaction, send_action_large ); WASM_TEST_HANDLER ( test_transaction, send_action_4k ); + WASM_TEST_HANDLER ( test_transaction, send_action_512k ); + WASM_TEST_HANDLER ( test_transaction, send_many_actions_512k ); WASM_TEST_HANDLER ( test_transaction, send_action_recurse ); WASM_TEST_HANDLER ( test_transaction, test_read_transaction ); WASM_TEST_HANDLER ( test_transaction, test_transaction_size ); diff --git a/unittests/test-contracts/test_api/test_api.hpp b/unittests/test-contracts/test_api/test_api.hpp index cffdc30db8..c6bec87812 100644 --- a/unittests/test-contracts/test_api/test_api.hpp +++ b/unittests/test-contracts/test_api/test_api.hpp @@ -93,6 +93,7 @@ struct test_print { struct test_action { static void read_action_normal(); + static void read_action(); static void read_action_to_0(); static void read_action_to_64k(); static void test_dummy_action(); @@ -202,6 +203,8 @@ struct test_transaction { static void send_action_max(); static void send_action_large(); static void send_action_4k(); + static void send_action_512k(); + static void send_many_actions_512k(); static void send_action_recurse(); static void send_action_inline_fail(); static void test_read_transaction(); diff --git a/unittests/test-contracts/test_api/test_api.wasm b/unittests/test-contracts/test_api/test_api.wasm index 987a355952..e81d415104 100755 Binary files a/unittests/test-contracts/test_api/test_api.wasm and b/unittests/test-contracts/test_api/test_api.wasm differ diff --git a/unittests/test-contracts/test_api/test_transaction.cpp b/unittests/test-contracts/test_api/test_transaction.cpp index f56d435e9c..c311a84a4d 100644 --- a/unittests/test-contracts/test_api/test_transaction.cpp +++ b/unittests/test-contracts/test_api/test_transaction.cpp @@ -84,17 +84,15 @@ void test_transaction::send_action_empty() { } /** - * cause failure due to a large action payload + * cause failure due to a large action payload, larger than max_inline_action_size of 512K */ void test_transaction::send_action_large() { using namespace eosio; - static char large_message[8 * 1024]; - test_action_action<"testapi"_n.value, WASM_TEST_ACTION( "test_action", "read_action_normal" )> test_action; - copy_data( large_message, 8*1024, test_action.data ); + test_action_action<"testapi"_n.value, WASM_TEST_ACTION( "test_action", "read_action" )> test_action; + test_action.data.resize(512*1024+1); std::vector permissions = { {"testapi"_n, "active"_n} }; - action act( permissions, name{"testapi"}, name{WASM_TEST_ACTION("test_action", "read_action_normal")}, test_action ); - + action act( permissions, name{"testapi"}, name{WASM_TEST_ACTION("test_action", "read_action")}, test_action ); act.send(); eosio_assert( false, "send_message_large() should've thrown an error" ); } @@ -104,16 +102,59 @@ void test_transaction::send_action_large() { */ void test_transaction::send_action_4k() { using namespace eosio; - static char large_message[4 * 1024]; - test_action_action<"testapi"_n.value, WASM_TEST_ACTION( "test_action", "test_action_ordinal4" )> test_action; - copy_data( large_message, 4*1024, test_action.data ); + test_action_action<"testapi"_n.value, WASM_TEST_ACTION( "test_action", "read_action" )> test_action; + test_action.data.resize(4*1024); + + std::vector permissions = { {"testapi"_n, "active"_n} }; + action act( permissions, name{"testapi"}, name{WASM_TEST_ACTION("test_action", "read_action")}, test_action ); + + act.send(); +} + +/** + * send an inline action that is 512K (limit is < 512K) + * the limit includes the size of the action + */ +void test_transaction::send_action_512k() { + using namespace eosio; + test_action_action<"testapi"_n.value, WASM_TEST_ACTION( "test_action", "read_action" )> test_action; + test_action.data.resize(1); std::vector permissions = { {"testapi"_n, "active"_n} }; - action act( permissions, name{"testapi"}, name{WASM_TEST_ACTION("test_action", "test_action_ordinal4")}, test_action ); + action temp_act( permissions, name{"testapi"}, name{WASM_TEST_ACTION("test_action", "read_action")}, test_action ); + + size_t action_size = pack_size(temp_act); + test_action.data.resize(512*1024-action_size-2); // check is < 512K + + // send at limit (512K - 1) + action act( permissions, name{"testapi"}, name{WASM_TEST_ACTION("test_action", "read_action")}, test_action ); + + if (pack_size(act) != 512*1024-1) { + std::string err = "send_action_512k action size is: " + std::to_string(action_size) + " not 512K-1"; + eosio_assert(false, err.c_str()); + } act.send(); } +/** + * send many inline actions that are 512K (limit is < 512K) + * the limit includes the size of the action + */ +void test_transaction::send_many_actions_512k() { + using namespace eosio; + test_action_action<"testapi"_n.value, WASM_TEST_ACTION( "test_transaction", "send_action_512k" )> test_action; + + test_action.data.resize(1); + std::vector permissions = { {"testapi"_n, "active"_n} }; + action act( permissions, name{"testapi"}, name{WASM_TEST_ACTION("test_transaction", "send_action_512k")}, test_action ); + + // 65 * 512K > wasm memory limit, which is ok because each gets their own wasm instantiation + for (size_t i = 0; i < 65; ++i) { + act.send(); + } +} + /** * cause failure due recursive loop */