Skip to content

Commit

Permalink
test: Generalize validate_state() (ethereum#795)
Browse files Browse the repository at this point in the history
Generalize `validate_deployed_code()` to `validate_state()`.
It validates an Ethereum state (likely a test pre-state).
New added check: disallow zero-value storage entries.
  • Loading branch information
chfast committed Feb 2, 2024
1 parent c4886ee commit ffd7390
Show file tree
Hide file tree
Showing 6 changed files with 41 additions and 24 deletions.
8 changes: 5 additions & 3 deletions test/statetest/statetest.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -95,9 +95,11 @@ json::json to_json(const std::unordered_map<address, state::Account>& 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.
///
Expand Down
20 changes: 16 additions & 4 deletions test/statetest/statetest_loader.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -271,9 +271,9 @@ state::State from_json<state::State>(const json::json& j)
{
for (const auto& [j_key, j_value] : storage_it->items())
{
const auto value = from_json<bytes32>(j_value);
acc.storage.insert(
{from_json<bytes32>(j_key), {.current = value, .original = value}});
if (const auto value = from_json<bytes32>(j_value); !is_zero(value))
acc.storage.insert(
{from_json<bytes32>(j_key), {.current = value, .original = value}});
}
}
}
Expand Down Expand Up @@ -434,10 +434,12 @@ StateTransitionTest load_state_test(std::istream& input)
return json::json::parse(input).get<StateTransitionTest>();
}

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)
Expand All @@ -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
3 changes: 1 addition & 2 deletions test/statetest/statetest_runner.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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));
Expand All @@ -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);

Expand Down
8 changes: 3 additions & 5 deletions test/t8n/t8n.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -112,19 +112,17 @@ int main(int argc, const char* argv[])
std::vector<state::TransactionReceipt> 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<state::Log> txs_logs;

Expand Down
9 changes: 1 addition & 8 deletions test/unittests/state_transition.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down
17 changes: 15 additions & 2 deletions test/unittests/statetest_loader_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<std::invalid_argument>(
"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<std::invalid_argument>(
"account 0x000000000000000000000000000000000000add4 contains invalid zero-value "
"storage entry "
"0x0000000000000000000000000000000000000000000000000000000000000001"));
}
}

0 comments on commit ffd7390

Please sign in to comment.