-
Notifications
You must be signed in to change notification settings - Fork 294
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #589 from ethereum/test_state_transition
test: Declarative state transition test suite
- Loading branch information
Showing
12 changed files
with
255 additions
and
8 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -13,6 +13,7 @@ target_sources( | |
bloom_filter.cpp | ||
errors.hpp | ||
hash_utils.hpp | ||
hash_utils.cpp | ||
host.hpp | ||
host.cpp | ||
mpt.hpp | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
// evmone: Fast Ethereum Virtual Machine implementation | ||
// Copyright 2023 The evmone Authors. | ||
// SPDX-License-Identifier: Apache-2.0 | ||
#include "hash_utils.hpp" | ||
|
||
std::ostream& operator<<(std::ostream& out, const evmone::address& a) | ||
{ | ||
return out << "0x" << hex(a); | ||
} | ||
|
||
std::ostream& operator<<(std::ostream& out, const evmone::bytes32& b) | ||
{ | ||
return out << "0x" << hex(b); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,73 @@ | ||
// evmone: Fast Ethereum Virtual Machine implementation | ||
// Copyright 2023 The evmone Authors. | ||
// SPDX-License-Identifier: Apache-2.0 | ||
|
||
#include "state_transition.hpp" | ||
|
||
namespace evmone::test | ||
{ | ||
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; | ||
expect.post[tx.sender].exists = true; | ||
} | ||
|
||
void state_transition::TearDown() | ||
{ | ||
auto& state = pre; | ||
const auto res = evmone::state::transition(state, block, tx, rev, vm); | ||
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); | ||
} | ||
|
||
for (const auto& [addr, expected_acc] : expect.post) | ||
{ | ||
const auto acc = state.find(addr); | ||
if (!expected_acc.exists) | ||
{ | ||
EXPECT_EQ(acc, nullptr) << "account " << addr << " should not exist"; | ||
} | ||
else | ||
{ | ||
ASSERT_NE(acc, nullptr) << "account " << addr << " should exist"; | ||
if (expected_acc.nonce.has_value()) | ||
{ | ||
EXPECT_EQ(acc->nonce, *expected_acc.nonce); | ||
} | ||
if (expected_acc.balance.has_value()) | ||
{ | ||
EXPECT_EQ(acc->balance, *expected_acc.balance) | ||
<< to_string(acc->balance) << " vs " << to_string(*expected_acc.balance); | ||
} | ||
if (expected_acc.code.has_value()) | ||
{ | ||
EXPECT_EQ(acc->code, *expected_acc.code); | ||
} | ||
for (const auto& [key, value] : expected_acc.storage) | ||
{ | ||
EXPECT_EQ(acc->storage[key].current, value); | ||
} | ||
for (const auto& [key, value] : acc->storage) | ||
{ | ||
// Find unexpected storage keys. This will also report entries with value 0. | ||
EXPECT_TRUE(expected_acc.storage.contains(key)) | ||
<< "unexpected storage key " << key << "=" << value.current << " in " << addr; | ||
} | ||
} | ||
} | ||
|
||
for (const auto& [addr, _] : state.get_accounts()) | ||
{ | ||
EXPECT_TRUE(expect.post.contains(addr)) << "unexpected account " << addr; | ||
} | ||
} | ||
} // namespace evmone::test |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,74 @@ | ||
// evmone: Fast Ethereum Virtual Machine implementation | ||
// Copyright 2023 The evmone Authors. | ||
// SPDX-License-Identifier: Apache-2.0 | ||
#pragma once | ||
|
||
#include <evmone/evmone.h> | ||
#include <gtest/gtest.h> | ||
#include <test/state/host.hpp> | ||
|
||
#pragma GCC diagnostic ignored "-Wmissing-field-initializers" | ||
|
||
namespace evmone::test | ||
{ | ||
using namespace evmone; | ||
using namespace evmone::state; | ||
|
||
/// Fixture to defining test cases in form similar to JSON State Tests. | ||
/// | ||
/// It takes the "pre" state and produces "post" state by applying the defined "tx" transaction. | ||
/// Then expectations declared in "except" are checked in the "post" state. | ||
class state_transition : public testing::Test | ||
{ | ||
protected: | ||
/// The default sender address of the test transaction. | ||
/// Private key: 0xa45355879. | ||
static constexpr auto Sender = 0xe100DeB58f38F7fd62d14E37e2Fe9ce019Aa001e_address; | ||
|
||
/// The default destination address of the test transaction. | ||
static constexpr auto To = 0xc0de_address; | ||
|
||
static constexpr auto Coinbase = 0xc014bace_address; | ||
|
||
static inline evmc::VM vm{evmc_create_evmone()}; | ||
|
||
struct ExpectedAccount | ||
{ | ||
bool exists = true; | ||
std::optional<uint64_t> nonce; | ||
std::optional<intx::uint256> balance; | ||
std::optional<bytes> code; | ||
std::unordered_map<bytes32, bytes32> storage; | ||
}; | ||
|
||
struct Expectation | ||
{ | ||
evmc_status_code status = EVMC_SUCCESS; | ||
std::optional<int64_t> gas_used; | ||
|
||
std::unordered_map<address, ExpectedAccount> post; | ||
}; | ||
|
||
|
||
evmc_revision rev = EVMC_SHANGHAI; | ||
BlockInfo block{ | ||
.gas_limit = 1'000'000, | ||
.coinbase = Coinbase, | ||
.base_fee = 999, | ||
}; | ||
Transaction tx{ | ||
.gas_limit = block.gas_limit, | ||
.max_gas_price = block.base_fee + 1, | ||
.max_priority_gas_price = 1, | ||
.sender = Sender, | ||
}; | ||
State pre; | ||
Expectation expect; | ||
|
||
void SetUp() override; | ||
|
||
/// The test runner. | ||
void TearDown() override; | ||
}; | ||
|
||
} // namespace evmone::test |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
// evmone: Fast Ethereum Virtual Machine implementation | ||
// Copyright 2023 The evmone Authors. | ||
// SPDX-License-Identifier: Apache-2.0 | ||
|
||
#include "../utils/bytecode.hpp" | ||
#include "state_transition.hpp" | ||
|
||
using namespace evmc::literals; | ||
using namespace evmone::test; | ||
|
||
TEST_F(state_transition, create2_factory) | ||
{ | ||
static constexpr auto create_address = 0xfd8e7707356349027a32d71eabc7cb0cf9d7cbb4_address; | ||
|
||
const auto factory_code = | ||
calldatacopy(0, 0, calldatasize()) + create2().input(0, calldatasize()); | ||
const auto initcode = mstore8(0, push(0xFE)) + ret(0, 1); | ||
|
||
tx.to = To; | ||
tx.data = initcode; | ||
pre.insert(*tx.to, {.nonce = 1, .code = factory_code}); | ||
|
||
expect.post[*tx.to].nonce = pre.get(*tx.to).nonce + 1; // CREATE caller's nonce must be bumped | ||
expect.post[create_address].code = bytes{0xFE}; | ||
} | ||
|
||
TEST_F(state_transition, create_tx) | ||
{ | ||
static constexpr auto create_address = 0x8ef300b6a6a0b41e4f5d717074d9fd5c605c7285_address; | ||
|
||
tx.data = mstore8(0, push(0xFE)) + ret(0, 1); | ||
|
||
expect.post[create_address].code = bytes{0xFE}; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
// evmone: Fast Ethereum Virtual Machine implementation | ||
// Copyright 2023 The evmone Authors. | ||
// SPDX-License-Identifier: Apache-2.0 | ||
|
||
#include "../utils/bytecode.hpp" | ||
#include "state_transition.hpp" | ||
|
||
using namespace evmc::literals; | ||
using namespace evmone::test; | ||
|
||
TEST_F(state_transition, eof_invalid_initcode) | ||
{ | ||
// TODO: Correction of this address is not verified. | ||
static constexpr auto create_address = 0x864bbda5c698ac34b47a9ea3bd4228802cc5ce3b_address; | ||
|
||
rev = EVMC_CANCUN; | ||
tx.to = To; | ||
pre.insert(*tx.to, | ||
{ | ||
.nonce = 1, | ||
.storage = {{0x01_bytes32, {.current = 0x01_bytes32, .original = 0x01_bytes32}}}, | ||
.code = eof1_bytecode(create() + push(1) + OP_SSTORE + OP_STOP, 3), | ||
}); | ||
|
||
EXPECT_EQ(pre.get(tx.sender).balance, 1'000'000'001); // Fixture sanity check. | ||
|
||
expect.gas_used = 985407; | ||
|
||
expect.post[tx.sender].nonce = pre.get(tx.sender).nonce + 1; | ||
expect.post[tx.sender].balance = | ||
pre.get(tx.sender).balance - | ||
(block.base_fee + tx.max_priority_gas_price) * static_cast<uint64_t>(*expect.gas_used); | ||
expect.post[*tx.to].nonce = pre.get(*tx.to).nonce + 1; // CREATE caller's nonce must be bumped | ||
expect.post[*tx.to].storage[0x01_bytes32] = 0x00_bytes32; // CREATE must fail | ||
expect.post[create_address].exists = false; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters