Skip to content

Commit

Permalink
Support withdrawals in t8n
Browse files Browse the repository at this point in the history
Co-authored-by: Alex Beregszaszi <alex@rtfs.hu>
  • Loading branch information
rodiazet and axic committed Apr 21, 2023
1 parent 4211942 commit f062020
Show file tree
Hide file tree
Showing 9 changed files with 95 additions and 9 deletions.
7 changes: 5 additions & 2 deletions test/state/state.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -117,8 +117,8 @@ evmc_message build_message(const Transaction& tx, int64_t execution_gas_limit) n
}
} // namespace

void finalize(
State& state, evmc_revision rev, const address& coinbase, std::optional<uint64_t> block_reward)
void finalize(State& state, evmc_revision rev, const address& coinbase,
std::optional<uint64_t> block_reward, std::span<Withdrawal> withdrawals)
{
if (block_reward.has_value())
state.touch(coinbase).balance += *block_reward;
Expand All @@ -131,6 +131,9 @@ void finalize(
return acc.erasable && acc.is_empty();
});
}

for (const auto& withdrawal : withdrawals)
state.touch(withdrawal.recipient).balance += withdrawal.get_amount();
}

std::variant<TransactionReceipt, std::error_code> transition(
Expand Down
20 changes: 17 additions & 3 deletions test/state/state.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,18 @@ class State
[[nodiscard]] const auto& get_accounts() const noexcept { return m_accounts; }
};

struct Withdrawal
{
address recipient;
uint64_t amount_in_gwei = 0; ///< The amount is denominated in gwei.

/// Returns withdrawal amount in wei.
[[nodiscard]] intx::uint256 get_amount() const noexcept
{
return intx::uint256{amount_in_gwei} * 1'000'000'000;
}
};

struct BlockInfo
{
int64_t number = 0;
Expand All @@ -74,6 +86,7 @@ struct BlockInfo
address coinbase;
bytes32 prev_randao;
uint64_t base_fee = 0;
std::vector<Withdrawal> withdrawals;
};

using AccessList = std::vector<std::pair<address, std::vector<bytes32>>>;
Expand Down Expand Up @@ -121,9 +134,10 @@ struct TransactionReceipt

/// Finalize state after applying a "block" of transactions.
///
/// Applies block reward to coinbase and deletes empty touched accounts (post Spurious Dragon).
void finalize(
State& state, evmc_revision rev, const address& coinbase, std::optional<uint64_t> block_reward);
/// Applies block reward to coinbase, withdrawals (post Shanghai) and deletes empty touched accounts
/// (post Spurious Dragon).
void finalize(State& state, evmc_revision rev, const address& coinbase,
std::optional<uint64_t> block_reward, std::span<Withdrawal> withdrawals);

[[nodiscard]] std::variant<TransactionReceipt, std::error_code> transition(
State& state, const BlockInfo& block, const Transaction& tx, evmc_revision rev, evmc::VM& vm);
Expand Down
14 changes: 13 additions & 1 deletion test/statetest/statetest_loader.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -170,9 +170,21 @@ state::BlockInfo from_json<state::BlockInfo>(const json::json& j)
from_json<uint64_t>(j.at("parentGasLimit")),
from_json<uint64_t>(j.at("parentBaseFee")));
}

std::vector<state::Withdrawal> withdrawals;
if (const auto withdrawals_it = j.find("withdrawals"); withdrawals_it != j.end())
{
for (const auto& withdrawal : *withdrawals_it)
{
withdrawals.push_back({from_json<evmc::address>(withdrawal.at("address")),
from_json<uint64_t>(withdrawal.at("amount"))});
}
}

return {from_json<int64_t>(j.at("currentNumber")), from_json<int64_t>(j.at("currentTimestamp")),
from_json<int64_t>(j.at("currentGasLimit")),
from_json<evmc::address>(j.at("currentCoinbase")), difficulty, base_fee};
from_json<evmc::address>(j.at("currentCoinbase")), difficulty, base_fee,
std::move(withdrawals)};
}

template <>
Expand Down
2 changes: 1 addition & 1 deletion test/statetest/statetest_runner.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ void run_state_test(const StateTransitionTest& test, evmc::VM& vm)
const auto res = state::transition(state, test.block, tx, rev, vm);

// Finalize block with reward 0.
state::finalize(state, rev, test.block.coinbase, 0);
state::finalize(state, rev, test.block.coinbase, 0, {});

