Skip to content

Commit

Permalink
test: Split know block hashes into TestBlockHashes (#1061)
Browse files Browse the repository at this point in the history
This removes `.known_block_hashes` from `state::BlockInfo`. Block hashes
in tests are now provided by `TestBlockHashes`.

The main goal here is to remove test-only fields from
`state::BlockInfo`.
  • Loading branch information
chfast authored Oct 28, 2024
1 parent 4c90042 commit f663ed2
Show file tree
Hide file tree
Showing 13 changed files with 75 additions and 90 deletions.
20 changes: 10 additions & 10 deletions test/blockchaintest/blockchaintest_runner.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -29,10 +29,10 @@ struct TransitionResult
namespace
{
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)
const state::BlockHashes& block_hashes, const std::vector<state::Transaction>& txs,
evmc_revision rev, std::optional<int64_t> block_reward)
{
system_call(state, block, rev, vm);
system_call(state, block, block_hashes, rev, vm);

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

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

if (holds_alternative<std::error_code>(res))
{
Expand All @@ -73,7 +74,7 @@ TransitionResult apply_block(TestState& state, evmc::VM& vm, const state::BlockI
}
}

test::finalize(state, rev, block.coinbase, block_reward, block.ommers, block.withdrawals);
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,20 +138,19 @@ void run_blockchain_tests(std::span<const BlockchainTest> tests, evmc::VM& vm)

auto state = c.pre_state;

std::unordered_map<int64_t, hash256> known_block_hashes{
TestBlockHashes block_hashes{
{c.genesis_block_header.block_number, c.genesis_block_header.hash}};

for (const auto& test_block : c.test_blocks)
{
auto bi = test_block.block_info;
bi.known_block_hashes = known_block_hashes;

const auto rev = c.rev.get_revision(bi.timestamp);

const auto res =
apply_block(state, vm, bi, test_block.transactions, rev, mining_reward(rev));
const auto res = apply_block(
state, vm, bi, block_hashes, test_block.transactions, rev, mining_reward(rev));

known_block_hashes[test_block.expected_block_header.block_number] =
block_hashes[test_block.expected_block_header.block_number] =
test_block.expected_block_header.hash;

SCOPED_TRACE(std::string{evmc::to_string(rev)} + '/' + std::to_string(case_index) +
Expand Down
4 changes: 0 additions & 4 deletions test/state/block.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -57,10 +57,6 @@ struct BlockInfo

std::vector<Ommer> ommers;
std::vector<Withdrawal> withdrawals;

/// Collection of known block hashes by block number. Only use in tests.
/// TODO: This should be moved to evmone::test.
std::unordered_map<int64_t, hash256> known_block_hashes;
};

/// Computes the current blob gas price based on the excess blob gas.
Expand Down
14 changes: 7 additions & 7 deletions test/state/test_state.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -61,20 +61,20 @@ bytes32 TestState::get_storage(const address& addr, const bytes32& key) const no

bytes32 TestBlockHashes::get_block_hash(int64_t block_number) const noexcept
{
if (const auto& it = known_block_hashes_.find(block_number); it != known_block_hashes_.end())
if (const auto& it = find(block_number); it != end())
return it->second;

// Convention for testing: if the block hash in unknown return the predefined "fake" value.
// Convention for testing: if the block hash is unknown return the predefined "fake" value.
// https://github.com/ethereum/go-ethereum/blob/v1.12.2/tests/state_test_util.go#L432
const auto s = std::to_string(block_number);
return keccak256({reinterpret_cast<const uint8_t*>(s.data()), s.size()});
}

[[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)
const state::BlockInfo& block, const state::BlockHashes& block_hashes,
const state::Transaction& tx, evmc_revision rev, evmc::VM& vm, int64_t block_gas_left,
int64_t blob_gas_left)
{
const TestBlockHashes block_hashes{block.known_block_hashes};
const auto result_or_error =
state::transition(state, block, block_hashes, tx, rev, vm, block_gas_left, blob_gas_left);
if (const auto result = get_if<state::TransactionReceipt>(&result_or_error))
Expand All @@ -90,9 +90,9 @@ void finalize(TestState& state, evmc_revision rev, const address& coinbase,
state.apply(diff);
}

void system_call(TestState& state, const state::BlockInfo& block, evmc_revision rev, evmc::VM& vm)
void system_call(TestState& state, const state::BlockInfo& block,
const state::BlockHashes& block_hashes, evmc_revision rev, evmc::VM& vm)
{
const TestBlockHashes block_hashes{block.known_block_hashes};
const auto diff = state::system_call(state, block, block_hashes, rev, vm);
state.apply(diff);
}
Expand Down
17 changes: 7 additions & 10 deletions test/state/test_state.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -72,31 +72,28 @@ class TestState : public state::StateView, public std::map<address, TestAccount>
void apply(const state::StateDiff& diff);
};

class TestBlockHashes : public state::BlockHashes
class TestBlockHashes : public state::BlockHashes, public std::unordered_map<int64_t, bytes32>
{
/// The map block_number => block hash of known blocks.
const std::unordered_map<int64_t, bytes32>& known_block_hashes_;

public:
explicit TestBlockHashes(const std::unordered_map<int64_t, bytes32>& known_block_hashes)
: known_block_hashes_{known_block_hashes}
{}
using std::unordered_map<int64_t, bytes32>::unordered_map;

bytes32 get_block_hash(int64_t block_number) const noexcept override;
};

/// 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);
const state::BlockInfo& block, const state::BlockHashes& block_hashes,
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);
void system_call(TestState& state, const state::BlockInfo& block,
const state::BlockHashes& block_hashes, evmc_revision rev, evmc::VM& vm);

} // namespace test
} // namespace evmone
4 changes: 4 additions & 0 deletions test/statetest/statetest.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ struct StateTransitionTest
std::string name;
TestState pre_state;
state::BlockInfo block;
TestBlockHashes block_hashes;
TestMultiTransaction multi_tx;
std::vector<Case> cases;
std::unordered_map<uint64_t, std::string> input_labels;
Expand All @@ -85,6 +86,9 @@ bytes from_json<bytes>(const json::json& j);
template <>
state::BlockInfo from_json<state::BlockInfo>(const json::json& j);

