Skip to content

Commit

Permalink
test: Wrap usage of state transition API
Browse files Browse the repository at this point in the history
Wrap the usage of the state transition API from the `evmone::state`
for tests so that the new API in `evmone::test` only exposes
`TestState` and hides `state::State`.

This isolates usage of the `evmone::state` to lower the disturbance
caused by API modifications,
e.g. in #802.
  • Loading branch information
chfast committed Sep 23, 2024
1 parent 7961b60 commit 6f92671
Show file tree
Hide file tree
Showing 7 changed files with 90 additions and 49 deletions.
22 changes: 9 additions & 13 deletions test/blockchaintest/blockchaintest_runner.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,6 @@

#include "../state/mpt_hash.hpp"
#include "../state/rlp.hpp"
#include "../state/state.hpp"
#include "../state/system_contracts.hpp"
#include "../test/statetest/statetest.hpp"
#include "blockchaintest.hpp"
#include <gtest/gtest.h>
Expand All @@ -30,11 +28,11 @@ struct TransitionResult

namespace
{
TransitionResult apply_block(state::State& state, evmc::VM& vm, const state::BlockInfo& block,
TransitionResult apply_block(TestState& state, evmc::VM& vm, const state::BlockInfo& block,
const std::vector<state::Transaction>& txs, evmc_revision rev,
std::optional<int64_t> block_reward)
{
state::system_call(state, block, rev, vm);
system_call(state, block, rev, vm);

std::vector<state::Log> txs_logs;
int64_t block_gas_left = block.gas_limit;
Expand All @@ -50,7 +48,7 @@ TransitionResult apply_block(state::State& state, evmc::VM& vm, const state::Blo
const auto& tx = txs[i];

const auto computed_tx_hash = keccak256(rlp::encode(tx));
auto res = state::transition(state, block, tx, rev, vm, block_gas_left, blob_gas_left);
auto res = test::transition(state, block, tx, rev, vm, block_gas_left, blob_gas_left);

if (holds_alternative<std::error_code>(res))
{
Expand All @@ -67,15 +65,15 @@ TransitionResult apply_block(state::State& state, evmc::VM& vm, const state::Blo
cumulative_gas_used += receipt.gas_used;
receipt.cumulative_gas_used = cumulative_gas_used;
if (rev < EVMC_BYZANTIUM)
receipt.post_state = state::mpt_hash(TestState{state});
receipt.post_state = state::mpt_hash(state);

block_gas_left -= receipt.gas_used;
blob_gas_left -= tx.blob_gas_used();
receipts.emplace_back(std::move(receipt));
}
}

state::finalize(state, rev, block.coinbase, block_reward, block.ommers, block.withdrawals);
test::finalize(state, rev, block.coinbase, block_reward, block.ommers, block.withdrawals);

const auto bloom = compute_bloom_filter(receipts);
return {std::move(receipts), std::move(rejected_txs), cumulative_gas_used, bloom};
Expand Down Expand Up @@ -137,7 +135,7 @@ void run_blockchain_tests(std::span<const BlockchainTest> tests, evmc::VM& vm)
bytes32{});
EXPECT_EQ(c.genesis_block_header.logs_bloom, bytes_view{state::BloomFilter{}});

auto state = c.pre_state.to_intra_state();
auto state = c.pre_state;

std::unordered_map<int64_t, hash256> known_block_hashes{
{c.genesis_block_header.block_number, c.genesis_block_header.hash}};
Expand All @@ -158,8 +156,7 @@ void run_blockchain_tests(std::span<const BlockchainTest> tests, evmc::VM& vm)
SCOPED_TRACE(std::string{evmc::to_string(rev)} + '/' + std::to_string(case_index) +
'/' + c.name + '/' + std::to_string(test_block.block_info.number));

EXPECT_EQ(
state::mpt_hash(TestState{state}), test_block.expected_block_header.state_root);
EXPECT_EQ(state::mpt_hash(state), test_block.expected_block_header.state_root);

if (rev >= EVMC_SHANGHAI)
{
Expand All @@ -178,14 +175,13 @@ void run_blockchain_tests(std::span<const BlockchainTest> tests, evmc::VM& vm)
// TODO: Add difficulty calculation verification.
}

const TestState post{state};
const auto expected_post_hash =
std::holds_alternative<TestState>(c.expectation.post_state) ?
state::mpt_hash(std::get<TestState>(c.expectation.post_state)) :
std::get<hash256>(c.expectation.post_state);
EXPECT_TRUE(state::mpt_hash(post) == expected_post_hash)
EXPECT_TRUE(state::mpt_hash(state) == expected_post_hash)
<< "Result state:\n"
<< print_state(post)
<< print_state(state)
<< (std::holds_alternative<TestState>(c.expectation.post_state) ?
"\n\nExpected state:\n" +
print_state(std::get<TestState>(c.expectation.post_state)) :
Expand Down
27 changes: 27 additions & 0 deletions test/state/test_state.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
// SPDX-License-Identifier: Apache-2.0
#include "test_state.hpp"
#include "state.hpp"
#include "system_contracts.hpp"

namespace evmone::test
{
Expand Down Expand Up @@ -31,4 +32,30 @@ state::State TestState::to_intra_state() const
}
return intra_state;
}

[[nodiscard]] std::variant<state::TransactionReceipt, std::error_code> transition(TestState& state,
const state::BlockInfo& block, const state::Transaction& tx, evmc_revision rev, evmc::VM& vm,
int64_t block_gas_left, int64_t blob_gas_left)
{
auto intra_state = state.to_intra_state();
auto res = state::transition(intra_state, block, tx, rev, vm, block_gas_left, blob_gas_left);
state = TestState{intra_state};
return res;
}

void finalize(TestState& state, evmc_revision rev, const address& coinbase,
std::optional<uint64_t> block_reward, std::span<const state::Ommer> ommers,
std::span<const state::Withdrawal> withdrawals)
{
auto intra_state = state.to_intra_state();
state::finalize(intra_state, rev, coinbase, block_reward, ommers, withdrawals);
state = TestState{intra_state};
}

void system_call(TestState& state, const state::BlockInfo& block, evmc_revision rev, evmc::VM& vm)
{
auto intra_state = state.to_intra_state();
state::system_call(intra_state, block, rev, vm);
state = TestState{intra_state};
}
} // namespace evmone::test
22 changes: 21 additions & 1 deletion test/state/test_state.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,20 @@
#include <evmc/evmc.hpp>
#include <intx/intx.hpp>
#include <map>
#include <span>
#include <variant>

namespace evmone
{
namespace state
{
struct BlockInfo;
struct Ommer;
struct Transaction;
struct TransactionReceipt;
struct Withdrawal;
class State;
}
} // namespace state

namespace test
{
Expand Down Expand Up @@ -63,5 +70,18 @@ class TestState : public std::map<address, TestAccount>
[[nodiscard]] state::State to_intra_state() const;
};

/// Wrapping of state::transition() which operates on TestState.
[[nodiscard]] std::variant<state::TransactionReceipt, std::error_code> transition(TestState& state,
const state::BlockInfo& block, const state::Transaction& tx, evmc_revision rev, evmc::VM& vm,
int64_t block_gas_left, int64_t blob_gas_left);

/// Wrapping of state::finalize() which operates on TestState.
void finalize(TestState& state, evmc_revision rev, const address& coinbase,
std::optional<uint64_t> block_reward, std::span<const state::Ommer> ommers,
std::span<const state::Withdrawal> withdrawals);

/// Wrapping of state::system_call() which operates on TestState.
void system_call(TestState& state, const state::BlockInfo& block, evmc_revision rev, evmc::VM& vm);

} // namespace test
} // namespace evmone
8 changes: 4 additions & 4 deletions test/statetest/statetest_runner.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -25,15 +25,15 @@ void run_state_test(const StateTransitionTest& test, evmc::VM& vm, bool trace_su

const auto& expected = cases[case_index];
const auto tx = test.multi_tx.get(expected.indexes);
auto state = test.pre_state.to_intra_state();
auto state = test.pre_state;

const auto res = state::transition(state, test.block, tx, rev, vm, test.block.gas_limit,
const auto res = test::transition(state, test.block, tx, rev, vm, test.block.gas_limit,
state::BlockInfo::MAX_BLOB_GAS_PER_BLOCK);

// Finalize block with reward 0.
state::finalize(state, rev, test.block.coinbase, 0, {}, {});
test::finalize(state, rev, test.block.coinbase, 0, {}, {});

const auto state_root = state::mpt_hash(TestState{state});
const auto state_root = state::mpt_hash(state);

if (trace_summary)
{
Expand Down
18 changes: 8 additions & 10 deletions test/t8n/t8n.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
#include "../state/ethash_difficulty.hpp"
#include "../state/mpt_hash.hpp"
#include "../state/rlp.hpp"
#include "../state/system_contracts.hpp"
#include "../statetest/statetest.hpp"
#include "../utils/utils.hpp"
#include <evmone/evmone.h>
Expand Down Expand Up @@ -76,14 +75,13 @@ int main(int argc, const char* argv[])
}

state::BlockInfo block;
state::State state;
TestState state;

if (!alloc_file.empty())
{
const auto j = json::json::parse(std::ifstream{alloc_file}, nullptr, false);
const auto test_state = test::from_json<TestState>(j);
validate_state(test_state, rev);
state = test_state.to_intra_state();
state = from_json<TestState>(j);
validate_state(state, rev);
}
if (!env_file.empty())
{
Expand Down Expand Up @@ -135,7 +133,7 @@ int main(int argc, const char* argv[])
j_result["receipts"] = json::json::array();
j_result["rejected"] = json::json::array();

state::system_call(state, block, rev, vm);
test::system_call(state, block, rev, vm);

for (size_t i = 0; i < j_txs.size(); ++i)
{
Expand Down Expand Up @@ -171,7 +169,7 @@ int main(int argc, const char* argv[])
}

auto res =
state::transition(state, block, tx, rev, vm, block_gas_left, blob_gas_left);
test::transition(state, block, tx, rev, vm, block_gas_left, blob_gas_left);

if (holds_alternative<std::error_code>(res))
{
Expand All @@ -196,7 +194,7 @@ int main(int argc, const char* argv[])
cumulative_gas_used += receipt.gas_used;
receipt.cumulative_gas_used = cumulative_gas_used;
if (rev < EVMC_BYZANTIUM)
receipt.post_state = state::mpt_hash(TestState{state});
receipt.post_state = state::mpt_hash(state);
j_receipt["cumulativeGasUsed"] = hex0x(cumulative_gas_used);

j_receipt["blockHash"] = hex0x(bytes32{});
Expand All @@ -218,11 +216,11 @@ int main(int argc, const char* argv[])
}
}

state::finalize(
test::finalize(
state, rev, block.coinbase, block_reward, block.ommers, block.withdrawals);

j_result["logsHash"] = hex0x(logs_hash(txs_logs));
j_result["stateRoot"] = hex0x(state::mpt_hash(TestState{state}));
j_result["stateRoot"] = hex0x(state::mpt_hash(state));
}

j_result["logsBloom"] = hex0x(compute_bloom_filter(receipts));
Expand Down
33 changes: 17 additions & 16 deletions test/unittests/state_system_call_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
#include <gtest/gtest.h>
#include <test/state/state.hpp>
#include <test/state/system_contracts.hpp>
#include <test/state/test_state.hpp>
#include <test/utils/bytecode.hpp>

using namespace evmc::literals;
Expand All @@ -16,15 +17,15 @@ class state_system_call : public testing::Test
{
protected:
evmc::VM vm{evmc_create_evmone()};
State state;
TestState state;
};

TEST_F(state_system_call, non_existient)
{
// Use MAX revision to invoke all activate system contracts.
system_call(state, {}, EVMC_MAX_REVISION, vm);

EXPECT_EQ(state.get_accounts().size(), 0) << "State must remain unchanged";
EXPECT_EQ(state.size(), 0) << "State must remain unchanged";
}

TEST_F(state_system_call, beacon_roots)
Expand All @@ -35,14 +36,14 @@ TEST_F(state_system_call, beacon_roots)

system_call(state, block, EVMC_CANCUN, vm);

ASSERT_EQ(state.get_accounts().size(), 1);
EXPECT_EQ(state.find(SYSTEM_ADDRESS), nullptr);
EXPECT_EQ(state.get(BEACON_ROOTS_ADDRESS).nonce, 0);
EXPECT_EQ(state.get(BEACON_ROOTS_ADDRESS).balance, 0);
const auto& storage = state.get(BEACON_ROOTS_ADDRESS).storage;
ASSERT_EQ(state.size(), 1);
EXPECT_FALSE(state.contains(SYSTEM_ADDRESS));
EXPECT_EQ(state.at(BEACON_ROOTS_ADDRESS).nonce, 0);
EXPECT_EQ(state.at(BEACON_ROOTS_ADDRESS).balance, 0);
const auto& storage = state.at(BEACON_ROOTS_ADDRESS).storage;
ASSERT_EQ(storage.size(), 2);
EXPECT_EQ(storage.at(0x01_bytes32).current, block.parent_beacon_block_root);
EXPECT_EQ(storage.at(0x00_bytes32).current, to_bytes32(SYSTEM_ADDRESS));
EXPECT_EQ(storage.at(0x01_bytes32), block.parent_beacon_block_root);
EXPECT_EQ(storage.at(0x00_bytes32), to_bytes32(SYSTEM_ADDRESS));
}

TEST_F(state_system_call, history_storage)
Expand All @@ -55,12 +56,12 @@ TEST_F(state_system_call, history_storage)

system_call(state, block, EVMC_PRAGUE, vm);

ASSERT_EQ(state.get_accounts().size(), 1);
EXPECT_EQ(state.find(SYSTEM_ADDRESS), nullptr);
EXPECT_EQ(state.get(HISTORY_STORAGE_ADDRESS).nonce, 0);
EXPECT_EQ(state.get(HISTORY_STORAGE_ADDRESS).balance, 0);
const auto& storage = state.get(HISTORY_STORAGE_ADDRESS).storage;
ASSERT_EQ(state.size(), 1);
EXPECT_FALSE(state.contains(SYSTEM_ADDRESS));
EXPECT_EQ(state.at(HISTORY_STORAGE_ADDRESS).nonce, 0);
EXPECT_EQ(state.at(HISTORY_STORAGE_ADDRESS).balance, 0);
const auto& storage = state.at(HISTORY_STORAGE_ADDRESS).storage;
ASSERT_EQ(storage.size(), 2);
EXPECT_EQ(storage.at(bytes32{NUMBER}).current, PREV_BLOCKHASH);
EXPECT_EQ(storage.at(0x00_bytes32).current, to_bytes32(SYSTEM_ADDRESS));
EXPECT_EQ(storage.at(bytes32{NUMBER}), PREV_BLOCKHASH);
EXPECT_EQ(storage.at(0x00_bytes32), to_bytes32(SYSTEM_ADDRESS));
}
9 changes: 4 additions & 5 deletions test/unittests/state_transition.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ void state_transition::TearDown()

// Execution:

auto state = pre;
const auto trace = !expect.trace.empty();
auto& selected_vm = trace ? tracing_vm : vm;

Expand All @@ -56,12 +57,10 @@ void state_transition::TearDown()
if (trace)
trace_capture.emplace();

auto intra_state = pre.to_intra_state();
const auto res = state::transition(intra_state, block, tx, rev, selected_vm, block.gas_limit,
const auto res = test::transition(state, block, tx, rev, selected_vm, block.gas_limit,
state::BlockInfo::MAX_BLOB_GAS_PER_BLOCK);
state::finalize(
intra_state, rev, block.coinbase, block_reward, block.ommers, block.withdrawals);
TestState post{intra_state};
test::finalize(state, rev, block.coinbase, block_reward, block.ommers, block.withdrawals);
const auto& post = state;

if (const auto expected_error = make_error_code(expect.tx_error))
{
Expand Down

0 comments on commit 6f92671

Please sign in to comment.