Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
170 changes: 170 additions & 0 deletions BAL_TRACING.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,170 @@
# EIP-7928 Block-Level Access Lists OpenTelemetry Tracing

This document describes the OpenTelemetry tracing implementation for EIP-7928 Block-Level Access Lists (BAL) in Besu.

## Overview

The implementation provides comprehensive OpenTelemetry tracing for BAL as specified in the EIP-7928 BAL OTel specification, including:

- Structured span hierarchy for block processing
- Counter and histogram metrics
- Configurable tracing levels
- Performance-optimized implementation

## Span Hierarchy

```
ethereum.block
├── ethereum.bal.prefetch
│ ├── ethereum.bal.prefetch.account (optional)
│ └── ethereum.bal.prefetch.slot (optional)
├── ethereum.tx.execute (per transaction)
└── ethereum.stateroot
```

## Key Components

### BalOtelTracer
Main tracer class that manages the complete span hierarchy. Integrates with `AbstractBlockProcessor` to provide tracing for block processing.

### BalMetrics
Implements all counter and histogram metrics as defined in sections 6.2 and 6.3 of the specification:

**Counters:**
- `ethereum.blocks.total`
- `ethereum.tx.total`
- `ethereum.bal.blocks.total`
- `ethereum.bal.prefetch.accounts`
- `ethereum.bal.prefetch.slots`
- `ethereum.bal.prefetch.cache_hits`
- `ethereum.bal.prefetch.cache_misses`

**Histograms:**
- `ethereum.block.duration`
- `ethereum.tx.duration`
- `ethereum.stateroot.duration`
- `ethereum.throughput.mgas_per_sec`
- `ethereum.bal.prefetch.duration`
- `ethereum.bal.size`

### BalPrefetchTracer
Specialized tracer for BAL prefetch operations with optional per-account and per-slot child spans.

### BalTracingConfig
Configuration class for enabling/disabling BAL tracing and setting OTLP endpoints.

## Usage

### Basic Configuration

```java
// Enable BAL tracing with default settings
BalTracingConfig config = BalTracingConfig.defaultEnabled();

// Create tracer
BalOtelTracer balTracer = new BalOtelTracer(
openTelemetryTracer,
metricsSystem,
chainId,
config.isEnabled(),
config.isDetailedTracingEnabled()
);
```

### Integration with Block Processing

The tracing is automatically integrated into `AbstractBlockProcessor`. To enable it, pass a `BalOtelTracer` instance to the constructor:

```java
AbstractBlockProcessor processor = new MainnetBlockProcessor(
transactionProcessor,
transactionReceiptFactory,
blockReward,
miningBeneficiaryCalculator,
skipZeroBlockRewards,
gasBudgetCalculator,
balTracer // Optional - pass null to disable BAL tracing
);
```

### Configuration Options

```java
// Disabled tracing
BalTracingConfig disabled = BalTracingConfig.disabled();

// Detailed tracing with per-account/slot spans
BalTracingConfig detailed = BalTracingConfig.detailedEnabled("production");

// Custom configuration
BalTracingConfig custom = new BalTracingConfig(
true, // enabled
false, // detailed tracing
"localhost:4318", // OTLP endpoint
0.1, // sampling rate (10%)
"staging" // environment
);
```

## Performance Characteristics

The implementation is designed to meet the performance requirements specified in section 7:

- **Tracing disabled overhead:** < 0.1% (null checks and early returns)
- **Tracing enabled overhead:** < 2% (lazy attribute setting, minimal span creation)
- **Per-span creation:** < 1μs (efficient OpenTelemetry usage)

## Resource Attributes

All spans include the following resource attributes as per section 4:

- `service.name`: "besu"
- `service.version`: Current Besu version
- `deployment.environment`: Configured environment
- `ethereum.chain.id`: Chain ID

## BAL Data Model

The implementation includes placeholder BAL data model classes:

### BlockAccessList
Represents a complete BAL for a block with:
- Block hash
- Map of addresses to access list entries
- Size in bytes
- Utility methods for counts and lookups

### BlockAccessListEntry
Represents individual entries with:
- Storage slot keys accessed
- Code access flag
- Utility methods for access checks

## Future Integration

This tracing implementation is designed to work with the actual EIP-7928 BAL implementation when it becomes available. The `extractBlockAccessList` method in `AbstractBlockProcessor` is a placeholder that should be replaced with actual BAL extraction logic.

