Skip to content

Commit

Permalink
state: Clean up state finalization (#609)
Browse files Browse the repository at this point in the history
- Separate selfdestruct handling from empty account clearing.
- Apply block reward to coinbase consistently.

Co-authored-by: Paweł Bylica <[email protected]>
  • Loading branch information
rodiazet and chfast authored Apr 13, 2023
1 parent 1fb6fa6 commit 31fd534
Show file tree
Hide file tree
Showing 5 changed files with 33 additions and 14 deletions.
29 changes: 19 additions & 10 deletions test/state/state.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -117,19 +117,30 @@ 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)
{
if (block_reward.has_value())
state.touch(coinbase).balance += *block_reward;

if (rev >= EVMC_SPURIOUS_DRAGON)
{
std::erase_if(
state.get_accounts(), [](const std::pair<const address, Account>& p) noexcept {
const auto& acc = p.second;
return acc.erasable && acc.is_empty();
});
}
}

std::variant<TransactionReceipt, std::error_code> transition(
State& state, const BlockInfo& block, const Transaction& tx, evmc_revision rev, evmc::VM& vm)
{
auto& sender_acc = state.get(tx.sender);
const auto validation_result = validate_transaction(sender_acc, block, tx, rev);

if (holds_alternative<std::error_code>(validation_result))
{
// Pre EIP-158 coinbase has to be touched also for invalid tx.
if (rev <= EVMC_TANGERINE_WHISTLE)
state.touch(block.coinbase);
return get<std::error_code>(validation_result);
}

const auto execution_gas_limit = get<int64_t>(validation_result);

Expand Down Expand Up @@ -176,11 +187,9 @@ std::variant<TransactionReceipt, std::error_code> transition(
state.get(tx.sender).balance += tx_max_cost - gas_used * effective_gas_price;
state.touch(block.coinbase).balance += gas_used * priority_gas_price;

// Apply destructs and clear erasable empty accounts.
std::erase_if(state.get_accounts(), [rev](const std::pair<const address, Account>& p) noexcept {
const auto& acc = p.second;
return acc.destructed || (rev >= EVMC_SPURIOUS_DRAGON && acc.erasable && acc.is_empty());
});
// Apply destructs.
std::erase_if(state.get_accounts(),
[](const std::pair<const address, Account>& p) noexcept { return p.second.destructed; });

auto receipt = TransactionReceipt{tx.kind, result.status_code, gas_used, host.take_logs(), {}};

Expand Down
6 changes: 6 additions & 0 deletions test/state/state.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,12 @@ struct TransactionReceipt
BloomFilter logs_bloom_filter;
};

/// 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);

[[nodiscard]] std::variant<TransactionReceipt, std::error_code> transition(
State& state, const BlockInfo& block, const Transaction& tx, evmc_revision rev, evmc::VM& vm);

Expand Down
4 changes: 4 additions & 0 deletions test/statetest/statetest_runner.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,10 @@ void run_state_test(const StateTransitionTest& test, evmc::VM& vm)
validate_deployed_code(state, rev);

const auto res = state::transition(state, test.block, tx, rev, vm);

// Finalize block with reward 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);
else
Expand Down
7 changes: 3 additions & 4 deletions test/t8n/t8n.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ int main(int argc, const char* argv[])
fs::path output_result_file;
fs::path output_alloc_file;
fs::path output_body_file;
std::optional<intx::uint256> block_reward;
std::optional<uint64_t> block_reward;
uint64_t chain_id = 0;

try
Expand Down Expand Up @@ -57,7 +57,7 @@ int main(int argc, const char* argv[])
else if (arg == "--output.alloc" && ++i < argc)
output_alloc_file = argv[i];
else if (arg == "--state.reward" && ++i < argc && argv[i] != "-1"sv)
block_reward = intx::from_string<intx::uint256>(argv[i]);
block_reward = intx::from_string<uint64_t>(argv[i]);
else if (arg == "--state.chainid" && ++i < argc)
chain_id = intx::from_string<uint64_t>(argv[i]);
else if (arg == "--output.body" && ++i < argc)
Expand Down Expand Up @@ -157,8 +157,7 @@ int main(int argc, const char* argv[])
}
}

if (block_reward.has_value())
state.touch(block.coinbase).balance += *block_reward;
state::finalize(state, rev, block.coinbase, block_reward);

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/state_transition.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +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);

EXPECT_EQ(receipt.status, expect.status);
if (expect.gas_used.has_value())
Expand Down

0 comments on commit 31fd534

Please sign in to comment.