Skip to content

Commit

Permalink
host: Split compute_new_account_address() (#804)
Browse files Browse the repository at this point in the history
Split the function `compute_new_account_address()` into two:
- `compute_create_address()`
- `compute_create2_address()`.

Use these in the state transition tests to predict the created accounts.

Fixes #782.
  • Loading branch information
chfast authored Feb 1, 2024
1 parent 17e1dcb commit 1580602
Show file tree
Hide file tree
Showing 5 changed files with 80 additions and 82 deletions.
55 changes: 28 additions & 27 deletions test/state/host.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -140,31 +140,31 @@ bool Host::selfdestruct(const address& addr, const address& beneficiary) noexcep
return false;
}

address compute_new_account_address(const address& sender, uint64_t sender_nonce,
const std::optional<bytes32>& salt, bytes_view init_code) noexcept
address compute_create_address(const address& sender, uint64_t sender_nonce) noexcept
{
hash256 addr_base_hash;
if (!salt.has_value()) // CREATE
{
// TODO: Compute CREATE address without using RLP library.
const auto rlp_list = rlp::encode_tuple(sender, sender_nonce);
addr_base_hash = keccak256(rlp_list);
}
else // CREATE2
{
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(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(sender) + sizeof(salt->bytes)]);
addr_base_hash = keccak256({buffer, std::size(buffer)});
}
evmc_address new_addr{};
std::copy_n(&addr_base_hash.bytes[12], sizeof(new_addr), new_addr.bytes);
return new_addr;
// TODO: Compute CREATE address without using RLP library.
const auto rlp_list = rlp::encode_tuple(sender, sender_nonce);
const auto base_hash = keccak256(rlp_list);
address addr;
std::copy_n(&base_hash.bytes[sizeof(base_hash) - sizeof(addr)], sizeof(addr), addr.bytes);
return addr;
}

address compute_create2_address(
const address& sender, const bytes32& salt, bytes_view init_code) noexcept
{
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);
auto it = std::begin(buffer);
*it++ = 0xff;
it = std::copy_n(sender.bytes, sizeof(sender), it);
it = std::copy_n(salt.bytes, sizeof(salt), it);
std::copy_n(init_code_hash.bytes, sizeof(init_code_hash), it);
const auto base_hash = keccak256({buffer, std::size(buffer)});
address addr;
std::copy_n(&base_hash.bytes[sizeof(base_hash) - sizeof(addr)], sizeof(addr), addr.bytes);
return addr;
}

std::optional<evmc_message> Host::prepare_message(evmc_message msg)
Expand All @@ -187,9 +187,10 @@ std::optional<evmc_message> Host::prepare_message(evmc_message msg)
// Compute and set the address of the account being created.
assert(msg.recipient == address{});
assert(msg.code_address == address{});
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});
msg.recipient = (msg.kind == EVMC_CREATE) ?
compute_create_address(msg.sender, sender_nonce) :
compute_create2_address(
msg.sender, msg.create2_salt, {msg.input_data, msg.input_size});

// By EIP-2929, the access to new created address is never reverted.
access_account(msg.recipient);
Expand Down
26 changes: 18 additions & 8 deletions test/state/host.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,18 +15,28 @@ 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 address of to-be-created contract with the CREATE scheme.
///
/// Computes the new account address for the contract creation context
/// as defined by 𝐀𝐃𝐃𝐑 in Yellow Paper, 7. Contract Creation, (86).
/// Computes the new account address for the contract creation context of the CREATE instruction
/// or a create transaction.
/// This is defined by 𝐀𝐃𝐃𝐑 in Yellow Paper, 7. Contract Creation, (88-90), the case for ζ = ∅.
///
/// @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,
const std::optional<bytes32>& salt, bytes_view init_code) noexcept;
/// @return The address computed with the CREATE scheme.
[[nodiscard]] address compute_create_address(const address& sender, uint64_t sender_nonce) noexcept;

/// Computes the address of to-be-created contract with the CREATE2 scheme.
///
/// Computes the new account address for the contract creation context of the CREATE2 instruction.
/// This is defined by 𝐀𝐃𝐃𝐑 in Yellow Paper, 7. Contract Creation, (88-90), the case for ζ ≠ ∅.
///
/// @param sender The address of the message sender. YP: 𝑠.
/// @param salt The salt. YP: ζ.
/// @param init_code The contract creation init code. YP: 𝐢.
/// @return The address computed with the CREATE2 scheme.
[[nodiscard]] address compute_create2_address(
const address& sender, const bytes32& salt, bytes_view init_code) noexcept;