template <>
TestBlockHashes from_json<TestBlockHashes>(const json::json& j);

template <>
state::Withdrawal from_json<state::Withdrawal>(const json::json& j);

Expand Down
21 changes: 13 additions & 8 deletions test/statetest/statetest_loader.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -212,13 +212,6 @@ state::BlockInfo from_json<state::BlockInfo>(const json::json& j)
withdrawals.push_back(from_json<state::Withdrawal>(withdrawal));
}

std::unordered_map<int64_t, hash256> block_hashes;
if (const auto block_hashes_it = j.find("blockHashes"); block_hashes_it != j.end())
{
for (const auto& [j_num, j_hash] : block_hashes_it->items())
block_hashes[from_json<int64_t>(j_num)] = from_json<hash256>(j_hash);
}

std::vector<state::Ommer> ommers;
if (const auto ommers_it = j.find("ommers"); ommers_it != j.end())
{
Expand Down Expand Up @@ -265,10 +258,21 @@ state::BlockInfo from_json<state::BlockInfo>(const json::json& j)
.blob_base_fee = state::compute_blob_gas_price(excess_blob_gas),
.ommers = std::move(ommers),
.withdrawals = std::move(withdrawals),
.known_block_hashes = std::move(block_hashes),
};
}

template <>
TestBlockHashes from_json<TestBlockHashes>(const json::json& j)
{
TestBlockHashes block_hashes;
if (const auto block_hashes_it = j.find("blockHashes"); block_hashes_it != j.end())
{
for (const auto& [j_num, j_hash] : block_hashes_it->items())
block_hashes[from_json<int64_t>(j_num)] = from_json<hash256>(j_hash);
}
return block_hashes;
}

