diff --git a/test/state/state.cpp b/test/state/state.cpp index b055de44f1..06969bae3b 100644 --- a/test/state/state.cpp +++ b/test/state/state.cpp @@ -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 block_reward) +void finalize(State& state, evmc_revision rev, const address& coinbase, + std::optional block_reward, std::span withdrawals) { if (block_reward.has_value()) state.touch(coinbase).balance += *block_reward; @@ -131,6 +131,10 @@ void finalize( return acc.erasable && acc.is_empty(); }); } + + // Apply withdrawals. Amount value is in gwei. + for (const auto& withdraw : withdrawals) + state.touch(withdraw.first).balance += intx::uint256{withdraw.second} * 1000000000; } std::variant transition( diff --git a/test/state/state.hpp b/test/state/state.hpp index 38717c91b1..c4a18338a4 100644 --- a/test/state/state.hpp +++ b/test/state/state.hpp @@ -66,6 +66,10 @@ class State [[nodiscard]] const auto& get_accounts() const noexcept { return m_accounts; } }; +/// Vector of withdrawals in a block. Contains `to` address and ETH `amount` in gwei. +using Withdrawal = std::pair; +using Withdrawals = std::vector; + struct BlockInfo { int64_t number = 0; @@ -74,6 +78,7 @@ struct BlockInfo address coinbase; bytes32 prev_randao; uint64_t base_fee = 0; + Withdrawals withdrawals; }; using AccessList = std::vector>>; @@ -121,9 +126,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 block_reward); +/// Applies block reward to coinbase, withdrawals and deletes empty touched accounts (post Spurious +/// Dragon). +void finalize(State& state, evmc_revision rev, const address& coinbase, + std::optional block_reward, std::span withdrawals); [[nodiscard]] std::variant transition( State& state, const BlockInfo& block, const Transaction& tx, evmc_revision rev, evmc::VM& vm); diff --git a/test/statetest/statetest_loader.cpp b/test/statetest/statetest_loader.cpp index 7ca4321c14..d22b8734d0 100644 --- a/test/statetest/statetest_loader.cpp +++ b/test/statetest/statetest_loader.cpp @@ -170,9 +170,21 @@ state::BlockInfo from_json(const json::json& j) from_json(j.at("parentGasLimit")), from_json(j.at("parentBaseFee"))); } + + state::Withdrawals withdrawals; + if (const auto withdrawals_it = j.find("withdrawals"); withdrawals_it != j.end()) + { + for (const auto& withdraw : *withdrawals_it) + { + withdrawals.push_back({from_json(withdraw.at("address")), + from_json(withdraw.at("amount"))}); + } + } + return {from_json(j.at("currentNumber")), from_json(j.at("currentTimestamp")), from_json(j.at("currentGasLimit")), - from_json(j.at("currentCoinbase")), difficulty, base_fee}; + from_json(j.at("currentCoinbase")), difficulty, base_fee, + std::move(withdrawals)}; } template <> diff --git a/test/statetest/statetest_runner.cpp b/test/statetest/statetest_runner.cpp index 72a0b56cae..f51490dd40 100644 --- a/test/statetest/statetest_runner.cpp +++ b/test/statetest/statetest_runner.cpp @@ -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(res)) EXPECT_EQ(logs_hash(get(res).logs), expected.logs_hash); diff --git a/test/t8n/t8n.cpp b/test/t8n/t8n.cpp index 5974fcd997..2a2df6271c 100644 --- a/test/t8n/t8n.cpp +++ b/test/t8n/t8n.cpp @@ -157,7 +157,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())); diff --git a/test/unittests/state_transition.cpp b/test/unittests/state_transition.cpp index 4f7a36afed..be56abce83 100644 --- a/test/unittests/state_transition.cpp +++ b/test/unittests/state_transition.cpp @@ -22,7 +22,7 @@ void state_transition::TearDown() ASSERT_TRUE(holds_alternative(res)) << std::get(res).message(); const auto& receipt = std::get(res); - evmone::state::finalize(state, rev, block.coinbase, 0); + evmone::state::finalize(state, rev, block.coinbase, 0, {}); EXPECT_EQ(receipt.status, expect.status); if (expect.gas_used.has_value()) diff --git a/test/unittests/statetest_loader_block_info_test.cpp b/test/unittests/statetest_loader_block_info_test.cpp index 65d4a982f4..b989b5b3bc 100644 --- a/test/unittests/statetest_loader_block_info_test.cpp +++ b/test/unittests/statetest_loader_block_info_test.cpp @@ -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(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].first, 0x0000000000000000000000000000000000000100_address); + EXPECT_EQ(bi.withdrawals[0].second, 0x800000000); + EXPECT_EQ(bi.withdrawals[1].first, 0x0000000000000000000000000000000000000200_address); + EXPECT_EQ(bi.withdrawals[1].second, 0xffffffffffffffff); +}