class Host : public evmc::Host
{
Expand Down
69 changes: 31 additions & 38 deletions test/unittests/state_new_account_address_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,53 +7,46 @@

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);
}
}
constexpr auto addr = evmone::state::compute_create_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);
auto s = senders[0];
EXPECT_EQ(addr(s, nonces[0]), 0xbd770416a3345f91e4b34576cb804a576fa48eb1_address);
EXPECT_EQ(addr(s, nonces[3]), 0x1262d73ea59d3a661bf8751d16cf1a5377149e75_address);

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

EXPECT_EQ(addr(senders[2], n, salts[0], init_codes[1]),
0x3517dea701ed18fc4a99dc111c5946e1f1541dad_address);
s = senders[2];
EXPECT_EQ(addr(s, nonces[0]), 0x3cb1045aee4a06f522ea2b69e4f3d21ed3c135d1_address);
EXPECT_EQ(addr(s, nonces[3]), 0xe1aa03e4a7b6991d69aff8ece53ceafdf347082e_address);

EXPECT_EQ(addr(senders[1], n, salts[1], init_codes[0]),
0x7be1c1cb3b8298f21c56add66defce03e2d32604_address);
const auto beacon_roots1 = addr(0xb20a608c624Ca5003905aA834De7156C68b2E1d0_address, 0);
EXPECT_EQ(beacon_roots1, 0x00000000219ab540356cbb839cbe05303d7705fa_address);

EXPECT_EQ(addr(senders[2], n, salts[1], init_codes[1]),
0x8f459e65c8f00a9c0c0493de7b0c61c3c27f7384_address);
}
const auto beacon_roots2 = addr(0x0B799C86a49DEeb90402691F1041aa3AF2d3C875_address, 0);
EXPECT_EQ(beacon_roots2, 0x000F3df6D732807Ef1319fB7B8bB8522d0Beac02_address);
}

TEST(state_new_account_address, create2)
{
constexpr auto addr = evmone::state::compute_create2_address;
constexpr auto z0 = 0x00_bytes32;
constexpr auto z1 = 0xe75fb554e433e03763a1560646ee22dcb74e5274b34c5ad644e7c0f619a7e1d0_bytes32;
const auto i0 = bytes{};
const auto i1 = bytes{0xFE};

EXPECT_EQ(addr(senders[0], z0, i0), 0xe33c0c7f7df4809055c3eba6c09cfe4baf1bd9e0_address);
EXPECT_EQ(addr(senders[2], z0, i1), 0x3517dea701ed18fc4a99dc111c5946e1f1541dad_address);
EXPECT_EQ(addr(senders[1], z1, i0), 0x7be1c1cb3b8298f21c56add66defce03e2d32604_address);
EXPECT_EQ(addr(senders[2], z1, i1), 0x8f459e65c8f00a9c0c0493de7b0c61c3c27f7384_address);
}
10 changes: 2 additions & 8 deletions test/unittests/state_transition_create_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,6 @@ using namespace evmone::test;

TEST_F(state_transition, create2_factory)
{
static constexpr auto create_address = 0xfd8e7707356349027a32d71eabc7cb0cf9d7cbb4_address;

const auto factory_code =
calldatacopy(0, 0, calldatasize()) + create2().input(0, calldatasize());
const auto initcode = mstore8(0, push(0xFE)) + ret(0, 1);
Expand All @@ -20,27 +18,23 @@ TEST_F(state_transition, create2_factory)
tx.data = initcode;
pre.insert(*tx.to, {.nonce = 1, .code = factory_code});

const auto create_address = compute_create2_address(*tx.to, {}, initcode);
expect.post[*tx.to].nonce = pre.get(*tx.to).nonce + 1; // CREATE caller's nonce must be bumped
expect.post[create_address].code = bytes{0xFE};
}

TEST_F(state_transition, create_tx)
{
static constexpr auto create_address = 0x3442a1dec1e72f337007125aa67221498cdd759d_address;

tx.data = mstore8(0, push(0xFE)) + ret(0, 1);

const auto create_address = compute_create_address(Sender, pre.get(Sender).nonce);
expect.post[create_address].code = bytes{0xFE};
}

TEST_F(state_transition, create2_max_nonce)
{
// The address to be created by CREATE2 of the "To" sender and empty initcode.
static constexpr auto create_address = 0x36fd63ce1cb5ee2993f19d1fae4e84d52f6f1595_address;

tx.to = To;
pre.insert(*tx.to, {.nonce = ~uint64_t{0}, .code = create2()});

expect.post[*tx.to].nonce = pre.get(*tx.to).nonce; // Nonce is unchanged.
expect.post[create_address].exists = false;
}
2 changes: 1 addition & 1 deletion test/unittests/tracing_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -295,7 +295,7 @@ TEST_F(tracing, trace_eof)
)");
}

TEST_F(tracing, trace_create_intrcution)
TEST_F(tracing, trace_create_instruction)
{
using namespace intx;
using evmc::operator""_address;
Expand Down

0 comments on commit 1580602

Please sign in to comment.