Skip to content

Commit

Permalink
Blockchain tests support: Add nonce value verification (#685)
Browse files Browse the repository at this point in the history
  • Loading branch information
rodiazet authored Aug 18, 2023
2 parents 1d0c994 + 39419a7 commit 7cef356
Show file tree
Hide file tree
Showing 8 changed files with 87 additions and 20 deletions.
6 changes: 6 additions & 0 deletions test/state/errors.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ enum ErrorCode : int
TX_TYPE_NOT_SUPPORTED,
INSUFFICIENT_FUNDS,
NONCE_HAS_MAX_VALUE,
NONCE_TOO_HIGH,
NONCE_TOO_LOW,
TIP_GT_FEE_CAP,
FEE_CAP_LESS_THEN_BLOCKS,
GAS_LIMIT_REACHED,
Expand Down Expand Up @@ -45,6 +47,10 @@ inline const std::error_category& evmone_category() noexcept
return "insufficient funds for gas * price + value";
case NONCE_HAS_MAX_VALUE:
return "nonce has max value:";
case NONCE_TOO_HIGH:
return "nonce too high";
case NONCE_TOO_LOW:
return "nonce too low";
case TIP_GT_FEE_CAP:
return "max priority fee per gas higher than max fee per gas";
case FEE_CAP_LESS_THEN_BLOCKS:
Expand Down
44 changes: 25 additions & 19 deletions test/state/state.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,25 @@ int64_t compute_tx_intrinsic_cost(evmc_revision rev, const Transaction& tx) noex
initcode_cost;
}

evmc_message build_message(const Transaction& tx, int64_t execution_gas_limit) noexcept
{
const auto recipient = tx.to.has_value() ? *tx.to : evmc::address{};
return {
tx.to.has_value() ? EVMC_CALL : EVMC_CREATE,
0,
0,
execution_gas_limit,
recipient,
tx.sender,
tx.data.data(),
tx.data.size(),
intx::be::store<evmc::uint256be>(tx.value),
{},
recipient,
};
}
} // namespace

