Skip to content

Commit

Permalink
Support EIP-7702 set code transactions
Browse files Browse the repository at this point in the history
  • Loading branch information
gumb0 committed Aug 1, 2024
1 parent 9696fcf commit 1bab8be
Show file tree
Hide file tree
Showing 4 changed files with 65 additions and 2 deletions.
6 changes: 6 additions & 0 deletions lib/evmone/constants.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,10 @@ constexpr auto MAX_CODE_SIZE = 0x6000;
/// The limit of the size of init codes for contract creation
/// defined by [EIP-3860](https://eips.ethereum.org/EIPS/eip-3860)
constexpr auto MAX_INITCODE_SIZE = 2 * MAX_CODE_SIZE;

/// Prefix of code for delegated accounts
/// defined by [EIP-7702](https://eips.ethereum.org/EIPS/eip-7702)
constexpr uint8_t DELEGATION_MAGIC_BYTES[] = {0xef, 0x01, 0x00};
constexpr bytes_view DELEGATION_MAGIC{DELEGATION_MAGIC_BYTES, std::size(DELEGATION_MAGIC_BYTES)};

} // namespace evmone
2 changes: 2 additions & 0 deletions test/state/errors.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ enum ErrorCode : int
EMPTY_BLOB_HASHES_LIST,
INVALID_BLOB_HASH_VERSION,
BLOB_GAS_LIMIT_EXCEEDED,
CREATE_SET_CODE_TX,
EMPTY_AUTHORIZATION_LIST,
UNKNOWN_ERROR,
};

Expand Down
41 changes: 40 additions & 1 deletion test/state/state.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -56,13 +56,16 @@ int64_t compute_tx_intrinsic_cost(evmc_revision rev, const Transaction& tx) noex
{
static constexpr auto call_tx_cost = 21000;
static constexpr auto create_tx_cost = 53000;
static constexpr auto per_auth_base_cost = 2500;
static constexpr auto initcode_word_cost = 2;
const auto is_create = !tx.to.has_value(); // Covers also EOF creation txs.
const auto auth_list_cost =
static_cast<int64_t>(per_auth_base_cost * tx.authorization_list.size());
const auto initcode_cost =
is_create && rev >= EVMC_SHANGHAI ? initcode_word_cost * num_words(tx.data.size()) : 0;
const auto tx_cost = is_create && rev >= EVMC_HOMESTEAD ? create_tx_cost : call_tx_cost;
return tx_cost + compute_tx_data_cost(rev, tx.data) + compute_access_list_cost(tx.access_list) +
compute_initcode_list_cost(rev, tx.initcodes) + initcode_cost;
compute_initcode_list_cost(rev, tx.initcodes) + auth_list_cost + initcode_cost;
}

evmc_message build_message(
Expand Down Expand Up @@ -283,6 +286,15 @@ std::variant<int64_t, std::error_code> validate_transaction(const Account& sende
return make_error_code(BLOB_GAS_LIMIT_EXCEEDED);
break;

case Transaction::Type::set_code:
if (rev < EVMC_PRAGUE)
return make_error_code(TX_TYPE_NOT_SUPPORTED);
if (!tx.to.has_value())
return make_error_code(CREATE_SET_CODE_TX);
if (tx.authorization_list.empty())
return make_error_code(EMPTY_AUTHORIZATION_LIST);
break;

case Transaction::Type::initcodes:
if (rev < EVMC_OSAKA)
return make_error_code(TX_TYPE_NOT_SUPPORTED);
Expand All @@ -305,6 +317,7 @@ std::variant<int64_t, std::error_code> validate_transaction(const Account& sende
{
case Transaction::Type::blob:
case Transaction::Type::initcodes:
case Transaction::Type::set_code:
case Transaction::Type::eip1559:
if (rev < EVMC_LONDON)
return make_error_code(TX_TYPE_NOT_SUPPORTED);
Expand All @@ -329,6 +342,7 @@ std::variant<int64_t, std::error_code> validate_transaction(const Account& sende
if (tx.max_gas_price < block.base_fee)
return make_error_code(FEE_CAP_LESS_THEN_BLOCKS);

// TODO this is relaxed for 7702
if (!sender_acc.code.empty())
return make_error_code(SENDER_NOT_EOA); // Origin must not be a contract (EIP-3607).

Expand Down Expand Up @@ -442,6 +456,11 @@ void finalize(State& state, evmc_revision rev, const address& coinbase,
delete_empty_accounts(state);
}

constexpr bool is_code_delegated(bytes_view code) noexcept
{
return code.starts_with(DELEGATION_MAGIC);
}

std::variant<TransactionReceipt, std::error_code> transition(State& state, const BlockInfo& block,
const Transaction& tx, evmc_revision rev, evmc::VM& vm, int64_t block_gas_left,
int64_t blob_gas_left)
Expand All @@ -458,6 +477,26 @@ std::variant<TransactionReceipt, std::error_code> transition(State& state, const
if (holds_alternative<std::error_code>(validation_result))
return get<std::error_code>(validation_result);

for (const auto& auth : tx.authorization_list)
{
// TODO check chain_id

auto& acc = state.get_or_insert(auth.signer);
if (!acc.code.empty() && !is_code_delegated(acc.code))
continue;

if (acc.nonce != auth.nonce)
continue;

acc.code.reserve(std::size(DELEGATION_MAGIC) + std::size(auth.addr.bytes));
acc.code = DELEGATION_MAGIC;
acc.code += auth.addr;

++acc.nonce;

acc.access_status = EVMC_ACCESS_WARM;
}

// Once the transaction is valid, create new sender account.
// The account won't be empty because its nonce will be bumped.
auto& sender_acc = (sender_ptr != nullptr) ? *sender_ptr : state.insert(tx.sender);
Expand Down
18 changes: 17 additions & 1 deletion test/state/state.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,17 @@ struct BlockInfo

using AccessList = std::vector<std::pair<address, std::vector<bytes32>>>;

struct Authorization
{
uint64_t chain_id = 0;
address addr;
uint64_t nonce = 0;
address signer;
intx::uint256 r;
intx::uint256 s;
bool y_parity = false;
};

struct Transaction
{
/// The type of the transaction.
Expand All @@ -197,8 +208,12 @@ struct Transaction
/// Introduced by EIP-4844 https://eips.ethereum.org/EIPS/eip-4844.
blob = 3,

/// The typed set code transaction (with authorization list).
/// Introduced by EIP-7702 https://eips.ethereum.org/EIPS/eip-7702.
set_code = 4,

/// The typed transaction with initcode list.
initcodes = 4,
initcodes = 5,
};

/// Returns amount of blob gas used by this transaction
Expand All @@ -224,6 +239,7 @@ struct Transaction
intx::uint256 r;
intx::uint256 s;
uint8_t v = 0;
std::vector<Authorization> authorization_list;
std::vector<bytes> initcodes;
};

Expand Down

0 comments on commit 1bab8be

Please sign in to comment.