template <>
TestState from_json<TestState>(const json::json& j)
{
Expand Down Expand Up @@ -425,6 +429,7 @@ static void from_json(const json::json& j_t, StateTransitionTest& o)
o.multi_tx = j_t.at("transaction").get<TestMultiTransaction>();

o.block = from_json<state::BlockInfo>(j_t.at("env"));
o.block_hashes = from_json<TestBlockHashes>(j_t.at("env"));

if (const auto info_it = j_t.find("_info"); info_it != j_t.end())
{
Expand Down
4 changes: 2 additions & 2 deletions test/statetest/statetest_runner.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,8 @@ 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;

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

// Finalize block with reward 0.
test::finalize(state, rev, test.block.coinbase, 0, {}, {});
Expand Down
10 changes: 6 additions & 4 deletions test/t8n/t8n.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ int main(int argc, const char* argv[])
}

state::BlockInfo block;
TestBlockHashes block_hashes;
TestState state;

if (!alloc_file.empty())
Expand All @@ -86,7 +87,8 @@ int main(int argc, const char* argv[])
if (!env_file.empty())
{
const auto j = json::json::parse(std::ifstream{env_file});
block = test::from_json<state::BlockInfo>(j);
block = from_json<state::BlockInfo>(j);
block_hashes = from_json<TestBlockHashes>(j);
}

json::json j_result;
Expand Down Expand Up @@ -133,7 +135,7 @@ int main(int argc, const char* argv[])
j_result["receipts"] = json::json::array();
j_result["rejected"] = json::json::array();

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

for (size_t i = 0; i < j_txs.size(); ++i)
{
Expand Down Expand Up @@ -168,8 +170,8 @@ int main(int argc, const char* argv[])
std::clog.rdbuf(trace_file_output.rdbuf());
}

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

if (holds_alternative<std::error_code>(res))
{
Expand Down
10 changes: 6 additions & 4 deletions test/unittests/state_system_call_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,13 @@ class state_system_call : public testing::Test
protected:
evmc::VM vm{evmc_create_evmone()};
TestState state;
TestBlockHashes block_hashes;
};

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

EXPECT_EQ(state.size(), 0) << "State must remain unchanged";
}
Expand All @@ -34,7 +35,7 @@ TEST_F(state_system_call, beacon_roots)
state.insert(
BEACON_ROOTS_ADDRESS, {.code = sstore(OP_NUMBER, calldataload(0)) + sstore(0, OP_CALLER)});

system_call(state, block, EVMC_CANCUN, vm);
system_call(state, block, block_hashes, EVMC_CANCUN, vm);

ASSERT_EQ(state.size(), 1);
EXPECT_FALSE(state.contains(SYSTEM_ADDRESS));
Expand All @@ -50,11 +51,12 @@ TEST_F(state_system_call, history_storage)
{
static constexpr auto NUMBER = 123456789;
static constexpr auto PREV_BLOCKHASH = 0xbbbb_bytes32;
const BlockInfo block{.number = NUMBER, .known_block_hashes = {{NUMBER - 1, PREV_BLOCKHASH}}};
const BlockInfo block{.number = NUMBER};
block_hashes = {{NUMBER - 1, PREV_BLOCKHASH}};
state.insert(HISTORY_STORAGE_ADDRESS,
{.code = sstore(OP_NUMBER, calldataload(0)) + sstore(0, OP_CALLER)});

system_call(state, block, EVMC_PRAGUE, vm);
system_call(state, block, block_hashes, EVMC_PRAGUE, vm);

ASSERT_EQ(state.size(), 1);
EXPECT_FALSE(state.contains(SYSTEM_ADDRESS));
Expand Down
4 changes: 2 additions & 2 deletions test/unittests/state_transition.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -57,8 +57,8 @@ void state_transition::TearDown()
if (trace)
trace_capture.emplace();

const auto res = test::transition(state, block, tx, rev, selected_vm, block.gas_limit,
state::BlockInfo::MAX_BLOB_GAS_PER_BLOCK);
const auto res = test::transition(state, block, block_hashes, tx, rev, selected_vm,
block.gas_limit, state::BlockInfo::MAX_BLOB_GAS_PER_BLOCK);
test::finalize(state, rev, block.coinbase, block_reward, block.ommers, block.withdrawals);
const auto& post = state;

Expand Down
1 change: 1 addition & 0 deletions test/unittests/state_transition.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ class state_transition : public ExportableFixture
.coinbase = Coinbase,
.base_fee = 999,
};
TestBlockHashes block_hashes;
Transaction tx{
// The default type corresponds to the default `rev` and majority of tests.
.type = Transaction::Type::eip1559,
Expand Down
5 changes: 3 additions & 2 deletions test/unittests/state_transition_block_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,10 @@ TEST_F(state_transition, block_apply_withdrawal)

TEST_F(state_transition, known_block_hash)
{
block.known_block_hashes = {
block_hashes = {
{1, 0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421_bytes32},
{2, 0x0000000000000000000000000000000000000000000000000000000000000111_bytes32}};
{2, 0x0000000000000000000000000000000000000000000000000000000000000111_bytes32},
};
block.number = 5;

tx.to = To;
Expand Down
Loading

0 comments on commit f663ed2

Please sign in to comment.