# 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