Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

state: Refactor compute_new_account_address() #575

Merged
merged 2 commits into from
Feb 27, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 13 additions & 12 deletions test/state/host.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -110,27 +110,26 @@ bool Host::selfdestruct(const address& addr, const address& beneficiary) noexcep
return !std::exchange(acc.destructed, true);
}

static address compute_new_address(const evmc_message& msg, uint64_t sender_nonce) noexcept
address compute_new_account_address(const address& sender, uint64_t sender_nonce,
const std::optional<bytes32>& salt, bytes_view init_code) noexcept
{
hash256 addr_base_hash;
if (msg.kind == EVMC_CREATE)
if (!salt.has_value()) // CREATE
{
// TODO: Compute CREATE address without using RLP library.
const auto rlp_list = rlp::encode_tuple(address{msg.sender}, sender_nonce);
const auto rlp_list = rlp::encode_tuple(sender, sender_nonce);
addr_base_hash = keccak256(rlp_list);
}
else
else // CREATE2
{
assert(msg.kind == EVMC_CREATE2);
const auto init_code_hash = keccak256({msg.input_data, msg.input_size});
uint8_t buffer[1 + sizeof(msg.sender) + sizeof(msg.create2_salt) + sizeof(init_code_hash)];
const auto init_code_hash = keccak256(init_code);
uint8_t buffer[1 + sizeof(sender) + sizeof(*salt) + sizeof(init_code_hash)];
static_assert(std::size(buffer) == 85);
buffer[0] = 0xff;
std::copy_n(msg.sender.bytes, sizeof(msg.sender), &buffer[1]);
std::copy_n(
msg.create2_salt.bytes, sizeof(msg.create2_salt), &buffer[1 + sizeof(msg.sender)]);
std::copy_n(sender.bytes, sizeof(sender), &buffer[1]);
std::copy_n(salt->bytes, sizeof(salt->bytes), &buffer[1 + sizeof(sender)]);
std::copy_n(init_code_hash.bytes, sizeof(init_code_hash),
&buffer[1 + sizeof(msg.sender) + sizeof(msg.create2_salt)]);
&buffer[1 + sizeof(sender) + sizeof(salt->bytes)]);
addr_base_hash = keccak256({buffer, std::size(buffer)});
}
evmc_address new_addr{};
Expand All @@ -156,7 +155,9 @@ std::optional<evmc_message> Host::prepare_message(evmc_message msg)
// Compute and fill create address.
assert(msg.recipient == address{});
assert(msg.code_address == address{});
msg.recipient = compute_new_address(msg, sender_nonce);
msg.recipient = compute_new_account_address(msg.sender, sender_nonce,
(msg.kind == EVMC_CREATE2) ? std::optional{msg.create2_salt} : std::nullopt,
{msg.input_data, msg.input_size});

// By EIP-2929, the access to new created address is never reverted.
access_account(msg.recipient);
Expand Down
13 changes: 13 additions & 0 deletions test/state/host.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,19 @@ using evmc::uint256be;
inline constexpr size_t max_code_size = 0x6000;
inline constexpr size_t max_initcode_size = 2 * max_code_size;

/// Computes the address of to-be-created contract.
///
/// Computes the new account address for the contract creation context
/// as defined by 𝐀𝐃𝐃𝐑 in Yellow Paper, 7. Contract Creation, (86).
///
/// @param sender The address of the message sender. YP: 𝑠.
/// @param sender_nonce The sender's nonce before the increase. YP: 𝑛.
/// @param salt The salt for CREATE2. If null, CREATE address is computed. YP: ζ.
/// @param init_code The contract creation init code. Value only affects CREATE2. YP: 𝐢.
/// @return The computed address for CREATE or CREATE2 scheme.
address compute_new_account_address(const address& sender, uint64_t sender_nonce,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not have two overloads:

address compute_new_account_address(const evmc_message& msg, uint64_t sender_nonce) noexcept;
address compute_new_account_address(const address& sender, bytes32& salt, bytes_view init_code) noexcept;

The sender_nonce is irrelevant for create2 and this separation makes it clear which values are used in which.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Mostly so that a user don't need to know which overload to select. Now it only needs to provide arguments (sometimes redundantly) and will get the right answer. Non-functional change from what we had.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it still makes sense to separate and I'd aim for that later, but it is debatable.

const std::optional<bytes32>& salt, bytes_view init_code) noexcept;

