github besu-eth/besu 26.10.0

3 hours ago

This release contains security fixes. Operators are advised to upgrade promptly. Details will be published in security advisories on besu-eth/besu after operators have had an opportunity to upgrade.

Headline: Amsterdam scheduled on Hoodi

  • The Amsterdam hard fork is scheduled on Hoodi at timestamp 1793036568 (Mon, 26 Oct 2026, 17:42:48 UTC). Hoodi node operators must upgrade before then. [#11531]

Action required on upgrade

  • Bonsai database auto-upgrade on first start (DB v4 / archive v3): stores jump-dest analysis alongside code. Takes a few minutes on mainnet, and the DB can no longer be opened by an older Besu afterward. A new besu storage revert-code-format subcommand can roll it back if you need to downgrade. [#11327, #11364]
  • Default discovery mode is now BOTH (DiscV4 + DiscV5 concurrently). Set --discovery-mode=V4 (or V5) to pin to one. [#11344]
  • Chain-pruning retention increased for EIP-4444/EIP-7928 history expiry — defaults/minimums rise from 113056 to 457568 blocks (more disk retained). [#11316]
  • Amsterdam rejects transactions with a gas limit above the EIP-8037 cap (2^32-1). [#11414]

Upcoming Breaking Changes

  • Plugin API
    • PluginTransactionSelectorFactory.create(final SelectorsStateManager selectorsStateManager) is deprecated for removal
    • MiningService is deprecated for removal and will be removed in a future release, with no replacement. It has no known usage
    • BesuEvents is deprecated and will be removed at the breaking release. Its event families are available on BlockchainService, TransactionPoolService and SynchronizationService through subscribe* methods returning a Subscription handle; TTDReachedListener has no replacement, since it never had an add method. #11283
    • The plugin API is being reorganized into per-feature modules (#10820). Nothing has changed for plugin authors yet: besu-plugin-api re-exports every module, so existing plugins compile and run unmodified. A future release will apply the breaking changes, batched into a single break with a migration guide:
      • packages are renamed to match their module, for example org.hyperledger.besu.plugin.services.storage becomes org.hyperledger.besu.plugin.storage, with contracts plugins implement moving under an spi sub-package
      • contracts that serve several unrelated audiences are split apart, and contracts that overlap are merged into one
      • types and methods whose names no longer describe what they do are renamed, and a number of signatures change
      • contracts and methods that are deprecated, superseded, or have no known usage are removed
    • The plugin lifecycle is being redesigned. The phases a plugin goes through, the services available in each of them, and the way a plugin obtains those services are all expected to change, and the changes will not be source compatible.
    • PluginVersionsProvider, plugin.data.Request, plugin.data.Restriction, plugin.data.UnsignedPrivateMarkerTransaction and plugin.data.Signature are deprecated for removal, with no replacement. None is reachable through any plugin service or data contract: the three privacy types were orphaned when private transaction support was removed, Request is implemented internally but never exposed, and PluginVersionsProvider is internal --version plumbing
  • --Xbft-legacy-protocol-encoding will be removed once Besu 25.x is no longer supported. #10499
  • --snapsync-synchronizer-pre-checkpoint-headers-only-enabled is deprecated (since 26.8.1) and will be removed in a future release; the flag is now a silent no-op.
  • --rpc-tx-feecap will treat a value of 0 as limiting fees to 0. Today it treats 0 as "do not cap fees". To achieve similar behaviour set it to a suitably large value to effectively prevent any fee capping.

Breaking Changes

  • --Xsnapsync-synchronizer-pivot-block-distance-before-caching has been removed; it was deprecated as a no-op since 26.6.1. #11499

  • The default discovery mode is now BOTH: nodes run DiscV4 and DiscV5 concurrently unless --discovery-mode=V4 or --discovery-mode=V5 selects a single protocol. #11344

  • trace_call and trace_callMany now select transaction validation as eth_call does, including its strict flag. Unless strict is true, a call whose gasPrice, maxFeePerGas and maxPriorityFeePerGas are all zero or omitted runs with GASPRICE and BASEFEE 0 and pays no execution gas fees. Previously such a call was rejected as underpriced at a block with a base fee, or, with its fees omitted, was charged at the base fee. Calls that eth_call validates and charges are still validated and charged, each call in a trace_callMany bundle is priced on its own, and, as in eth_call, a nonce above the sender's is accepted. A trace_call that fails validation now returns the reason, such as Gas price below current base fee (-32009), instead of Internal error. #11404

  • Chain data pruning now retains blocks and block access lists for the EIP-4444 history expiry window (HISTORY_PRUNE_EPOCHS = 14299 epochs) instead of the weak subjectivity period (3533 epochs), as required by EIP-7928. The default and minimum of --Xchain-pruning-blocks-retained, --Xchain-pruning-bals-retained and --Xchain-pruning-retained-minimum change from 113056 to 457568 blocks. #11316

  • trace_transaction now returns null for an unknown transaction hash instead of an empty array, matching trace_get, eth_getTransactionByHash, Erigon and Reth. An empty array no longer means the transaction is unknown. #11379

  • trace_block and trace_replayBlockTransactions now return a Block not found error (-32000) for a block number that is not on the chain instead of a null result, as trace_call and trace_callMany already do. #11381

  • trace_filter now honours blockHash and traces exactly that block, as eth_getLogs does. Previously the member was parsed and ignored, so {"blockHash": ...} traced the latest block, and with fromBlock or toBlock the hash was dropped. A non-null blockHash combined with a non-null fromBlock or toBlock is now rejected with Invalid filter params (-32602). An unknown or noncanonical hash returns Block not found (-32000), a canonical block without a stored body, as after history pruning, returns Pruned history unavailable (4444), and a block that cannot be traced on its parent state returns World state unavailable (-32000), including with count: 0. A null blockHash is still ignored. #11407

  • Bonsai databases storing code by its hash are upgraded on first start to store the jump destination analysis next to the code (Bonsai database version 4, archive version 3); the upgrade takes a few minutes on mainnet, after which the database cannot be opened by an older version of Besu. #11327

Additions and Improvements

  • Update Bouncycastle to 1.85 to address CVEs CVE-2026-8763 and CVE-2026-13506. #11336
  • Update Jackson to 2.21.6 to address CVE CVE-2026-68497 #11396
  • Update Jackson to 2.21.7 to address CVE CVE-2026-91777 #11503
  • PoaQueryService and BftQueryService are no longer deprecated. #11376
  • Add engine_newPayloadWithWitnessV5 whose VALID response also carries the EIP-8025 execution witness #11181
  • Reference tests no longer keep a second copy of the execution-spec fixtures under build/resources, and the new Gradle property besu.referenceTests.fixturesDir lets all checkouts and worktrees share one unpacked copy of each fixture version. #11413
  • Add debug_getRawExecutionRequests, which re-executes a block and returns the EIP-7685 execution requests it produced, in the Engine API executionRequests form (null before Prague). #11481
  • Add --include-bals option to besu blocks export, writing a <to>.bals sidecar with BALs for each exported block. #11042
  • callTracer now honours the withLog tracer option. Each call frame that emitted logs carries a logs array of {address, topics, data, position, index}, where index equals the receipt logIndex of the same log and position is the number of subcalls the frame had made when the log was emitted. Logs of reverted frames are omitted, as specified in execution-apis#855. #11342
  • Warn at startup when a PoA chain on Amsterdam leaves the EIP-8282 builder deposit or exit request contract address out of the genesis, since blocks are invalid from the fork unless a contract is deployed at the default address. #NNNN
  • The jump destination analysis of contract code is computed once, when the code is stored, instead of on every code cache miss. #11327
  • EVM v2: add AND, OR, XOR and NOT. #11477
  • Schedule the Amsterdam fork on Hoodi at timestamp 1793036568 (Mon, 26 Oct 2026, 17:42:48 UTC). #11531
  • The EVM no longer calls the operation tracer for every operation when tracing is disabled.
  • New besu storage revert-code-format subcommand reverts the contract code storage and the database metadata, so that a database upgraded to store the jump destination analysis can be opened by an older Besu version again; Besu logs how to run it after the upgrade. #11364

Bug fixes

  • Receipts received from a peer carrying a transaction type byte that no type defines are now rejected on decode, on both the EIP-2718 and eth/69 paths.
  • evmtool t8n-server keeps serving requests again. Since evmtool began exiting with the subcommand's exit code, the server process exited right after printing Transition server listening on, so filling tests with Besu as the transition tool failed. A server that cannot bind its port now exits with a non-zero code instead of 0. #11555
  • DNS-discovered dual-stack peers now respect --p2p-ipv6-outbound-enabled when choosing the connection address. #11422
  • Block propagation no longer repeatedly re-requests an announced block that is already saved as pending while waiting for its parent. The repeated header and body requests could use up the announcing peer's request capacity and starve the parent request, leaving a node that connects well behind the chain head stuck at its starting block. #11517
  • admin_logsRemoveCache now returns an error when any log bloom cache segment could not be deleted, instead of reporting Cache Removed after a partial deletion. #11067
  • txparse --corpus-file now closes the corpus file stream after processing instead of leaking the underlying file descriptor (it previously suppressed the StreamResourceLeak warning rather than releasing the resource). #11423
  • besu blocks export --include-bals no longer truncates an existing RLP output when the BAL sidecar cannot be opened. #11467
  • eth_simulateV1 blocks after Amsterdam now carry slotNumber (the parent's plus one), a block access list that includes the system calls, and a header gasUsed that follows EIP-8037, as real blocks do. Previously slotNumber was missing, and the block hash differed from other clients. #11394
  • eth_simulateV1 now returns -38014 when a call's value exceeds the sender balance. It returned -32603. #11394
  • Publish the chain head only after sync block bodies and receipts are committed, so readers cannot observe a head absent from storage. #10842
  • Transaction objects returned by eth_getTransactionBy*, eth_getBlockBy* with full transactions, eth_simulateV1 and the txpool methods now carry "to": null for a contract creation instead of omitting the field, as other clients do and the execution-apis schema describes. #11434
  • debug_traceCall now accepts a block hash as its block parameter, as execution-apis specifies. A hash was rejected with -32602 Invalid block param; an unknown hash now returns -32000 Block not found, as an unknown block number does. #11433
  • A contract created from within a system call now gets its initial nonce and deployed code. SystemCallProcessor ran every frame with the message-call processor, so a CREATE in a system call's code, reachable when a system contract's code is replaced on a custom or test chain, left the new account with nonce 0 and ran its initcode without deploying anything.
  • Block access lists now record the net change of the withdrawals and the system calls that share the post-execution index, measured from the state at the start of that index, and include writes made in a system call's own frame. When replaced system contract code changed a value across calls, or changed it twice, each call was diffed against the one before it, giving extra or duplicate entries.
  • trace_callMany now treats each call as a separate transaction for EIP-2200/EIP-3529 SSTORE gas and refunds. A call writing a slot that an earlier call in the bundle had written was priced against the slot's value before the bundle, for example charging 2,800 gas less for a nonzero-to-nonzero write since Berlin. #11365
  • BlobCache.restoreBlob now reports that a blob transaction cannot be restored instead of throwing NullPointerException when any of its blobs is no longer cached.
  • DiscV5 discovery now throttles to --Xv5-discovery-interval-seconds (default 30s) once connected peers reach --Xv5-minimum-peer-ratio of --max-peers, instead of stopping peer search entirely. Below that ratio it runs at --Xv5-fast-discovery-interval-seconds (default 1s). #11344
  • Backward sync no longer retries a block every few milliseconds when its parent world state is unavailable. The forward sync step swallowed the error meant to stop backward sync, so the same block was fetched, validated and logged in a tight loop. The Backward sync halted warning is now also logged only once per block. #11303
  • JSON-RPC response streaming no longer parks a Vert.x worker thread for the full backpressure timeout after the client has gone away, so a client that stops reading a large response can no longer starve the worker pool that also serves the Engine API. #11146
  • Every method advertised by engine_exchangeCapabilities is now registered: on networks without Amsterdam the Amsterdam engine methods answer -38005 Unsupported fork where a fork check applies, instead of -32604 Method not enabled, and engine_getBlobsV4 is served from Osaka on. #11425
  • The transaction pool is no longer disabled, leaving built blocks empty and engine_getBlobs returning null, when peers on a different chain report a higher head than the one the consensus client is driving. #11424
  • trace_callMany now answers a bundle with an invalid call with a JSON-RPC error. Previously it returned the complete error response ({"jsonrpc":"2.0","id":1,"error":{...}}) as the result of a successful response. An unavailable world state now returns World state unavailable instead of a null result. #11401
  • txpool_besuPendingTransactions: the gasPrice filter no longer fails on EIP-1559 transactions, and a negative limit is rejected as an invalid parameter. #11374
  • engine_getPayload no longer waits up to 500ms before returning an empty block when no block is being built. #11426
  • trace_call and trace_callMany validate unsigned calls as the draft trace spec and the other clients do. A trace_callMany bundle with an invalid call returns that call's validation error, such as Gas price below current base fee (-32009), instead of Internal error. A positive maxPriorityFeePerGas above the maxFeePerGas is reported as Max priority fee per gas exceeds max fee per gas before a fee cap below the base fee, also in eth_call. A supplied nonce is accepted but neither validated nor used: the call runs at the sender's nonce, which a CREATE address derives from. The blob fee is priced on its own, in the simulation path shared with eth_call, eth_estimateGas and debug_traceCall: unless strict is true, a blob call whose maxFeePerBlobGas is omitted or 0 runs with BLOBBASEFEE 0 and pays no blob fee, a positive maxFeePerBlobGas is priced at the block's blob base fee instead of 1 and charged, and a blob call priced for gas pays for its gas instead of running unpriced. Calls without blob fields are unchanged. A call that sets gasPrice together with maxFeePerGas or maxPriorityFeePerGas is rejected with -32602 in eth_call, eth_estimateGas, eth_createAccessList, debug_traceCall, trace_call and trace_callMany, as in Geth, instead of ignoring gasPrice. A gasPrice with an authorizationList serves as both fee caps instead of failing with Internal error, and maxFeePerGas or maxPriorityFeePerGas before London are rejected instead of dropped. #11432
  • GraphQL sendRawTransaction accepts typed transactions in their standard encoding (type || rlp(payload)), as eth_sendRawTransaction does. It decoded the data as a block body transaction, so an EIP-1559 transaction was rejected with -32602 Invalid params. #11444
  • debug_traceBlock, debug_traceBlockByHash and debug_traceBlockByNumber include returnData in struct logs when enableReturnData is set, as debug_traceTransaction and batched requests already do. #11450
  • eth_estimateGas and eth_createAccessList now accept a block hash as well as a block number or tag. #11380
  • A failing EIP-4788 beacon roots or EIP-2935 history storage system call no longer invalidates the block. #11415
  • Publish the jump destination analysis of a contract safely to other threads #11403
  • Retry bootnodes while under-peered to avoid a node on a small network staying at zero peers. #11368
  • The Bonsai code cache and the EVM jump destination cache refuse to store empty code under a non-empty code hash. #11420
  • When fetching announced transactions from a peer, Besu again requests them in the order they were announced and, after a partial response, asks only for the ones not yet returned. Since 26.6.0 the request order did not follow the announcements and every retry asked for the whole batch again, so a peer that returned the whole batch was asked for it a second time unless the last transaction it sent was also the last one announced. #11465
  • Replacing a payload build because the consensus client sent new payload attributes is now logged at debug level, with the inputs that changed, instead of as a warning. #11504
  • A block build whose transaction selection timed out no longer closes its world state while a transaction is still executing on it. #11473
  • The block creation timing logged for an empty payload now shows when that block was built, instead of the process start time. #11461
  • Amsterdam now rejects transactions with a gas limit above the EIP-8037 cap of 2^32-1. #11414
  • Receipt sync no longer rejects a partial receipt list from a peer when one of its transactions used more than 45M gas. #11414
  • A client closing the connection during a streamed JSON-RPC response is logged as a closed connection instead of an error. #11459
  • A transaction whose signature does not recover a public key is rejected as Invalid signature instead of causing an internal error. #11458
  • The finalized block is updated once at the end of a backward sync session, instead of once per forkchoice update received during it. #11457
  • Backward sync, the Engine API and block import no longer move the head world state concurrently, which could corrupt it and stall the node. #11402
  • The layered transaction pool no longer gets corrupted when a sender with priority is promoted from the sparse layer, or when a late block selection result refers to a transaction the pool has since replaced or removed. #11462
  • Snap sync now restarts instead of staying idle when one of its world state download pipelines fails. #11515
  • Snap sync no longer downloads the storage and code of an account twice when an account range response goes past the end of the range. #11516
  • Snap sync no longer stalls when a download completes while the pipeline stage is handing on another result. #11514

36ddd158df8ce15c613f0ae9fa66b628f7a31ec3bc09de02efa09c0ae11f41b1 besu-26.10.0.zip
927429dd887aca3deb09196c42c88f883258b97d3b9269a12e0204f00d8a5d96 besu-26.10.0.tar.gz

Don't miss a new besu release

NewReleases is sending notifications on new releases.