github NethermindEth/nethermind 2.2.0
v2.2.0

4 hours ago

Release notes

586 merged pull requests since 2.1.0, from 31 contributors.

Important

Hoodi: Glamsterdam activates on Mon, 26 Oct 2026, 17:42:48 UTC (timestamp 1793036568, epoch 132352). Hoodi nodes must run 2.2.0 or later before then. Your consensus client must support Glamsterdam on Hoodi as well.

Note

Upgrading from 2.0.x or 1.39.x? The breaking changes below cover 2.1.0 → 2.2.0 only. Apply the 2.1.0 breaking changes first (and, from 1.39.x, the 2.0.0 breaking changes and upgrade checklist).

For most nodes this is an in-place upgrade with no resync and no config change. Read Breaking changes first if you set JsonRpc.Host / JsonRpc.EngineHost to a host name, run with Init.DiagnosticMode=VerifyTrie or FlatDb.ImportFromPruningTrieState=true, use KeyStore.BlockAuthorAccount, pipeline requests over WebSocket or IPC, consume Parity-style trace_* output, or build plugins against Nethermind.

Highlights

  • Amsterdam (Glamsterdam) on Hoodi. The fork is scheduled at 1793036568 (Mon, 26 Oct 2026, 17:42:48 UTC), and Hoodi's chainspec now carries its correct beacon-chain genesis timestamp. (#14489, #13732)
  • Catch up finalized blocks from block access lists. On chains with EIP-7928, a node that is behind rebuilds the state of consensus-finalized ancestors from their verified block access lists and receipts instead of executing them, and executes only the unfinalized tail. It is on by default (Sync.ReconstructFinalizedStateFromBlockAccessLists=true). Missing data or a commitment mismatch falls back to execution. (#14157)
  • Faster tracing on Amsterdam blocks. A single-transaction trace (debug_traceTransaction, trace_transaction, trace_replayTransaction) on a block with a block access list no longer replays the transactions before the target, and whole-block traces run in parallel. All parallel block tracing now shares one node-wide budget, JsonRpc.TraceBlockParallelism (default 4). (#13906, #14363)
  • Blocks that load many large contracts. Glamsterdam allows 64 KiB contracts and 200M-gas blocks, so one block can touch ~58k distinct maximum-size contracts. The code DB moves from Snappy to LZ4 (mean block time on such a devnet range 2.6 s → 1.7 s, no block ≥ 6 s), missed code is copied once instead of twice (allocations per block 7.17 → 3.59 GB), and a block keeps the code it loaded for the rest of the block. (#13907, #14086, #14322)
  • Lower engine_newPayload latency. Storage trie nodes on the flat backend are written after the block is reported valid, committed storage writes are applied on an idle thread during execution, block-processing stages share one worker budget, sender recovery no longer competes with the transactions root, Kestrel reads large payload bodies in 64 KiB blocks, and the payload pipeline is warmed before RPC opens (first-payload latency 66.8 ms → 5.4 ms in the PR's measurement). (#14058, #14059, #14182, #14164, #14213, #14443, #13931, #14481)
  • Prewarmer hand-off. Block processing takes over a transaction the prewarmer already executed when its recorded footprint matches the state the block has reached, instead of executing it again (hidden Blocks.PreWarmHandoff, on by default; not used for blocks with a block access list or under EIP-8037). (#14312, #14476)
  • Parity-style trace standardisation. A long series of trace_* fixes aligns Nethermind with the draft trace spec (execution-apis#890) and with Erigon/Reth: failed precheck frames, reverted-frame results, Parity error labels, trace_filter mode/blockHash, trace_get by traceAddress path, null for unknown transactions, and stricter parameter validation. Several of these change output; see Breaking changes. (#13957, #13981, #13857, #14111, #13858, #13937)
  • Geth-compatible debug tracing. keccak256PreimageTracer, Geth opcode-logger byte limits, debug_traceCall txIndex, block-wide callTracer log indices, corrected trace-file records, and per-request V8 runtimes so JavaScript traces run in parallel. (#13662, #13670, #13700, #13693, #13709, #13727, #13737, #13541)
  • Faster JSON-RPC serialization. Blocks, RPC transactions and logs are written by source-generated writers (full eth_getBlockByNumber with 200 txs −13% time / −93% allocations; eth_getLogs of 1,000 logs −14%), and public WebSocket/IPC connections process up to 16 requests concurrently. (#14299, #14345)
  • New mainnet_aztec config with a rolling 4,096-block trace window on the flat-history backend and the per-transaction trace index. (#14038)
  • SendPolicy plugin (opt-in). Checks every transaction submitted through the node's JSON-RPC against an owner-written rule file (allowed senders/destinations, token-call allow-lists, fee and spend caps) and refuses the ones that break it. Off by default (SendPolicy.Enabled=false). (#14396)
  • Next-fork prototypes (not scheduled on any public network). EIP-8141 frame transactions with EIP-8250 keyed nonces and EIP-8272 recent roots, EIP-7979, EIP-8131, EIP-3298, EIP-7668, EIP-8253, EIP-8279. They activate only through explicit chainspec/genesis keys or the Bogota fork alias. (#12526, #13932, #13929, #13928, #13934, #13943, #13942, #14386)

Performance

  • Code DB on LZ4: on a Glamsterdam devnet range of blocks that each read ~3.7 GB of 64 KiB code, mean block time fell from 2.58–2.63 s to 1.70–1.72 s and the slowest block from 7.8 s to 5.3 s (measured on LZ4-recompacted files); point reads of large values 88–93 µs → 47–49 µs; the code DB shrank 475 MB → 303 MB. New and compacted SST files use LZ4. (#13907)
  • Large-contract blocks: missed code is copied once (−31% block time, −50% allocations on a 58k × 64 KiB block); code reads no longer fill the RocksDB row cache, which is turned off for the code DB. (#14086)
  • Payload start-up: median first-payload latency 66.8 ms → 5.4 ms with the pipeline warm-up (process start to first VALID +0.8 s; disable with Init.PipelineWarmupEnabled=false). The ~2 s KZG trusted-setup load is no longer on the startup critical path. (#13931, #13998)
  • JSON-RPC: generated writers for blocks/transactions/logs (−13–27% time, up to −93% allocations), fewer passes over request and batch bodies, allocation-free filter binding, newHeads serialized once for all subscribers. (#14299, #14187, #14197, #14198, #14195, #13894)
  • RLP: built-in transaction types encode/decode without virtual dispatch (legacy encode −29%, EIP-1559 encode −11%). (#14455)
  • EVM: a long series of interpreter micro-optimisations (frameless MLOAD/MSTORE, padded code copy without end-of-code checks, cheaper DUP/EXP/SUB/SIGNEXTEND, per-depth call frame reuse, cached EIP-7702 delegation targets). (#14028, #13896, #13893, #13878, #13802, #14139, #13799 and others)
  • Flat backend reads: a bloom filter lets read-only execution (eth_call, eth_estimateGas, eth_createAccessList, eth_simulateV1, traces) skip in-memory layers for slots none of them wrote (FlatDb.InMemorySnapshotBloomBitsPerKey, default 14). The carry-forward slot cache becomes a fixed 64 MB set-associative table. (#14094, #14330, #14027)
  • Tracing: streamed struct logs flush every 1 MiB so a slow reader no longer holds hundreds of MB per request; trace_filter stops once count traces are collected. (#14461, #13890)
  • Networking: inbound transaction admission is budgeted before decoding (16 MiB per peer, 64 MiB total), oversized broadcast transactions are skipped before decoding, eth/snap response request IDs are checked before decoding, and discovery runs on a plain UDP socket instead of DotNetty. (#14434, #13506, #14184, #14346)
  • Transaction pool: an underpriced replacement of a set-code, blob or frame transaction is rejected before its authorization signatures are recovered or its blob proofs checked. (#14577)
  • zkVM guests: dozens of ZisK/SP1 guest optimisations (precompile-backed 256-bit arithmetic, Keccak and SHA-256, register-based dispatch, cheaper trie and witness handling) and tests-zkevm v21.0.1. (#14232, #14217, #13975, #14402, #14118 and others)

⚠️ Breaking changes

If you run a node (config and startup)

  • JsonRpc.Host, JsonRpc.EngineHost and AdditionalRpcUrls host names are resolved and bound to their addresses. Before, any host that was not an IP literal or localhost made Kestrel listen on all interfaces. Now the node listens only on the addresses the name resolves to; if it cannot resolve or resolves to a wildcard address, JSON-RPC does not start (the node keeps running and logs the host). Migration: behind NAT or with public DNS, configure a local interface IP; with Docker service names, point health checks at the bind address; to keep listening on every IPv4 interface, set the host to 0.0.0.0 explicitly. IP literals, localhost, * and + are unchanged. (#13746)
  • Init.DiagnosticMode=VerifyTrie and FlatDb.ImportFromPruningTrieState=true now run only their step and exit, instead of doing the work mid-boot and continuing into a normal start. Equivalent commands: nethermind verify-trie and nethermind import-flat-db. verify-trie reports its verdict through the exit code (and fails on a DB with no head); the import fails if there is nothing to import. Migration: run the import/verification as a one-off, then remove the setting before starting the node normally. (#13715)
  • KeyStore.BlockAuthorAccount whose key cannot be loaded is now a configuration error on nodes with mining enabled (Clique, AuRa and XDC stop at startup). Before, the node fell back to sealing with the node key. Nodes that leave it unset are unaffected. (#14290)
  • The node refuses to start when JsonRpc.WebSocketsPort equals the engine port and Init.WebSocketsEnabled=true. Before, it started without an Engine API and only logged a warning. Use a different WebSocketsPort. (#14285)
  • WebSocket and IPC connections process up to 16 requests concurrently (JsonRpc.WebSocketsProcessingConcurrency and JsonRpc.IpcProcessingConcurrency 1 → 16), so pipelined requests on one public connection can be answered out of order — match responses by id. A batch is still processed and answered in order. Authenticated/Engine connections, and IPC when JsonRpc.EnabledModules includes Engine, still process one request at a time. Migration: set the option to 1 if a client relies on ordering (for example, pipelined dependent blob transactions with consecutive nonces). (#14345)
  • Buffered struct-log traces are capped at 256 MiB of opcode log per transaction (new JsonRpc.MaxBufferedTraceLogSize); larger non-streamed traces are truncated like a caller limit. Set 0 for the previous unlimited behaviour. (#14460)
  • eth_sendRawTransactionSync concurrency is capped by the new JsonRpc.RpcTxSyncMaxConcurrentRequests (default 128; 0 disables). Calls over the cap fail immediately with -32005 (HTTP 503) before the transaction is submitted. (#13735)
  • TraceStore serves only the trace types it recorded and falls back to live tracing otherwise; with TraceStore.TraceTypes=Trace alone, trace_block and trace_filter now need historical state (configure Trace and Rewards to serve them from the store). On pruned nodes these can return a missing-state error instead of incomplete output. (#14123)
  • Log and metric changes: snap sync logs Phase 1 of 2 / State Sync (Phase 2 of 2, healing) instead of the misleading healing percentage (#14308, #14344); flat DB metric nethermind_carry_forward_slot_wipes is replaced by nethermind_carry_forward_slot_evictions (one eviction = one entry, do not reuse wipe thresholds) (#14027). Update dashboards and log alerts that match the old strings.
  • FlatDb.HistoryTransactionIndexTraceParallelism is deprecated in favour of JsonRpc.TraceBlockParallelism; a non-zero value still overrides it. (#14363)
  • A block that takes longer than 3 minutes to process is abandoned without a verdict (new Blocks.BlockProcessingTimeoutMs, default 180000; 0 disables). It applies only to the main block-processing loop, not to tracing, simulation or block production. The block is not marked invalid, engine_newPayload does not answer INVALID for it, and it can be processed again; each consecutive retry of the same block gets twice the time, up to 16×. The node logs Processing block <hash> took longer than <n> ms; abandoned without a verdict at Error. Migration: on slow hardware that legitimately needs longer (for example, a full sync through unusually heavy historical blocks), raise the value or set it to 0. (#14487)

If you integrate over JSON-RPC / Engine API

Parity-style trace_* (most changes follow the draft trace spec in execution-apis#890):

  • trace_get returns a single trace object or null, not an array. Its argument is one traceAddress path: [] is the root, ["0x6","0x0"] is the first child of child 6. Migration: read an object/null; to address by flat position, index the result of trace_transaction. (#13858)
  • Reverted frames in Parity traces (trace_*) now include result: {gasUsed, output} next to error: "Reverted" (CREATE included, without address/code). In 2.1 only error was present; exceptional halts still have no result. Error labels are renamed: Stack overflow → Out of stack, Access violation → Out of bounds, Static call violation → Mutable Call In Static Context, the generic Error for an EIP-3541 0xEF code prefix → Invalid code, and TransactionCollision → Contract address collision. This aligns Nethermind with Geth, Erigon and reth. Traces already saved by TraceStore keep the old labels until traced again. Migration: consumers that read "no result" as "failed" must check error; update any matching on the old labels. (#13981)
  • A CALL that fails its balance or call-depth precheck now appears as a failed frame in both callTracer (error: "insufficient balance for transfer" / "max call depth exceeded") and Parity output (Insufficient balance for transfer / Max call depth exceeded, no result). A CREATE that fails its precheck is reported the same way, and a colliding CREATE2 gets a failed frame with Contract address collision (callTracer gasUsed equals its gas). The parent's subtraces and later siblings' traceAddress shift accordingly. In vmTrace, the cost of such a CREATE now includes the gas it forwarded, and the operation after a collision starts with the gas actually left. This matches Geth and Erigon. (#13957, #13958)
  • vmTrace ex.store.key / ex.store.val are minimal hex quantities (0x0, 0x1) instead of a padded word / byte string. (#13780) DUPn now reports n + 1 push values (#13781); buffered mem.off is a number (#13940).
  • trace_filter validation: unknown members, negative after/count, and an unknown mode return -32602 (#14050, #13857); mode: "union" is now honoured and an empty fromAddress/toAddress array behaves like an omitted one, so some requests that returned [] now return records (#13857). A block hash is no longer accepted as fromBlock/toBlock — use the new blockHash member (#14111). Bounds past the head or reversed return -32602 (#14040, #13935). Post-merge blocks no longer produce zero-value reward records, which shifts pagination over PoS ranges (#13938).
  • pending is rejected (-32602) by trace_call, trace_callMany, trace_block, trace_replayBlockTransactions and trace_filter, instead of silently tracing latest. eth_* and debug_* are unchanged. (#13936)
  • trace_rawTransaction rejects a signed gas limit above JsonRpc.GasCap (-38026) instead of lowering it; raise the cap to trace larger transactions (#14060). It also validates the transaction like block inclusion (nonce, sender code, chain ID, fee cap vs base fee) instead of tracing it with the sender's nonce (#14091, #14048).
  • trace_transaction, trace_get and trace_replayTransaction return null for an unknown transaction instead of -32000, including transactions outside the TxLookupLimit window. (#13937, #14037)
  • trace_call / trace_callMany errors use the eth_simulateV1 codes (e.g. -38014 insufficient funds, -38015 batch over block gas limit) instead of -32000; messages are unchanged. A mismatched chainId is -32602. (#14158, #13956)
  • trace_call / trace_callMany calls without a price run with BASEFEE 0, as eth_call, debug_traceCall and eth_simulateV1 already did. A call with no fee fields, gasPrice 0, or both fee caps 0 used to see the block's base fee; in trace_callMany each unpriced call now gets BASEFEE 0 and each priced call the block's. A call whose execution depends on BASEFEE can therefore return a different output, vmTrace and stateDiff. Migration: set gasPrice or maxFeePerGas to trace against the block's base fee. (#14045)

Other JSON-RPC:

  • Call objects with both data and input set to different values are rejected with -32602. Send calldata in input (or both, equal). An explicit null input no longer clears calldata given in data, and an explicit null for accessList, blobVersionedHashes or authorizationList no longer selects a transaction type. (#14089, #14049, #13897)
  • eth_sendTransaction (node wallet) on a post-London network with zero base fee rejects gasPrice: 0x0 / maxFeePerGas: 0x0. Send a non-zero fee or sign locally and use eth_sendRawTransaction. (#14375)
  • eth_sendRawTransactionSync timeouts return EIP-7966 error code 4 with the transaction hash in data, instead of -32016 without data, so a client can keep polling for the receipt. (#13820)
  • Blob calls without a positive maxFeePerBlobGas run at a blob base fee of 0 in eth_call, eth_estimateGas, trace_call and trace_callMany (Geth behaviour). (#14092)
  • eth_simulateV1 drops logs from reverted inner frames while keeping log indices aligned with Geth. (#13979, #14062)
  • eth_call / eth_estimateGas report precompile input failures with go-ethereum's error text; eth_estimateGas no longer reports them as gas required exceeds allowance. (#14281)
  • debug_setHead / debug_resetHead are now guarded rewinds: pause with admin_pauseBlockProcessing and let processing drain first, then resume with admin_resumeBlockProcessing. While paused, every engine_forkchoiceUpdated returns SYNCING (including with payload attributes), so the node builds no payloads until resumed. (#13558)
  • Engine API: engine_newPayloadV3+ rejects omitted or null payload fields; engine_newPayloadV3/V4 outside their fork window return Unsupported fork (incomplete payloads there get invalid params); a forkchoice to a processed block whose state was pruned no longer moves the head, and a resubmitted known block whose state was pruned is re-executed instead of answered VALID. (#14025, #14024, #14131, #14366, #13686)

If you build against Nethermind (plugin / library authors)

These are source-breaking changes: rebuild your plugins against 2.2.0.

  • IReadOnlyStateProvider.GetCode and IWorldStateScopeProvider.ICodeDb.GetCode return ReadOnlyMemory<byte> instead of byte[] (default = not served). (#14086)
  • BlockForRpc.Transactions is typed as BlockTransactions (hashes or full transactions). (#14299)
  • Built-in transaction decoders are no longer generic over the transaction type and drop their protected virtual hooks; BaseAccessListTxDecoder and OptimismLegacyTxDecoder are removed (LegacyTxDecoder takes allowEmptySignature). (#14455)
  • ICommitter / IBlockCommitter quota methods are replaced by TryEnableParallelCommit / SupportsParallelCommit; the quota constructor parameter is removed from FlatStorageTree and AbstractMinimalCommitter. (#13988)
  • WaitHandleExtensions (WaitOneAsync) is removed. (#13945)
  • TxPoolTxSource.GetOrderedTransactions (protected virtual) takes an additional IReleaseSpec spec parameter; overrides must add it. (#14304)
  • Custom ITxTracer implementations receive paired instruction callbacks (ReportOperationRemainingGas only after a reported StartOperation; resumes via ReportGasUpdateForVmTrace). (#13551)
  • Init steps can be declared as standalone commands with [StepCommand]; a step carrying it never runs during a normal start. (#13715)

Consensus / protocol (prototype forks only)

  • The EIP-8272 recent-root contract moves to 0x8272D9679689Ea2f307140CdF9002D27dC00Ffff and is deployed by an ordinary pre-signed transaction instead of being installed at activation; the EIP-8250 nonce manager and EIP-8141 expiry verifier follow the same model. (#14437, #14438, #14378)
  • EIP-7708 finalization logs are emitted in destroy order. Not observable on any shipped configuration (Amsterdam enables EIP-8246, which suppresses them). (#13568)

Default and configuration changes

Diffed I*Config.cs defaults and Nethermind.Runner/configs/*.json between 2.1.0 and 2.2.0.

Changed defaults

Key 2.1.0 2.2.0 PR
JsonRpc.WebSocketsProcessingConcurrency 1 16 #14345
JsonRpc.IpcProcessingConcurrency 1 16 #14345
Db.CodeDbRowCacheSize (hidden) 16 MiB unset (no row cache) #14086
Db.CodeDbRocksDbOptions (hidden) Snappy (inherited) compression=kLZ4Compression; prepended #13907

New settings

Key Default Purpose PR
Sync.ReconstructFinalizedStateFromBlockAccessLists true Rebuild finalized ancestors from verified BALs instead of executing them (EIP-7928 chains only) #14157
Init.PipelineWarmupEnabled true Warm the payload pipeline before RPC opens #13931
JsonRpc.TraceBlockParallelism 4 Node-wide parallel block-trace budget #13906, #14363
JsonRpc.MaxBufferedTraceLogSize 268435456 (256 MiB) Cap on non-streamed struct-log size per tx; 0 = unlimited #14460
JsonRpc.RpcTxSyncMaxConcurrentRequests 128 Cap on concurrent eth_sendRawTransactionSync; 0 disables #13735
FlatDb.DeferStorageTrieCommit true Write storage trie nodes after the block is reported valid #14058
FlatDb.ApplyStorageWritesOnIdleThread true Apply committed storage writes on an idle-priority thread #14059
FlatDb.MaxInMemorySnapshotBytes 0 (off) Byte budget for the in-memory snapshot window #13972
FlatDb.InMemorySnapshotBloomBitsPerKey 14.0 Negative filter for read-only slot reads; 0 disables #14094
SendPolicy.Enabled / RulesPath / JournalPath / WarnOnly false / null / null / false SendPolicy plugin #14396
TxPool.FrameTx* (9 keys) — EIP-8141 frame-transaction pool limits (prototype) #12526 and follow-ups
Blocks.PreWarmHandoff, Blocks.PrefetchBlockAccessListCode (hidden) true, false Prewarmer hand-off; BAL code prefetch #14312
Blocks.BlockProcessingTimeoutMs 180000 (3 min) Abandon a block that takes longer to process, without a verdict; each retry gets twice the time (up to 16×); 0 disables #14487

Deprecated: FlatDb.HistoryTransactionIndexTraceParallelism (hidden; use JsonRpc.TraceBlockParallelism). Removed keys: none.

Bundled configs: new mainnet_aztec.json; sync pivots refreshed for op-mainnet, op-sepolia, worldchain-mainnet, worldchain-sepolia; OP Superchain chainspecs refreshed (#13944, #14231). Chains/hoodi.json gains amsterdamTime and beaconChainGenesisTimestamp.

Sync, database & state

  • A block access list sync stops at the EIP-7928 activation block instead of scanning back to genesis. (#14368)
  • Snap sync warns when a restart discards existing flat state and reports how long the wipe took. (#14297)
  • Account wipes survive BAL healing; TreeSync node dependencies are resolved atomically. (#14156, #14051)
  • Unanchored forks stay out of bulk snapshot conversion; carry-forward generation is rechecked after a cache hit. (#13997, #14026)
  • A periodic log line reports how far the head is behind the chain tip and the estimated catch-up time. (#10892)
  • The flat import reports progress at Info every 60 s. (#13724)
  • During snap sync, a flat-DB node answers JSON-RPC reads of latest at once with a missing-state error, instead of retrying for 5 s and failing with -32603. (#14635)

JSON-RPC & tracing

  • eth_call, eth_estimateGas and eth_createAccessList treat a null or zero JsonRpc.GasCap as uncapped instead of failing every request. (#14001)
  • eth_createAccessList fills the missing half of a fee pair: the suggested priority fee next to a maxFeePerGas, or the priority fee plus twice the base fee next to a maxPriorityFeePerGas. (#14377)
  • Code written during a call replaces a stateOverride code override for the rest of the call in eth_call, eth_estimateGas, eth_simulateV1, trace_call and debug_traceCall. (#14114)
  • eth_simulateV1 on Amsterdam applies the blobBaseFee and movePrecompileToAddress overrides, and simulated blocks carry a slot number, so SLOTNUM works. (#14057, #13688)
  • eth_getLogs rejects a reversed range that ends at block zero (latest to earliest) with -32602, as it already did for other reversed ranges. (#13999)
  • trace_block and trace_replayBlockTransactions return [] for the genesis block instead of header not found. (#13783, #13801)
  • trace_filter address filters match rewards by their author, and match a failed CREATE only by its creator. (#13800, #14039)
  • trace_call / trace_callMany reject a priority fee above the fee cap with eth_call's reason; debug_traceCall rejects state and stateDiff overrides for the same account. (#14046, #14359)
  • Streamed traces that fail return a complete error instead of a truncated success. An unknown JavaScript tracer name is rejected before a JavaScript engine is created, and ctx.txIndex reports the transaction's position in its block. (#13666, #13529, #14249)
  • TraceStore reads stored vmTrace and stateDiff traces, selects them like live replay, and serves stored transaction traces only from canonical blocks. (#14093, #14115, #14122)
  • RPC metrics and stats use the dispatched method name, so whitespace variants no longer create separate labels. (#14194)

Engine API & block processing

  • engine_getPayloadV3 / V4 choose their fork window by whether execution requests are enabled, not by EIP-7623. (#13833)
  • A block engine_newPayload has already accepted stays queued for processing when the request's time budget runs out; the request still answers SYNCING. (#14337)
  • At startup, a side branch with no ancestor state is dropped instead of re-executing genesis on an empty state. (#14324)
  • The transaction pool re-adds the transactions of every block the head rewinds past. (#14492)
  • The pre-merge block producer is stopped on shutdown. (#14168)

Networking & protocol

  • discv4: Ping forward compatibility, verified ENRs retained for mismatched PING endpoints, EIP-1459 DNS tree-root signatures verified, second Hello disconnects the peer, a pong completes a ping only when it comes from the pinged endpoint. (#14151, #14277, #14183, #13832, #14576)
  • Concurrent node-filter acceptance is serialised; rejected peer candidates back off. (#14381, #14190)

Stability & hardening

  • .NET libraries updated to the October 2026 servicing release, which carries Microsoft's security fixes (.NET 10.0.13 CVE list) (#14628):
    • 10.0.12 → 10.0.13: Microsoft.AspNetCore.DataProtection, Microsoft.AspNetCore.DataProtection.Extensions, Microsoft.Extensions.Caching.Memory, Microsoft.Extensions.DependencyInjection, Microsoft.Extensions.FileProviders.Embedded, Microsoft.Extensions.Logging.Abstractions, Microsoft.Extensions.Logging.Console, Microsoft.Extensions.ObjectPool, System.Configuration.ConfigurationManager, System.IO.Hashing, System.Security.Cryptography.ProtectedData
    • 2.0.12 → 2.0.13: System.CommandLine
    • 10.0.401 → 10.0.402 (build-time only): Microsoft.Build.Tasks.Git, Microsoft.SourceLink.GitHub
  • Nethermind.RocksDbBindings updated to the stable 11.8.1.153. (#14617)
  • Docker images run on .NET 10.0.13: runtime base aspnet:10.0.13-resolute (and -resolute-chiseled), build SDK 10.0.402, so the runtime and ASP.NET Core fixes of the same servicing release ship in the images too. (#14632)

L2 & other chains

  • Optimism: Jovian operator fee and DA footprint reported in receipts (#13811); the sequencer's error is returned when forwarding eth_sendRawTransaction (#13815); EIP-1559 params with zero elasticity and a non-zero denominator are rejected (#13816).
  • Taiko: Shasta headers whose extraData is not exactly 7 bytes are rejected. (#13817)
  • XDC: XDPoS_getV2Block* header encoding and XDPoS_getMissedRoundsInEpochByBlockNum before the V2 switch fixed. (#14175, #14008)
  • Shutter: missed keys are counted atomically. (#14238)

Glamsterdam fork on Hoodi

Network Epoch Start slot Unix UTC
Hoodi 132352 4235264 1793036568 Mon, 26 Oct 2026, 17:42:48 UTC

Fork hash after activation: 0x3d068b59 (#14489). Your consensus client must support Glamsterdam as well. Check your CL's release notes for its Hoodi Glamsterdam release. Sepolia activated Glamsterdam on 06 Oct 2026 (2.1.0). Mainnet is not scheduled.

Build signatures

The packages are signed with the following OpenPGP key: AD12 7976 5093 C675 9CD8 A400 24A7 7461 6F1E 617E

What's Changed

New Contributors

Full Changelog: 2.1.0...2.2.0

Don't miss a new nethermind release

NewReleases is sending notifications on new releases.