# EIP-7928 Block-Level Access Lists: OpenTelemetry Tracing Specification
## 1. Motivation
### 1.1 What BAL Enables
EIP-7928 Block-Level Access Lists record all state accessed during block execution:
- **Accounts** — addresses read or modified (balance, nonce, code)
- **Storage** — slots read (`storage_reads`) or modified (`storage_changes`)
- **Code** — contract bytecode deployed or modified
The BAL hash is stored in the block header. This enables:
| Capability | Without BAL | With BAL |
|------------|-------------|----------|
| State prefetch | On-demand during execution | Batch read before execution |
| Transaction execution | Sequential | Parallel |
| Storage trie updates | Sequential per account | Parallel across accounts |
### 1.2 What We Need to Measure
1. **Prefetch effectiveness** — Cache hit rate after prefetch
2. **Execution time** — Per-transaction and total block duration
3. **BAL overhead** — Size of BAL data
4. **Throughput impact** — MGas/sec with vs without BAL
---
## 2. Terminology
| Term | Definition |
|------|------------|
| **BAL** | Block-Level Access List per EIP-7928 |
| **Prefetch** | Batch reading state into cache before execution using BAL data |
---
## 3. Conformance Levels
### Mappings
This spec proposes OTel span mappings for the following metrics:
| Cross-Client JSON | Proposed OTel Span |
|-------------------|-------------------|
| `block.*` | `ethereum.block` |
| `timing.execution_ms` | `ethereum.tx.execute` (aggregate) |
| `timing.state_hash_ms` | `ethereum.stateroot` |
### BAL Extensions
Clients implementing BAL MUST add:
- `bal.*` attributes on cross-client spans (Section 5.2)
- `ethereum.bal.prefetch` span (Section 5.3)
- `ethereum.bal.prefetch.account` — Per-account prefetch timing
- `ethereum.bal.prefetch.slot` — Per-slot prefetch timing
---
## 4. Resource Attributes
Set once per client instance:
| Attribute | Type | Required | Example |
|-----------|------|----------|---------|
| `service.name` | string | Yes | `geth` |
| `service.version` | string | Yes | `1.14.0` |
| `deployment.environment` | string | Yes | `mainnet` |
| `ethereum.chain.id` | int64 | Yes | `1` |
---
## 5. Span Specification
### 5.1 Naming Conventions
- Cross-client spans: `ethereum.*`
- BAL-specific spans: `ethereum.bal.*`
- All spans use kind `INTERNAL`
### 5.2 Span Definition
These spans map to [Cross-Client Execution Metrics](https://hackmd.io/dg7rizTyTXuCf2LSa2LsyQ). BAL adds extension attributes.
---
#### `ethereum.block`
Root span for block processing.
**Cross-Client Attributes** (from `block.*`):
| Attribute | Type | Description |
|-----------|------|-------------|
| `block.number` | int64 | Block height |
| `block.hash` | string | Block hash (0x-prefixed) |
| `block.gas_used` | int64 | Total gas consumed |
| `block.tx_count` | int64 | Transaction count |
**BAL Extensions**:
| Attribute | Type | Description |
|-----------|------|-------------|
| `bal.hash` | string | BAL hash from block header (0x-prefixed) |
| `bal.accounts_count` | int64 | Unique accounts in BAL (reads + writes) |
| `bal.storage_slots_count` | int64 | Total storage slots in BAL (reads + writes) |
| `bal.code_count` | int64 | Unique contracts with code in BAL |
| `bal.size_bytes` | int64 | RLP-encoded BAL size |
---
#### `ethereum.tx.execute`
Individual transaction execution.
**Parent:** `ethereum.block`
| Attribute | Type | Description |
|-----------|------|-------------|
| `tx.index` | int64 | Transaction index in block |
| `tx.hash` | string | Transaction hash (0x-prefixed) |
| `tx.gas_used` | int64 | Gas consumed |
---
#### `ethereum.stateroot`
State root calculation. Maps to `timing.state_hash_ms`.
**Parent:** `ethereum.block`
**Cross-Client Attributes** (from `state_writes.*`):
| Attribute | Type | Description |
|-----------|------|-------------|
| `accounts_updated` | int64 | Accounts with changes |
| `storage_slots_updated` | int64 | Storage slots modified |
**BAL Extensions** (when `bal.enabled=true`):
| Attribute | Type | Description |
|-----------|------|-------------|
| `bal.parallel` | bool | Whether trie updates were parallelized |
---
### 5.3 BAL-Specific Spans
---
#### `ethereum.bal.prefetch`
Batch state prefetching before execution.
**Parent:** `ethereum.block`
| Attribute | Type | Description |
|-----------|------|-------------|
| `accounts_count` | int64 | Accounts prefetched |
| `storage_slots_count` | int64 | Storage slots prefetched |
| `code_count` | int64 | Bytecodes prefetched |
| `code_bytes` | int64 | Total bytes of code prefetched |
| `cache_hits` | int64 | Items already in cache |
| `cache_misses` | int64 | Items loaded from DB |
---
#### `ethereum.bal.prefetch.account` *(optional)*
**Parent:** `ethereum.bal.prefetch`
| Attribute | Type | Description |
|-----------|------|-------------|
| `address` | string | Account address (0x-prefixed) |
| `cache_hit` | bool | Whether already cached |
---
#### `ethereum.bal.prefetch.slot` *(optional)*
**Parent:** `ethereum.bal.prefetch`
| Attribute | Type | Description |
|-----------|------|-------------|
| `address` | string | Contract address (0x-prefixed) |
| `key` | string | Storage slot key (0x-prefixed) |
| `cache_hit` | bool | Whether already cached |
---
### 5.4 Span Hierarchy
```
ethereum.block
│ + block.number, block.hash, block.gas_used, block.tx_count
│ + bal.enabled, bal.hash, bal.accounts_count, bal.storage_slots_count, bal.size_bytes
│
├── ethereum.bal.prefetch
│ │ + accounts_count, storage_slots_count, code_count, code_bytes, cache_hits, cache_misses
│ ├── ethereum.bal.prefetch.account (optional, per account)
│ └── ethereum.bal.prefetch.slot (optional, per slot)
│
├── ethereum.tx.execute (per transaction)
│ + tx.index, tx.hash, tx.gas_used
│
└── ethereum.stateroot
+ accounts_updated, storage_slots_updated
+ bal.parallel
```
### 5.5 Processing Flow
```
┌─────────────────────────────────────────────────────────┐
│ ethereum.block │
│ │
│ ┌──────────────────┐ │
│ │ bal.prefetch │ Batch load state using BAL │
│ └────────┬─────────┘ │
│ │ │
│ ▼ │
│ ┌──────────────────┐ │
│ │ tx.execute (x N) │ Execute transactions (parallel) │
│ └────────┬─────────┘ │
│ │ │
│ ▼ │
│ ┌──────────────────┐ │
│ │ stateroot │ Compute state root │
│ └──────────────────┘ │
└─────────────────────────────────────────────────────────┘
```
---
## 6. Metrics Specification
### 6.1 Naming Conventions
- Cross-client metrics: `ethereum.*`
- BAL-specific metrics: `ethereum.bal.*`
- Units: `s` (seconds), `By` (bytes)
### 6.2 Counter Metrics
**Cross-Client:**
| Metric | Unit | Description |
|--------|------|-------------|
| `ethereum.blocks.total` | `{block}` | Blocks processed |
| `ethereum.tx.total` | `{tx}` | Transactions processed |
**BAL-Specific:**
| Metric | Unit | Description |
|--------|------|-------------|
| `ethereum.bal.blocks.total` | `{block}` | BAL-enabled blocks |
| `ethereum.bal.prefetch.accounts` | `{account}` | Accounts prefetched |
| `ethereum.bal.prefetch.slots` | `{slot}` | Storage slots prefetched |
| `ethereum.bal.prefetch.cache_hits` | `{item}` | Prefetch cache hits |
| `ethereum.bal.prefetch.cache_misses` | `{item}` | Prefetch cache misses |
### 6.3 Histogram Metrics
**Cross-Client:**
| Metric | Unit | Description |
|--------|------|-------------|
| `ethereum.block.duration` | `s` | Block processing time |
| `ethereum.tx.duration` | `s` | Per-transaction time |
| `ethereum.stateroot.duration` | `s` | State root time |
| `ethereum.throughput.mgas_per_sec` | `{mgas}/s` | Gas throughput |
**BAL-Specific:**
| Metric | Unit | Description |
|--------|------|-------------|
| `ethereum.bal.prefetch.duration` | `s` | Prefetch phase time |
| `ethereum.bal.size` | `By` | BAL size in bytes |
---
## 7. Implementation Requirements
### 7.1 Performance
| Requirement | Threshold |
|-------------|-----------|
| Tracing disabled overhead | < 0.1% |
| Tracing enabled overhead | < 2% |
| Per-span creation | < 1μs |
### 7.2 Configuration
Clients SHOULD support:
- OTLP endpoint (default: `localhost:4317`)
- Sampling rate (default: 1.0)
- Enable/disable BAL tracing
---
## 8. Analysis Queries
### 8.1 BAL Effectiveness
```sql
SELECT
AVG(CASE WHEN bal_enabled THEN block_duration_s END) AS avg_bal_block_s,
AVG(CASE WHEN NOT bal_enabled THEN block_duration_s END) AS avg_no_bal_block_s,
AVG(CASE WHEN bal_enabled THEN mgas_per_sec END) AS avg_bal_throughput,
AVG(CASE WHEN NOT bal_enabled THEN mgas_per_sec END) AS avg_no_bal_throughput
FROM block_metrics
```
### 8.2 Prefetch Cache Effectiveness
```sql
SELECT
AVG(cache_hits * 100.0 / NULLIF(cache_hits + cache_misses, 0)) AS hit_rate_pct,
AVG(prefetch_duration_s * 1000) AS avg_prefetch_ms
FROM bal_prefetch_spans
```
---
## 9. References
1. [EIP-7928: Block-Level Access Lists](https://eips.ethereum.org/EIPS/eip-7928)
2. [Cross-Client Execution Metrics Spec](https://hackmd.io/dg7rizTyTXuCf2LSa2LsyQ)
3. [Cross-Client Metrics Discussion](https://ethresear.ch/t/a-small-step-towards-data-driven-protocol-decisions-unified-slowblock-metrics-across-clients/23907)
4. [OpenTelemetry Semantic Conventions](https://opentelemetry.io/docs/specs/semconv/)
### Implementation PRs (Cross-Client Metrics)
- Geth: [#33655](https://github.com/ethereum/go-ethereum/pull/33655)
- reth: [#21433](https://github.com/paradigmxyz/reth/pull/21433)
- Besu: [#9660](https://github.com/hyperledger/besu/pull/9660)
- Nethermind: [#10288](https://github.com/NethermindEth/nethermind/pull/10288)