Skip to content

Commit

Permalink
Export state tests with invalid txs
Browse files Browse the repository at this point in the history
  • Loading branch information
pdobacz committed Apr 18, 2024
1 parent 44a029d commit b6b7a0a
Show file tree
Hide file tree
Showing 5 changed files with 50 additions and 25 deletions.
13 changes: 10 additions & 3 deletions test/statetest/statetest_runner.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -50,11 +50,18 @@ void run_state_test(const StateTransitionTest& test, evmc::VM& vm, bool trace_su
std::clog << R"("stateRoot":"0x)" << hex(state_root) << "\"}\n";
}

if (holds_alternative<state::TransactionReceipt>(res))
EXPECT_EQ(logs_hash(get<state::TransactionReceipt>(res).logs), expected.logs_hash);
if (expected.exception)
{
ASSERT_FALSE(holds_alternative<state::TransactionReceipt>(res))
<< "unexpected valid transaction";
EXPECT_EQ(logs_hash(std::vector<state::Log>()), expected.logs_hash);
}
else
EXPECT_TRUE(expected.exception)
{
ASSERT_TRUE(holds_alternative<state::TransactionReceipt>(res))
<< "unexpected invalid transaction: " << get<std::error_code>(res).message();
EXPECT_EQ(logs_hash(get<state::TransactionReceipt>(res).logs), expected.logs_hash);
}

EXPECT_EQ(state_root, expected.state_hash);
}
Expand Down
50 changes: 31 additions & 19 deletions test/unittests/state_transition.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,7 @@ void state_transition::SetUp()
{
pre.insert(tx.sender, {.nonce = 1, .balance = tx.gas_limit * tx.max_gas_price + tx.value + 1});

// Default expectations.
expect.post[Coinbase].exists = true;
// Default expectation (coinbase is added later for valid txs only).
expect.post[tx.sender].exists = true;
}

Expand Down Expand Up @@ -61,28 +60,37 @@ void state_transition::TearDown()
const auto res = state::transition(state, block, tx, rev, selected_vm, block.gas_limit,
state::BlockInfo::MAX_BLOB_GAS_PER_BLOCK);

if (const auto expected_error = make_error_code(expect.tx_error))
const auto expected_error = make_error_code(expect.tx_error);
if (expected_error)
{
ASSERT_TRUE(holds_alternative<std::error_code>(res))
<< "tx expected to be invalid with error: " << expected_error.message();
const auto tx_error = std::get<std::error_code>(res);
EXPECT_EQ(tx_error, expected_error)
<< tx_error.message() << " vs " << expected_error.message();

// TODO: Compare states carefully, they should be identical.
EXPECT_EQ(state.get_accounts().size(), pre.get_accounts().size());
for (const auto& [addr, acc] : state.get_accounts())
{
EXPECT_TRUE(pre.get_accounts().contains(addr)) << "unexpected account " << addr;
}

// TODO: Export also tests with invalid transactions.
return; // Do not check anything else.
}
else
{
ASSERT_TRUE(holds_alternative<TransactionReceipt>(res))
<< std::get<std::error_code>(res).message();
const auto& receipt = std::get<TransactionReceipt>(res);

ASSERT_TRUE(holds_alternative<TransactionReceipt>(res))
<< std::get<std::error_code>(res).message();
const auto& receipt = std::get<TransactionReceipt>(res);
EXPECT_EQ(receipt.status, expect.status);
if (expect.gas_used.has_value())
{
EXPECT_EQ(receipt.gas_used, *expect.gas_used);
}
// Update default expectations - valid transaction means coinbase exists unless explicitly
// requested otherwise
if (expect.post.find(Coinbase) == expect.post.end())
expect.post[Coinbase].exists = true;
}
state::finalize(state, rev, block.coinbase, block_reward, block.ommers, block.withdrawals);

if (trace)
Expand All @@ -92,12 +100,6 @@ void state_transition::TearDown()
EXPECT_EQ(trace_capture->get_capture(), expect.trace);
}

EXPECT_EQ(receipt.status, expect.status);
if (expect.gas_used.has_value())
{
EXPECT_EQ(receipt.gas_used, *expect.gas_used);
}

for (const auto& [addr, expected_acc] : expect.post)
{
const auto acc = state.find(addr);
Expand Down Expand Up @@ -146,7 +148,7 @@ void state_transition::TearDown()
}

if (!export_file_path.empty())
export_state_test(receipt, state);
export_state_test(res, state);
}