class Host : public evmc::Host
{
evmc_revision m_rev;
Expand Down
1 change: 1 addition & 0 deletions test/unittests/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ add_executable(evmone-unittests
state_bloom_filter_test.cpp
state_mpt_hash_test.cpp
state_mpt_test.cpp
state_new_account_address_test.cpp
state_rlp_test.cpp
statetest_loader_test.cpp
statetest_loader_tx_test.cpp
Expand Down
59 changes: 59 additions & 0 deletions test/unittests/state_new_account_address_test.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
// evmone: Fast Ethereum Virtual Machine implementation
// Copyright 2023 The evmone Authors.
// SPDX-License-Identifier: Apache-2.0

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

using namespace evmc;
using namespace evmc::literals;
inline constexpr auto addr = evmone::state::compute_new_account_address;

inline constexpr uint64_t nonces[] = {0, 1, 0x80, 0xffffffffffffffff};
inline constexpr address senders[] = {
0x00_address, 0x01_address, 0x8000000000000000000000000000000000000000_address};
inline const bytes init_codes[] = {bytes{}, bytes{0xFE}};
inline constexpr bytes32 salts[] = {
0x00_bytes32, 0xe75fb554e433e03763a1560646ee22dcb74e5274b34c5ad644e7c0f619a7e1d0_bytes32};

TEST(state_new_account_address, create)
{
for (const auto& ic : init_codes) // Init-code doesn't affect CREATE.
{
auto s = senders[0];
EXPECT_EQ(addr(s, nonces[0], {}, ic), 0xbd770416a3345f91e4b34576cb804a576fa48eb1_address);
EXPECT_EQ(addr(s, nonces[3], {}, ic), 0x1262d73ea59d3a661bf8751d16cf1a5377149e75_address);

s = senders[1];
EXPECT_EQ(addr(s, nonces[0], {}, ic), 0x522b3294e6d06aa25ad0f1b8891242e335d3b459_address);
EXPECT_EQ(addr(s, nonces[1], {}, ic), 0x535b3d7a252fa034ed71f0c53ec0c6f784cb64e1_address);
EXPECT_EQ(addr(s, nonces[2], {}, ic), 0x09c1ef8f55c61b94e8b92a55d0891d408a991e18_address);
EXPECT_EQ(addr(s, nonces[3], {}, ic), 0x001567239734aeadea21023c2a7c0d9bb9ae4af9_address);

s = senders[2];
EXPECT_EQ(addr(s, nonces[0], {}, ic), 0x3cb1045aee4a06f522ea2b69e4f3d21ed3c135d1_address);
EXPECT_EQ(addr(s, nonces[3], {}, ic), 0xe1aa03e4a7b6991d69aff8ece53ceafdf347082e_address);

const auto beacon_deposit_address =
addr(0xb20a608c624Ca5003905aA834De7156C68b2E1d0_address, 0, {}, ic);
EXPECT_EQ(beacon_deposit_address, 0x00000000219ab540356cbb839cbe05303d7705fa_address);
}
}

TEST(state_new_account_address, create2)
{
for (const auto n : nonces) // Nonce doesn't affect CREATE2.
{
EXPECT_EQ(addr(senders[0], n, salts[0], init_codes[0]),
0xe33c0c7f7df4809055c3eba6c09cfe4baf1bd9e0_address);

EXPECT_EQ(addr(senders[2], n, salts[0], init_codes[1]),
0x3517dea701ed18fc4a99dc111c5946e1f1541dad_address);

EXPECT_EQ(addr(senders[1], n, salts[1], init_codes[0]),
0x7be1c1cb3b8298f21c56add66defce03e2d32604_address);

EXPECT_EQ(addr(senders[2], n, salts[1], init_codes[1]),
0x8f459e65c8f00a9c0c0493de7b0c61c3c27f7384_address);
}
}