A full execution-layer block — header, transactions, ommers, and withdrawals — and the public path to build, encode, validate, and execute one.
decode/1andencode/1convert to and from canonical RLP.validate/3runs every check that needs no state: the EIP-7934 size limit,EEVM.Block.HeaderValidatoragainst the parent, and the body against the header's commitments.execute/4validates the block, applies it to a database (system calls, each transaction throughEEVM.Handler, withdrawals), and checks the header'sgas_used,logs_bloom,receipts_root, andstate_rootagainst the result.build/4produces a child block of a parent: it derives the header, executes the transactions, fills in every commitment, and hashes it.
Options follow EEVM.Handler.execute/4: :hardfork (default :cancun) or
a full :config, and :chain_id (default 1). :block_hashes maps
recent block numbers to their hashes for BLOCKHASH.
Errors are {:error, reason} with the reasons catalogued in EEVM.Error
as EEVM.Error.block_error/0.
Example
iex> alias EEVM.Block
iex> parent = EEVM.Block.Header.new(gas_limit: 30_000_000, base_fee_per_gas: 7, hash: <<1::256>>)
iex> db = EEVM.Database.InMemory.new()
iex> {:ok, block, _result} = Block.build(parent, [], db, hardfork: :shanghai, timestamp: 12)
iex> {:ok, decoded} = block |> Block.encode() |> Block.decode()
iex> decoded == block
true
iex> {:ok, result} = Block.execute(decoded, parent, db, hardfork: :shanghai)
iex> result.gas_used
0Not covered: ommer (uncle) validation and rewards, fork transitions inside
a chain, and EIP-7685 request computation. requests_hash is carried and
round-tripped but not recomputed; build/4 sets the empty-requests hash.
Summary
Functions
Builds the child of parent holding transactions (signed wire
transactions, in order) and executes it on db.
Decodes a canonical RLP block. The header's hash is set from its bytes.
Encodes block as canonical RLP:
[header, transactions, ommers], plus withdrawals when it isn't nil.
Validates block with validate/3, then applies it to db: the EIP-4788
and EIP-2935 system calls, each transaction through EEVM.Handler, and the
EIP-4895 withdrawals. Finally checks the header's gas_used, logs_bloom,
receipts_root, and state_root against the result.
Returns MAX_RLP_BLOCK_SIZE, the largest encoded block valid from Osaka (EIP-7934).
Checks block against parent without touching state, in this order
Types
@type build_opts() :: [ hardfork: EEVM.HardforkConfig.spec_id(), config: EEVM.Config.t(), chain_id: non_neg_integer(), block_hashes: %{required(non_neg_integer()) => non_neg_integer()}, timestamp: non_neg_integer(), coinbase: non_neg_integer(), gas_limit: non_neg_integer(), prev_randao: non_neg_integer(), extra_data: binary(), parent_beacon_block_root: non_neg_integer(), withdrawals: [EEVM.Block.Withdrawal.t()] ]
@type opts() :: [ hardfork: EEVM.HardforkConfig.spec_id(), config: EEVM.Config.t(), chain_id: non_neg_integer(), block_hashes: %{required(non_neg_integer()) => non_neg_integer()} ]
@type t() :: %EEVM.Block{ header: EEVM.Block.Header.t(), ommers: [EEVM.Block.Header.t()], transactions: [transaction()], withdrawals: [EEVM.Block.Withdrawal.t()] | nil }
@type transaction() :: EEVM.Transaction.Legacy.t() | EEVM.Transaction.AccessList.t() | EEVM.Transaction.FeeMarket.t() | EEVM.Transaction.Blob.t() | EEVM.Transaction.SetCode.t()
Functions
@spec build(EEVM.Block.Header.t(), [transaction()], EEVM.Database.t(), build_opts()) :: {:ok, t(), EEVM.Block.Result.t()} | {:error, EEVM.Error.block_error()}
Builds the child of parent holding transactions (signed wire
transactions, in order) and executes it on db.
The header takes number, parent_hash, the EIP-1559 base fee, and the
EIP-4844 excess blob gas from parent, and every commitment from the
execution result; hash is computed. Fork-dependent fields are set exactly
for the forks that define them.
Options are those of execute/4 plus header attributes: :timestamp
(required), :coinbase, :gas_limit (default: the parent's),
:prev_randao, :extra_data, :parent_beacon_block_root, and
:withdrawals (from Shanghai).
Returns {:ok, block, result} or the first error execute/4 would report.
Decodes a canonical RLP block. The header's hash is set from its bytes.
Returns {:error, :invalid_block_encoding} when the bytes aren't a
canonical encoding of a block (malformed RLP, a wrong-sized field, a
non-canonical integer, a withdrawal field over 64 bits, or an undecodable
transaction).
Encodes block as canonical RLP:
[header, transactions, ommers], plus withdrawals when it isn't nil.
Legacy transactions are embedded as RLP lists and typed transactions as their EIP-2718 envelope bytes.
@spec execute(t(), EEVM.Block.Header.t(), EEVM.Database.t(), opts()) :: {:ok, EEVM.Block.Result.t()} | {:error, EEVM.Error.block_error()}
Validates block with validate/3, then applies it to db: the EIP-4788
and EIP-2935 system calls, each transaction through EEVM.Handler, and the
EIP-4895 withdrawals. Finally checks the header's gas_used, logs_bloom,
receipts_root, and state_root against the result.
Returns {:ok, %EEVM.Block.Result{}} whose post_state_db is the state
after the block. A transaction that EEVM.Handler rejects, or whose gas
limit exceeds the gas left in the block, fails the block with
{:error, {:tx_failed, index, reason}}.
@spec max_rlp_block_size() :: pos_integer()
Returns MAX_RLP_BLOCK_SIZE, the largest encoded block valid from Osaka (EIP-7934).
@spec validate(t(), EEVM.Block.Header.t(), opts()) :: :ok | {:error, EEVM.Error.block_error()}
Checks block against parent without touching state, in this order:
- From Osaka, the encoded block is at most
max_rlp_block_size/0bytes (EIP-7934), else:rlp_block_size_exceeded. EEVM.Block.HeaderValidator.validate/3.- The body matches the header:
ommers_hash,transactions_root,withdrawals_root(withdrawals are present exactly from Shanghai), and from Cancun the total blob gas of the transactions equalsblob_gas_used.