# EIP-7928 Block-Level Access Lists: OpenTelemetry Tracing Specification
**Version:** 2.1.0
**Status:** Draft
**Date:** 2025-12-03
## Abstract
This specification defines a uniform OpenTelemetry (OTel) tracing and metrics standard for EIP-7928 Block-Level Access Lists (BAL) across Ethereum execution clients. The focus is on measuring the two primary performance benefits BAL enables:
1. **Parallel State Prefetching** — Reading state from disk in parallel before execution
2. **Parallel Transaction Execution** — Executing non-conflicting transactions concurrently
Critical to this specification is capturing the **timing relationships** between operations — not just individual durations, but whether prefetching completed before execution needed the data, and whether parallel execution was ready when the sequential processor reached each transaction.
This specification follows [OpenTelemetry Semantic Conventions](https://opentelemetry.io/docs/specs/semconv/).
---
## Table of Contents
1. [Motivation](#1-motivation)
2. [Terminology](#2-terminology)
3. [Conformance Levels](#3-conformance-levels)
4. [Resource Attributes](#4-resource-attributes)
5. [Span Specification](#5-span-specification)
6. [Metrics Specification](#6-metrics-specification)
7. [Implementation Requirements](#7-implementation-requirements)
8. [Client Implementation Mapping](#8-client-implementation-mapping)
9. [Analysis Queries](#9-analysis-queries)
10. [References](#10-references)
---
## 1. Motivation
### 1.1 What BAL Enables
EIP-7928 Block-Level Access Lists provide execution clients with advance knowledge of:
- Which accounts will be read/modified
- Which storage slots will be accessed
- The sequence of modifications per transaction
Per EIP-7928: "60-80% of transactions access disjoint storage slots, enabling effective parallelization. The remaining 20-40% can be parallelized by having post-transaction state diffs." This means **all transactions can be executed speculatively in parallel** — the difference is whether results are used directly (no conflict) or validated via state diffs (potential conflict detected → sequential replay).
This enables:
| Capability | Without BAL | With BAL |
|------------|-------------|----------|
| State prefetch | After tx execution discovers access | Before execution starts |
| Parallel tx execution | Not possible (unknown conflicts) | Possible for all txs (speculative) |
| Storage trie updates | Sequential per account | Parallel across accounts |
| Origin storage loading | During execution | Parallel before state root |
### 1.2 What We Need to Measure
BAL is only beneficial if:
1. **Prefetch completes before execution needs the data** — otherwise execution blocks waiting
2. **Parallel execution completes before sequential processing reaches each tx** — otherwise we timeout and run sequentially anyway
3. **Conflict rate is manageable** — high conflict rates require more sequential replays, reducing parallelism benefit
This specification captures these timing relationships, not just individual operation durations.
### 1.3 Key Questions This Spec Answers
| Question | Spans/Metrics That Answer It |
|----------|------------------------------|
| Is prefetch ahead of execution? | `prefetch.execution_waited_count`, `prefetch.wait` spans |
| How often does parallel execution help? | `tx.parallel_status` distribution |
| What causes conflicts? | `conflict.detect` with `type`, `pattern`, `prior_tx_index` |
| How much time does origin storage loading take? | `stateroot.origin_load` span |
| Does the whole block ever fall back? | `block.fallback` span |
---
## 2. Terminology
| Term | Definition |
|------|------------|
| **BAL** | Block-Level Access List per EIP-7928 |
| **Prefetch** | Reading state into cache before execution needs it |
| **Prewarm** | Speculatively executing transactions to populate caches |
| **Conflict** | Overlapping state access between transactions that invalidates speculative parallel result |
| **Read-Write Conflict** | Parallel tx read state that was written by prior tx |
| **Write-Write Conflict** | Parallel tx wrote state that was also written by prior tx |
| **Confirmed** | Parallel execution result accepted without conflict |
| **Conflicted** | Parallel execution completed but conflict detected; sequential replay required |
| **Not Ready** | Parallel execution did not complete before sequential processor reached the tx |
| **Origin Storage** | Pre-block storage values for slots that will be modified (needed for state update) |
---
## 3. Conformance Levels
### Level 1: Core (Required)
All clients MUST implement:
- `ethereum.bal.block` — root span with BAL metadata
- `ethereum.bal.prefetch` — prefetch timing with wait tracking
- `ethereum.bal.tx.execute` — per-tx with `parallel_status`
- `ethereum.bal.conflict.detect` — conflict detection
- Core counter and histogram metrics
### Level 2: Extended (Recommended)
Additional detail for performance analysis:
- `ethereum.bal.prefetch.wait` — per-item wait spans
- `ethereum.bal.parallel.batch` — batch execution detail
- `ethereum.bal.stateroot.origin_load` — origin storage loading
- `ethereum.bal.stateroot.storage_trie` — per-account storage trie
- Extended metrics with cache source breakdown
### Level 3: Diagnostic (Optional)
Fine-grained spans for debugging:
- `ethereum.bal.state.account_read` — individual account reads
- `ethereum.bal.state.storage_read` — individual storage reads
- `ethereum.bal.block.fallback` — block-level fallback events
---
## 4. Resource Attributes
Set once per client instance (not per span):
| Attribute | Type | Required | Example | Description |
|-----------|------|----------|---------|-------------|
| `service.name` | string | Yes | `geth` | Client identifier |
| `service.version` | string | Yes | `1.14.0` | Client version |
| `service.namespace` | string | Yes | `ethereum.execution` | Service category |
| `deployment.environment` | string | Yes | `mainnet` | Network name |
| `ethereum.chain.id` | int64 | Yes | `1` | Chain ID |
---
## 5. Span Specification
### 5.1 Naming Conventions
- Root namespace: `ethereum.bal`
- Dot-delimited hierarchy: `ethereum.bal.prefetch.wait`
- Snake_case within segments: `ethereum.bal.tx.parallel_status`
- All spans use kind `INTERNAL` unless noted
### 5.2 Core Spans (Level 1)
---
#### `ethereum.bal.block`
Root span for BAL-enabled block processing.
| Attribute | Type | Req | Description |
|-----------|------|-----|-------------|
| `ethereum.block.number` | int64 | Yes | Block height |
| `ethereum.block.hash` | string | Yes | Block hash (0x-prefixed) |
| `ethereum.block.tx_count` | int64 | Yes | Transaction count |
| `ethereum.bal.enabled` | bool | Yes | Whether BAL was used |
| `ethereum.bal.accounts_count` | int64 | Cond | Unique accounts in BAL |
| `ethereum.bal.storage_slots_count` | int64 | Cond | Total storage slots in BAL |
| `ethereum.bal.size` | int64 | Cond | Encoded BAL size (bytes) |
---
#### `ethereum.bal.prefetch`
Parallel state prefetching using BAL data.
**Parent:** `ethereum.bal.block`
| Attribute | Type | Req | Description |
|-----------|------|-----|-------------|
| `ethereum.bal.prefetch.accounts_count` | int64 | Yes | Accounts to prefetch |
| `ethereum.bal.prefetch.storage_slots_count` | int64 | Yes | Storage slots to prefetch |
| `ethereum.bal.prefetch.parallel` | bool | Yes | Whether prefetch was parallelized |
| `ethereum.bal.prefetch.workers` | int64 | Cond | Worker count (if parallel) |
| `ethereum.bal.prefetch.completed_count` | int64 | Yes | Items successfully prefetched |
| `ethereum.bal.prefetch.failed_count` | int64 | Yes | Items that failed to load |
| `ethereum.bal.prefetch.cache_hits` | int64 | No | Items already in cache |
| `ethereum.bal.prefetch.db_reads` | int64 | No | Items loaded from database |
| `ethereum.bal.prefetch.execution_waited_count` | int64 | Yes | Times execution blocked waiting |
| `ethereum.bal.prefetch.execution_waited_duration` | float64 | Yes | Total seconds execution waited |
**Key Insight:** If `execution_waited_count > 0`, prefetch did not fully complete before execution needed the data.
---
#### `ethereum.bal.tx.execute`
Individual transaction execution.
**Parent:** `ethereum.bal.block` or `ethereum.bal.parallel.batch`
| Attribute | Type | Req | Description |
|-----------|------|-----|-------------|
| `ethereum.transaction.hash` | string | Yes | Transaction hash (0x-prefixed) |
| `ethereum.transaction.index` | int64 | Yes | Position in block |
| `ethereum.bal.tx.parallel_status` | string | Yes | See below |
| `ethereum.bal.tx.parallel_ready` | bool | Cond | Was parallel result available? |
| `ethereum.bal.tx.parallel_wait_duration` | float64 | Cond | Seconds waited for parallel result |
| `ethereum.bal.tx.gas_used` | int64 | Yes | Gas consumed |
| `ethereum.bal.tx.prefetch_waited` | bool | No | Whether this tx waited on prefetch |
| `ethereum.bal.tx.prefetch_wait_duration` | float64 | No | Seconds this tx waited on prefetch |
**`parallel_status` Values:**
| Value | Description |
|-------|-------------|
| `confirmed` | Parallel execution completed, no conflict, result used |
| `conflicted` | Parallel execution completed, conflict detected, sequential replay needed |
| `not_ready` | Parallel execution did not complete in time, sequential execution |
| `sequential` | No parallel attempt (first tx, parallel disabled, or after block fallback) |
**Critical Distinction:**
- `conflicted` = parallel execution was fast enough, but state conflict detected via post-tx state diff validation; sequential replay needed
- `not_ready` = parallel execution was too slow, couldn't validate result before sequential processor reached this tx
**Note:** All transactions can be speculatively executed in parallel. The `parallel_status` indicates the outcome of that speculative execution, not whether parallel execution was attempted.
---
#### `ethereum.bal.conflict.detect`
Conflict detection between parallel transaction result and block state.
**Parent:** `ethereum.bal.tx.execute`
| Attribute | Type | Req | Description |
|-----------|------|-----|-------------|
| `ethereum.bal.conflict.detected` | bool | Yes | Whether conflict was found |
| `ethereum.bal.conflict.type` | string | Cond | `account`, `balance`, `nonce`, `code`, `storage` |
| `ethereum.bal.conflict.pattern` | string | Cond | `read_write` or `write_write` |
| `ethereum.bal.conflict.address` | string | Cond | Conflicting address |
| `ethereum.bal.conflict.storage_slot` | string | Cond | Conflicting slot (if type=storage) |
| `ethereum.bal.conflict.prior_tx_index` | int64 | Cond | Index of tx that wrote conflicting state |
| `ethereum.bal.conflict.addresses_checked` | int64 | Yes | Addresses examined |
| `ethereum.bal.conflict.slots_checked` | int64 | Yes | Storage slots examined |
**Conflict Pattern:**
- `read_write`: Parallel tx read a value that a prior tx wrote
- `write_write`: Parallel tx wrote a value that a prior tx also wrote
---
### 5.3 Extended Spans (Level 2)
---
#### `ethereum.bal.prefetch.wait`
Records when execution blocked waiting for a specific prefetch item.
**Parent:** `ethereum.bal.tx.execute` or `ethereum.bal.stateroot`
| Attribute | Type | Req | Description |
|-----------|------|-----|-------------|
| `ethereum.bal.prefetch.wait.address` | string | Yes | Address being waited on |
| `ethereum.bal.prefetch.wait.type` | string | Yes | `account` or `storage` |
| `ethereum.bal.prefetch.wait.slot` | string | Cond | Storage slot (if type=storage) |
**Duration:** Time spent blocked. Non-trivial duration indicates prefetch is not ahead of execution.
---
#### `ethereum.bal.parallel.batch`
Execution of a batch of transactions in parallel.
**Parent:** `ethereum.bal.block`
| Attribute | Type | Req | Description |
|-----------|------|-----|-------------|
| `ethereum.bal.parallel.batch_index` | int64 | Yes | Batch number (0-indexed) |
| `ethereum.bal.parallel.batch_size` | int64 | Yes | Transactions in batch |
| `ethereum.bal.parallel.workers_used` | int64 | Yes | Worker threads active |
| `ethereum.bal.parallel.confirmed_count` | int64 | Yes | Txs confirmed (used parallel result) |
| `ethereum.bal.parallel.conflicted_count` | int64 | Yes | Txs with conflicts |
| `ethereum.bal.parallel.not_ready_count` | int64 | Yes | Txs where parallel wasn't ready |
---
#### `ethereum.bal.stateroot`
State root calculation.
**Parent:** `ethereum.bal.block`
| Attribute | Type | Req | Description |
|-----------|------|-----|-------------|
| `ethereum.bal.stateroot.mode` | string | Yes | `sequential`, `parallel`, `sparse` |
| `ethereum.bal.stateroot.accounts_updated` | int64 | Yes | Accounts with changes |
| `ethereum.bal.stateroot.storage_tries_updated` | int64 | Yes | Storage tries modified |
| `ethereum.bal.stateroot.parallel_storage` | bool | Yes | Storage tries computed in parallel |
---
#### `ethereum.bal.stateroot.origin_load`
Parallel loading of origin (pre-block) storage values for modified slots.
**Parent:** `ethereum.bal.stateroot`
| Attribute | Type | Req | Description |
|-----------|------|-----|-------------|
| `ethereum.bal.origin_load.accounts_count` | int64 | Yes | Accounts with storage changes |
| `ethereum.bal.origin_load.slots_count` | int64 | Yes | Total slots to load |
| `ethereum.bal.origin_load.parallel` | bool | Yes | Whether loading was parallelized |
| `ethereum.bal.origin_load.cache_hits` | int64 | No | Slots found in cache |
| `ethereum.bal.origin_load.db_reads` | int64 | No | Slots read from database |
**Why This Matters:** This is a distinct BAL-enabled operation. The BAL tells us exactly which storage slots will be modified, allowing parallel loading of their original values (needed for state update computation).
---
#### `ethereum.bal.stateroot.storage_trie`
Per-account storage trie update.
**Parent:** `ethereum.bal.stateroot`
| Attribute | Type | Req | Description |
|-----------|------|-----|-------------|
| `ethereum.bal.storage_trie.address` | string | Yes | Account address |
| `ethereum.bal.storage_trie.slots_updated` | int64 | Yes | Slots changed |
| `ethereum.bal.storage_trie.slots_deleted` | int64 | Yes | Slots cleared |
| `ethereum.bal.storage_trie.nodes_updated` | int64 | Yes | Trie nodes modified |
| `ethereum.bal.storage_trie.wiped` | bool | Yes | Storage was fully cleared |
---
#### `ethereum.bal.stateroot.account_trie`
Account trie update and hash.
**Parent:** `ethereum.bal.stateroot`
| Attribute | Type | Req | Description |
|-----------|------|-----|-------------|
| `ethereum.bal.account_trie.accounts_updated` | int64 | Yes | Accounts modified |
| `ethereum.bal.account_trie.accounts_deleted` | int64 | Yes | Accounts removed |
| `ethereum.bal.account_trie.nodes_updated` | int64 | Yes | Trie nodes modified |
| `ethereum.bal.account_trie.prefetch_hit` | bool | No | Whether prefetch helped |
---
### 5.4 Diagnostic Spans (Level 3)
---
#### `ethereum.bal.block.fallback`
Block-level fallback from parallel to fully sequential processing.
**Parent:** `ethereum.bal.block`
| Attribute | Type | Req | Description |
|-----------|------|-----|-------------|
| `ethereum.bal.fallback.reason` | string | Yes | `error`, `timeout`, `too_many_conflicts` |
| `ethereum.bal.fallback.tx_index` | int64 | No | Tx that triggered fallback |
| `ethereum.bal.fallback.parallel_duration` | float64 | Yes | Time in parallel mode before fallback |
| `ethereum.bal.fallback.error_message` | string | Cond | Error details (if reason=error) |
---
#### `ethereum.bal.state.account_read`
Individual account state read.
**Parent:** `ethereum.bal.prefetch`
| Attribute | Type | Req | Description |
|-----------|------|-----|-------------|
| `ethereum.bal.account_read.address` | string | Yes | Account address |
| `ethereum.bal.account_read.cache_hit` | bool | Yes | Found in cache |
| `ethereum.bal.account_read.exists` | bool | Yes | Account exists |
---
#### `ethereum.bal.state.storage_read`
Individual storage slot read.
**Parent:** `ethereum.bal.prefetch`
| Attribute | Type | Req | Description |
|-----------|------|-----|-------------|
| `ethereum.bal.storage_read.address` | string | Yes | Contract address |
| `ethereum.bal.storage_read.slot` | string | Yes | Storage slot |
| `ethereum.bal.storage_read.cache_hit` | bool | Yes | Found in cache |
---
### 5.5 Span Links
When a transaction is replayed after conflict, create a link from the replay span to the original parallel attempt:
```
ethereum.bal.tx.execute (parallel_status=sequential, tx.index=5) # replay
└── Link → ethereum.bal.tx.execute (parallel_status=conflicted, tx.index=5) # original
```
---
### 5.6 Example Span Tree
```
ethereum.bal.block (block.number=20000000, bal.enabled=true)
│
├── ethereum.bal.prefetch
│ ├── accounts_count=156, storage_slots_count=2340
│ ├── completed_count=2496, failed_count=0
│ ├── execution_waited_count=3, execution_waited_duration=0.002
│ └── [children: ethereum.bal.prefetch.wait spans for the 3 waits]
│
├── ethereum.bal.parallel.batch (batch_index=0, batch_size=50)
│ ├── confirmed_count=45, conflicted_count=3, not_ready_count=2
│ │
│ ├── ethereum.bal.tx.execute (tx.index=0, parallel_status=confirmed)
│ │
│ ├── ethereum.bal.tx.execute (tx.index=12, parallel_status=conflicted)
│ │ └── ethereum.bal.conflict.detect
│ │ ├── detected=true, type=storage, pattern=write_write
│ │ ├── address=0xdead..., storage_slot=0x0001
│ │ └── prior_tx_index=8
│ │
│ └── ethereum.bal.tx.execute (tx.index=23, parallel_status=not_ready)
│ └── [no conflict span - parallel never finished]
│
├── ethereum.bal.tx.execute (tx.index=12, parallel_status=sequential)
│ └── [Link → conflicted span above]
│ └── ethereum.bal.prefetch.wait (address=0xbeef..., type=storage, slot=0x0005)
│
├── ethereum.bal.tx.execute (tx.index=23, parallel_status=sequential)
│
└── ethereum.bal.stateroot (mode=parallel, accounts_updated=89, storage_tries_updated=45)
│
├── ethereum.bal.stateroot.origin_load
│ ├── accounts_count=45, slots_count=890
│ ├── parallel=true, cache_hits=650, db_reads=240
│ └── duration: 12ms
│
├── ethereum.bal.stateroot.storage_trie (address=0xabc..., slots_updated=23)
├── ethereum.bal.stateroot.storage_trie (address=0xdef..., slots_updated=8)
├── ... [43 more, parallel]
│
└── ethereum.bal.stateroot.account_trie (accounts_updated=89, nodes_updated=156)
```
---
## 6. Metrics Specification
### 6.1 Naming Conventions
- Units NOT in metric names
- UCUM units: `s` (seconds), `By` (bytes)
- Countable items: `{transaction}`, `{account}`, `{slot}`
### 6.2 Counter Metrics
| Metric | Unit | Description |
|--------|------|-------------|
| `ethereum.bal.blocks` | `{block}` | BAL-enabled blocks processed |
| `ethereum.bal.transactions.total` | `{transaction}` | Total txs in BAL blocks |
| `ethereum.bal.transactions.confirmed` | `{transaction}` | Parallel confirmed |
| `ethereum.bal.transactions.conflicted` | `{transaction}` | Parallel conflicted |
| `ethereum.bal.transactions.not_ready` | `{transaction}` | Parallel not ready in time |
| `ethereum.bal.transactions.sequential` | `{transaction}` | No parallel attempt |
| `ethereum.bal.prefetch.items` | `{item}` | Items prefetched |
| `ethereum.bal.prefetch.cache_hits` | `{item}` | Found in cache |
| `ethereum.bal.prefetch.db_reads` | `{item}` | Loaded from database |
| `ethereum.bal.prefetch.wait_events` | `{event}` | Times execution waited on prefetch |
| `ethereum.bal.conflicts.total` | `{conflict}` | Total conflicts |
| `ethereum.bal.conflicts.read_write` | `{conflict}` | Read-write conflicts |
| `ethereum.bal.conflicts.write_write` | `{conflict}` | Write-write conflicts |
| `ethereum.bal.origin_load.slots` | `{slot}` | Origin storage slots loaded |
| `ethereum.bal.blocks.fallback` | `{block}` | Blocks with full sequential fallback |
### 6.3 Histogram Metrics
#### Duration Histograms (unit: `s`)
| Metric | Buckets | Description |
|--------|---------|-------------|
| `ethereum.bal.block.duration` | 0.05, 0.1, 0.25, 0.5, 1, 2.5, 5, 10 | Total block time |
| `ethereum.bal.prefetch.duration` | 0.001, 0.005, 0.01, 0.025, 0.05, 0.1, 0.25, 0.5 | Prefetch phase |
| `ethereum.bal.prefetch.wait.duration` | 0.0001, 0.0005, 0.001, 0.005, 0.01, 0.025 | Per-wait duration |
| `ethereum.bal.tx.duration` | 0.0001, 0.0005, 0.001, 0.005, 0.01, 0.025, 0.05, 0.1 | Per-tx execution |
| `ethereum.bal.parallel.batch.duration` | 0.001, 0.005, 0.01, 0.025, 0.05, 0.1, 0.25 | Batch duration |
| `ethereum.bal.conflict.detect.duration` | 0.00001, 0.0001, 0.0005, 0.001, 0.005 | Conflict check time |
| `ethereum.bal.stateroot.duration` | 0.01, 0.025, 0.05, 0.1, 0.25, 0.5, 1, 2.5 | State root total |
| `ethereum.bal.origin_load.duration` | 0.001, 0.005, 0.01, 0.025, 0.05, 0.1 | Origin storage load |
| `ethereum.bal.storage_trie.duration` | 0.001, 0.005, 0.01, 0.025, 0.05, 0.1 | Per-account storage trie |
#### Size Histograms
| Metric | Unit | Buckets | Description |
|--------|------|---------|-------------|
| `ethereum.bal.size` | `By` | 1000, 5000, 10000, 50000, 100000, 500000, 1M | BAL encoded size |
| `ethereum.bal.accounts` | `{account}` | 10, 50, 100, 250, 500, 1000, 2500 | Accounts per block |
| `ethereum.bal.storage_slots` | `{slot}` | 100, 500, 1000, 5000, 10000, 50000 | Slots per block |
| `ethereum.bal.origin_load.slots` | `{slot}` | 50, 100, 250, 500, 1000, 2500, 5000 | Origin slots per block |
#### Ratio Histograms (unit: `1`)
| Metric | Buckets | Description |
|--------|---------|-------------|
| `ethereum.bal.parallel.confirmed_ratio` | 0, 0.25, 0.5, 0.75, 0.9, 0.95, 0.99, 1.0 | Confirmed / (confirmed + conflicted + not_ready) |
| `ethereum.bal.parallel.ready_ratio` | 0, 0.25, 0.5, 0.75, 0.9, 0.95, 0.99, 1.0 | (confirmed + conflicted) / total parallel attempts |
| `ethereum.bal.prefetch.ahead_ratio` | 0, 0.25, 0.5, 0.75, 0.9, 0.95, 0.99, 1.0 | Prefetch items ready before needed / total |
### 6.4 Gauge Metrics
| Metric | Unit | Description |
|--------|------|-------------|
| `ethereum.bal.parallel.workers_active` | `{thread}` | Active parallel workers |
| `ethereum.bal.prefetch.workers_active` | `{thread}` | Active prefetch workers |
---
## 7. Implementation Requirements
### 7.1 Performance
| Requirement | Threshold |
|-------------|-----------|
| Tracing disabled overhead | <0.1% |
| Tracing enabled overhead | <2% |
| Per-span creation | <1μs |
| Metric recording | Lock-free |
### 7.2 Configuration
```yaml
bal_tracing:
enabled: true
level: extended # core, extended, diagnostic
endpoint: localhost:4317 # OTLP gRPC
sampling:
rate: 0.1 # 10% of blocks
keep_slow: true # 100% of blocks >2x average
keep_conflicts: true # 100% of blocks with >5% conflict rate
keep_fallbacks: true # 100% of blocks with full fallback
```
### 7.3 Error Handling
- Span status `ERROR` on failures
- `error.type` attribute with classification
- `exception.message` for details
- No stack traces in production
---
## 8. Client Implementation Mapping
### 8.1 go-ethereum
| Spec Element | Code Location |
|--------------|---------------|
| `prefetch.wait` | `core/state/bal_reader.go:65-78` — `prestateResolver.account()` blocks on channel |
| `stateroot.origin_load` | `core/state/bal_state_transition.go:383-401` — parallel goroutine |
| `stateroot.storage_trie` | `core/state/bal_state_transition.go:424-463` — per-account goroutine |
| `conflict.detect` | `core/state/bal_reader.go:303-321` — `ValidateStateDiff` |
| Metrics: `StatePrefetch` | `BALStateTransitionMetrics.StatePrefetch` |
| Metrics: `OriginStorageLoadTime` | `BALStateTransitionMetrics.OriginStorageLoadTime` |
### 8.2 reth
| Spec Element | Code Location |
|--------------|---------------|
| `prefetch` / `prewarm` | `crates/engine/tree/src/tree/payload_processor/prewarm.rs:71-344` |
| `tx.execute` (prewarm) | `prewarm.rs:440-513` — `transact_batch` |
| `parallel_status=not_ready` | `prewarm.rs:464` — `terminate_execution.load()` check |
| `stateroot` | `sparse_trie.rs:54-128` — `SparseTrieTask.run` |
| `stateroot.storage_trie` | `sparse_trie.rs:168-220` — parallel via `par_bridge()` |
| Metrics | `PrewarmMetrics` in `prewarm.rs:600-615` |
### 8.3 besu
| Spec Element | Code Location |
|--------------|---------------|
| `parallel.batch` | `parallelization/ParallelizedConcurrentTransactionProcessor.java:102-132` — `runAsyncBlock` |
| `tx.execute` | `ParallelizedConcurrentTransactionProcessor.java:134-222` — `runTransaction` |
| `parallel_status` check | `ParallelizedConcurrentTransactionProcessor.java:257-258` — null check for readiness |
| `conflict.detect` | `TransactionCollisionDetector.java:54-87` — `hasCollision` |
| `conflict.pattern` | `TransactionCollisionDetector.java:69-85` — account vs storage check |
| `block.fallback` | `MainnetParallelBlockProcessor.java:152-161` |
| Counter: confirmed | `confirmedParallelizedTransactionCounter` |
| Counter: conflicted | `conflictingButCachedTransactionCounter` |
### 8.4 nethermind
| Spec Element | Code Location |
|--------------|---------------|
| State tracking | `Nethermind.Evm/State/TracedAccessWorldState.cs` |
| Storage reads | `TracedAccessWorldState.cs:58-65` — `Get(StorageCell)` |
| Balance changes | `TracedAccessWorldState.cs:25-34` — `AddToBalance` |
| Block processing | `Consensus/Processing/BlockProcessor.cs:119-199` |
---
## 9. Analysis Queries
### 9.1 Is Prefetch Ahead of Execution?
```sql
SELECT
service_name AS client,
COUNT(*) AS blocks,
AVG(prefetch_execution_waited_count) AS avg_waits_per_block,
SUM(CASE WHEN prefetch_execution_waited_count = 0 THEN 1 ELSE 0 END) * 100.0 / COUNT(*) AS pct_no_waits,
AVG(prefetch_execution_waited_duration * 1000) AS avg_wait_ms_when_waiting
FROM bal_prefetch_spans
GROUP BY client
```
**Interpretation:**
- `pct_no_waits` close to 100% = prefetch is fully ahead
- High `avg_waits_per_block` = prefetch is a bottleneck
### 9.2 Parallel Execution Breakdown
```sql
SELECT
service_name AS client,
parallel_status,
COUNT(*) AS tx_count,
COUNT(*) * 100.0 / SUM(COUNT(*)) OVER (PARTITION BY service_name) AS pct
FROM bal_tx_spans
WHERE parallel_status != 'sequential'
GROUP BY client, parallel_status
ORDER BY client, tx_count DESC
```
**Interpretation:**
- High `confirmed` % = parallel execution working well
- High `not_ready` % = parallel execution too slow, need more workers or faster execution
- High `conflicted` % = need better conflict prediction or scheduling
### 9.3 Conflict Analysis
```sql
SELECT
service_name AS client,
conflict_type,
conflict_pattern,
COUNT(*) AS conflicts,
AVG(transaction_index - prior_tx_index) AS avg_tx_distance
FROM bal_conflict_spans
WHERE detected = true
GROUP BY client, conflict_type, conflict_pattern
ORDER BY conflicts DESC
```
**Interpretation:**
- Small `avg_tx_distance` = nearby txs conflict (maybe reorder?)
- Large `avg_tx_distance` = long-range dependencies
- High `storage` + `write_write` = hot storage slots
### 9.4 Origin Storage Load Impact
```sql
SELECT
service_name AS client,
AVG(origin_load_duration_ms) AS avg_origin_ms,
AVG(stateroot_duration_ms) AS avg_stateroot_ms,
AVG(origin_load_slots_count) AS avg_slots,
AVG(origin_load_duration_ms / NULLIF(origin_load_slots_count, 0) * 1000) AS us_per_slot
FROM bal_stateroot_spans
WHERE origin_load_duration_ms IS NOT NULL
GROUP BY client
```
### 9.5 Block Fallback Rate
```sql
SELECT
service_name AS client,
DATE_TRUNC('hour', timestamp) AS hour,
COUNT(*) AS blocks,
SUM(CASE WHEN had_fallback THEN 1 ELSE 0 END) AS fallback_blocks,
SUM(CASE WHEN had_fallback THEN 1 ELSE 0 END) * 100.0 / COUNT(*) AS fallback_pct
FROM bal_block_spans
GROUP BY client, hour
ORDER BY hour DESC
```
---
## 10. References
1. [EIP-7928: Block-Level Access Lists](https://eips.ethereum.org/EIPS/eip-7928)
2. [OpenTelemetry Specification](https://opentelemetry.io/docs/specs/)
3. [OpenTelemetry Semantic Conventions](https://opentelemetry.io/docs/specs/semconv/)
4. [OTel Attribute Naming](https://opentelemetry.io/docs/specs/semconv/general/attribute-naming/)
5. [OTel Metric Guidelines](https://opentelemetry.io/docs/specs/semconv/general/metrics/)
---
## Appendix A: Stability Status
| Component | Status |
|-----------|--------|
| Level 1 spans | **Stable** |
| Level 1 metrics | **Stable** |
| Level 2 spans | **Experimental** |
| Level 2 metrics | **Experimental** |
| Level 3 spans | **Experimental** |
| Histogram buckets | **Experimental** |
---
## Appendix B: Quick Reference
### Span Hierarchy
```
ethereum.bal.block
├── ethereum.bal.prefetch
│ └── ethereum.bal.prefetch.wait (Level 2)
│ └── ethereum.bal.state.account_read (Level 3)
│ └── ethereum.bal.state.storage_read (Level 3)
├── ethereum.bal.parallel.batch (Level 2)
│ └── ethereum.bal.tx.execute
│ └── ethereum.bal.conflict.detect
├── ethereum.bal.tx.execute (for sequential/replay)
├── ethereum.bal.stateroot (Level 2)
│ ├── ethereum.bal.stateroot.origin_load (Level 2)
│ ├── ethereum.bal.stateroot.storage_trie (Level 2)
│ └── ethereum.bal.stateroot.account_trie (Level 2)
└── ethereum.bal.block.fallback (Level 3)
```
### Key Attributes Quick Reference
| Question | Attribute |
|----------|-----------|
| Did prefetch help? | `prefetch.execution_waited_count` (0 = yes) |
| Did parallel work? | `tx.parallel_status` = `confirmed` |
| Why not parallel? | `tx.parallel_status` = `conflicted` vs `not_ready` |
| What conflicted? | `conflict.type`, `conflict.pattern`, `conflict.address` |
| Who caused conflict? | `conflict.prior_tx_index` |
| Did block fall back? | `block.fallback` span exists |
---
## Changelog
- **v2.1.0** (2025-12-03): Major improvements
- Added `parallel_status` with 4 states (confirmed, conflicted, not_ready, sequential)
- Added prefetch-execution timing (`execution_waited_count`, `execution_waited_duration`)
- Added `prefetch.wait` span for per-item wait tracking
- Added `stateroot.origin_load` span for BAL-enabled origin storage loading
- Added conflict pattern (`read_write`, `write_write`) and `prior_tx_index`
- Added `block.fallback` span for catastrophic fallback
- Added `ready_ratio` and `ahead_ratio` histogram metrics
- Reorganized client implementation mapping with specific line references
- Added analysis queries with interpretation guidance
- Added quick reference appendix
- **v2.0.0** (2025-12-03): Aligned with OTel conventions, added conformance levels
- **v1.0.0** (2025-12-03): Initial specification