namespace
Expand All @@ -166,7 +168,8 @@ std::string_view to_test_fork_name(evmc_revision rev) noexcept
}
} // namespace

void state_transition::export_state_test(const TransactionReceipt& receipt, const State& post)
void state_transition::export_state_test(
const std::variant<TransactionReceipt, std::error_code>& res, const State& post)
{
json::json j;
auto& jt = j[export_test_name];
Expand Down Expand Up @@ -225,7 +228,16 @@ void state_transition::export_state_test(const TransactionReceipt& receipt, cons
auto& jpost = jt["post"][to_test_fork_name(rev)][0];
jpost["indexes"] = {{"data", 0}, {"gas", 0}, {"value", 0}};
jpost["hash"] = hex0x(mpt_hash(post.get_accounts()));
jpost["logs"] = hex0x(logs_hash(receipt.logs));

if (holds_alternative<std::error_code>(res))
{
jpost["expectException"] = std::get<std::error_code>(res).message();
jpost["logs"] = hex0x(logs_hash(std::vector<Log>()));
}
else
{
jpost["logs"] = hex0x(logs_hash(std::get<TransactionReceipt>(res).logs));
}

std::ofstream{export_file_path} << std::setw(2) << j;
}
Expand Down
3 changes: 2 additions & 1 deletion test/unittests/state_transition.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,8 @@ class state_transition : public ExportableFixture
void TearDown() override;

/// Exports the test in the JSON State Test format to ExportableFixture::export_out.
void export_state_test(const TransactionReceipt& receipt, const State& post);
void export_state_test(
const std::variant<TransactionReceipt, std::error_code>& res, const State& post);
};

} // namespace evmone::test
4 changes: 4 additions & 0 deletions test/unittests/state_transition_eof_create_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1243,6 +1243,7 @@ TEST_F(state_transition, txcreate_initcontainer_empty)
pre.insert(*tx.to, {.nonce = 1, .code = factory_container});

expect.tx_error = INIT_CODE_EMPTY;
expect.post[*tx.to].exists = true;
}

TEST_F(state_transition, txcreate_no_initcontainer)
Expand All @@ -1258,6 +1259,7 @@ TEST_F(state_transition, txcreate_no_initcontainer)
pre.insert(*tx.to, {.nonce = 1, .code = factory_container});

expect.tx_error = INIT_CODE_COUNT_ZERO;
expect.post[*tx.to].exists = true;
}

TEST_F(state_transition, txcreate_initcontainer_too_large)
Expand Down Expand Up @@ -1287,6 +1289,7 @@ TEST_F(state_transition, txcreate_initcontainer_too_large)
pre.insert(*tx.to, {.nonce = 1, .code = factory_container});

expect.tx_error = INIT_CODE_SIZE_LIMIT_EXCEEDED;
expect.post[*tx.to].exists = true;
}

TEST_F(state_transition, txcreate_too_many_initcontainers)
Expand All @@ -1312,6 +1315,7 @@ TEST_F(state_transition, txcreate_too_many_initcontainers)
pre.insert(*tx.to, {.nonce = 1, .code = factory_container});

expect.tx_error = INIT_CODE_COUNT_LIMIT_EXCEEDED;
expect.post[*tx.to].exists = true;
}

TEST_F(state_transition, initcode_transaction_before_prague)
Expand Down
5 changes: 3 additions & 2 deletions test/unittests/state_transition_tx_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ TEST_F(state_transition, tx_non_existing_sender)

expect.status = EVMC_SUCCESS;
expect.post.at(Sender).nonce = 1;
expect.post.at(Coinbase).exists = false;
expect.post[Coinbase].exists = false;
}

TEST_F(state_transition, invalid_tx_non_existing_sender)
Expand All @@ -43,6 +43,7 @@ TEST_F(state_transition, invalid_tx_non_existing_sender)
pre.get_accounts().erase(Sender);

expect.tx_error = INSUFFICIENT_FUNDS;
expect.post.at(Sender).exists = false;
}

TEST_F(state_transition, tx_blob_gas_price)
Expand All @@ -60,7 +61,7 @@ TEST_F(state_transition, tx_blob_gas_price)

pre.get(tx.sender).balance = 0x20000 + tx.gas_limit * tx.max_gas_price;

expect.post.at(Coinbase).exists = false; // all gas is burned, Coinbase gets nothing
expect.post[Coinbase].exists = false; // all gas is burned, Coinbase gets nothing
expect.status = EVMC_SUCCESS;
}

Expand Down

0 comments on commit b6b7a0a

Please sign in to comment.