# `EEVM.Block`
[🔗](https://github.com/mw2000/eevm/blob/main/lib/eevm/block.ex#L1)

A full execution-layer block — header, transactions, ommers, and
withdrawals — and the public path to build, encode, validate, and execute
one.

- `decode/1` and `encode/1` convert to and from canonical RLP.
- `validate/3` runs every check that needs no state: the EIP-7934 size
  limit, `EEVM.Block.HeaderValidator` against the parent, and the body
  against the header's commitments.
- `execute/4` validates the block, applies it to a database (system calls,
  each transaction through `EEVM.Handler`, withdrawals), and checks the
  header's `gas_used`, `logs_bloom`, `receipts_root`, and `state_root`
  against the result.
- `build/4` produces 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 `t: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
    0

Not 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.

# `build_opts`

```elixir
@type build_opts() :: [
  hardfork: EEVM.HardforkConfig.spec_id(),
  config: EEVM.Config.t(),
  chain_id: non_neg_integer(),
  block_hashes: %{required(non_neg_integer()) =&gt; 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()]
]
```

# `opts`

```elixir
@type opts() :: [
  hardfork: EEVM.HardforkConfig.spec_id(),
  config: EEVM.Config.t(),
  chain_id: non_neg_integer(),
  block_hashes: %{required(non_neg_integer()) =&gt; non_neg_integer()}
]
```

# `t`

```elixir
@type t() :: %EEVM.Block{
  header: EEVM.Block.Header.t(),
  ommers: [EEVM.Block.Header.t()],
  transactions: [transaction()],
  withdrawals: [EEVM.Block.Withdrawal.t()] | nil
}
```

# `transaction`

```elixir
@type transaction() ::
  EEVM.Transaction.Legacy.t()
  | EEVM.Transaction.AccessList.t()
  | EEVM.Transaction.FeeMarket.t()
  | EEVM.Transaction.Blob.t()
  | EEVM.Transaction.SetCode.t()
```

# `build`

```elixir
@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.

# `decode`

```elixir
@spec decode(binary()) :: {:ok, t()} | {:error, :invalid_block_encoding}
```

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).

# `encode`

```elixir
@spec encode(t()) :: binary()
```

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.

# `execute`

```elixir
@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}}`.

# `max_rlp_block_size`

```elixir
@spec max_rlp_block_size() :: pos_integer()
```

Returns `MAX_RLP_BLOCK_SIZE`, the largest encoded block valid from Osaka (EIP-7934).

# `validate`

```elixir
@spec validate(t(), EEVM.Block.Header.t(), opts()) ::
  :ok | {:error, EEVM.Error.block_error()}
```

Checks `block` against `parent` without touching state, in this order:

1. From Osaka, the encoded block is at most `max_rlp_block_size/0` bytes
   (EIP-7934), else `:rlp_block_size_exceeded`.
2. `EEVM.Block.HeaderValidator.validate/3`.
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 equals
   `blob_gas_used`.

---

*Consult [api-reference.md](api-reference.md) for complete listing*
