diff --git a/test/statetest/statetest.hpp b/test/statetest/statetest.hpp index 5ad6a1d594..bdf3b1815b 100644 --- a/test/statetest/statetest.hpp +++ b/test/statetest/statetest.hpp @@ -95,9 +95,11 @@ json::json to_json(const std::unordered_map& accounts); StateTransitionTest load_state_test(std::istream& input); -/// Validates deployed EOF containers before running state test. -/// Throws exception on any invalid EOF in state. -void validate_deployed_code(const state::State& state, evmc_revision rev); +/// Validates an Ethereum state: +/// - checks that there are no zero-value storage entries, +/// - checks that there are no invalid EOF codes. +/// Throws std::invalid_argument exception. +void validate_state(const state::State& state, evmc_revision rev); /// Execute the state @p test using the @p vm. /// diff --git a/test/statetest/statetest_loader.cpp b/test/statetest/statetest_loader.cpp index a1d9ecce74..90be1dec50 100644 --- a/test/statetest/statetest_loader.cpp +++ b/test/statetest/statetest_loader.cpp @@ -271,9 +271,9 @@ state::State from_json(const json::json& j) { for (const auto& [j_key, j_value] : storage_it->items()) { - const auto value = from_json(j_value); - acc.storage.insert( - {from_json(j_key), {.current = value, .original = value}}); + if (const auto value = from_json(j_value); !is_zero(value)) + acc.storage.insert( + {from_json(j_key), {.current = value, .original = value}}); } } } @@ -434,10 +434,12 @@ StateTransitionTest load_state_test(std::istream& input) return json::json::parse(input).get(); } -void validate_deployed_code(const state::State& state, evmc_revision rev) +void validate_state(const state::State& state, evmc_revision rev) { for (const auto& [addr, acc] : state.get_accounts()) { + // TODO: Check for empty accounts after Paris. + // https://github.com/ethereum/tests/issues/1331 if (is_eof_container(acc.code)) { if (rev >= EVMC_PRAGUE) @@ -451,6 +453,16 @@ void validate_deployed_code(const state::State& state, evmc_revision rev) } } } + + for (const auto& [key, value] : acc.storage) + { + if (is_zero(value.original)) + { + throw std::invalid_argument{"account " + hex0x(addr) + + " contains invalid zero-value storage entry " + + hex0x(key)}; + } + } } } } // namespace evmone::test diff --git a/test/statetest/statetest_runner.cpp b/test/statetest/statetest_runner.cpp index 717e05ec73..8c92e06cc9 100644 --- a/test/statetest/statetest_runner.cpp +++ b/test/statetest/statetest_runner.cpp @@ -13,6 +13,7 @@ void run_state_test(const StateTransitionTest& test, evmc::VM& vm, bool trace_su { for (const auto& [rev, cases] : test.cases) { + validate_state(test.pre_state, rev); for (size_t case_index = 0; case_index != cases.size(); ++case_index) { SCOPED_TRACE(std::string{evmc::to_string(rev)} + '/' + std::to_string(case_index)); @@ -25,8 +26,6 @@ void run_state_test(const StateTransitionTest& test, evmc::VM& vm, bool trace_su const auto tx = test.multi_tx.get(expected.indexes); auto state = test.pre_state; - validate_deployed_code(state, rev); - const auto res = state::transition(state, test.block, tx, rev, vm, test.block.gas_limit, state::BlockInfo::MAX_BLOB_GAS_PER_BLOCK); diff --git a/test/t8n/t8n.cpp b/test/t8n/t8n.cpp index 37612d34c8..bdad568f17 100644 --- a/test/t8n/t8n.cpp +++ b/test/t8n/t8n.cpp @@ -112,19 +112,17 @@ int main(int argc, const char* argv[]) std::vector receipts; int64_t block_gas_left = block.gas_limit; - // Validate eof code in pre-state - if (rev >= EVMC_PRAGUE) - validate_deployed_code(state, rev); + validate_state(state, rev); // Parse and execute transactions if (!txs_file.empty()) { const auto j_txs = json::json::parse(std::ifstream{txs_file}); - evmc::VM vm{evmc_create_evmone(), {{"O", "0"}}}; + evmc::VM vm{evmc_create_evmone()}; if (trace) - vm.set_option("trace", "0"); + vm.set_option("trace", "1"); std::vector txs_logs; diff --git a/test/unittests/state_transition.cpp b/test/unittests/state_transition.cpp index 6078a3f912..35a1ad0e78 100644 --- a/test/unittests/state_transition.cpp +++ b/test/unittests/state_transition.cpp @@ -40,14 +40,7 @@ class TraceCapture void state_transition::TearDown() { - for (const auto& [addr, acc] : pre.get_accounts()) - { - if (is_eof_container(acc.code)) - { - ASSERT_EQ(validate_eof(rev, acc.code), EOFValidationError::success) - << "invalid EOF in prestate at " << addr; - } - } + validate_state(pre, rev); auto state = pre; const auto trace = !expect.trace.empty(); diff --git a/test/unittests/statetest_loader_test.cpp b/test/unittests/statetest_loader_test.cpp index ede5fbc9c2..5698c5fb4c 100644 --- a/test/unittests/statetest_loader_test.cpp +++ b/test/unittests/statetest_loader_test.cpp @@ -133,14 +133,27 @@ TEST(statetest_loader, load_minimal_test) EXPECT_EQ(st.input_labels.size(), 0); } -TEST(statetest_loader, validate_deployed_code_test) +TEST(statetest_loader, validate_state_invalid_eof) { { state::State state; state.insert(0xadd4_address, {.code = "EF0001010000020001000103000100FEDA"_hex}); - EXPECT_THAT([&] { validate_deployed_code(state, EVMC_PRAGUE); }, + EXPECT_THAT([&] { validate_state(state, EVMC_PRAGUE); }, ThrowsMessage( "EOF container at 0x000000000000000000000000000000000000add4 is invalid: " "zero_section_size")); } } + +TEST(statetest_loader, validate_state_zero_storage_slot) +{ + { + state::State state; + state.insert(0xadd4_address, {.storage = {{0x01_bytes32, {0x00_bytes32}}}}); + EXPECT_THAT([&] { validate_state(state, EVMC_PRAGUE); }, + ThrowsMessage( + "account 0x000000000000000000000000000000000000add4 contains invalid zero-value " + "storage entry " + "0x0000000000000000000000000000000000000000000000000000000000000001")); + } +}