A full execution-layer block — header, transactions, ommers, and withdrawals — and the public path to build, encode, validate, and execute one.
| Field | Description |
|---|---|
header | The EEVM.Block.Header |
transactions | Signed transactions (see EEVM.Transaction), in order |
ommers | Uncle headers; always empty after the Merge |
withdrawals | EEVM.Block.Withdrawals from Shanghai on, nil before |
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, EIP-7685 requests), and checks the header'sgas_used,logs_bloom,receipts_root,state_root, andrequests_hashagainst 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: a full :config (hardfork, chain
id, custom precompiles) or the :hardfork shorthand (default :cancun);
:chain_id is deprecated in favour of the config's. :block_hashes maps
recent block numbers to their 32-byte hashes for BLOCKHASH. From Prague,
:deposit_contract_address names the chain's EIP-6110 deposit contract
(default: mainnet's, EEVM.Block.Requests.mainnet_deposit_contract/0).
tracer: true traces every transaction: result.trace is a list with one
EEVM.Tracer per transaction, in block order (like geth's
debug_traceBlock), so transaction boundaries stay visible.
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, and fork transitions inside a chain.
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, the
EIP-4895 withdrawals, and from Prague the EIP-7685 requests (EIP-6110
deposits, then the EIP-7002 and EIP-7251 system calls; see
EEVM.Block.Requests). Finally checks the header's gas_used,
logs_bloom, receipts_root, state_root, and requests_hash 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()) => <<_::256>>}, deposit_contract_address: non_neg_integer(), tracer: boolean(), 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: <<_::256>> | 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()) => <<_::256>>}, deposit_contract_address: non_neg_integer(), tracer: boolean() ]
@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
(default: 12 seconds, one mainnet slot, after the parent's), :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,
including {:error, :simulation_config} for a config with a simulation
switch set.
From Prague the state must hold the EIP-7002 and EIP-7251 request contracts,
as on mainnet; give a new chain its system contracts with
EEVM.SystemContracts.genesis_accounts/1.
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, the
EIP-4895 withdrawals, and from Prague the EIP-7685 requests (EIP-6110
deposits, then the EIP-7002 and EIP-7251 system calls; see
EEVM.Block.Requests). Finally checks the header's gas_used,
logs_bloom, receipts_root, state_root, and requests_hash against
the result.
With tracer: true, each transaction is traced and result.trace lists
one EEVM.Tracer per transaction, in block order; system calls are not
traced.
Returns {:ok, %EEVM.Block.Result{}} whose db is the state after the
block and whose requests are the block's requests. 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}}; a request that can't be collected
fails it with that 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:
- The config sets no simulation switch (
EEVM.Config.simulation?/1), else:simulation_config: blocks never run with relaxed rules. - From Osaka, the encoded block is at most
max_rlp_block_size/0bytes (EIP-7934), else:rlp_block_size_exceeded. - The header is a valid child of
parent(EEVM.Block.HeaderValidator; the checks and their errors are listed inEEVM.Error). - 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.