From 442f059fbad13446f24527c6d5defda203adc57b Mon Sep 17 00:00:00 2001 From: yarkin Date: Tue, 20 Aug 2024 14:39:35 +0800 Subject: [PATCH 1/4] Add simple stack limit tests. --- tests/CMakeLists.txt | 1 + tests/stack_limit_tests.cpp | 90 +++++++++++++++++++++++++++++++++++++ 2 files changed, 91 insertions(+) create mode 100644 tests/stack_limit_tests.cpp diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 7499bf0a..aa6a56b1 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -39,6 +39,7 @@ add_eosio_test_executable( unit_test ${CMAKE_SOURCE_DIR}/chainid_tests.cpp ${CMAKE_SOURCE_DIR}/bridge_message_tests.cpp ${CMAKE_SOURCE_DIR}/admin_actions_tests.cpp + ${CMAKE_SOURCE_DIR}/stack_limit_tests.cpp ${CMAKE_SOURCE_DIR}/main.cpp ${CMAKE_SOURCE_DIR}/../silkworm/silkworm/core/rlp/encode.cpp ${CMAKE_SOURCE_DIR}/../silkworm/silkworm/core/rlp/decode.cpp diff --git a/tests/stack_limit_tests.cpp b/tests/stack_limit_tests.cpp new file mode 100644 index 00000000..86131851 --- /dev/null +++ b/tests/stack_limit_tests.cpp @@ -0,0 +1,90 @@ +#include + +#include "basic_evm_tester.hpp" +#include +#include "utils.hpp" + +using namespace evm_test; +using eosio::testing::eosio_assert_message_is; +using eosio::testing::expect_assert_message; + + + +struct stack_limit_tester : basic_evm_tester { + + evmc::address m_contract_addr; + + stack_limit_tester() { + create_accounts({"alice"_n}); + transfer_token(faucet_account_name, "alice"_n, make_asset(10000'0000)); + init(); + } + + auto deploy_simple_contract(evm_eoa& evm_account) { + // SPDX-License-Identifier: MIT + // pragma solidity >=0.4.18; + + // contract StackTest { + // function internalRecursive(uint256 level) public { + // if (level == 0) return; + // internalRecursive(level - 1); + // } + + // function externalRecursive(uint256 level) public { + // if (level == 0) return; + // StackTest(this).externalRecursive(level - 1); + // } + // } + + const std::string simple_bytecode = "6080604052348015600f57600080fd5b506102448061001f6000396000f3fe608060405234801561001057600080fd5b50600436106100365760003560e01c8063973c32f81461003b578063e06afd0a14610057575b600080fd5b61005560048036038101906100509190610154565b610073565b005b610071600480360381019061006c9190610154565b610095565b005b60008103156100925761009160018261008c91906101b0565b610073565b5b50565b6000810315610116573073ffffffffffffffffffffffffffffffffffffffff1663e06afd0a6001836100c791906101b0565b6040518263ffffffff1660e01b81526004016100e391906101f3565b600060405180830381600087803b1580156100fd57600080fd5b505af1158015610111573d6000803e3d6000fd5b505050505b50565b600080fd5b6000819050919050565b6101318161011e565b811461013c57600080fd5b50565b60008135905061014e81610128565b92915050565b60006020828403121561016a57610169610119565b5b60006101788482850161013f565b91505092915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b60006101bb8261011e565b91506101c68361011e565b92508282039050818111156101de576101dd610181565b5b92915050565b6101ed8161011e565b82525050565b600060208201905061020860008301846101e4565b9291505056fea264697066735822122006713188904378b93322ae651845d43a307ffa88f5a92afb1ca87184c28fb84464736f6c634300081a0033"; + + // Deploy simple contract + auto contract_addr = deploy_contract(evm_account, evmc::from_hex(simple_bytecode).value()); + uint64_t contract_account_id = find_account_by_address(contract_addr).value().id; + m_contract_addr = contract_addr; + return std::make_tuple(contract_addr, contract_account_id); + } + + void call_test(const intx::uint256& level, evm_eoa& evm_account, bool internal) { + auto pad = [](const std::string& s){ + const size_t l = 64; + if (s.length() >= l) return s; + size_t pl = l - s.length(); + return std::string(pl, '0') + s; + }; + + auto txn = generate_tx(m_contract_addr, 0, 500'000); + txn.data = internal ? evmc::from_hex("973c32f8").value() : evmc::from_hex("e06afd0a").value(); + + txn.data += evmc::from_hex(pad(intx::hex(level))).value(); + evm_account.sign(txn); + + pushtx(txn); + + produce_blocks(1); + } + +}; + +BOOST_AUTO_TEST_SUITE(stack_limit_tests) +BOOST_FIXTURE_TEST_CASE(max_limit, stack_limit_tester) try { + + // Fund evm1 address with 100 EOS + evm_eoa evm1; + const int64_t to_bridge = 1000000; + transfer_token("alice"_n, evm_account_name, make_asset(to_bridge), evm1.address_0x()); + + deploy_simple_contract(evm1); + + int64_t level = 0; + + // At least 1024 + call_test(1024, evm1, true); + + // At least 11 + call_test(11, evm1, false); + + +} FC_LOG_AND_RETHROW() + +BOOST_AUTO_TEST_SUITE_END() From 8f6c7f55803f8e4c76ce5fe7f648bcdc2dccd693 Mon Sep 17 00:00:00 2001 From: yarkin Date: Thu, 22 Aug 2024 14:21:49 +0800 Subject: [PATCH 2/4] update test --- tests/stack_limit_tests.cpp | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/tests/stack_limit_tests.cpp b/tests/stack_limit_tests.cpp index 86131851..2880c8e1 100644 --- a/tests/stack_limit_tests.cpp +++ b/tests/stack_limit_tests.cpp @@ -78,12 +78,22 @@ BOOST_FIXTURE_TEST_CASE(max_limit, stack_limit_tester) try { int64_t level = 0; - // At least 1024 + // At least 1024. It would cost too much time to test all values up to 1024. So we directly test 1024 call_test(1024, evm1, true); - // At least 11 - call_test(11, evm1, false); + // At least 11. We will try every value until it fails just in case. + try { + for (level = 0; level < 256; ++level) { + call_test(level, evm1, false); + } + } + catch (...) { + + } + // We check it will fail at exactly 12 so that the test can fail if we optimize the code to support more levels. + // In this way, the test can keep itself updated to guard against latest limits. + BOOST_REQUIRE_EQUAL(level, 12); } FC_LOG_AND_RETHROW() From 85d1d634dfc4272614b7f76bf9b5edebab415901 Mon Sep 17 00:00:00 2001 From: yarkin Date: Fri, 23 Aug 2024 09:07:50 +0800 Subject: [PATCH 3/4] force the stack limit test stop at certain level for now. --- tests/stack_limit_tests.cpp | 36 ++++++++++++++++++++++++++++-------- 1 file changed, 28 insertions(+), 8 deletions(-) diff --git a/tests/stack_limit_tests.cpp b/tests/stack_limit_tests.cpp index 2880c8e1..4a429683 100644 --- a/tests/stack_limit_tests.cpp +++ b/tests/stack_limit_tests.cpp @@ -67,7 +67,7 @@ struct stack_limit_tester : basic_evm_tester { }; BOOST_AUTO_TEST_SUITE(stack_limit_tests) -BOOST_FIXTURE_TEST_CASE(max_limit, stack_limit_tester) try { +BOOST_FIXTURE_TEST_CASE(max_limit_internal, stack_limit_tester) try { // Fund evm1 address with 100 EOS evm_eoa evm1; @@ -76,14 +76,29 @@ BOOST_FIXTURE_TEST_CASE(max_limit, stack_limit_tester) try { deploy_simple_contract(evm1); - int64_t level = 0; - - // At least 1024. It would cost too much time to test all values up to 1024. So we directly test 1024 + // At least 1024 for internal calls. It would cost too much time to test all values up to 1024. So we directly test 1024 call_test(1024, evm1, true); - // At least 11. We will try every value until it fails just in case. +} FC_LOG_AND_RETHROW() + +BOOST_FIXTURE_TEST_CASE(max_limit_external, stack_limit_tester) try { + + // Fund evm1 address with 100 EOS + evm_eoa evm1; + const int64_t to_bridge = 1000000; + transfer_token("alice"_n, evm_account_name, make_asset(to_bridge), evm1.address_0x()); + + deploy_simple_contract(evm1); + + // At least 11 for external calls. We will try every value until it fails just in case. + const int64_t external_limit = 11; + int64_t level = 0; try { for (level = 0; level < 256; ++level) { + // We have some problems in test framework that will sometimes raise low level access violation if we go beyond limit here. + // So we have to limit the loop at external_limit and do not test if it will go beyond limit at external_limit + 1. + if (level > external_limit) + break; call_test(level, evm1, false); } } @@ -91,9 +106,14 @@ BOOST_FIXTURE_TEST_CASE(max_limit, stack_limit_tester) try { } - // We check it will fail at exactly 12 so that the test can fail if we optimize the code to support more levels. - // In this way, the test can keep itself updated to guard against latest limits. - BOOST_REQUIRE_EQUAL(level, 12); + // We check it will fail at exactly external_limit + 1 so that the test can fail evem the actual supported level goes up. + // In this way, the test can keep itself updated to guard against limit changes from optimizations. + // !!! NOTE !!! + // We have some problems in test framework that will sometimes raise low level access violation if we go beyond limit here. + // So we imposed some extra limit above to make sure the loop always ends at external_limit + 1. + // Therefore the check here will only work if the actual level go below defined external_limit. + // The feature for guarding against optimizations is not working for now. + BOOST_REQUIRE_EQUAL(level, external_limit + 1); } FC_LOG_AND_RETHROW() From 6e2fed6cbd4168473ee71c580bac97af9b06481e Mon Sep 17 00:00:00 2001 From: yarkin Date: Tue, 27 Aug 2024 10:39:52 +0800 Subject: [PATCH 4/4] Go back to old test design as the test framework is fixed. --- tests/stack_limit_tests.cpp | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/tests/stack_limit_tests.cpp b/tests/stack_limit_tests.cpp index 4a429683..9f539a78 100644 --- a/tests/stack_limit_tests.cpp +++ b/tests/stack_limit_tests.cpp @@ -77,6 +77,7 @@ BOOST_FIXTURE_TEST_CASE(max_limit_internal, stack_limit_tester) try { deploy_simple_contract(evm1); // At least 1024 for internal calls. It would cost too much time to test all values up to 1024. So we directly test 1024 + // The number 1024 is from the spec of ethereum. call_test(1024, evm1, true); } FC_LOG_AND_RETHROW() @@ -95,10 +96,6 @@ BOOST_FIXTURE_TEST_CASE(max_limit_external, stack_limit_tester) try { int64_t level = 0; try { for (level = 0; level < 256; ++level) { - // We have some problems in test framework that will sometimes raise low level access violation if we go beyond limit here. - // So we have to limit the loop at external_limit and do not test if it will go beyond limit at external_limit + 1. - if (level > external_limit) - break; call_test(level, evm1, false); } } @@ -108,11 +105,6 @@ BOOST_FIXTURE_TEST_CASE(max_limit_external, stack_limit_tester) try { // We check it will fail at exactly external_limit + 1 so that the test can fail evem the actual supported level goes up. // In this way, the test can keep itself updated to guard against limit changes from optimizations. - // !!! NOTE !!! - // We have some problems in test framework that will sometimes raise low level access violation if we go beyond limit here. - // So we imposed some extra limit above to make sure the loop always ends at external_limit + 1. - // Therefore the check here will only work if the actual level go below defined external_limit. - // The feature for guarding against optimizations is not working for now. BOOST_REQUIRE_EQUAL(level, external_limit + 1); } FC_LOG_AND_RETHROW()