if (holds_alternative<state::TransactionReceipt>(res))
EXPECT_EQ(logs_hash(get<state::TransactionReceipt>(res).logs), expected.logs_hash);
Expand Down
2 changes: 1 addition & 1 deletion test/t8n/t8n.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -158,7 +158,7 @@ int main(int argc, const char* argv[])
}
}

state::finalize(state, rev, block.coinbase, block_reward);
state::finalize(state, rev, block.coinbase, block_reward, block.withdrawals);

j_result["logsHash"] = hex0x(logs_hash(txs_logs));
j_result["stateRoot"] = hex0x(state::mpt_hash(state.get_accounts()));
Expand Down
1 change: 1 addition & 0 deletions test/unittests/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ target_sources(
state_rlp_test.cpp
state_transition.hpp
state_transition.cpp
state_transition_block_test.cpp
state_transition_create_test.cpp
state_transition_eof_test.cpp
statetest_loader_block_info_test.cpp
Expand Down
2 changes: 1 addition & 1 deletion test/unittests/state_transition.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ void state_transition::TearDown()
ASSERT_TRUE(holds_alternative<TransactionReceipt>(res))
<< std::get<std::error_code>(res).message();
const auto& receipt = std::get<TransactionReceipt>(res);
evmone::state::finalize(state, rev, block.coinbase, 0);
evmone::state::finalize(state, rev, block.coinbase, 0, block.withdrawals);

EXPECT_EQ(receipt.status, expect.status);
if (expect.gas_used.has_value())
Expand Down
16 changes: 16 additions & 0 deletions test/unittests/state_transition_block_test.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
// evmone: Fast Ethereum Virtual Machine implementation
// Copyright 2023 The evmone Authors.
// SPDX-License-Identifier: Apache-2.0

#include "state_transition.hpp"

using namespace evmc::literals;
using namespace evmone::test;

TEST_F(state_transition, block_apply_withdrawal)
{
static constexpr auto withdrawal_address = 0x8ef300b6a6a0b41e4f5d717074d9fd5c605c7285_address;

block.withdrawals = {{withdrawal_address, 3}};
expect.post[withdrawal_address].balance = intx::uint256{3} * 1'000'000'000;
}
40 changes: 40 additions & 0 deletions test/unittests/statetest_loader_block_info_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -170,3 +170,43 @@ TEST(statetest_loader, block_info_0_random)
EXPECT_EQ(bi.timestamp, 0);
EXPECT_EQ(bi.number, 0);
}

TEST(statetest_loader, block_info_withdrawals)
{
constexpr std::string_view input = R"({
"currentCoinbase": "0x1111111111111111111111111111111111111111",
"currentDifficulty": "0x0",
"currentGasLimit": "0x0",
"currentNumber": "0",
"currentTimestamp": "0",
"currentBaseFee": "7",
"currentRandom": "0x00",
"withdrawals": [
{
"index": "0x0",
"validatorIndex": "0x0",
"address": "0x0000000000000000000000000000000000000100",
"amount": "0x800000000"
},
{
"index": "0x1",
"validatorIndex": "0x1",
"address": "0x0000000000000000000000000000000000000200",
"amount": "0xffffffffffffffff"
}
]
})";

const auto bi = test::from_json<state::BlockInfo>(json::json::parse(input));
EXPECT_EQ(bi.coinbase, 0x1111111111111111111111111111111111111111_address);
EXPECT_EQ(bi.prev_randao, 0x00_bytes32);
EXPECT_EQ(bi.gas_limit, 0x0);
EXPECT_EQ(bi.base_fee, 7);
EXPECT_EQ(bi.timestamp, 0);
EXPECT_EQ(bi.number, 0);
EXPECT_EQ(bi.withdrawals.size(), 2);
EXPECT_EQ(bi.withdrawals[0].recipient, 0x0000000000000000000000000000000000000100_address);
EXPECT_EQ(bi.withdrawals[0].get_amount(), intx::uint256{0x800000000} * 1'000'000'000);
EXPECT_EQ(bi.withdrawals[1].recipient, 0x0000000000000000000000000000000000000200_address);
EXPECT_EQ(bi.withdrawals[1].get_amount(), intx::uint256{0xffffffffffffffff} * 1'000'000'000);
}

0 comments on commit f062020

Please sign in to comment.