## Testing

Comprehensive unit tests are provided for all major components:
- `BalOtelTracerTest`: Tests tracer functionality and span management
- `BalMetricsTest`: Tests metrics recording and timers
- `BlockAccessListTest`: Tests BAL data model

## Configuration Files

The tracing can be enabled through Besu configuration. Future work should include:
- Command-line options for BAL tracing
- Configuration file settings
- Environment variable support

## Monitoring and Observability

When enabled, the tracing provides rich observability into:
- Block processing performance
- Transaction execution timing
- State root calculation performance
- BAL prefetch effectiveness
- Cache hit/miss ratios

This data can be exported to any OpenTelemetry-compatible observability platform (Jaeger, Zipkin, etc.) for analysis and monitoring.
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
/*
* Copyright ConsenSys AG.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
*
* SPDX-License-Identifier: Apache-2.0
*/
package org.hyperledger.besu.ethereum.bal;

import org.hyperledger.besu.ethereum.core.Address;
import org.hyperledger.besu.ethereum.core.Hash;

import java.util.List;
import java.util.Map;
import java.util.Objects;

/**
* Represents a Block-level Access List (BAL) as defined in EIP-7928.
* This is a data structure that tracks which accounts, storage slots, and code
* are accessed during block execution to enable prefetching optimizations.
*/
public class BlockAccessList {

private final Hash blockHash;
private final Map<Address, BlockAccessListEntry> entries;
private final long sizeBytes;

/**
* Creates a new BlockAccessList.
*
* @param blockHash The hash of the block this BAL belongs to
* @param entries Map of addresses to their access list entries
* @param sizeBytes Total size of the BAL in bytes
*/
public BlockAccessList(
final Hash blockHash,
final Map<Address, BlockAccessListEntry> entries,
final long sizeBytes) {
this.blockHash = Objects.requireNonNull(blockHash, "blockHash cannot be null");
this.entries = Objects.requireNonNull(entries, "entries cannot be null");
this.sizeBytes = sizeBytes;
}

/** @return The hash of the block this BAL belongs to */
public Hash getBlockHash() {
return blockHash;
}

/** @return Map of addresses to their access list entries */
public Map<Address, BlockAccessListEntry> getEntries() {
return entries;
}

/** @return Total number of accounts in the BAL */
public int getAccountsCount() {
return entries.size();
}

/** @return Total number of storage slots across all accounts in the BAL */
public int getStorageSlotsCount() {
return entries.values().stream()
.mapToInt(entry -> entry.getStorageKeys().size())
.sum();
}

/** @return Number of accounts that have code access */
public int getCodeCount() {
return (int) entries.values().stream()
.filter(BlockAccessListEntry::hasCodeAccess)
.count();
}

/** @return Total size of the BAL in bytes */
public long getSizeBytes() {
return sizeBytes;
}

/** @return List of all addresses in the BAL */
public List<Address> getAddresses() {
return List.copyOf(entries.keySet());
}

/**
* Gets the access list entry for a specific address.
*
* @param address The address to look up
* @return The access list entry for the address, or null if not present
*/
public BlockAccessListEntry getEntry(final Address address) {
return entries.get(address);
}

/**
* Checks if the BAL contains an entry for the specified address.
*
* @param address The address to check
* @return true if the address is in the BAL, false otherwise
*/
public boolean containsAddress(final Address address) {
return entries.containsKey(address);
}

/** @return true if the BAL is empty (no entries), false otherwise */
public boolean isEmpty() {
return entries.isEmpty();
}

@Override
public boolean equals(final Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
final BlockAccessList that = (BlockAccessList) o;
return sizeBytes == that.sizeBytes
&& Objects.equals(blockHash, that.blockHash)
&& Objects.equals(entries, that.entries);
}

@Override
public int hashCode() {
return Objects.hash(blockHash, entries, sizeBytes);
}

@Override
public String toString() {
return String.format(
"BlockAccessList{blockHash=%s, accountsCount=%d, storageSlotsCount=%d, codeCount=%d, sizeBytes=%d}",
blockHash.toHexString(),
getAccountsCount(),
getStorageSlotsCount(),
getCodeCount(),
sizeBytes);
}
}
Loading