Skip to content

Commit

Permalink
state: Add error code to result of tx validation
Browse files Browse the repository at this point in the history
  • Loading branch information
rodiazet authored and chfast committed Feb 7, 2023
1 parent 670fa8c commit d5ca7df
Show file tree
Hide file tree
Showing 3 changed files with 41 additions and 21 deletions.
43 changes: 26 additions & 17 deletions test/state/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 "state.hpp"
#include "errors.hpp"
#include "host.hpp"
#include <evmone/evmone.h>
#include <evmone/execution_state.hpp>
Expand Down Expand Up @@ -53,42 +54,47 @@ int64_t compute_tx_intrinsic_cost(evmc_revision rev, const Transaction& tx) noex
/// Validates transaction and computes its execution gas limit (the amount of gas provided to EVM).
/// @return Non-negative execution gas limit for valid transaction
/// or negative value for invalid transaction.
int64_t validate_transaction(const Account& sender_acc, const BlockInfo& block,
const Transaction& tx, evmc_revision rev) noexcept
std::variant<int64_t, std::error_code> validate_transaction(const Account& sender_acc,
const BlockInfo& block, const Transaction& tx, evmc_revision rev) noexcept
{
if (rev < EVMC_LONDON && tx.kind == Transaction::Kind::eip1559)
return -1;
return make_error_code(TX_TYPE_NOT_SUPPORTED);

if (rev < EVMC_BERLIN && !tx.access_list.empty())
return -1;
return make_error_code(TX_TYPE_NOT_SUPPORTED);

if (tx.max_priority_gas_price > tx.max_gas_price)
return -1; // Priority gas price is too high.
return make_error_code(TIP_GT_FEE_CAP); // Priority gas price is too high.

if (tx.gas_limit > block.gas_limit)
return -1;
return make_error_code(GAS_LIMIT_REACHED);

if (rev >= EVMC_LONDON && tx.max_gas_price < block.base_fee)
return -1;
return make_error_code(FEE_CAP_LESS_THEN_BLOCKS);

if (!sender_acc.code.empty())
return -1; // Origin must not be a contract (EIP-3607).
return make_error_code(SENDER_NOT_EOA); // Origin must not be a contract (EIP-3607).

if (sender_acc.nonce == Account::NonceMax)
return -1;
return make_error_code(NONCE_HAS_MAX_VALUE);

// initcode size is limited by EIP-3860.
if (rev >= EVMC_SHANGHAI && !tx.to.has_value() && tx.data.size() > max_initcode_size)
return -1; // initcode size is limited by EIP-3860.
return make_error_code(INIT_CODE_SIZE_LIMIT_EXCEEDED);

// Compute and check if sender has enough balance for the theoretical maximum transaction cost.
// Note this is different from tx_max_cost computed with effective gas price later.
// The computation cannot overflow if done with 512-bit precision.
if (const auto tx_cost_limit_512 =
umul(intx::uint256{tx.gas_limit}, tx.max_gas_price) + tx.value;
sender_acc.balance < tx_cost_limit_512)
return -1;
return make_error_code(INSUFFICIENT_FUNDS);

return tx.gas_limit - compute_tx_intrinsic_cost(rev, tx);
const auto intrinsic_cost = compute_tx_intrinsic_cost(rev, tx);
if (intrinsic_cost > tx.gas_limit)
return make_error_code(INTRINSIC_GAS_TOO_LOW);

return tx.gas_limit - intrinsic_cost;
}

evmc_message build_message(const Transaction& tx, int64_t execution_gas_limit) noexcept
Expand All @@ -110,19 +116,22 @@ evmc_message build_message(const Transaction& tx, int64_t execution_gas_limit) n
}
} // namespace

std::optional<std::vector<Log>> transition(
std::variant<TransactionReceipt, std::error_code> transition(
State& state, const BlockInfo& block, const Transaction& tx, evmc_revision rev, evmc::VM& vm)
{
auto& sender_acc = state.get(tx.sender);
const auto execution_gas_limit = validate_transaction(sender_acc, block, tx, rev);
if (execution_gas_limit < 0)
const auto validation_result = validate_transaction(sender_acc, block, tx, rev);

if (holds_alternative<std::error_code>(validation_result))
{
// Pre EIP-158 coinbase has to be touched also for invalid tx.
if (rev <= EVMC_TANGERINE_WHISTLE)
state.touch(block.coinbase);
return {};
return get<std::error_code>(validation_result);
}

const auto execution_gas_limit = get<int64_t>(validation_result);

const auto base_fee = (rev >= EVMC_LONDON) ? block.base_fee : 0;
assert(tx.max_gas_price >= base_fee); // Checked at the front.
assert(tx.max_gas_price >= tx.max_priority_gas_price); // Checked at the front.
Expand Down Expand Up @@ -167,6 +176,6 @@ std::optional<std::vector<Log>> transition(
return acc.destructed || (rev >= EVMC_SPURIOUS_DRAGON && acc.erasable && acc.is_empty());
});

return host.take_logs();
return TransactionReceipt{gas_used, host.take_logs()};
}
} // namespace evmone::state
8 changes: 7 additions & 1 deletion test/state/state.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
#include "hash_utils.hpp"
#include <cassert>
#include <optional>
#include <variant>
#include <vector>

namespace evmone::state
Expand Down Expand Up @@ -100,8 +101,13 @@ struct Log
std::vector<hash256> topics;
};

struct TransactionReceipt
{
int64_t gas_used = 0;
std::vector<Log> logs;
};

[[nodiscard]] std::optional<std::vector<Log>> transition(
[[nodiscard]] std::variant<TransactionReceipt, std::error_code> transition(
State& state, const BlockInfo& block, const Transaction& tx, evmc_revision rev, evmc::VM& vm);

} // namespace evmone::state
11 changes: 8 additions & 3 deletions test/statetest/statetest_runner.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -34,11 +34,16 @@ void run_state_test(const StateTransitionTest& test, evmc::VM& vm)
const auto tx = test.multi_tx.get(expected.indexes);
auto state = test.pre_state;

const auto tx_logs = state::transition(state, test.block, tx, rev, vm);
if (tx_logs.has_value())
EXPECT_EQ(keccak256(rlp::encode(*tx_logs)), expected.logs_hash);
const auto res = state::transition(state, test.block, tx, rev, vm);
if (holds_alternative<state::TransactionReceipt>(res))
{
EXPECT_EQ(keccak256(rlp::encode(get<state::TransactionReceipt>(res).logs)),
expected.logs_hash);
}
else
{
EXPECT_TRUE(expected.exception);
}

EXPECT_EQ(state::mpt_hash(state.get_accounts()), expected.state_hash);
}
Expand Down

0 comments on commit d5ca7df

Please sign in to comment.