Skip to content

Commit

Permalink
test: Add some mising State Test export features (#807)
Browse files Browse the repository at this point in the history
Add support for exporting state transition tests:
- with legacy transactions (pre EIP-1559)
- with access list
- support EVMC and common spelling of revisions
  • Loading branch information
chfast authored Feb 28, 2024
2 parents a980168 + 11cfcf6 commit 0de1555
Show file tree
Hide file tree
Showing 6 changed files with 119 additions and 22 deletions.
2 changes: 1 addition & 1 deletion test/integration/statetest/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,7 @@ set_tests_properties(
add_test(
NAME ${PREFIX}/execute_exported_tests
# TODO: Broken exported tests are filtered out.
COMMAND evmone-statetest ${EXPORT_DIR}/state_transition --gtest_filter=-*block.*:*tx.tx_non_existing_sender
COMMAND evmone-statetest ${EXPORT_DIR}/state_transition --gtest_filter=-*block.*
)
set_tests_properties(
${PREFIX}/execute_exported_tests PROPERTIES
Expand Down
59 changes: 56 additions & 3 deletions test/unittests/state_transition.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,22 @@ class TraceCapture

void state_transition::TearDown()
{
// Validation:

if (rev < EVMC_LONDON)
{
ASSERT_EQ(block.base_fee, 0);
ASSERT_EQ(tx.type, state::Transaction::Type::legacy);
}
if (tx.type == state::Transaction::Type::legacy)
{
ASSERT_EQ(tx.max_gas_price, tx.max_priority_gas_price);
}

validate_state(pre, rev);

// Execution:

auto state = pre;
const auto trace = !expect.trace.empty();
auto& selected_vm = trace ? tracing_vm : vm;
Expand Down Expand Up @@ -135,6 +149,23 @@ void state_transition::TearDown()
export_state_test(receipt, state);
}

namespace
{
/// Converts EVM revision to the fork name commonly used in tests.
std::string_view to_test_fork_name(evmc_revision rev) noexcept
{
switch (rev)
{
case EVMC_TANGERINE_WHISTLE:
return "EIP150";
case EVMC_SPURIOUS_DRAGON:
return "EIP158";
default:
return evmc::to_string(rev);
}
}
} // namespace

void state_transition::export_state_test(const TransactionReceipt& receipt, const State& post)
{
json::json j;
Expand All @@ -155,14 +186,36 @@ void state_transition::export_state_test(const TransactionReceipt& receipt, cons
jtx["sender"] = hex0x(tx.sender);
jtx["secretKey"] = hex0x(SenderSecretKey);
jtx["nonce"] = hex0x(tx.nonce);
jtx["maxFeePerGas"] = hex0x(tx.max_gas_price);
jtx["maxPriorityFeePerGas"] = hex0x(tx.max_priority_gas_price);
if (rev < EVMC_LONDON)
{
assert(tx.max_gas_price == tx.max_priority_gas_price);
jtx["gasPrice"] = hex0x(tx.max_gas_price);
}
else
{
jtx["maxFeePerGas"] = hex0x(tx.max_gas_price);
jtx["maxPriorityFeePerGas"] = hex0x(tx.max_priority_gas_price);
}

jtx["data"][0] = hex0x(tx.data);
jtx["gasLimit"][0] = hex0x(tx.gas_limit);
jtx["value"][0] = hex0x(tx.value);

auto& jpost = jt["post"][evmc::to_string(rev)][0];
if (!tx.access_list.empty())
{
auto& ja = jtx["accessLists"][0];
for (const auto& [addr, storage_keys] : tx.access_list)
{
json::json je;
je["address"] = hex0x(addr);
auto& jstorage_keys = je["storageKeys"] = json::json::array();
for (const auto& k : storage_keys)
jstorage_keys.emplace_back(hex0x(k));
ja.emplace_back(std::move(je));
}
}

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));
Expand Down
2 changes: 1 addition & 1 deletion test/unittests/state_transition.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ class state_transition : public ExportableFixture
Transaction tx{
.gas_limit = block.gas_limit,
.max_gas_price = block.base_fee + 1,
.max_priority_gas_price = 1,
.max_priority_gas_price = block.base_fee + 1,
.sender = Sender,
.nonce = 1,
};
Expand Down
3 changes: 1 addition & 2 deletions test/unittests/state_transition_eof_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,7 @@ TEST_F(state_transition, eof_invalid_initcode)

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);
pre.get(tx.sender).balance - tx.max_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;
Expand Down
71 changes: 58 additions & 13 deletions test/unittests/state_transition_tx_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,57 +3,102 @@
// SPDX-License-Identifier: Apache-2.0

#include "state_transition.hpp"
#include <test/utils/bytecode.hpp>

using namespace evmc::literals;
using namespace evmone::test;

TEST_F(state_transition, tx_legacy)
{
rev = EVMC_ISTANBUL;
block.base_fee = 0; // should be 0 before London
tx.to = To;

expect.post.at(Sender).nonce = pre.get(Sender).nonce + 1;
}

TEST_F(state_transition, tx_non_existing_sender)
{
rev = EVMC_BERLIN;
block.base_fee = 0; // should be 0 before London
tx.to = To;
tx.max_gas_price = 0;
tx.max_priority_gas_price = 0;
tx.nonce = 0;
block.base_fee = 0;
pre.get_accounts().erase(Sender);

rev = EVMC_BERLIN;

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

TEST_F(state_transition, invalid_tx_non_existing_sender)
{
rev = EVMC_BERLIN;
block.base_fee = 0; // should be 0 before London
tx.to = To;
tx.max_gas_price = 1;
tx.max_priority_gas_price = 1;
tx.nonce = 0;
block.base_fee = 1;
pre.get_accounts().erase(Sender);

rev = EVMC_BERLIN;

expect.tx_error = INSUFFICIENT_FUNDS;
}

TEST_F(state_transition, blob_tx_insuficient_funds)
TEST_F(state_transition, tx_blob_gas_price)
{
rev = EVMC_CANCUN;
tx.to = To;
tx.gas_limit = 25000;
tx.max_gas_price = 1;
tx.max_gas_price = block.base_fee; // minimal gas price to make it
tx.max_priority_gas_price = 0;
tx.nonce = 1;
tx.type = Transaction::Type::blob;
tx.blob_hashes.emplace_back(
0x0100000000000000000000000000000000000000000000000000000000000000_bytes32);
tx.max_blob_gas_price = 1;
block.base_fee = 1;

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

rev = EVMC_CANCUN;

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

TEST_F(state_transition, empty_coinbase_fee_0_sd)
{
rev = EVMC_SPURIOUS_DRAGON;
block_reward = 0;
block.base_fee = 0; // should be 0 before London
tx.max_gas_price = 0;
tx.max_priority_gas_price = 0;
tx.to = To;
pre.insert(Coinbase, {});
expect.post[To].exists = false;
expect.post[Coinbase].exists = false;
}

TEST_F(state_transition, empty_coinbase_fee_0_tw)
{
rev = EVMC_TANGERINE_WHISTLE;
block_reward = 0;
block.base_fee = 0; // should be 0 before London
tx.max_gas_price = 0;
tx.max_priority_gas_price = 0;
tx.to = To;
pre.insert(Coinbase, {});
expect.post[To].exists = true;
expect.post[Coinbase].balance = 0;
}

TEST_F(state_transition, access_list_storage)
{
tx.to = To;
tx.access_list = {{To, {0x01_bytes32}}};

pre.insert(To,
{.storage = {{0x01_bytes32, {0x01_bytes32, 0x01_bytes32}}}, .code = sstore(2, sload(1))});

expect.post[To].storage[0x01_bytes32] = 0x01_bytes32;
expect.post[To].storage[0x02_bytes32] = 0x01_bytes32;
expect.gas_used = 47506; // Without access list: 45206
}
4 changes: 2 additions & 2 deletions test/utils/utils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,9 @@ evmc_revision to_rev(std::string_view s)
return EVMC_FRONTIER;
if (s == "Homestead")
return EVMC_HOMESTEAD;
if (s == "EIP150")
if (s == "Tangerine Whistle" || s == "EIP150")
return EVMC_TANGERINE_WHISTLE;
if (s == "EIP158")
if (s == "Spurious Dragon" || s == "EIP158")
return EVMC_SPURIOUS_DRAGON;
if (s == "Byzantium")
return EVMC_BYZANTIUM;
Expand Down

0 comments on commit 0de1555

Please sign in to comment.