Besu Documentation

repository·main·Indexed 23 days ago

https://github.com/besu-eth/besu

Besu is an Apache 2.0 licensed, Mainnet-compatible Ethereum execution client written in Java, designed for public and private networks. This documentation covers development tasks including the Engine API architecture, OpenTelemetry tracing setup, the EVM Tool for testing and validation, and guidelines for creating acceptance test plugins.

Tokens
36.7K
Snippets
63
Records
338
Agent score
83%

What's inside Besu

  1. Core Features in Besu 1.0

    main

    Besu version 1.0 introduced several foundational features for private and authenticated networks:

    • IBFT 2.0 Support: Implementation of the Istanbul Byzantine Fault Tolerance 2.0 consensus mechanism.
    • Permissioning: A system to control access to the network and specific RPC methods.
    • JSON-RPC Authentication: Support for securing JSON-RPC interfaces, including JWT-based authentication.
    • Genesis Contract Code: Support for including contract code directly in the genesis block.
    • RLP Encode Subcommand: A new CLI subcommand for RLP encoding.
  2. How ParallelStoredMerklePatriciaTrie works

    main

    The ParallelStoredMerklePatriciaTrie is designed to speed up batch updates to a Merkle Patricia Trie by processing independent branches across multiple CPU cores. Instead of updating keys one-by-one, it uses a three-phase approach:

    1. Accumulation Phase: Collects all updates without applying them.
    2. Parallel Processing Phase: Recursively applies updates, splitting work into parallel tasks when beneficial.
    3. Persistence Phase: Writes all modified nodes to storage in a single batch.

    Parallelization is achieved by grouping updates by their next hex nibble at branch nodes. If a group contains multiple updates and the branch has multiple active groups, the work is dispatched to a thread pool. Otherwise, it is processed sequentially to avoid task creation overhead.

    Key architectural principles include:

    • Immutability: Nodes are never modified in place; every update creates a new node instance, allowing safe concurrent reads.
    • Independence: Updates to different children of a branch node (e.g., child[A] vs child[F]) are data-independent and can run in parallel.
    • Deferred Persistence: A CommitCache (a thread-safe map) collects nodes during parallel processing to allow a single, batched I/O flush at the end.
  3. Configure transaction pool limits

    main
    Starting with version 22.7.3, you can limit transaction pool consumption by a specific sender to a configurable percentage of the total pool size. This helps prevent a single sender from filling the pool with non-executable transactions that could lead to empty or semi-empty block proposals.
  4. How Engine API methods are architected and extended

    main

    The Besu Engine API (engine_* JSON-RPC methods) uses a strict versioning pattern based on sealed class hierarchies. This ensures that each new version of a method (e.g., engine_forkchoiceUpdatedV2) extends the previous version (e.g., engine_forkchoiceUpdatedV1) and only overrides the specific hooks required by the specification change.

    Key Architectural Components

    • Sealed Class Hierarchies: Method series (like EngineNewPayloadV*) are implemented as sealed classes where version $N$ extends $N-1$. The latest version is marked final.
    • Base Class: All versions extend ExecutionEngineJsonRpcMethod, which handles fork-window validation using minSupportedFork and firstUnsupportedFork.
    • Constructor Pattern: Migrated series use a single ExecutionEngineJsonRpcMethod.ConstructorArguments record (built via ConstructorArgumentsBuilder) instead of positional arguments. This allows the VersionScheduler to manage versioning through a shared factory shape.
    • Data Structures: Request parameters (in ..internal.parameters) and results (in ..internal.results) also follow sealed hierarchy patterns to mirror specification versions.
    • Hooks: Version classes override protected hooks (e.g., createResponse, validateParameters) rather than re-implementing the entire request flow.
  5. Parallelization Decision Logic for Trie Updates

    main

    The ParallelStoredMerklePatriciaTrie uses specific rules to decide whether to spawn a new parallel task or continue processing sequentially. This prevents the overhead of thread management from outweighing the benefits of concurrency.

    A group of updates is assigned to parallel processing only if both of the following conditions are met:

    1. The group contains more than 1 update.
    2. The branch has more than 1 active group (i.e., updates are destined for different children).

    Summary of behaviors:

    • Single update in a group: Processed sequentially (faster for tiny tasks).
    • Multiple updates to the same child: Processed sequentially at the current level, but may parallelize further down the tree if that child is a branch.
    • Multiple updates to different children: Processed in parallel via a thread pool.
  6. How Bonsai cross-block cache and versioning work

    main

    When bonsaiCrossBlockCacheEnabled is enabled, Besu uses a versioned cross-block cache to manage accounts and storage slots. This design allows a single shared cache to remain correct across reorgs, snapshots, and head reads by using a global monotonic version instead of block numbers.

    Key Concepts

    • Global Monotonic Version: Managed by VersionedCacheManager, this version increments on every successful world-state commit (via CachedUpdater.commit()). It is independent of block height; even if a reorg causes a block height to repeat, the cache version continues to move forward, ensuring different histories do not incorrectly reuse cache generations.
    • Pinned Versions:
      • Head Storage: The cacheVersion is updated to match the globalVersion after every commit.
      • Snapshot Storage: When a snapshot is created (BonsaiSnapshotWorldStateKeyValueStorage), it captures the current cacheVersion and keeps it immutable. This pins the snapshot to a specific logical cache epoch.
    • Cache Entry Lifecycle: The cache maps a key to a VersionedValue. If a write occurs at version V and the existing entry is older (existing.version < V), the old entry is replaced. The cache does not retain multiple versions of the same key simultaneously.

    Read Path Logic

    For a read request getFromCacheOrStorage(segment, key, version, storageGetter):

    1. Cache Hit: Occurs if an entry exists and cachedEntry.version <= readerVersion. The reader can see cached data as long as it is not newer than their pinned epoch.
    2. Cache Miss: If no valid entry is found, the value is loaded from storage via storageGetter.
    3. Cache Population: A miss only populates the cache if readerVersion == globalVersion. This ensures the moving head can warm the cache, while stale snapshots (where readerVersion < globalVersion) read from storage without overwriting the head's cache state with potentially stale data.

    Cached Segments

    The VersionedCacheManager only caches the following segments:

    • ACCOUNT_INFO_STATE
    • ACCOUNT_STORAGE_STORAGE
  7. Configure JWT authentication for Engine APIs in Besu 22.1.2

    main
    Starting with version 22.1.2, Besu adds JWT authentication to Engine APIs to support the Execution Layer (The Merge). This is required for secure communication between the Execution Client and the Consensus Client.
  8. How to analyze Besu profiling flamegraphs

    main

    After profiling, open the generated HTML file (e.g., /tmp/besu-profile.html) in a browser.

    Reading the Flamegraph:

    • Top boxes: Represent methods actively running when CPU samples were taken.
    • Boxes underneath: Represent the call stack leading up to the active methods.
    • Width: Indicates the total time spent in that method (including its children).
      • Wide boxes indicate potential performance hot spots.
    • Height: Indicates the depth of the call chains.