Skip to content
Draft
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
4 changes: 3 additions & 1 deletion category/execution/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -30,14 +30,16 @@ project(monad_execution)
add_library(
monad_execution_ethereum OBJECT
# ethereum/chain
"ethereum/chain/chain_config.h"
"ethereum/chain/chain.cpp"
"ethereum/chain/chain.hpp"
"ethereum/chain/chain_config.h"
"ethereum/chain/ethereum_mainnet.cpp"
"ethereum/chain/ethereum_mainnet.hpp"
"ethereum/chain/ethereum_mainnet_alloc.hpp"
"ethereum/chain/genesis_state.cpp"
"ethereum/chain/genesis_state.hpp"
"ethereum/chain/patch_output_header.cpp"
"ethereum/chain/patch_output_header.hpp"
# ethereum/core
"ethereum/core/account.hpp"
"ethereum/core/address.hpp"
Expand Down
8 changes: 4 additions & 4 deletions category/execution/ethereum/block_hash_history.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ void deploy_block_hash_history_contract(State &state)
EXPLICIT_TRAITS(deploy_block_hash_history_contract);

template <Traits traits>
void set_block_hash_history(State &state, BlockHeader const &header)
void set_block_hash_history(State &state, BlockHeaderInputs const &inputs)
{
if constexpr (traits::evm_rev() < EVMC_PRAGUE) {
return;
Expand All @@ -90,15 +90,15 @@ void set_block_hash_history(State &state, BlockHeader const &header)
}
}

if (MONAD_UNLIKELY(!header.number)) {
if (MONAD_UNLIKELY(!inputs.number)) {
return;
}

if (MONAD_LIKELY(state.account_exists(BLOCK_HISTORY_ADDRESS))) {
uint64_t const parent_number = header.number - 1;
uint64_t const parent_number = inputs.number - 1;
uint256_t const index{parent_number % BLOCK_HISTORY_LENGTH};
bytes32_t const key{to_bytes(to_big_endian(index))};
state.set_storage(BLOCK_HISTORY_ADDRESS, key, header.parent_hash);
state.set_storage(BLOCK_HISTORY_ADDRESS, key, inputs.parent_hash);
}
}

Expand Down
4 changes: 2 additions & 2 deletions category/execution/ethereum/block_hash_history.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,8 @@

MONAD_NAMESPACE_BEGIN

struct BlockHeaderInputs;
class State;
struct BlockHeader;

constexpr Address BLOCK_HISTORY_ADDRESS{
0x0000F90827F1C53a10cb7A02335B175320002935_address};
Expand All @@ -36,7 +36,7 @@ template <Traits traits>
void deploy_block_hash_history_contract(State &);

template <Traits traits>
void set_block_hash_history(State &, BlockHeader const &);
void set_block_hash_history(State &, BlockHeaderInputs const &);

bytes32_t get_block_hash_history(State &, uint64_t block_number);

Expand Down
9 changes: 5 additions & 4 deletions category/execution/ethereum/block_reward.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@
#include <category/core/int.hpp>
#include <category/core/likely.h>
#include <category/execution/ethereum/block_reward.hpp>
#include <category/execution/ethereum/core/block.hpp>
#include <category/execution/ethereum/state3/state.hpp>
#include <category/vm/evm/explicit_traits.hpp>
#include <category/vm/evm/traits.hpp>
Expand Down Expand Up @@ -74,7 +73,7 @@ constexpr uint256_t const calculate_ommer_reward(
}

template <Traits traits>
void apply_block_reward(State &state, Block const &block)
void apply_block_reward(State &state, InputBlockView const block)
{
auto const miner_reward = calculate_block_reward(
block_reward<traits>(),
Expand All @@ -83,13 +82,15 @@ void apply_block_reward(State &state, Block const &block)

// reward block beneficiary, YP Eqn. 172
if (MONAD_LIKELY(miner_reward)) {
state.add_to_balance(block.header.beneficiary, miner_reward);
state.add_to_balance(block.header_inputs.beneficiary, miner_reward);
}

// reward ommers, YP Eqn. 175
for (auto const &ommer : block.ommers) {
auto const ommer_reward = calculate_ommer_reward(
block_reward<traits>(), block.header.number, ommer.number);
block_reward<traits>(),
block.header_inputs.number,
ommer.number);
if (MONAD_LIKELY(ommer_reward)) {
state.add_to_balance(ommer.beneficiary, ommer_reward);
}
Expand Down
3 changes: 2 additions & 1 deletion category/execution/ethereum/block_reward.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
#pragma once

#include <category/core/config.hpp>
#include <category/execution/ethereum/core/block.hpp>
#include <category/vm/evm/traits.hpp>

#include <evmc/evmc.h>
Expand All @@ -26,6 +27,6 @@ struct Block;
class State;

template <Traits traits>
void apply_block_reward(State &, Block const &);
void apply_block_reward(State &, InputBlockView);

MONAD_NAMESPACE_END
2 changes: 1 addition & 1 deletion category/execution/ethereum/chain/chain.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ MONAD_NAMESPACE_BEGIN

using BOOST_OUTCOME_V2_NAMESPACE::success;

Result<void> Chain::static_validate_header(BlockHeader const &) const
Result<void> Chain::static_validate_header(BlockHeaderInputs const &) const
{
return success();
}
Expand Down
7 changes: 2 additions & 5 deletions category/execution/ethereum/chain/chain.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@
MONAD_NAMESPACE_BEGIN

class State;
struct BlockHeader;
struct BlockHeaderInputs;
struct Receipt;
struct Transaction;

Expand All @@ -42,10 +42,7 @@ struct Chain
virtual evmc_revision
get_revision(uint64_t block_number, uint64_t timestamp) const = 0;

virtual Result<void> static_validate_header(BlockHeader const &) const;

virtual Result<void> validate_output_header(
BlockHeader const &input, BlockHeader const &output) const = 0;
virtual Result<void> static_validate_header(BlockHeaderInputs const &) const;

virtual GenesisState get_genesis_state() const = 0;

Expand Down
54 changes: 4 additions & 50 deletions category/execution/ethereum/chain/ethereum_mainnet.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -89,64 +89,18 @@ evmc_revision EthereumMainnet::get_revision(
}

Result<void>
EthereumMainnet::static_validate_header(BlockHeader const &header) const
EthereumMainnet::static_validate_header(BlockHeaderInputs const &inputs) const
{
// EIP-779
if (MONAD_UNLIKELY(
header.number >= dao::dao_block_number &&
header.number <= dao::dao_block_number + 9 &&
header.extra_data != dao::extra_data)) {
inputs.number >= dao::dao_block_number &&
inputs.number <= dao::dao_block_number + 9 &&
inputs.extra_data != dao::extra_data)) {
return BlockError::WrongDaoExtraData;
}
return success();
}

Result<void> EthereumMainnet::validate_output_header(
BlockHeader const &input, BlockHeader const &output) const
{
// First, validate execution inputs.
if (MONAD_UNLIKELY(input.ommers_hash != output.ommers_hash)) {
return BlockError::WrongOmmersHash;
}
if (MONAD_UNLIKELY(input.transactions_root != output.transactions_root)) {
return BlockError::WrongMerkleRoot;
}
if (MONAD_UNLIKELY(input.withdrawals_root != output.withdrawals_root)) {
return BlockError::WrongMerkleRoot;
}

// Second, validate execution outputs known before commit.

// YP eq. 170
if (MONAD_UNLIKELY(input.gas_used != output.gas_used)) {
return BlockError::InvalidGasUsed;
}

// YP eq. 56
if (MONAD_UNLIKELY(output.gas_used > output.gas_limit)) {
return BlockError::GasAboveLimit;
}

// YP eq. 33
if (MONAD_UNLIKELY(input.logs_bloom != output.logs_bloom)) {
return BlockError::WrongLogsBloom;
}

if (MONAD_UNLIKELY(input.parent_hash != output.parent_hash)) {
return BlockError::WrongParentHash;
}

// Lastly, validate execution outputs only known after commit.
if (MONAD_UNLIKELY(input.state_root != output.state_root)) {
return BlockError::WrongMerkleRoot;
}
if (MONAD_UNLIKELY(input.receipts_root != output.receipts_root)) {
return BlockError::WrongMerkleRoot;
}

return success();
}

GenesisState EthereumMainnet::get_genesis_state() const
{
BlockHeader header;
Expand Down
5 changes: 1 addition & 4 deletions category/execution/ethereum/chain/ethereum_mainnet.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -42,10 +42,7 @@ struct EthereumMainnet : Chain
get_revision(uint64_t block_number, uint64_t timestamp) const override;

virtual Result<void>
static_validate_header(BlockHeader const &) const override;

virtual Result<void> validate_output_header(
BlockHeader const &input, BlockHeader const &output) const override;
static_validate_header(BlockHeaderInputs const &) const override;

virtual GenesisState get_genesis_state() const override;

Expand Down
42 changes: 42 additions & 0 deletions category/execution/ethereum/chain/patch_output_header.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
// Copyright (C) 2025 Category Labs, Inc.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.

#include <category/execution/ethereum/chain/chain.hpp>
#include <category/execution/ethereum/chain/patch_output_header.hpp>
#include <category/execution/ethereum/core/block.hpp>

#include <evmc/evmc.h>

MONAD_NAMESPACE_BEGIN

void patch_output_header(
Chain const &chain, BlockHeader const &input_header,
BlockHeader &output_header)
{
auto const rev =
chain.get_revision(output_header.number, output_header.timestamp);
if (rev <= EVMC_BYZANTIUM) {
// TODO: TrieDb does not calculate receipts root correctly before the
// BYZANTIUM fork. However, for empty receipts our receipts root
// calculation is correct.
//
// On monad, the receipts root input is always null. On replay, we set
// our receipts root to any non-null header input so our eth header is
// correct in the Db.
output_header.withdrawals_root = input_header.withdrawals_root;
}
}

MONAD_NAMESPACE_END
29 changes: 29 additions & 0 deletions category/execution/ethereum/chain/patch_output_header.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
// Copyright (C) 2025 Category Labs, Inc.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.

#pragma once

#include <category/core/config.hpp>

struct BlockHeader;
class Chain;

MONAD_NAMESPACE_BEGIN

void patch_output_header(
Chain const &chain, BlockHeader const &input_header,
BlockHeader &output_header);

MONAD_NAMESPACE_END
43 changes: 31 additions & 12 deletions category/execution/ethereum/core/block.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -29,37 +29,47 @@

MONAD_NAMESPACE_BEGIN

struct BlockHeader
struct BlockHeaderInputs
{
Receipt::Bloom logs_bloom{}; // H_b
bytes32_t parent_hash{}; // H_p
bytes32_t ommers_hash{NULL_LIST_HASH}; // H_o
bytes32_t state_root{NULL_ROOT}; // H_r
Address beneficiary{}; // H_c
bytes32_t transactions_root{NULL_ROOT}; // H_t
bytes32_t receipts_root{NULL_ROOT}; // H_e
bytes32_t prev_randao{}; // H_a
uint256_t difficulty{}; // H_d

uint64_t number{0}; // H_i
uint64_t gas_limit{0}; // H_l
uint64_t gas_used{0}; // H_g
uint64_t timestamp{0}; // H_s

byte_string_fixed<8> nonce{}; // H_n
byte_string extra_data{}; // H_x

Address beneficiary{}; // H_c

bytes32_t prev_randao{}; // H_a
byte_string_fixed<8> nonce{}; // H_n
std::optional<uint256_t> base_fee_per_gas{std::nullopt}; // H_f
std::optional<bytes32_t> withdrawals_root{std::nullopt}; // H_w
std::optional<uint64_t> blob_gas_used{std::nullopt}; // EIP-4844
std::optional<uint64_t> excess_blob_gas{std::nullopt}; // EIP-4844
std::optional<bytes32_t> parent_beacon_block_root{std::nullopt}; // EIP-4788
std::optional<bytes32_t> requests_hash{std::nullopt}; // EIP-7685

friend bool
operator==(BlockHeaderInputs const &, BlockHeaderInputs const &) = default;
};

struct BlockHeader : public BlockHeaderInputs
{
Receipt::Bloom logs_bloom{}; // H_b
bytes32_t state_root{NULL_ROOT}; // H_r
bytes32_t receipts_root{NULL_ROOT}; // H_e
uint64_t gas_used{0}; // H_g
friend bool operator==(BlockHeader const &, BlockHeader const &) = default;
};

struct InputBlockView
{
BlockHeaderInputs const &header_inputs;
std::vector<Transaction> const &transactions;
std::vector<BlockHeader> const &ommers;
std::optional<std::vector<Withdrawal>> const &withdrawals;
};

struct Block
{
BlockHeader header;
Expand All @@ -68,6 +78,15 @@ struct Block
std::optional<std::vector<Withdrawal>> withdrawals{std::nullopt};

friend bool operator==(Block const &, Block const &) = default;

InputBlockView to_input_view() const noexcept
{
return InputBlockView{
.header_inputs = static_cast<BlockHeaderInputs const &>(header),
.transactions = transactions,
.ommers = ommers,
.withdrawals = withdrawals};
}
};

static_assert(sizeof(BlockHeader) == 760);
Expand Down
Loading
Loading