/// Validates transaction and computes its execution gas limit (the amount of gas provided to EVM).
/// @return Execution gas limit or transaction validation error.
std::variant<int64_t, std::error_code> validate_transaction(const Account& sender_acc,
Expand Down Expand Up @@ -90,6 +109,12 @@ std::variant<int64_t, std::error_code> validate_transaction(const Account& sende
if (sender_acc.nonce == Account::NonceMax)
return make_error_code(NONCE_HAS_MAX_VALUE);

if (sender_acc.nonce < tx.nonce)
return make_error_code(NONCE_TOO_HIGH);

if (sender_acc.nonce > tx.nonce)
return make_error_code(NONCE_TOO_LOW);

// initcode size is limited by EIP-3860.
if (rev >= EVMC_SHANGHAI && !tx.to.has_value() && tx.data.size() > max_initcode_size)
return make_error_code(INIT_CODE_SIZE_LIMIT_EXCEEDED);
Expand All @@ -109,25 +134,6 @@ std::variant<int64_t, std::error_code> validate_transaction(const Account& sende
return execution_gas_limit;
}

evmc_message build_message(const Transaction& tx, int64_t execution_gas_limit) noexcept
{
const auto recipient = tx.to.has_value() ? *tx.to : evmc::address{};
return {
tx.to.has_value() ? EVMC_CALL : EVMC_CREATE,
0,
0,
execution_gas_limit,
recipient,
tx.sender,
tx.data.data(),
tx.data.size(),
intx::be::store<evmc::uint256be>(tx.value),
{},
recipient,
};
}
} // namespace

void finalize(State& state, evmc_revision rev, const address& coinbase,
std::optional<uint64_t> block_reward, std::span<Withdrawal> withdrawals)
{
Expand Down
4 changes: 4 additions & 0 deletions test/state/state.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,10 @@ void finalize(State& state, evmc_revision rev, const address& coinbase,
const BlockInfo& block, const Transaction& tx, evmc_revision rev, evmc::VM& vm,
int64_t block_gas_left);

std::variant<int64_t, std::error_code> validate_transaction(const Account& sender_acc,
const BlockInfo& block, const Transaction& tx, evmc_revision rev,
int64_t block_gas_left) noexcept;

/// Defines how to RLP-encode a Transaction.
[[nodiscard]] bytes rlp_encode(const Transaction& tx);

Expand Down
1 change: 1 addition & 0 deletions test/statetest/statetest_loader.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -250,6 +250,7 @@ evmc_revision to_rev(std::string_view s)
static void from_json_tx_common(const json::json& j, state::Transaction& o)
{
o.sender = from_json<evmc::address>(j.at("sender"));
o.nonce = from_json<uint64_t>(j.at("nonce"));

if (const auto to_it = j.find("to"); to_it != j.end() && !to_it->get<std::string>().empty())
o.to = from_json<evmc::address>(*to_it);
Expand Down
1 change: 1 addition & 0 deletions test/unittests/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ target_sources(
state_transition_block_test.cpp
state_transition_create_test.cpp
state_transition_eof_test.cpp
state_tx_test.cpp
statetest_loader_block_info_test.cpp
statetest_loader_test.cpp
statetest_loader_tx_test.cpp
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 @@ -61,6 +61,7 @@ class state_transition : public testing::Test
.max_gas_price = block.base_fee + 1,
.max_priority_gas_price = 1,
.sender = Sender,
.nonce = 1,
};
State pre;
Expectation expect;
Expand Down
46 changes: 46 additions & 0 deletions test/unittests/state_tx_test.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
// evmone: Fast Ethereum Virtual Machine implementation
// Copyright 2023 The evmone Authors.
// SPDX-License-Identifier: Apache-2.0

#include <gtest/gtest.h>
#include <test/state/errors.hpp>
#include <test/state/state.hpp>

using namespace evmone::state;

TEST(state_tx, validate_nonce)
{
using evmc::operator""_address;

const BlockInfo bi{.gas_limit = 0x989680,
.coinbase = 0x01_address,
.prev_randao = {},
.base_fee = 0x0a,
.withdrawals = {}};
const Account acc{.nonce = 1, .balance = 0xe8d4a51000};
Transaction tx{
.data = {},
.gas_limit = 60000,
.max_gas_price = bi.base_fee,
.max_priority_gas_price = 0,
.sender = 0x02_address,
.to = {},
.value = 0,
.access_list = {},
.nonce = 1,
.r = 0,
.s = 0,
};
ASSERT_FALSE(
holds_alternative<std::error_code>(validate_transaction(acc, bi, tx, EVMC_BERLIN, 60000)));

tx.nonce = 0;
EXPECT_EQ(
std::get<std::error_code>(validate_transaction(acc, bi, tx, EVMC_BERLIN, 60000)).message(),
"nonce too low");

tx.nonce = 2;
EXPECT_EQ(
std::get<std::error_code>(validate_transaction(acc, bi, tx, EVMC_BERLIN, 60000)).message(),
"nonce too high");
}
4 changes: 3 additions & 1 deletion test/unittests/statetest_loader_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,8 @@ TEST(statetest_loader, load_minimal_test)
"to": "",
"data": null,
"gasLimit": "0",
"value": null
"value": null,
"nonce" : "0"
},
"post": {},
"env": {
Expand Down Expand Up @@ -110,6 +111,7 @@ TEST(statetest_loader, load_minimal_test)
EXPECT_EQ(st.multi_tx.sender, address{});
EXPECT_EQ(st.multi_tx.to, std::nullopt);
EXPECT_EQ(st.multi_tx.value, 0);
EXPECT_EQ(st.multi_tx.nonce, 0);
EXPECT_EQ(st.multi_tx.access_list.size(), 0);
EXPECT_EQ(st.multi_tx.chain_id, 0);
EXPECT_EQ(st.multi_tx.nonce, 0);
Expand Down

0 comments on commit 7cef356

Please sign in to comment.