From a80eb34215b421b536eb319abfa52225fe42653f Mon Sep 17 00:00:00 2001 From: Andrei Maiboroda Date: Tue, 25 Feb 2025 12:48:13 +0100 Subject: [PATCH 1/7] statetest: Check prestate for invalid EIP-7702 delegation prefix --- test/statetest/statetest_loader.cpp | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/test/statetest/statetest_loader.cpp b/test/statetest/statetest_loader.cpp index df88c7ab97..0bac8ad4c3 100644 --- a/test/statetest/statetest_loader.cpp +++ b/test/statetest/statetest_loader.cpp @@ -5,6 +5,8 @@ #include "../utils/stdx/utility.hpp" #include "../utils/utils.hpp" #include "statetest.hpp" + +#include #include #include @@ -498,6 +500,24 @@ void validate_state(const TestState& state, evmc_revision rev) { // TODO: Check for empty accounts after Paris. // https://github.com/ethereum/tests/issues/1331 + + if (is_code_delegated(acc.code)) + { + if (rev >= EVMC_PRAGUE) + { + if (acc.code.size() != std::size(DELEGATION_MAGIC) + sizeof(evmc::address)) + { + throw std::invalid_argument( + "EIP-7702 delegation designator at " + hex0x(addr) + " has invalid size"); + } + } + else + { + throw std::invalid_argument( + "unexpected code with EIP-7702 delegation prefix at " + hex0x(addr)); + } + } + if (is_eof_container(acc.code)) { if (rev >= EVMC_OSAKA) From 380f77d49d03529eb83623f96e35cf6b680fda88 Mon Sep 17 00:00:00 2001 From: Andrei Maiboroda Date: Mon, 17 Feb 2025 12:13:06 +0100 Subject: [PATCH 2/7] statetest: Check prestate for empty accounts with non-empty storage --- test/statetest/statetest_loader.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/test/statetest/statetest_loader.cpp b/test/statetest/statetest_loader.cpp index 0bac8ad4c3..0726f5c6c8 100644 --- a/test/statetest/statetest_loader.cpp +++ b/test/statetest/statetest_loader.cpp @@ -498,8 +498,9 @@ void validate_state(const TestState& state, evmc_revision rev) { for (const auto& [addr, acc] : state) { - // TODO: Check for empty accounts after Paris. - // https://github.com/ethereum/tests/issues/1331 + if (rev >= EVMC_PARIS && acc.code.empty() && acc.balance == 0 && acc.nonce == 0 && + !acc.storage.empty()) + throw std::invalid_argument("empty account with non-empty storage at " + hex0x(addr)); if (is_code_delegated(acc.code)) { From 2d8bfc55b85ed26d21124f5f84c189d9951fd37b Mon Sep 17 00:00:00 2001 From: Andrei Maiboroda Date: Mon, 17 Feb 2025 12:55:46 +0100 Subject: [PATCH 3/7] statetest: Check prestate for account codes starting with 0xEF --- test/statetest/statetest_loader.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/test/statetest/statetest_loader.cpp b/test/statetest/statetest_loader.cpp index 0726f5c6c8..3052f2b0dc 100644 --- a/test/statetest/statetest_loader.cpp +++ b/test/statetest/statetest_loader.cpp @@ -498,6 +498,13 @@ void validate_state(const TestState& state, evmc_revision rev) { for (const auto& [addr, acc] : state) { + const bool allowedEF = (rev >= EVMC_PRAGUE && is_code_delegated(acc.code)) || + (rev >= EVMC_OSAKA && is_eof_container(acc.code)) || + // exceptions to EIP-3541 rule existing on Mainnet + acc.code == "EF"_hex || acc.code == "EFF09f918bf09f9fa9"_hex; + if (rev >= EVMC_LONDON && !allowedEF && !acc.code.empty() && acc.code[0] == 0xEF) + throw std::invalid_argument("unexpected code starting with 0xEF at " + hex0x(addr)); + if (rev >= EVMC_PARIS && acc.code.empty() && acc.balance == 0 && acc.nonce == 0 && !acc.storage.empty()) throw std::invalid_argument("empty account with non-empty storage at " + hex0x(addr)); From 50d9a92a9f378bfa137b9884e84f699471305700 Mon Sep 17 00:00:00 2001 From: Andrei Maiboroda Date: Mon, 17 Feb 2025 12:42:04 +0100 Subject: [PATCH 4/7] Remove redundant checks --- test/statetest/statetest_loader.cpp | 36 ++++++++--------------------- 1 file changed, 9 insertions(+), 27 deletions(-) diff --git a/test/statetest/statetest_loader.cpp b/test/statetest/statetest_loader.cpp index 3052f2b0dc..5cdf4cc633 100644 --- a/test/statetest/statetest_loader.cpp +++ b/test/statetest/statetest_loader.cpp @@ -509,38 +509,20 @@ void validate_state(const TestState& state, evmc_revision rev) !acc.storage.empty()) throw std::invalid_argument("empty account with non-empty storage at " + hex0x(addr)); - if (is_code_delegated(acc.code)) + if (rev >= EVMC_PRAGUE && is_code_delegated(acc.code) && + acc.code.size() != std::size(DELEGATION_MAGIC) + sizeof(evmc::address)) { - if (rev >= EVMC_PRAGUE) - { - if (acc.code.size() != std::size(DELEGATION_MAGIC) + sizeof(evmc::address)) - { - throw std::invalid_argument( - "EIP-7702 delegation designator at " + hex0x(addr) + " has invalid size"); - } - } - else - { - throw std::invalid_argument( - "unexpected code with EIP-7702 delegation prefix at " + hex0x(addr)); - } + throw std::invalid_argument( + "EIP-7702 delegation designator at " + hex0x(addr) + " has invalid size"); } - if (is_eof_container(acc.code)) + if (rev >= EVMC_OSAKA && is_eof_container(acc.code)) { - if (rev >= EVMC_OSAKA) - { - if (const auto result = validate_eof(rev, ContainerKind::runtime, acc.code); - result != EOFValidationError::success) - { - throw std::invalid_argument( - "EOF container at " + hex0x(addr) + - " is invalid: " + std::string(get_error_message(result))); - } - } - else + if (const auto result = validate_eof(rev, ContainerKind::runtime, acc.code); + result != EOFValidationError::success) { - throw std::invalid_argument("unexpected code with EOF prefix at " + hex0x(addr)); + throw std::invalid_argument("EOF container at " + hex0x(addr) + " is invalid: " + + std::string(get_error_message(result))); } } From c4368437a91ca526351543b40b29ab49cd5ff2c0 Mon Sep 17 00:00:00 2001 From: Andrei Maiboroda Date: Tue, 25 Feb 2025 12:48:57 +0100 Subject: [PATCH 5/7] statetest: Check prestate for code at precompile addresses --- test/statetest/statetest_loader.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/test/statetest/statetest_loader.cpp b/test/statetest/statetest_loader.cpp index 5cdf4cc633..ec9cb82b6c 100644 --- a/test/statetest/statetest_loader.cpp +++ b/test/statetest/statetest_loader.cpp @@ -5,6 +5,7 @@ #include "../utils/stdx/utility.hpp" #include "../utils/utils.hpp" #include "statetest.hpp" +#include #include #include @@ -498,6 +499,9 @@ void validate_state(const TestState& state, evmc_revision rev) { for (const auto& [addr, acc] : state) { + if (state::is_precompile(rev, addr) && !acc.code.empty()) + throw std::invalid_argument("unexpected code at precompile address " + hex0x(addr)); + const bool allowedEF = (rev >= EVMC_PRAGUE && is_code_delegated(acc.code)) || (rev >= EVMC_OSAKA && is_eof_container(acc.code)) || // exceptions to EIP-3541 rule existing on Mainnet From 52d7bf98734a9a7d8e272d874be4267784b5e98d Mon Sep 17 00:00:00 2001 From: Andrei Maiboroda Date: Mon, 17 Feb 2025 14:57:22 +0100 Subject: [PATCH 6/7] Fix tests --- test/unittests/state_transition_create_test.cpp | 4 ++++ test/unittests/statetest_loader_test.cpp | 4 ++-- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/test/unittests/state_transition_create_test.cpp b/test/unittests/state_transition_create_test.cpp index ee19cfe9db..437c131839 100644 --- a/test/unittests/state_transition_create_test.cpp +++ b/test/unittests/state_transition_create_test.cpp @@ -184,6 +184,8 @@ TEST_F(state_transition, create_tx_collision) TEST_F(state_transition, create_tx_collision_storage) { + rev = EVMC_LONDON; + // Test create address collision with an account only having storage (EIP-7610). const auto created = compute_create_address(Sender, pre[Sender].nonce); pre[created] = {.storage = {{0x00_bytes32, 0x01_bytes32}}}; @@ -206,6 +208,8 @@ TEST_F(state_transition, create_collision) TEST_F(state_transition, create_collision_storage) { + rev = EVMC_LONDON; + // Test create address collision with an account only having storage (EIP-7610). tx.to = To; pre[To] = {.code = sstore(0, create())}; diff --git a/test/unittests/statetest_loader_test.cpp b/test/unittests/statetest_loader_test.cpp index 5e673a529d..5a170b673c 100644 --- a/test/unittests/statetest_loader_test.cpp +++ b/test/unittests/statetest_loader_test.cpp @@ -176,13 +176,13 @@ TEST(statetest_loader, validate_state_unexpected_eof) TestState state{{0xadd4_address, {.code = "EF00"_hex}}}; EXPECT_THAT([&] { validate_state(state, EVMC_CANCUN); }, ThrowsMessage( - "unexpected code with EOF prefix at 0x000000000000000000000000000000000000add4")); + "unexpected code starting with 0xEF at 0x000000000000000000000000000000000000add4")); } TEST(statetest_loader, validate_state_zero_storage_slot) { TestState state{{0xadd4_address, {.storage = {{0x01_bytes32, 0x00_bytes32}}}}}; - EXPECT_THAT([&] { validate_state(state, EVMC_PRAGUE); }, + EXPECT_THAT([&] { validate_state(state, EVMC_LONDON); }, ThrowsMessage( "account 0x000000000000000000000000000000000000add4 contains invalid zero-value " "storage entry " From b812b48fea5a4604b1040f18dff2f6af7dd75920 Mon Sep 17 00:00:00 2001 From: Andrei Maiboroda Date: Mon, 17 Feb 2025 14:57:29 +0100 Subject: [PATCH 7/7] Add tests for new prestate validations --- test/unittests/statetest_loader_test.cpp | 42 ++++++++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/test/unittests/statetest_loader_test.cpp b/test/unittests/statetest_loader_test.cpp index 5a170b673c..08425de065 100644 --- a/test/unittests/statetest_loader_test.cpp +++ b/test/unittests/statetest_loader_test.cpp @@ -188,3 +188,45 @@ TEST(statetest_loader, validate_state_zero_storage_slot) "storage entry " "0x0000000000000000000000000000000000000000000000000000000000000001")); } + +TEST(statetest_loader, validate_state_unexpected_ef_prefix) +{ + TestState state{{0xadd4_address, {.code = "EF00"_hex}}}; + EXPECT_THAT([&] { validate_state(state, EVMC_LONDON); }, + ThrowsMessage( + "unexpected code starting with 0xEF at 0x000000000000000000000000000000000000add4")); +} + +TEST(statetest_loader, validate_state_invalid_delegation_size) +{ + TestState state{{0xadd4_address, {.code = "EF010000"_hex}}}; + EXPECT_THAT([&] { validate_state(state, EVMC_PRAGUE); }, + ThrowsMessage( + "EIP-7702 delegation designator at 0x000000000000000000000000000000000000add4 has " + "invalid size")); +} + +TEST(statetest_loader, validate_state_unexpected_delegation) +{ + TestState state{ + {0xadd4_address, {.code = "EF01000000000000000000000000000000000000000001"_hex}}}; + EXPECT_THAT([&] { validate_state(state, EVMC_CANCUN); }, + ThrowsMessage( + "unexpected code starting with 0xEF at 0x000000000000000000000000000000000000add4")); +} + +TEST(statetest_loader, validate_empty_account_with_storage) +{ + TestState state{{0xadd4_address, {.storage = {{0x01_bytes32, 0x01_bytes32}}}}}; + EXPECT_THAT([&] { validate_state(state, EVMC_CANCUN); }, + ThrowsMessage( + "empty account with non-empty storage at 0x000000000000000000000000000000000000add4")); +} + +TEST(statetest_loader, validate_code_at_precompile_address) +{ + TestState state{{0x0a_address, {.code = "00"_hex}}}; + EXPECT_THAT([&] { validate_state(state, EVMC_CANCUN); }, + ThrowsMessage( + "unexpected code at precompile address 0x000000000000000000000000000000000000000a")); +}