diff --git a/.github/workflows/build-nethermind-packages.yml b/.github/workflows/build-nethermind-packages.yml
index 4155421f3cc..2fd74db605e 100644
--- a/.github/workflows/build-nethermind-packages.yml
+++ b/.github/workflows/build-nethermind-packages.yml
@@ -22,7 +22,7 @@ jobs:
run: |
sudo apt-get update && sudo apt-get install xmlstarlet -y --no-install-recommends
version_prefix=$(xmlstarlet sel -t -v "//Project/PropertyGroup/VersionPrefix" Directory.Build.props)
- version_suffix=$(xmlstarlet sel -t -v "//Project/PropertyGroup/VersionSuffix" Directory.Build.props)
+ version_suffix=$(xmlstarlet sel -t -v "//Project/PropertyGroup/VersionSuffix" Directory.Build.props 2>/dev/null || echo "")
version=$([[ -n "$version_suffix" ]] && echo "$version_prefix-$version_suffix" || echo "$version_prefix")
echo "Detected version $version"
echo "PACKAGE_PREFIX=nethermind-$version-${GITHUB_SHA:0:8}" >> $GITHUB_ENV
diff --git a/Dockerfile b/Dockerfile
index 8b491797c68..fad4df00175 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -1,7 +1,7 @@
# SPDX-FileCopyrightText: 2022 Demerzel Solutions Limited
# SPDX-License-Identifier: LGPL-3.0-only
-FROM --platform=$BUILDPLATFORM mcr.microsoft.com/dotnet/sdk:9.0.306-noble@sha256:953b8dd2d8e25c934579905b00d7077c5622632ff617f471a211ce9b72013205 AS build
+FROM --platform=$BUILDPLATFORM mcr.microsoft.com/dotnet/sdk:9.0.306-noble@sha256:d88e637d15248531967111fba05c6725ad45ea1dace3d35d8cfe2c4d4094e25d AS build
ARG BUILD_CONFIG=release
ARG CI=true
@@ -25,7 +25,7 @@ RUN arch=$([ "$TARGETARCH" = "amd64" ] && echo "x64" || echo "$TARGETARCH") && \
# A temporary symlink to support the old executable name
RUN ln -sr /publish/nethermind /publish/Nethermind.Runner
-FROM mcr.microsoft.com/dotnet/aspnet:9.0.10-noble@sha256:d3c20e8e331018eb5e7402066fde168304b7c605ecde4dbadb40872dc8bf28db
+FROM mcr.microsoft.com/dotnet/aspnet:9.0.10-noble@sha256:e183ff1306983bdb53592c3a76503c1c003461e2acd8fa7c62e04dff35819ee8
WORKDIR /nethermind
diff --git a/Dockerfile.chiseled b/Dockerfile.chiseled
index f0db4430f7a..f5b27248434 100644
--- a/Dockerfile.chiseled
+++ b/Dockerfile.chiseled
@@ -1,7 +1,7 @@
# SPDX-FileCopyrightText: 2024 Demerzel Solutions Limited
# SPDX-License-Identifier: LGPL-3.0-only
-FROM --platform=$BUILDPLATFORM mcr.microsoft.com/dotnet/sdk:9.0.306-noble@sha256:953b8dd2d8e25c934579905b00d7077c5622632ff617f471a211ce9b72013205 AS build
+FROM --platform=$BUILDPLATFORM mcr.microsoft.com/dotnet/sdk:9.0.306-noble@sha256:d88e637d15248531967111fba05c6725ad45ea1dace3d35d8cfe2c4d4094e25d AS build
ARG BUILD_CONFIG=release
ARG CI=true
diff --git a/scripts/build/Dockerfile b/scripts/build/Dockerfile
index d08a89349ec..49bbc7ab59b 100644
--- a/scripts/build/Dockerfile
+++ b/scripts/build/Dockerfile
@@ -1,7 +1,7 @@
# SPDX-FileCopyrightText: 2025 Demerzel Solutions Limited
# SPDX-License-Identifier: LGPL-3.0-only
-FROM mcr.microsoft.com/dotnet/sdk:9.0.306-noble@sha256:953b8dd2d8e25c934579905b00d7077c5622632ff617f471a211ce9b72013205
+FROM mcr.microsoft.com/dotnet/sdk:9.0.306-noble@sha256:d88e637d15248531967111fba05c6725ad45ea1dace3d35d8cfe2c4d4094e25d
ARG COMMIT_HASH
ARG SOURCE_DATE_EPOCH
diff --git a/src/Nethermind/Chains/foundation.json b/src/Nethermind/Chains/foundation.json
index 554155c6879..30a9ec37e5e 100644
--- a/src/Nethermind/Chains/foundation.json
+++ b/src/Nethermind/Chains/foundation.json
@@ -199,6 +199,14 @@
"eip7251TransitionTimestamp": "0x681b3057",
"eip7702TransitionTimestamp": "0x681b3057",
"eip7623TransitionTimestamp": "0x681b3057",
+ "eip7594TransitionTimestamp": "0x6930b057",
+ "eip7823TransitionTimestamp": "0x6930b057",
+ "eip7825TransitionTimestamp": "0x6930b057",
+ "eip7883TransitionTimestamp": "0x6930b057",
+ "eip7918TransitionTimestamp": "0x6930b057",
+ "eip7934TransitionTimestamp": "0x6930b057",
+ "eip7939TransitionTimestamp": "0x6930b057",
+ "eip7951TransitionTimestamp": "0x6930b057",
"depositContractAddress": "0x00000000219ab540356cbb839cbe05303d7705fa",
"terminalTotalDifficulty": "C70D808A128D7380000",
"blobSchedule": [
@@ -208,6 +216,20 @@
"target": 6,
"max": 9,
"baseFeeUpdateFraction": "0x4c6964"
+ },
+ {
+ "name": "bpo1",
+ "timestamp": "0x69383057",
+ "target": 10,
+ "max": 15,
+ "baseFeeUpdateFraction": "0x7f5a51"
+ },
+ {
+ "name": "bpo2",
+ "timestamp": "0x695db057",
+ "target": 14,
+ "max": 21,
+ "baseFeeUpdateFraction": "0xb24b3f"
}
]
},
diff --git a/src/Nethermind/Directory.Build.props b/src/Nethermind/Directory.Build.props
index 4f9ae5b1a3b..ccd398530a8 100644
--- a/src/Nethermind/Directory.Build.props
+++ b/src/Nethermind/Directory.Build.props
@@ -5,7 +5,7 @@
$(SOURCE_DATE_EPOCH)
$([System.DateTimeOffset]::UtcNow.ToUnixTimeSeconds())
- 1.35.0
+ 1.35.1
diff --git a/src/Nethermind/Nethermind.Benchmark/Evm/MemoryCostBenchmark.cs b/src/Nethermind/Nethermind.Benchmark/Evm/MemoryCostBenchmark.cs
index a2f6ac7b7fe..54092dc5652 100644
--- a/src/Nethermind/Nethermind.Benchmark/Evm/MemoryCostBenchmark.cs
+++ b/src/Nethermind/Nethermind.Benchmark/Evm/MemoryCostBenchmark.cs
@@ -9,8 +9,7 @@ namespace Nethermind.Benchmarks.Evm
{
public class MemoryCostBenchmark
{
- private IEvmMemory _current = new EvmPooledMemory();
- private IEvmMemory _improved = new EvmPooledMemory();
+ private readonly IEvmMemory _current = new EvmPooledMemory();
private UInt256 _location;
private UInt256 _length;
diff --git a/src/Nethermind/Nethermind.Blockchain.Test/BlockhashProviderTests.cs b/src/Nethermind/Nethermind.Blockchain.Test/BlockhashProviderTests.cs
index 0a04c6bce7f..5020c48f7ef 100644
--- a/src/Nethermind/Nethermind.Blockchain.Test/BlockhashProviderTests.cs
+++ b/src/Nethermind/Nethermind.Blockchain.Test/BlockhashProviderTests.cs
@@ -1,6 +1,7 @@
// SPDX-FileCopyrightText: 2022 Demerzel Solutions Limited
// SPDX-License-Identifier: LGPL-3.0-only
+using System;
using FluentAssertions;
using Nethermind.Blockchain.Blocks;
using Nethermind.Blockchain.Find;
@@ -9,12 +10,11 @@
using Nethermind.Core.Specs;
using Nethermind.Core.Test;
using Nethermind.Core.Test.Builders;
+using Nethermind.Evm.State;
using Nethermind.Logging;
using Nethermind.Specs;
using Nethermind.Specs.Forks;
using Nethermind.Specs.Test;
-using Nethermind.Evm.State;
-using Nethermind.State;
using NUnit.Framework;
namespace Nethermind.Blockchain.Test;
@@ -80,7 +80,8 @@ public void Can_lookup_correctly_on_disconnected_main_chain()
BlockhashProvider provider = CreateBlockHashProvider(tree, Frontier.Instance);
BlockHeader notCanonParent = tree.FindHeader(chainLength - 4, BlockTreeLookupOptions.None)!;
- BlockHeader expectedHeader = tree.FindHeader(chainLength - 3, BlockTreeLookupOptions.None)!;
+ Block expected = tree.FindBlock(chainLength - 3, BlockTreeLookupOptions.None)!;
+
Block headParent = tree.FindBlock(chainLength - 2, BlockTreeLookupOptions.None)!;
Block head = tree.FindBlock(chainLength - 1, BlockTreeLookupOptions.None)!;
@@ -88,12 +89,12 @@ public void Can_lookup_correctly_on_disconnected_main_chain()
tree.Insert(branch, BlockTreeInsertBlockOptions.SaveHeader).Should().Be(AddBlockResult.Added);
tree.UpdateMainChain(branch); // Update branch
- tree.UpdateMainChain([headParent, head], true); // Update back to original again, but skipping the branch block.
+ tree.UpdateMainChain([expected, headParent, head], true); // Update back to original again, but skipping the branch block.
Block current = Build.A.Block.WithParent(head).TestObject; // At chainLength
Hash256? result = provider.GetBlockhash(current.Header, chainLength - 3);
- Assert.That(result, Is.EqualTo(expectedHeader.Hash));
+ Assert.That(result, Is.EqualTo(expected.Header.Hash));
}
[Test, MaxTime(Timeout.MaxTestTime)]
diff --git a/src/Nethermind/Nethermind.Blockchain.Test/Blocks/HeaderStoreTests.cs b/src/Nethermind/Nethermind.Blockchain.Test/Blocks/HeaderStoreTests.cs
index bc1d967eb9e..c918b6a3a84 100644
--- a/src/Nethermind/Nethermind.Blockchain.Test/Blocks/HeaderStoreTests.cs
+++ b/src/Nethermind/Nethermind.Blockchain.Test/Blocks/HeaderStoreTests.cs
@@ -17,7 +17,7 @@ public class HeaderStoreTests
[Test]
public void TestCanStoreAndGetHeader()
{
- HeaderStore store = new(new MemDb(), new MemDb());
+ IHeaderStore store = new HeaderStore(new MemDb(), new MemDb());
BlockHeader header = Build.A.BlockHeader.WithNumber(100).TestObject;
BlockHeader header2 = Build.A.BlockHeader.WithNumber(102).TestObject;
@@ -39,7 +39,7 @@ public void TestCanStoreAndGetHeader()
public void TestCanReadHeaderStoredWithHash()
{
IDb headerDb = new MemDb();
- HeaderStore store = new(headerDb, new MemDb());
+ IHeaderStore store = new HeaderStore(headerDb, new MemDb());
BlockHeader header = Build.A.BlockHeader.WithNumber(100).TestObject;
headerDb.Set(header.Hash!, new HeaderDecoder().Encode(header).Bytes);
@@ -50,7 +50,7 @@ public void TestCanReadHeaderStoredWithHash()
[Test]
public void TestCanReadCacheHeader()
{
- HeaderStore store = new(new MemDb(), new MemDb());
+ IHeaderStore store = new HeaderStore(new MemDb(), new MemDb());
BlockHeader header = Build.A.BlockHeader.WithNumber(100).TestObject;
store.Cache(header);
@@ -60,7 +60,7 @@ public void TestCanReadCacheHeader()
[Test]
public void TestCanDeleteHeader()
{
- HeaderStore store = new(new MemDb(), new MemDb());
+ IHeaderStore store = new HeaderStore(new MemDb(), new MemDb());
BlockHeader header = Build.A.BlockHeader.WithNumber(100).TestObject;
store.Insert(header);
store.Delete(header.Hash!);
@@ -72,7 +72,7 @@ public void TestCanDeleteHeader()
public void TestCanDeleteHeaderStoredWithHash()
{
IDb headerDb = new MemDb();
- HeaderStore store = new(headerDb, new MemDb());
+ IHeaderStore store = new HeaderStore(headerDb, new MemDb());
BlockHeader header = Build.A.BlockHeader.WithNumber(100).TestObject;
headerDb.Set(header.Hash!, new HeaderDecoder().Encode(header).Bytes);
diff --git a/src/Nethermind/Nethermind.Blockchain/BlockTree.cs b/src/Nethermind/Nethermind.Blockchain/BlockTree.cs
index c17cd8becb1..cb4505b4478 100644
--- a/src/Nethermind/Nethermind.Blockchain/BlockTree.cs
+++ b/src/Nethermind/Nethermind.Blockchain/BlockTree.cs
@@ -554,7 +554,15 @@ public AddBlockResult SuggestBlock(Block block, BlockTreeSuggestOptions options
return blockHash is null ? null : FindHeader(blockHash, options, blockNumber: number);
}
- public Hash256? FindBlockHash(long blockNumber) => GetBlockHashOnMainOrBestDifficultyHash(blockNumber);
+ public Hash256? FindBlockHash(long blockNumber)
+ {
+ Hash256? blockHash = _headerStore.GetBlockHash(blockNumber);
+ if (blockHash is not null)
+ {
+ return blockHash;
+ }
+ return GetBlockHashOnMainOrBestDifficultyHash(blockNumber);
+ }
public bool HasBlock(long blockNumber, Hash256 blockHash) => _blockStore.HasBlock(blockNumber, blockHash);
@@ -566,7 +574,17 @@ public AddBlockResult SuggestBlock(Block block, BlockTreeSuggestOptions options
return null;
}
- BlockHeader? header = _headerStore.Get(blockHash, shouldCache: false, blockNumber: blockNumber);
+ BlockHeader? header = null;
+ if ((options & BlockTreeLookupOptions.RequireCanonical) == 0)
+ {
+ header = _headerStore.GetFromCache(blockHash);
+ if (header is not null && (!blockNumber.HasValue || header.Number == blockNumber.Value))
+ {
+ return header;
+ }
+ }
+
+ header = _headerStore.Get(blockHash, out bool fromCache, shouldCache: false, blockNumber: blockNumber);
if (header is null)
{
bool allowInvalid = (options & BlockTreeLookupOptions.AllowInvalid) == BlockTreeLookupOptions.AllowInvalid;
@@ -583,6 +601,7 @@ public AddBlockResult SuggestBlock(Block block, BlockTreeSuggestOptions options
bool createLevelIfMissing = (options & BlockTreeLookupOptions.DoNotCreateLevelIfMissing) == BlockTreeLookupOptions.None;
bool requiresCanonical = (options & BlockTreeLookupOptions.RequireCanonical) == BlockTreeLookupOptions.RequireCanonical;
+ bool isMainChain = false;
if ((totalDifficultyNeeded && header.TotalDifficulty is null) || requiresCanonical)
{
(BlockInfo blockInfo, ChainLevelInfo level) = LoadInfo(header.Number, header.Hash, true);
@@ -609,16 +628,16 @@ public AddBlockResult SuggestBlock(Block block, BlockTreeSuggestOptions options
SetTotalDifficultyFromBlockInfo(header, blockInfo);
}
+ isMainChain = level?.MainChainBlock?.BlockHash?.Equals(blockHash) == true;
if (requiresCanonical)
{
- bool isMain = level.MainChainBlock?.BlockHash?.Equals(blockHash) == true;
- header = isMain ? header : null;
+ header = isMainChain ? header : null;
}
}
- if (header is not null && ShouldCache(header.Number))
+ if (header is not null && !fromCache && ShouldCache(header.Number))
{
- _headerStore.Cache(header);
+ _headerStore.Cache(header, isMainChain);
}
return header;
@@ -645,11 +664,6 @@ public AddBlockResult SuggestBlock(Block block, BlockTreeSuggestOptions options
return null;
}
- public Hash256? FindHash(long number)
- {
- return GetBlockHashOnMainOrBestDifficultyHash(number);
- }
-
public IOwnedReadOnlyList FindHeaders(Hash256? blockHash, int numberOfBlocks, int skip, bool reverse)
{
if (numberOfBlocks == 0)
@@ -755,7 +769,9 @@ as it does not require the step of resolving number -> hash */
if (level.HasBlockOnMainChain)
{
- return level.BlockInfos[0].BlockHash;
+ Hash256 blockHash = level.BlockInfos[0].BlockHash;
+ _headerStore.CacheBlockHash(blockNumber, blockHash);
+ return blockHash;
}
UInt256 bestDifficultySoFar = UInt256.Zero;
@@ -939,11 +955,9 @@ public void MarkChainAsProcessed(IReadOnlyList blocks)
for (int i = 0; i < blocks.Count; i++)
{
Block block = blocks[i];
- if (ShouldCache(block.Number))
- {
- _blockStore.Cache(block);
- _headerStore.Cache(block.Header);
- }
+ // Header also updates the number <-> hash mapping so always update
+ _blockStore.Cache(block);
+ _headerStore.Cache(block.Header, isMainChain: true);
ChainLevelInfo? level = LoadLevel(block.Number);
int? index = (level?.FindIndex(block.Hash)) ?? throw new InvalidOperationException($"Cannot mark unknown block {block.ToString(Block.Format.FullHashAndNumber)} as processed");
@@ -1008,12 +1022,9 @@ public void UpdateMainChain(IReadOnlyList blocks, bool wereProcessed, boo
for (int i = 0; i < blocks.Count; i++)
{
Block block = blocks[i];
- if (ShouldCache(block.Number))
- {
- _blockStore.Cache(block);
- _headerStore.Cache(block.Header);
- }
-
+ _blockStore.Cache(block);
+ // Header also updates the number <-> hash mapping so always update
+ _headerStore.Cache(block.Header, wereProcessed);
// we only force update head block for last block in processed blocks
bool lastProcessedBlock = i == blocks.Count - 1;
@@ -1430,12 +1441,23 @@ private bool ShouldCache(long number)
}
Block? block = null;
+ if ((options & BlockTreeLookupOptions.RequireCanonical) == 0)
+ {
+ block = _blockStore.GetFromCache(blockHash);
+ if (block is not null && block.TotalDifficulty.HasValue && (!blockNumber.HasValue || block.Number == blockNumber.Value))
+ {
+ return block;
+ }
+ }
+
+ bool fromCache = false;
blockNumber ??= _headerStore.GetBlockNumber(blockHash);
if (blockNumber is not null)
{
block = _blockStore.Get(
blockNumber.Value,
blockHash,
+ out fromCache,
(options & BlockTreeLookupOptions.ExcludeTxHashes) != 0 ? RlpBehaviors.ExcludeHashes : RlpBehaviors.None,
shouldCache: false);
}
@@ -1458,6 +1480,7 @@ private bool ShouldCache(long number)
bool requiresCanonical = (options & BlockTreeLookupOptions.RequireCanonical) ==
BlockTreeLookupOptions.RequireCanonical;
+ bool isMainChain = false;
if ((totalDifficultyNeeded && block.TotalDifficulty is null) || requiresCanonical)
{
(BlockInfo blockInfo, ChainLevelInfo level) = LoadInfo(block.Number, block.Hash, true);
@@ -1484,17 +1507,17 @@ private bool ShouldCache(long number)
SetTotalDifficultyFromBlockInfo(block.Header, blockInfo);
}
+ isMainChain = level?.MainChainBlock?.BlockHash.Equals(blockHash) == true;
if (requiresCanonical)
{
- bool isMain = level.MainChainBlock?.BlockHash.Equals(blockHash) == true;
- block = isMain ? block : null;
+ block = isMainChain ? block : null;
}
}
- if (block is not null && ShouldCache(block.Number))
+ if (block is not null && !fromCache && ShouldCache(block.Number))
{
_blockStore.Cache(block);
- _headerStore.Cache(block.Header);
+ _headerStore.Cache(block.Header, isMainChain);
}
return block;
diff --git a/src/Nethermind/Nethermind.Blockchain/BlockTreeOverlay.cs b/src/Nethermind/Nethermind.Blockchain/BlockTreeOverlay.cs
index 4e2a519c14f..12d710f96f6 100644
--- a/src/Nethermind/Nethermind.Blockchain/BlockTreeOverlay.cs
+++ b/src/Nethermind/Nethermind.Blockchain/BlockTreeOverlay.cs
@@ -108,7 +108,7 @@ public void UpdateMainChain(IReadOnlyList blocks, bool wereProcessed, boo
public BlockInfo FindCanonicalBlockInfo(long blockNumber) => _overlayTree.FindCanonicalBlockInfo(blockNumber) ?? _baseTree.FindCanonicalBlockInfo(blockNumber);
- public Hash256 FindHash(long blockNumber) => _overlayTree.FindHash(blockNumber) ?? _baseTree.FindHash(blockNumber);
+ public Hash256 FindBlockHash(long blockNumber) => _overlayTree.FindBlockHash(blockNumber) ?? _baseTree.FindBlockHash(blockNumber);
public IOwnedReadOnlyList FindHeaders(Hash256 hash, int numberOfBlocks, int skip, bool reverse)
{
@@ -268,9 +268,6 @@ public bool HasBlock(long blockNumber, Hash256 blockHash) =>
public BlockHeader? FindHeader(long blockNumber, BlockTreeLookupOptions options) =>
_overlayTree.FindHeader(blockNumber, options) ?? _baseTree.FindHeader(blockNumber, options);
- public Hash256? FindBlockHash(long blockNumber) =>
- _overlayTree.FindBlockHash(blockNumber) ?? _baseTree.FindBlockHash(blockNumber);
-
public bool IsMainChain(BlockHeader blockHeader) =>
_baseTree.IsMainChain(blockHeader) || _overlayTree.IsMainChain(blockHeader);
diff --git a/src/Nethermind/Nethermind.Blockchain/BlockhashProvider.cs b/src/Nethermind/Nethermind.Blockchain/BlockhashProvider.cs
index 52bee65baf3..f92fc45ac18 100644
--- a/src/Nethermind/Nethermind.Blockchain/BlockhashProvider.cs
+++ b/src/Nethermind/Nethermind.Blockchain/BlockhashProvider.cs
@@ -46,6 +46,13 @@ public BlockhashProvider(IBlockFinder blockTree, ISpecProvider specProvider, IWo
return null;
}
+ return (currentBlock.ParentHash == _blockTree.HeadHash || currentBlock.ParentHash == _blockTree.Head?.ParentHash) ?
+ _blockTree.FindBlockHash(number) :
+ GetBlockHashFromNonHeadParent(currentBlock, number);
+ }
+
+ private Hash256 GetBlockHashFromNonHeadParent(BlockHeader currentBlock, long number)
+ {
BlockHeader header = _blockTree.FindParentHeader(currentBlock, BlockTreeLookupOptions.TotalDifficultyNotNeeded) ??
throw new InvalidDataException("Parent header cannot be found when executing BLOCKHASH operation");
diff --git a/src/Nethermind/Nethermind.Blockchain/Blocks/BlockStore.cs b/src/Nethermind/Nethermind.Blockchain/Blocks/BlockStore.cs
index 3cb168ba391..1fe38c8532f 100644
--- a/src/Nethermind/Nethermind.Blockchain/Blocks/BlockStore.cs
+++ b/src/Nethermind/Nethermind.Blockchain/Blocks/BlockStore.cs
@@ -18,8 +18,7 @@ public class BlockStore([KeyFilter(DbNames.Blocks)] IDb blockDb) : IBlockStore
private readonly BlockDecoder _blockDecoder = new();
public const int CacheSize = 128 + 32;
- private readonly ClockCache
- _blockCache = new(CacheSize);
+ private readonly ClockCache _blockCache = new(CacheSize);
public void SetMetadata(byte[] key, byte[] value)
{
@@ -67,9 +66,16 @@ public void Delete(long blockNumber, Hash256 blockHash)
public Block? Get(long blockNumber, Hash256 blockHash, RlpBehaviors rlpBehaviors = RlpBehaviors.None, bool shouldCache = false)
{
- Block? b = blockDb.Get(blockNumber, blockHash, _blockDecoder, _blockCache, rlpBehaviors, shouldCache);
+ Block? b = blockDb.Get(blockNumber, blockHash, _blockDecoder, out _, _blockCache, rlpBehaviors, shouldCache);
if (b is not null) return b;
- return blockDb.Get(blockHash, _blockDecoder, _blockCache, rlpBehaviors, shouldCache);
+ return blockDb.Get(blockHash, _blockDecoder, out _, _blockCache, rlpBehaviors, shouldCache);
+ }
+
+ public Block? Get(long blockNumber, Hash256 blockHash, out bool fromCache, RlpBehaviors rlpBehaviors = RlpBehaviors.None, bool shouldCache = false)
+ {
+ Block? b = blockDb.Get(blockNumber, blockHash, _blockDecoder, out fromCache, _blockCache, rlpBehaviors, shouldCache);
+ if (b is not null) return b;
+ return blockDb.Get(blockHash, _blockDecoder, out fromCache, _blockCache, rlpBehaviors, shouldCache);
}
public byte[]? GetRlp(long blockNumber, Hash256 blockHash)
@@ -96,4 +102,7 @@ public void Cache(Block block)
{
_blockCache.Set(block.Hash, block);
}
+
+ public Block? GetFromCache(Hash256 blockHash)
+ => _blockCache.Get(blockHash);
}
diff --git a/src/Nethermind/Nethermind.Blockchain/Blocks/IBlockStore.cs b/src/Nethermind/Nethermind.Blockchain/Blocks/IBlockStore.cs
index eca635bc9d2..b5e62629eb7 100644
--- a/src/Nethermind/Nethermind.Blockchain/Blocks/IBlockStore.cs
+++ b/src/Nethermind/Nethermind.Blockchain/Blocks/IBlockStore.cs
@@ -15,9 +15,12 @@ public interface IBlockStore
{
void Insert(Block block, WriteFlags writeFlags = WriteFlags.None);
void Delete(long blockNumber, Hash256 blockHash);
- Block? Get(long blockNumber, Hash256 blockHash, RlpBehaviors rlpBehaviors = RlpBehaviors.None, bool shouldCache = true);
+ Block? Get(long blockNumber, Hash256 blockHash, RlpBehaviors rlpBehaviors = RlpBehaviors.None, bool shouldCache = true)
+ => Get(blockNumber, blockHash, out _, rlpBehaviors, shouldCache);
+ Block? Get(long blockNumber, Hash256 blockHash, out bool fromCache, RlpBehaviors rlpBehaviors = RlpBehaviors.None, bool shouldCache = true);
byte[]? GetRlp(long blockNumber, Hash256 blockHash);
ReceiptRecoveryBlock? GetReceiptRecoveryBlock(long blockNumber, Hash256 blockHash);
void Cache(Block block);
+ Block? GetFromCache(Hash256 blockHash);
bool HasBlock(long blockNumber, Hash256 blockHash);
}
diff --git a/src/Nethermind/Nethermind.Blockchain/Headers/HeaderStore.cs b/src/Nethermind/Nethermind.Blockchain/Headers/HeaderStore.cs
index 425d56d1955..f6426eb9e90 100644
--- a/src/Nethermind/Nethermind.Blockchain/Headers/HeaderStore.cs
+++ b/src/Nethermind/Nethermind.Blockchain/Headers/HeaderStore.cs
@@ -19,12 +19,15 @@ public class HeaderStore : IHeaderStore
{
// SyncProgressResolver MaxLookupBack is 256, add 16 wiggle room
public const int CacheSize = 256 + 16;
+ // Go a bit further back for numbers as smaller
+ public const int NumberCacheSize = CacheSize * 4;
private readonly IDb _headerDb;
private readonly IDb _blockNumberDb;
private readonly HeaderDecoder _headerDecoder = new();
- private readonly ClockCache _headerCache =
- new(CacheSize);
+ private readonly ClockCache _headerCache = new(CacheSize);
+ private readonly ClockCache _numberCache = new(NumberCacheSize);
+ private readonly ClockCache _hashCache = new(NumberCacheSize);
public HeaderStore([KeyFilter(DbNames.Headers)] IDb headerDb, [KeyFilter(DbNames.BlockNumbers)] IDb blockNumberDb)
{
@@ -45,40 +48,58 @@ public void BulkInsert(IReadOnlyList headers)
using IWriteBatch blockNumberWriteBatch = _blockNumberDb.StartWriteBatch();
Span blockNumberSpan = stackalloc byte[8];
- foreach (var header in headers)
+ foreach (BlockHeader header in headers)
{
using NettyRlpStream newRlp = _headerDecoder.EncodeToNewNettyStream(header);
headerWriteBatch.Set(header.Number, header.Hash, newRlp.AsSpan());
header.Number.WriteBigEndian(blockNumberSpan);
blockNumberWriteBatch.Set(header.Hash, blockNumberSpan);
+ CacheNumber(header.Hash, header.Number, isMainChain: false);
}
}
- public BlockHeader? Get(Hash256 blockHash, bool shouldCache = false, long? blockNumber = null)
+ public BlockHeader? Get(Hash256 blockHash, out bool fromCache, bool shouldCache = false, long? blockNumber = null)
{
blockNumber ??= GetBlockNumberFromBlockNumberDb(blockHash);
- BlockHeader? header = null;
+ BlockHeader? header;
if (blockNumber is not null)
{
- header = _headerDb.Get(blockNumber.Value, blockHash, _headerDecoder, _headerCache, shouldCache: shouldCache);
+ header = _headerDb.Get(blockNumber.Value, blockHash, _headerDecoder, out fromCache, _headerCache, shouldCache: shouldCache);
+ if (header is not null)
+ {
+ return header;
+ }
}
- return header ?? _headerDb.Get(blockHash, _headerDecoder, _headerCache, shouldCache: shouldCache);
+
+ return _headerDb.Get(blockHash, _headerDecoder, out fromCache, _headerCache, shouldCache: shouldCache);
}
- public void Cache(BlockHeader header)
+ public void CacheBlockHash(long blockNumber, Hash256 blockHash)
+ => CacheNumber(blockHash, blockNumber, isMainChain: true);
+
+ public void Cache(BlockHeader header, bool isMainChain)
{
+ CacheNumber(header.Hash, header.Number, isMainChain);
_headerCache.Set(header.Hash, header);
}
+ public BlockHeader? GetFromCache(Hash256 blockHash)
+ => _headerCache.Get(blockHash);
+
public void Delete(Hash256 blockHash)
{
- long? blockNumber = GetBlockNumberFromBlockNumberDb(blockHash);
- if (blockNumber is not null) _headerDb.Delete(blockNumber.Value, blockHash);
+ long? blockNumber = GetBlockNumber(blockHash);
+ if (blockNumber is not null)
+ {
+ _headerDb.Delete(blockNumber.Value, blockHash);
+ _hashCache.Delete(blockNumber.Value);
+ }
_blockNumberDb.Delete(blockHash);
_headerDb.Delete(blockHash);
_headerCache.Delete(blockHash);
+ _numberCache.Delete(blockHash);
}
public void InsertBlockNumber(Hash256 blockHash, long blockNumber)
@@ -86,19 +107,66 @@ public void InsertBlockNumber(Hash256 blockHash, long blockNumber)
Span blockNumberSpan = stackalloc byte[8];
blockNumber.WriteBigEndian(blockNumberSpan);
_blockNumberDb.Set(blockHash, blockNumberSpan);
+ CacheNumber(blockHash, blockNumber, isMainChain: false);
+ }
+
+ private void CacheNumber(Hash256 blockHash, long blockNumber, bool isMainChain)
+ {
+ _numberCache.Set(blockHash, blockNumber);
+ if (isMainChain)
+ {
+ _hashCache.Set(blockNumber, blockHash);
+ }
+ }
+
+ public Hash256? GetBlockHash(long blockNumber)
+ {
+ if (_hashCache.TryGet(blockNumber, out Hash256? hash))
+ {
+ return hash;
+ }
+
+ return null;
}
public long? GetBlockNumber(Hash256 blockHash)
{
+ if (_numberCache.TryGet(blockHash, out long number))
+ {
+ return number;
+ }
+
+ return GetBlockNumberThroughHeaderCache(blockHash);
+ }
+
+ private long? GetBlockNumberThroughHeaderCache(Hash256 blockHash)
+ {
+ if (_headerCache.TryGet(blockHash, out BlockHeader? header))
+ {
+ CacheNumber(blockHash, header.Number, isMainChain: false);
+ return header.Number;
+ }
+
long? blockNumber = GetBlockNumberFromBlockNumberDb(blockHash);
if (blockNumber is not null) return blockNumber.Value;
// Probably still hash based
- return Get(blockHash)?.Number;
+ blockNumber = Get(blockHash, out _)?.Number;
+ if (blockNumber.HasValue)
+ {
+ CacheNumber(blockHash, blockNumber.Value, isMainChain: false);
+ }
+ return blockNumber;
}
private long? GetBlockNumberFromBlockNumberDb(Hash256 blockHash)
{
+ // Double check cache as we have done a fair amount of checks
+ // since the cache check and something else might have populated it.
+ if (_numberCache.TryGet(blockHash, out long number))
+ {
+ return number;
+ }
Span numberSpan = _blockNumberDb.GetSpan(blockHash);
if (numberSpan.IsNullOrEmpty()) return null;
try
@@ -108,7 +176,9 @@ public void InsertBlockNumber(Hash256 blockHash, long blockNumber)
throw new InvalidDataException($"Unexpected number span length: {numberSpan.Length}");
}
- return BinaryPrimitives.ReadInt64BigEndian(numberSpan);
+ number = BinaryPrimitives.ReadInt64BigEndian(numberSpan);
+ CacheNumber(blockHash, number, isMainChain: false);
+ return number;
}
finally
{
diff --git a/src/Nethermind/Nethermind.Blockchain/Headers/IHeaderStore.cs b/src/Nethermind/Nethermind.Blockchain/Headers/IHeaderStore.cs
index dc56e2d9116..66f57b98cdc 100644
--- a/src/Nethermind/Nethermind.Blockchain/Headers/IHeaderStore.cs
+++ b/src/Nethermind/Nethermind.Blockchain/Headers/IHeaderStore.cs
@@ -12,9 +12,14 @@ public interface IHeaderStore
{
void Insert(BlockHeader header);
void BulkInsert(IReadOnlyList headers);
- BlockHeader? Get(Hash256 blockHash, bool shouldCache, long? blockNumber = null);
- void Cache(BlockHeader header);
+ BlockHeader? Get(Hash256 blockHash, bool shouldCache = true, long? blockNumber = null)
+ => Get(blockHash, out _, shouldCache, blockNumber);
+ BlockHeader? Get(Hash256 blockHash, out bool fromCache, bool shouldCache = true, long? blockNumber = null);
+ void Cache(BlockHeader header, bool isMainChain = false);
void Delete(Hash256 blockHash);
void InsertBlockNumber(Hash256 blockHash, long blockNumber);
long? GetBlockNumber(Hash256 blockHash);
+ Hash256? GetBlockHash(long blockNumber);
+ void CacheBlockHash(long blockNumber, Hash256 blockHash);
+ BlockHeader? GetFromCache(Hash256 blockHash);
}
diff --git a/src/Nethermind/Nethermind.Blockchain/IBlockTree.cs b/src/Nethermind/Nethermind.Blockchain/IBlockTree.cs
index 22bd63cbded..1c01cc0fb03 100644
--- a/src/Nethermind/Nethermind.Blockchain/IBlockTree.cs
+++ b/src/Nethermind/Nethermind.Blockchain/IBlockTree.cs
@@ -155,8 +155,6 @@ AddBlockResult Insert(Block block, BlockTreeInsertBlockOptions insertBlockOption
BlockInfo FindCanonicalBlockInfo(long blockNumber);
- Hash256? FindHash(long blockNumber);
-
IOwnedReadOnlyList FindHeaders(Hash256 hash, int numberOfBlocks, int skip, bool reverse);
void DeleteInvalidBlock(Block invalidBlock);
diff --git a/src/Nethermind/Nethermind.Blockchain/ReadOnlyBlockTree.cs b/src/Nethermind/Nethermind.Blockchain/ReadOnlyBlockTree.cs
index b6635141370..51143c0198a 100644
--- a/src/Nethermind/Nethermind.Blockchain/ReadOnlyBlockTree.cs
+++ b/src/Nethermind/Nethermind.Blockchain/ReadOnlyBlockTree.cs
@@ -99,8 +99,6 @@ public void UpdateHeadBlock(Hash256 blockHash)
public bool IsMainChain(BlockHeader blockHeader) => _wrapped.IsMainChain(blockHeader);
- public Hash256 FindHash(long blockNumber) => _wrapped.FindHash(blockNumber);
-
public IOwnedReadOnlyList FindHeaders(Hash256 hash, int numberOfBlocks, int skip, bool reverse) => _wrapped.FindHeaders(hash, numberOfBlocks, skip, reverse);
public Block FindBlock(long blockNumber, BlockTreeLookupOptions options) => _wrapped.FindBlock(blockNumber, options);
diff --git a/src/Nethermind/Nethermind.Consensus.AuRa/Validators/ValidatorInfoDecoder.cs b/src/Nethermind/Nethermind.Consensus.AuRa/Validators/ValidatorInfoDecoder.cs
index 462db51a196..71b9c3e5a65 100644
--- a/src/Nethermind/Nethermind.Consensus.AuRa/Validators/ValidatorInfoDecoder.cs
+++ b/src/Nethermind/Nethermind.Consensus.AuRa/Validators/ValidatorInfoDecoder.cs
@@ -23,7 +23,9 @@ internal class ValidatorInfoDecoder : IRlpStreamDecoder, IRlpObje
int addressesSequenceLength = rlpStream.ReadSequenceLength();
int addressesCheck = rlpStream.Position + addressesSequenceLength;
- Address[] addresses = new Address[addressesSequenceLength / Rlp.LengthOfAddressRlp];
+ var count = addressesSequenceLength / Rlp.LengthOfAddressRlp;
+ rlpStream.GuardLimit(count);
+ Address[] addresses = new Address[count];
int i = 0;
while (rlpStream.Position < addressesCheck)
{
diff --git a/src/Nethermind/Nethermind.Consensus.Clique/SnapshotDecoder.cs b/src/Nethermind/Nethermind.Consensus.Clique/SnapshotDecoder.cs
index 436256334cb..8bf9756d81d 100644
--- a/src/Nethermind/Nethermind.Consensus.Clique/SnapshotDecoder.cs
+++ b/src/Nethermind/Nethermind.Consensus.Clique/SnapshotDecoder.cs
@@ -68,8 +68,9 @@ private static (int contentLength, int signersLength, int votesLength, int tally
private static SortedList DecodeSigners(RlpStream rlpStream)
{
rlpStream.ReadSequenceLength();
- SortedList signers = new SortedList(AddressComparer.Instance);
int length = rlpStream.DecodeInt();
+ rlpStream.GuardLimit(length);
+ SortedList signers = new(AddressComparer.Instance);
for (int i = 0; i < length; i++)
{
Address signer = rlpStream.DecodeAddress();
@@ -83,8 +84,9 @@ private static SortedList DecodeSigners(RlpStream rlpStream)
private static List DecodeVotes(RlpStream rlpStream)
{
rlpStream.ReadSequenceLength();
- List votes = new List();
int length = rlpStream.DecodeInt();
+ rlpStream.GuardLimit(length);
+ List votes = new(length);
for (int i = 0; i < length; i++)
{
Address signer = rlpStream.DecodeAddress();
@@ -100,8 +102,9 @@ private static List DecodeVotes(RlpStream rlpStream)
private static Dictionary DecodeTally(RlpStream rlpStream)
{
rlpStream.ReadSequenceLength();
- Dictionary tally = new Dictionary();
int length = rlpStream.DecodeInt();
+ rlpStream.GuardLimit(length);
+ Dictionary tally = new(length);
for (int i = 0; i < length; i++)
{
Address address = rlpStream.DecodeAddress();
diff --git a/src/Nethermind/Nethermind.Consensus.Test/GenesisLoaderTests.cs b/src/Nethermind/Nethermind.Consensus.Test/GenesisLoaderTests.cs
new file mode 100644
index 00000000000..9c41299d4ec
--- /dev/null
+++ b/src/Nethermind/Nethermind.Consensus.Test/GenesisLoaderTests.cs
@@ -0,0 +1,217 @@
+// SPDX-FileCopyrightText: 2025 Demerzel Solutions Limited
+// SPDX-License-Identifier: LGPL-3.0-only
+
+using System;
+using System.Threading;
+using FluentAssertions;
+using Nethermind.Blockchain;
+using Nethermind.Consensus.Processing;
+using Nethermind.Core;
+using Nethermind.Core.Test.Builders;
+using Nethermind.Evm.State;
+using Nethermind.Logging;
+using Nethermind.State;
+using NSubstitute;
+using NUnit.Framework;
+
+namespace Nethermind.Consensus.Test;
+
+[TestFixture]
+public class GenesisLoaderTests
+{
+ [Test]
+ public void Load_ShouldFlushCacheAfterSuccessfulGenesisProcessing()
+ {
+ // Arrange
+ Block genesisBlock = Build.A.Block.Genesis.TestObject;
+
+ IGenesisBuilder genesisBuilder = Substitute.For();
+ genesisBuilder.Build().Returns(genesisBlock);
+
+ IStateReader stateReader = Substitute.For();
+
+ IBlockTree blockTree = Substitute.For();
+ blockTree.When(x => x.SuggestBlock(Arg.Any())).Do(_ =>
+ {
+ // Simulate block processing by triggering NewHeadBlock event
+ blockTree.NewHeadBlock += Raise.EventWith(blockTree, new BlockEventArgs(genesisBlock));
+ });
+
+ IWorldState worldState = Substitute.For();
+ IDisposable scopeDisposable = Substitute.For();
+ worldState.BeginScope(IWorldState.PreGenesis).Returns(scopeDisposable);
+
+ IWorldStateManager worldStateManager = Substitute.For();
+
+ IBlockchainProcessor blockchainProcessor = Substitute.For();
+
+ GenesisLoader.Config config = new(null, TimeSpan.FromSeconds(10));
+ ILogManager logManager = LimboLogs.Instance;
+
+ GenesisLoader loader = new(
+ genesisBuilder,
+ stateReader,
+ blockTree,
+ worldState,
+ worldStateManager,
+ blockchainProcessor,
+ config,
+ logManager
+ );
+
+ // Act
+ loader.Load();
+
+ // Assert - verify FlushCache was called
+ worldStateManager.Received(1).FlushCache(Arg.Any());
+ }
+
+ [Test]
+ public void Load_ShouldNotFlushCache_WhenGenesisProcessingTimesOut()
+ {
+ // Arrange
+ Block genesisBlock = Build.A.Block.Genesis.TestObject;
+
+ IGenesisBuilder genesisBuilder = Substitute.For();
+ genesisBuilder.Build().Returns(genesisBlock);
+
+ IStateReader stateReader = Substitute.For();
+
+ IBlockTree blockTree = Substitute.For();
+ blockTree.When(x => x.SuggestBlock(Arg.Any())).Do(_ =>
+ {
+ // Do nothing - simulate timeout
+ });
+
+ IWorldState worldState = Substitute.For();
+ IDisposable scopeDisposable = Substitute.For();
+ worldState.BeginScope(IWorldState.PreGenesis).Returns(scopeDisposable);
+
+ IWorldStateManager worldStateManager = Substitute.For();
+
+ IBlockchainProcessor blockchainProcessor = Substitute.For();
+
+ GenesisLoader.Config config = new(null, TimeSpan.FromMilliseconds(100));
+ ILogManager logManager = LimboLogs.Instance;
+
+ GenesisLoader loader = new(
+ genesisBuilder,
+ stateReader,
+ blockTree,
+ worldState,
+ worldStateManager,
+ blockchainProcessor,
+ config,
+ logManager
+ );
+
+ // Act & Assert - expect timeout exception
+ Assert.Throws(() => loader.Load());
+
+ // Verify FlushCache was NOT called since genesis processing failed
+ worldStateManager.DidNotReceive().FlushCache(Arg.Any());
+ }
+
+ [Test]
+ public void Load_ShouldNotFlushCache_WhenGenesisBlockIsInvalid()
+ {
+ // Arrange
+ Block genesisBlock = Build.A.Block.Genesis.TestObject;
+
+ IGenesisBuilder genesisBuilder = Substitute.For();
+ genesisBuilder.Build().Returns(genesisBlock);
+
+ IStateReader stateReader = Substitute.For();
+
+ IBlockTree blockTree = Substitute.For();
+
+ IWorldState worldState = Substitute.For();
+ IDisposable scopeDisposable = Substitute.For();
+ worldState.BeginScope(IWorldState.PreGenesis).Returns(scopeDisposable);
+
+ IWorldStateManager worldStateManager = Substitute.For();
+
+ IBlockchainProcessor blockchainProcessor = Substitute.For();
+ blockTree.When(x => x.SuggestBlock(Arg.Any())).Do(_ =>
+ {
+ // Simulate invalid block by triggering InvalidBlock event
+ blockchainProcessor.InvalidBlock += Raise.EventWith(
+ blockchainProcessor,
+ new IBlockchainProcessor.InvalidBlockEventArgs { InvalidBlock = genesisBlock });
+ });
+
+ GenesisLoader.Config config = new(null, TimeSpan.FromSeconds(10));
+ ILogManager logManager = LimboLogs.Instance;
+
+ GenesisLoader loader = new(
+ genesisBuilder,
+ stateReader,
+ blockTree,
+ worldState,
+ worldStateManager,
+ blockchainProcessor,
+ config,
+ logManager
+ );
+
+ // Act & Assert - expect InvalidBlockException
+ Assert.Throws(() => loader.Load());
+
+ // Verify FlushCache was NOT called since genesis was invalid
+ worldStateManager.DidNotReceive().FlushCache(Arg.Any());
+ }
+
+ [Test]
+ public void Load_ShouldFlushCacheAfterScopeExit()
+ {
+ // Arrange
+ Block genesisBlock = Build.A.Block.Genesis.TestObject;
+
+ IGenesisBuilder genesisBuilder = Substitute.For();
+ genesisBuilder.Build().Returns(genesisBlock);
+
+ IStateReader stateReader = Substitute.For();
+
+ IBlockTree blockTree = Substitute.For();
+ bool scopeExited = false;
+ blockTree.When(x => x.SuggestBlock(Arg.Any())).Do(_ =>
+ {
+ // Simulate block processing by triggering NewHeadBlock event
+ blockTree.NewHeadBlock += Raise.EventWith(blockTree, new BlockEventArgs(genesisBlock));
+ });
+
+ IWorldState worldState = Substitute.For();
+ IDisposable scopeDisposable = Substitute.For();
+ scopeDisposable.When(x => x.Dispose()).Do(_ => scopeExited = true);
+ worldState.BeginScope(IWorldState.PreGenesis).Returns(scopeDisposable);
+
+ IWorldStateManager worldStateManager = Substitute.For();
+ worldStateManager.When(x => x.FlushCache(Arg.Any())).Do(_ =>
+ {
+ // Verify that scope was exited before FlushCache was called
+ scopeExited.Should().BeTrue("FlushCache should be called after scope exit");
+ });
+
+ IBlockchainProcessor blockchainProcessor = Substitute.For();
+
+ GenesisLoader.Config config = new(null, TimeSpan.FromSeconds(10));
+ ILogManager logManager = LimboLogs.Instance;
+
+ GenesisLoader loader = new(
+ genesisBuilder,
+ stateReader,
+ blockTree,
+ worldState,
+ worldStateManager,
+ blockchainProcessor,
+ config,
+ logManager
+ );
+
+ // Act
+ loader.Load();
+
+ // Assert - verify FlushCache was called after scope exit
+ worldStateManager.Received(1).FlushCache(Arg.Any());
+ }
+}
diff --git a/src/Nethermind/Nethermind.Consensus/Processing/GenesisLoader.cs b/src/Nethermind/Nethermind.Consensus/Processing/GenesisLoader.cs
index c743aefa99e..fd369d528e8 100644
--- a/src/Nethermind/Nethermind.Consensus/Processing/GenesisLoader.cs
+++ b/src/Nethermind/Nethermind.Consensus/Processing/GenesisLoader.cs
@@ -18,6 +18,7 @@ public class GenesisLoader(
IStateReader stateReader,
IBlockTree blockTree,
IWorldState worldState,
+ IWorldStateManager worldStateManager,
IBlockchainProcessor blockchainProcessor,
GenesisLoader.Config genesisConfig,
ILogManager logManager
@@ -29,6 +30,12 @@ public record Config(Hash256? ExpectedGenesisHash, TimeSpan GenesisTimeout);
ILogger _logger = logManager.GetClassLogger();
public void Load()
+ {
+ DoLoad();
+ worldStateManager.FlushCache(CancellationToken.None);
+ }
+
+ private void DoLoad()
{
using var _ = worldState.BeginScope(IWorldState.PreGenesis);
diff --git a/src/Nethermind/Nethermind.Consensus/Stateless/StatelessBlockTree.cs b/src/Nethermind/Nethermind.Consensus/Stateless/StatelessBlockTree.cs
index 68dd42dd8df..c19570b472b 100644
--- a/src/Nethermind/Nethermind.Consensus/Stateless/StatelessBlockTree.cs
+++ b/src/Nethermind/Nethermind.Consensus/Stateless/StatelessBlockTree.cs
@@ -143,9 +143,6 @@ public Task Accept(IBlockTreeVisitor blockTreeVisitor, CancellationToken cancell
public BlockInfo FindCanonicalBlockInfo(long blockNumber)
=> throw new NotSupportedException();
- public Hash256 FindHash(long blockNumber)
- => throw new NotSupportedException();
-
public IOwnedReadOnlyList FindHeaders(Hash256 hash, int numberOfBlocks, int skip, bool reverse)
=> throw new NotSupportedException();
diff --git a/src/Nethermind/Nethermind.Core.Test/Builders/BlockTreeBuilder.cs b/src/Nethermind/Nethermind.Core.Test/Builders/BlockTreeBuilder.cs
index 676d91b3646..11283658999 100644
--- a/src/Nethermind/Nethermind.Core.Test/Builders/BlockTreeBuilder.cs
+++ b/src/Nethermind/Nethermind.Core.Test/Builders/BlockTreeBuilder.cs
@@ -33,6 +33,7 @@ public class BlockTreeBuilder : BuilderBase
private IReceiptStorage? _receiptStorage;
private IEthereumEcdsa? _ecdsa;
private Hash256? _stateRoot;
+ private Func? _stateRootGen;
private Func>? _logCreationFunction;
private bool _onlyHeaders;
@@ -200,6 +201,12 @@ public BlockTreeBuilder WithStateRoot(Hash256 stateRoot)
return this;
}
+ public BlockTreeBuilder WithStateRoot(Func stateRootGen)
+ {
+ _stateRootGen = stateRootGen;
+ return this;
+ }
+
public BlockTreeBuilder OfChainLength(int chainLength, int splitVariant = 0, int splitFrom = 0, bool withWithdrawals = false, params Address[] blockBeneficiaries)
{
OfChainLength(out _, chainLength, splitVariant, splitFrom, withWithdrawals, blockBeneficiaries);
@@ -324,6 +331,10 @@ private Block CreateBlock(int splitVariant, int splitFrom, int blockIndex, Block
.TestObject;
}
+ if (_stateRootGen is not null)
+ {
+ currentBlock.Header.StateRoot = _stateRootGen(currentBlock);
+ }
currentBlock.Header.AuRaStep = blockIndex;
return currentBlock;
diff --git a/src/Nethermind/Nethermind.Core.Test/RlpTests.cs b/src/Nethermind/Nethermind.Core.Test/RlpTests.cs
index b45b0c29fdf..49e9a776687 100644
--- a/src/Nethermind/Nethermind.Core.Test/RlpTests.cs
+++ b/src/Nethermind/Nethermind.Core.Test/RlpTests.cs
@@ -272,5 +272,41 @@ public void RlpContextWithSliceMemory_shouldNotCopyUnderlyingData(bool sliceValu
isACopy.Should().NotBe(sliceValue);
}
}
+
+ [TestCase(50)]
+ [TestCase(100)]
+ public void Over_limit_throws(int limit)
+ {
+ RlpStream stream = Prepare100BytesStream();
+ RlpLimit rlpLimit = new(limit);
+ if (limit < 100)
+ {
+ Assert.Throws(() => stream.DecodeByteArray(rlpLimit));
+ }
+ else
+ {
+ Assert.DoesNotThrow(() => stream.DecodeByteArray(rlpLimit));
+ }
+ }
+
+ [Test]
+ public void Not_enough_bytes_throws()
+ {
+ RlpStream stream = Prepare100BytesStream();
+ stream.Data[1] = 101; // tamper with length, it is more than available bytes
+ Assert.Throws(() => stream.DecodeByteArray());
+ }
+
+ private static RlpStream Prepare100BytesStream()
+ {
+ byte[] randomBytes = new byte[100];
+ Random.Shared.NextBytes(randomBytes);
+
+ int requiredLength = Rlp.LengthOf(randomBytes);
+ RlpStream stream = new(requiredLength);
+ stream.Encode(randomBytes);
+ stream.Reset();
+ return stream;
+ }
}
}
diff --git a/src/Nethermind/Nethermind.Core/Buffers/CappedArray.cs b/src/Nethermind/Nethermind.Core/Buffers/CappedArray.cs
index 50412982399..8b93eb27754 100644
--- a/src/Nethermind/Nethermind.Core/Buffers/CappedArray.cs
+++ b/src/Nethermind/Nethermind.Core/Buffers/CappedArray.cs
@@ -113,12 +113,10 @@ public readonly Span AsSpan(int start, int length)
return AsSpan().ToArray();
}
- public override string? ToString()
- {
- return typeof(T) == typeof(byte) ?
- SpanExtensions.ToHexString(MemoryMarshal.AsBytes(AsSpan()), withZeroX: true) :
+ public override string? ToString() =>
+ typeof(T) == typeof(byte) ?
+ MemoryMarshal.AsBytes(AsSpan()).ToHexString(withZeroX: true) :
base.ToString();
- }
public readonly ArraySegment AsArraySegment()
{
diff --git a/src/Nethermind/Nethermind.Core/Caching/ClockCache.cs b/src/Nethermind/Nethermind.Core/Caching/ClockCache.cs
index a13c6eaef1f..ed7e2e98db3 100644
--- a/src/Nethermind/Nethermind.Core/Caching/ClockCache.cs
+++ b/src/Nethermind/Nethermind.Core/Caching/ClockCache.cs
@@ -58,8 +58,12 @@ public bool Set(TKey key, TValue val)
if (_cacheMap.TryGetValue(key, out LruCacheItem ov))
{
- // Fast path: atomic update using TryUpdate
- if (_cacheMap.TryUpdate(key, new(val, ov.Offset), comparisonValue: ov))
+ bool needsUpdate = !(typeof(TValue).IsValueType ?
+ EqualityComparer.Default.Equals(ov.Value, val) :
+ ReferenceEquals(ov.Value, val));
+
+ // Fast path: no update or atomic update using TryUpdate
+ if (!needsUpdate || _cacheMap.TryUpdate(key, new(val, ov.Offset), comparisonValue: ov))
{
MarkAccessed(ov.Offset);
return false;
diff --git a/src/Nethermind/Nethermind.Core/KeyValueStoreExtensions.cs b/src/Nethermind/Nethermind.Core/KeyValueStoreExtensions.cs
index 906b39acf45..bea10d063f4 100644
--- a/src/Nethermind/Nethermind.Core/KeyValueStoreExtensions.cs
+++ b/src/Nethermind/Nethermind.Core/KeyValueStoreExtensions.cs
@@ -22,8 +22,6 @@ public static IWriteBatch LikeABatch(this IWriteOnlyKeyValueStore keyValueStore,
return new FakeWriteBatch(keyValueStore, onDispose);
}
- #region Getters
-
public static byte[]? Get(this IReadOnlyKeyValueStore db, Hash256 key)
{
#if DEBUG
@@ -88,12 +86,6 @@ public static bool KeyExists(this IReadOnlyKeyValueStore db, long key)
return span.IsNullOrEmpty() ? null : new DbSpanMemoryManager(db, span);
}
-
- #endregion
-
-
- #region Setters
-
public static void Set(this IWriteOnlyKeyValueStore db, Hash256 key, byte[] value, WriteFlags writeFlags = WriteFlags.None)
{
if (db.PreferWriteByArray)
@@ -115,14 +107,15 @@ public static void Set(this IWriteOnlyKeyValueStore db, Hash256 key, in CappedAr
db.PutSpan(key.Bytes, value.AsSpan(), writeFlags);
}
+ [SkipLocalsInit]
public static void Set(this IWriteOnlyKeyValueStore db, long blockNumber, Hash256 key, ReadOnlySpan value, WriteFlags writeFlags = WriteFlags.None)
{
Span blockNumberPrefixedKey = stackalloc byte[40];
- GetBlockNumPrefixedKey(blockNumber, key, blockNumberPrefixedKey);
+ GetBlockNumPrefixedKey(blockNumber, in key.ValueHash256, blockNumberPrefixedKey);
db.PutSpan(blockNumberPrefixedKey, value, writeFlags);
}
- public static void GetBlockNumPrefixedKey(long blockNumber, ValueHash256 blockHash, Span output)
+ public static void GetBlockNumPrefixedKey(long blockNumber, in ValueHash256 blockHash, Span output)
{
blockNumber.WriteBigEndian(output);
blockHash!.Bytes.CopyTo(output[8..]);
@@ -147,7 +140,7 @@ public static void Delete(this IWriteOnlyKeyValueStore db, long key)
public static void Delete(this IWriteOnlyKeyValueStore db, long blockNumber, Hash256 hash)
{
Span key = stackalloc byte[40];
- GetBlockNumPrefixedKey(blockNumber, hash, key);
+ GetBlockNumPrefixedKey(blockNumber, in hash.ValueHash256, key);
db.Remove(key);
}
@@ -155,7 +148,5 @@ public static void Set(this IWriteOnlyKeyValueStore db, long key, byte[] value)
{
db[key.ToBigEndianSpanWithoutLeadingZeros(out _)] = value;
}
-
- #endregion
}
}
diff --git a/src/Nethermind/Nethermind.Evm/CodeAnalysis/JumpDestinationAnalyzer.cs b/src/Nethermind/Nethermind.Evm/CodeAnalysis/JumpDestinationAnalyzer.cs
index 7069f962465..f5fb43269de 100644
--- a/src/Nethermind/Nethermind.Evm/CodeAnalysis/JumpDestinationAnalyzer.cs
+++ b/src/Nethermind/Nethermind.Evm/CodeAnalysis/JumpDestinationAnalyzer.cs
@@ -217,7 +217,7 @@ internal static long[] PopulateJumpDestinationBitmap_Vector512(long[] bitmap, Re
int overflow = size - inThis;
// Clear bits [lane .. lane+inThis-1] from both masks
- ulong clearThis = (Bmi1.X64.IsSupported ?
+ ulong clearThis = (Bmi2.X64.IsSupported ?
Bmi2.X64.ZeroHighBits(ulong.MaxValue, (uint)inThis) :
((1UL << inThis) - 1UL))
<< lane;
@@ -228,7 +228,7 @@ internal static long[] PopulateJumpDestinationBitmap_Vector512(long[] bitmap, Re
// If it spilled, mark those lanes for the next chunk
if (overflow > 0)
{
- carryMask = (Bmi1.X64.IsSupported ?
+ carryMask = (Bmi2.X64.IsSupported ?
Bmi2.X64.ZeroHighBits(ulong.MaxValue, (uint)overflow) :
((1UL << overflow) - 1UL));
break;
diff --git a/src/Nethermind/Nethermind.Facade/Eth/RpcTransaction/AccessListForRpc.cs b/src/Nethermind/Nethermind.Facade/Eth/RpcTransaction/AccessListForRpc.cs
index 7527ee93a49..d29ca507813 100644
--- a/src/Nethermind/Nethermind.Facade/Eth/RpcTransaction/AccessListForRpc.cs
+++ b/src/Nethermind/Nethermind.Facade/Eth/RpcTransaction/AccessListForRpc.cs
@@ -36,7 +36,7 @@ private class Item
[JsonConstructor]
public Item() { }
- public Item(Address address, IEnumerable storageKeys)
+ public Item(Address address, IEnumerable? storageKeys)
{
Address = address;
StorageKeys = storageKeys;
@@ -67,10 +67,98 @@ public class JsonConverter : JsonConverter
{
public override AccessListForRpc? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
- List- ? list = JsonSerializer.Deserialize
>(ref reader, options);
- return list is null ? null : new AccessListForRpc(list);
+ if (reader.TokenType != JsonTokenType.StartArray)
+ throw new JsonException("Expected start of array");
+
+ const int maxItems = 1000;
+ const int maxStorageKeysPerItem = 1000;
+ const int maxStorageKeys = 10000;
+
+ var items = new List- ();
+ int itemCount = 0;
+ int totalItemStorageItemsCount = 0;
+
+ while (reader.Read())
+ {
+ if (reader.TokenType == JsonTokenType.EndArray)
+ break;
+
+ if (itemCount >= maxItems)
+ throw new JsonException($"Access list cannot have more than {maxItems} items.");
+
+ if (reader.TokenType != JsonTokenType.StartObject)
+ throw new JsonException("Expected start of item object");
+
+ Address? address = null;
+ List? storageKeys = null;
+
+ // Read Item properties
+ while (reader.Read())
+ {
+ if (reader.TokenType == JsonTokenType.EndObject)
+ break;
+
+ if (reader.TokenType != JsonTokenType.PropertyName)
+ throw new JsonException("Expected property name");
+
+ string propName = reader.GetString() ?? throw new JsonException("Property name cannot be null");
+ reader.Read(); // move to property value
+
+ if (string.Equals(propName, nameof(Item.Address), StringComparison.OrdinalIgnoreCase))
+ {
+ address = JsonSerializer.Deserialize(ref reader, options);
+ }
+ else if (string.Equals(propName, nameof(Item.StorageKeys), StringComparison.OrdinalIgnoreCase))
+ {
+ if (reader.TokenType == JsonTokenType.Null)
+ {
+ storageKeys = null;
+ }
+ else if (reader.TokenType == JsonTokenType.StartArray)
+ {
+ storageKeys = new List();
+ int currentItemStorageItemsCount = 0;
+
+ while (reader.Read())
+ {
+ if (reader.TokenType == JsonTokenType.EndArray)
+ break;
+
+ if (currentItemStorageItemsCount >= maxStorageKeysPerItem)
+ throw new JsonException($"An item cannot have more than {maxStorageKeysPerItem} storage keys.");
+
+ if (totalItemStorageItemsCount >= maxStorageKeys)
+ throw new JsonException($"Access List cannot have more than {maxStorageKeys} storage keys.");
+
+ UInt256 key = JsonSerializer.Deserialize(ref reader, options);
+ storageKeys.Add(key);
+ currentItemStorageItemsCount++;
+ totalItemStorageItemsCount++;
+ }
+ }
+ else
+ {
+ throw new JsonException("Expected array or null for StorageKeys");
+ }
+ }
+ else
+ {
+ // Skip unknown properties
+ reader.Skip();
+ }
+ }
+
+ if (address is null)
+ throw new JsonException("Item missing required Address");
+
+ items.Add(new Item(address, storageKeys));
+ itemCount++;
+ }
+
+ return new AccessListForRpc(items);
}
+
public override void Write(Utf8JsonWriter writer, AccessListForRpc value, JsonSerializerOptions options)
{
JsonSerializer.Serialize(writer, value._items, options);
diff --git a/src/Nethermind/Nethermind.Facade/Simulate/SimulateDictionaryBlockStore.cs b/src/Nethermind/Nethermind.Facade/Simulate/SimulateDictionaryBlockStore.cs
index ea2f6f219b1..a10917a84b6 100644
--- a/src/Nethermind/Nethermind.Facade/Simulate/SimulateDictionaryBlockStore.cs
+++ b/src/Nethermind/Nethermind.Facade/Simulate/SimulateDictionaryBlockStore.cs
@@ -28,13 +28,15 @@ public void Delete(long blockNumber, Hash256 blockHash)
_blockNumDict.Remove(blockNumber);
}
- public Block? Get(long blockNumber, Hash256 blockHash, RlpBehaviors rlpBehaviors = RlpBehaviors.None, bool shouldCache = true)
+ public Block? Get(long blockNumber, Hash256 blockHash, out bool fromCache, RlpBehaviors rlpBehaviors = RlpBehaviors.None, bool shouldCache = true)
{
if (_blockNumDict.TryGetValue(blockNumber, out Block block))
{
+ fromCache = true;
return block;
}
+ fromCache = false;
block = readonlyBaseBlockStore.Get(blockNumber, blockHash, rlpBehaviors, false);
if (block is not null && shouldCache)
{
@@ -67,6 +69,8 @@ public void Delete(long blockNumber, Hash256 blockHash)
public void Cache(Block block)
=> Insert(block);
+ public Block? GetFromCache(Hash256 blockHash) => null;
+
public bool HasBlock(long blockNumber, Hash256 blockHash)
=> _blockNumDict.ContainsKey(blockNumber);
}
diff --git a/src/Nethermind/Nethermind.Facade/Simulate/SimulateDictionaryHeaderStore.cs b/src/Nethermind/Nethermind.Facade/Simulate/SimulateDictionaryHeaderStore.cs
index bcc2487ed0a..95bb5f3e45f 100644
--- a/src/Nethermind/Nethermind.Facade/Simulate/SimulateDictionaryHeaderStore.cs
+++ b/src/Nethermind/Nethermind.Facade/Simulate/SimulateDictionaryHeaderStore.cs
@@ -34,19 +34,21 @@ public void BulkInsert(IReadOnlyList headers)
}
}
- public BlockHeader? Get(Hash256 blockHash, bool shouldCache = false, long? blockNumber = null)
+ public BlockHeader? Get(Hash256 blockHash, out bool fromCache, bool shouldCache = false, long? blockNumber = null)
{
blockNumber ??= GetBlockNumber(blockHash);
if (blockNumber.HasValue && _headerDict.TryGetValue(blockHash, out BlockHeader? header))
{
+ fromCache = true;
if (shouldCache)
{
- Cache(header);
+ InsertBlockNumber(header.Hash, header.Number);
}
return header;
}
+ fromCache = false;
header = readonlyBaseHeaderStore.Get(blockHash, false, blockNumber);
if (header is not null && shouldCache)
{
@@ -55,7 +57,7 @@ public void BulkInsert(IReadOnlyList headers)
return header;
}
- public void Cache(BlockHeader header)
+ public void Cache(BlockHeader header, bool isCanonical = false)
{
Insert(header);
}
@@ -75,4 +77,10 @@ public void InsertBlockNumber(Hash256 blockHash, long blockNumber)
{
return _blockNumberDict.TryGetValue(blockHash, out var blockNumber) ? blockNumber : readonlyBaseHeaderStore.GetBlockNumber(blockHash);
}
+
+ public Hash256? GetBlockHash(long blockNumber) => null;
+
+ public BlockHeader? GetFromCache(Hash256 blockHash) => null;
+
+ public void CacheBlockHash(long blockNumber, Hash256 blockHash) { }
}
diff --git a/src/Nethermind/Nethermind.Init/Steps/InitializeNetwork.cs b/src/Nethermind/Nethermind.Init/Steps/InitializeNetwork.cs
index d5bd3ef2df0..191592ca38b 100644
--- a/src/Nethermind/Nethermind.Init/Steps/InitializeNetwork.cs
+++ b/src/Nethermind/Nethermind.Init/Steps/InitializeNetwork.cs
@@ -292,7 +292,6 @@ private async Task InitPeer()
_forkInfo,
_api.GossipPolicy,
_api.WorldStateManager!,
- _api.BlockTree,
_api.LogManager,
_api.TxGossipPolicy);
diff --git a/src/Nethermind/Nethermind.JsonRpc.Test/Modules/Eth/EthRpcModuleTests.cs b/src/Nethermind/Nethermind.JsonRpc.Test/Modules/Eth/EthRpcModuleTests.cs
index 484cfe255e8..6007f2194ee 100644
--- a/src/Nethermind/Nethermind.JsonRpc.Test/Modules/Eth/EthRpcModuleTests.cs
+++ b/src/Nethermind/Nethermind.JsonRpc.Test/Modules/Eth/EthRpcModuleTests.cs
@@ -309,6 +309,14 @@ public async Task Eth_get_filter_changes_missing()
Assert.That(serialized2, Is.EqualTo("{\"jsonrpc\":\"2.0\",\"error\":{\"code\":-32000,\"message\":\"Filter not found\"},\"id\":67}"));
}
+ [Test]
+ public async Task Eth_get_filter_changes_too_big()
+ {
+ using Context ctx = await Context.Create();
+ string serialized2 = await ctx.Test.TestEthRpc("eth_getFilterChanges", ((UInt256)uint.MaxValue + 1).ToHexString(true));
+ Assert.That(serialized2, Is.EqualTo("{\"jsonrpc\":\"2.0\",\"error\":{\"code\":-32000,\"message\":\"Filter not found\"},\"id\":67}"));
+ }
+
[Test]
public async Task Eth_uninstall_filter()
{
diff --git a/src/Nethermind/Nethermind.JsonRpc.Test/Modules/TestRpcBlockchain.cs b/src/Nethermind/Nethermind.JsonRpc.Test/Modules/TestRpcBlockchain.cs
index 9e994e034d9..1a43ecd6fa4 100644
--- a/src/Nethermind/Nethermind.JsonRpc.Test/Modules/TestRpcBlockchain.cs
+++ b/src/Nethermind/Nethermind.JsonRpc.Test/Modules/TestRpcBlockchain.cs
@@ -227,7 +227,6 @@ await base.Build(builder =>
Container.Resolve(),
Substitute.For(),
WorldStateManager,
- Substitute.For(),
LimboLogs.Instance,
Substitute.For()
);
diff --git a/src/Nethermind/Nethermind.JsonRpc/Modules/DebugModule/DebugBridge.cs b/src/Nethermind/Nethermind.JsonRpc/Modules/DebugModule/DebugBridge.cs
index 65cdf2cd9bf..3937a3f6565 100644
--- a/src/Nethermind/Nethermind.JsonRpc/Modules/DebugModule/DebugBridge.cs
+++ b/src/Nethermind/Nethermind.JsonRpc/Modules/DebugModule/DebugBridge.cs
@@ -171,7 +171,7 @@ public IReadOnlyCollection GetBlockTrace(Block block, Cancellat
{
if (parameter.BlockNumber is long number)
{
- Hash256? hash = _blockTree.FindHash(number);
+ Hash256? hash = _blockTree.FindBlockHash(number);
if (hash is null) return null;
return _blockStore.GetRlp(number, hash);
}
diff --git a/src/Nethermind/Nethermind.JsonRpc/Modules/Eth/EthRpcModule.cs b/src/Nethermind/Nethermind.JsonRpc/Modules/Eth/EthRpcModule.cs
index 6fd03117474..6adb304f637 100644
--- a/src/Nethermind/Nethermind.JsonRpc/Modules/Eth/EthRpcModule.cs
+++ b/src/Nethermind/Nethermind.JsonRpc/Modules/Eth/EthRpcModule.cs
@@ -527,7 +527,7 @@ public ResultWrapper eth_getTransactionByBlockNumberAndIndex(
public ResultWrapper> eth_getFilterChanges(UInt256 filterId)
{
- int id = (int)filterId;
+ int id = filterId <= int.MaxValue ? (int)filterId : -1;
FilterType filterType = _blockchainBridge.GetFilterType(id);
switch (filterType)
{
@@ -535,19 +535,19 @@ public ResultWrapper> eth_getFilterChanges(UInt256 filterId)
{
return _blockchainBridge.FilterExists(id)
? ResultWrapper>.Success(_blockchainBridge.GetBlockFilterChanges(id))
- : ResultWrapper>.Fail($"Filter not found", ErrorCodes.InvalidInput);
+ : ResultWrapper>.Fail("Filter not found", ErrorCodes.InvalidInput);
}
case FilterType.PendingTransactionFilter:
{
return _blockchainBridge.FilterExists(id)
? ResultWrapper>.Success(_blockchainBridge.GetPendingTransactionFilterChanges(id))
- : ResultWrapper>.Fail($"Filter not found", ErrorCodes.InvalidInput);
+ : ResultWrapper>.Fail("Filter not found", ErrorCodes.InvalidInput);
}
case FilterType.LogFilter:
{
return _blockchainBridge.FilterExists(id)
? ResultWrapper>.Success(_blockchainBridge.GetLogFilterChanges(id).ToArray())
- : ResultWrapper>.Fail($"Filter not found", ErrorCodes.InvalidInput);
+ : ResultWrapper>.Fail("Filter not found", ErrorCodes.InvalidInput);
}
default:
{
diff --git a/src/Nethermind/Nethermind.Logging.NLog/NLogManager.cs b/src/Nethermind/Nethermind.Logging.NLog/NLogManager.cs
index 830e25fd2ab..4497f7cf048 100644
--- a/src/Nethermind/Nethermind.Logging.NLog/NLogManager.cs
+++ b/src/Nethermind/Nethermind.Logging.NLog/NLogManager.cs
@@ -31,6 +31,7 @@ public NLogManager(string logFileName, string logDirectory = null, string logRul
// Required since 'NLog.config' could change during runtime, we need to re-apply the configuration
_logManagerOnConfigurationChanged = (sender, args) => Setup(logFileName, logDirectory, logRules);
LogManager.ConfigurationChanged += _logManagerOnConfigurationChanged;
+ Static.LogManager = this;
}
private static void Setup(string logFileName, string logDirectory = null, string logRules = null)
diff --git a/src/Nethermind/Nethermind.Logging/StaticLoggerFactory.cs b/src/Nethermind/Nethermind.Logging/StaticLoggerFactory.cs
new file mode 100644
index 00000000000..c38d81648b9
--- /dev/null
+++ b/src/Nethermind/Nethermind.Logging/StaticLoggerFactory.cs
@@ -0,0 +1,11 @@
+// SPDX-FileCopyrightText: 2025 Demerzel Solutions Limited
+// SPDX-License-Identifier: LGPL-3.0-only
+
+using System;
+
+namespace Nethermind.Logging;
+
+public static class Static
+{
+ public static ILogManager LogManager { get; set; } = LimboLogs.Instance;
+}
diff --git a/src/Nethermind/Nethermind.Merge.Plugin.Test/EngineModuleTests.V1.cs b/src/Nethermind/Nethermind.Merge.Plugin.Test/EngineModuleTests.V1.cs
index 8ee688de990..fd73ea05442 100644
--- a/src/Nethermind/Nethermind.Merge.Plugin.Test/EngineModuleTests.V1.cs
+++ b/src/Nethermind/Nethermind.Merge.Plugin.Test/EngineModuleTests.V1.cs
@@ -140,7 +140,7 @@ public async Task can_parse_forkchoiceUpdated_with_implicit_null_payloadAttribut
public void ForkchoiceV1_ToString_returns_correct_results()
{
ForkchoiceStateV1 forkchoiceState = new(TestItem.KeccakA, TestItem.KeccakF, TestItem.KeccakC);
- forkchoiceState.ToString().Should().Be("ForkChoice: 0x03783f...35b760, Safe: 0x017e66...b18f72, Finalized: 0xe61d9a...97c37a");
+ forkchoiceState.ToString().Should().Be("ForkChoice: 0x03783fac2efed8fbc9ad443e592ee30e61d65f471140c10ca155e937b435b760, Safe: 0x017e667f4b8c174291d1543c466717566e206df1bfd6f30271055ddafdb18f72, Finalized: 0xe61d9a3d3848fb2cdd9a2ab61e2f21a10ea431275aed628a0557f9dee697c37a");
}
[Test]
@@ -1551,6 +1551,9 @@ public void Should_return_expected_capabilities_for_mainnet()
nameof(IEngineRpcModule.engine_getPayloadV4),
nameof(IEngineRpcModule.engine_newPayloadV4),
+
+ nameof(IEngineRpcModule.engine_getPayloadV5),
+ nameof(IEngineRpcModule.engine_getBlobsV2)
};
Assert.That(result, Is.EquivalentTo(expectedMethods));
}
diff --git a/src/Nethermind/Nethermind.Merge.Plugin/Data/ForkchoiceStateV1.cs b/src/Nethermind/Nethermind.Merge.Plugin/Data/ForkchoiceStateV1.cs
index 546e2fe4df9..1eb47a3018f 100644
--- a/src/Nethermind/Nethermind.Merge.Plugin/Data/ForkchoiceStateV1.cs
+++ b/src/Nethermind/Nethermind.Merge.Plugin/Data/ForkchoiceStateV1.cs
@@ -10,33 +10,26 @@ namespace Nethermind.Merge.Plugin.Data;
///
///
///
-public class ForkchoiceStateV1
+public class ForkchoiceStateV1(Hash256 headBlockHash, Hash256 finalizedBlockHash, Hash256 safeBlockHash)
{
- public ForkchoiceStateV1(Hash256 headBlockHash, Hash256 finalizedBlockHash, Hash256 safeBlockHash)
- {
- HeadBlockHash = headBlockHash;
- FinalizedBlockHash = finalizedBlockHash;
- SafeBlockHash = safeBlockHash;
- }
-
///
/// Hash of the head of the canonical chain.
///
- public Hash256 HeadBlockHash { get; set; }
+ public Hash256 HeadBlockHash { get; set; } = headBlockHash;
///
/// Safe block hash of the canonical chain under certain synchrony and honesty assumptions. This value MUST be either equal to or an ancestor of headBlockHash.
///
/// Can be when transition block is not finalized yet.
- public Hash256 SafeBlockHash { get; set; }
+ public Hash256 SafeBlockHash { get; set; } = safeBlockHash;
///
/// Hash of the most recent finalized block
///
/// Can be when transition block is not finalized yet.
- public Hash256 FinalizedBlockHash { get; set; }
+ public Hash256 FinalizedBlockHash { get; set; } = finalizedBlockHash;
- public override string ToString() => $"ForkChoice: {HeadBlockHash.ToShortString()}, Safe: {SafeBlockHash.ToShortString()}, Finalized: {FinalizedBlockHash.ToShortString()}";
+ public override string ToString() => $"ForkChoice: {HeadBlockHash}, Safe: {SafeBlockHash}, Finalized: {FinalizedBlockHash}";
public string ToString(long? headNumber, long? safeNumber, long? finalizedNumber) =>
headNumber is null || safeNumber is null || finalizedNumber is null
? ToString()
diff --git a/src/Nethermind/Nethermind.Network.Benchmark/InFlowBenchmarks.cs b/src/Nethermind/Nethermind.Network.Benchmark/InFlowBenchmarks.cs
index 1a06dbf9cfc..e9a29e1c87c 100644
--- a/src/Nethermind/Nethermind.Network.Benchmark/InFlowBenchmarks.cs
+++ b/src/Nethermind/Nethermind.Network.Benchmark/InFlowBenchmarks.cs
@@ -19,9 +19,9 @@ namespace Nethermind.Network.Benchmarks
{
public class InFlowBenchmarks
{
- private static byte[] _input = Bytes.FromHexString("96cb6a910a279cab5bf0e0cd0432c7d1d28f76b794402bd97ba5a7dfa1b0e163fc75cd604bbc43d3c888618db2453158b06548d9bdb1b8208b70e0075d675bbe80268afff07570005454467c0c3e85a3468e3225015521fecb9a0c1a2092fd445f14552e33d4ebb4d7b533606989210b4c702662b4d2417f293b2701ee4336624916cddefb4c3777d1a144e1df66162b60bcaed3319becbbe19062f7e3715505dc84fa23a531d7dfd35b9ad95042a85faa643a40365d52d0b57e67cc428676cc364567a6c8cf7fda48bd4c54fd2c8fad1e504f3c2451f538793c022fcdfaef9d8fdafae2c989a7e61dba88879cc9062b774bc3f999b0cdece9aab3d34fafc1513e2de18e6c297d07a5992cc68adb27ce3de39c290884e2f6f0f8b24645c622b2cd9d396498e689a44dc775c22f4baa6b5c1685c19b020110c5788b0e61ebf1794f05df9237d61ca95f49f00fa0217c6a0935f5674c32f8eb7b9c5fe351d429ba0383761d980b53b1187a9a367f80075c020fcc71a75689a4a9e74b8c6137ab267ce6e697008ff5f8c29af53fd4f97018da197f990181f112be738fd0f57bcfc72a78dda731b64aecdd3f83586f1fcfdbdf2c58eff42b38fed19730a1fd0dd21d248d78a396689a508561590543dbe62b44475bec724f2c13678dc877d6cc15099634c10ee4206b339e45c4829c8d81537c3b57fab792929f79a391c29d4f10f6db8318d9f2bffffdafb3038f005ab5e9ce4bf46960f7cf078cebc4287da0330f6a02555912ce7db52a893e4739af43dd64eb9a70e8b4460da9955c39e5c24d2d35b29999b4c9979ba5acd16abd09427b57ad78db5076b0671fa4bf90a48b863db8a372c99e28a21213246f15cf36470f3a397b9e3cd1b0e4ce4e9ff1f95c5bc4c63c691a960d12e45f452adc7961eaca8dd24a3f51b16c6b15c930500b3c9b9e99638eedbf18332de4f5fa3e948872f46bbe2594de8bf32a1ca36a80a284dc1e055379543bded00cf110bdf5da82f0dc5e5a410c5aa925dd4a7dd2408796f05d3f1ab43c5b30a17e7e392212b0a61eacdfb200a1788e99f85a896e77fe7a86a17e61fdf00da6d9b2aac1935068bb6271a265e6b41aca2662b9beca7005bbbcbb371bb7daf60bf3be282e67ba17cb7d120015a8f7867391197c457e913a9558382f4dfbdd1e8693babf5835707c0142be013ec92869415c772dfc4581c5bb0aaaf0f7dabee7d488ea7e8a8954507e7f04f942429be3dda2317f719f8cf8e68720a91cce20d2406f35347cadb24e4ce3024a70c7938fa14179a9434996f73d666bd9fcb9b0dc02492fdef10a52c39a976058d611d854e6cad01cf85ff380499909638f7ca8f7397cb52b7d3cd9401b5905838b6134e6dbf4a03dff05551043532dbe9833502bba97839964806d55655804eef2317f68caf724f28625bcae7b761fac920d256c78e3843529e4ce7d25861db489128112002df79ee13cdcf5776e0416bdb36c022deba8d5329900da5c9a3bac6d39284691ca2ee684ee7fc9335c5183542b3751199587e9a0a0cad9b7e5365703ceda47d5959548a6a097fe8ff0f1fe8c59ad377db05f11002bacf555d14e99d7ae8cfd7960967384d912ecc3e11cc24cf9849d2592b834fa04ce12e0cb75eb4668ad6d3e43f16cf0dc2c9f54d77877014859b3b74b2bf9489b06682e0bd8f868a7fc34d8e7ff7d2c03435c5a15cc61a0a0791db38a9cc205e9f18e28e1445bfe982e25aeaa196d8dd14d25b65add73760726e15a1f54a414fd9377d2f87d33f87e117731fa6d04377686f33578c8b5481580fd81a74a17e4fb011bdcd9a10ee65ca37ae9eb5e606fde06d95d50880f4732ea6cb81f3d9adc88d43aa15aeadd4e790973f88b0dcdbe18cab73a9309f9e151c21d4025aa1af0fb37fe31ce25ce39c51fdb4e932b45c168111f54398fea71dd3e82ab01499db695e36308bc800246f17393d218a241191086fa02a4d70514e5bb3c5ff229a8320e53e0cd2b605c674dd6006b74a394fcfcf4d8fe0b61c3768b9e53b6601854ebe5dcd210b3db16adf68065a4f60a7dbd3c79776a21f6440cdc263aaa423b11954795d9424e3543b27efbe8dd76e002c30889e872c6d12d82c48933c382347ee89e69cf5d0120b342969312f98a19a8e972a8ef4f78a23701ac80deaf90cbc6b13fddea674790d6e9fe46d054f900b569e624bd50f8c8b20d3cdb29117f0cd28cb3a15c8530fb4827ebd3af739e872b59e6566c928933");
+ private static readonly byte[] _input = Bytes.FromHexString("96cb6a910a279cab5bf0e0cd0432c7d1d28f76b794402bd97ba5a7dfa1b0e163fc75cd604bbc43d3c888618db2453158b06548d9bdb1b8208b70e0075d675bbe80268afff07570005454467c0c3e85a3468e3225015521fecb9a0c1a2092fd445f14552e33d4ebb4d7b533606989210b4c702662b4d2417f293b2701ee4336624916cddefb4c3777d1a144e1df66162b60bcaed3319becbbe19062f7e3715505dc84fa23a531d7dfd35b9ad95042a85faa643a40365d52d0b57e67cc428676cc364567a6c8cf7fda48bd4c54fd2c8fad1e504f3c2451f538793c022fcdfaef9d8fdafae2c989a7e61dba88879cc9062b774bc3f999b0cdece9aab3d34fafc1513e2de18e6c297d07a5992cc68adb27ce3de39c290884e2f6f0f8b24645c622b2cd9d396498e689a44dc775c22f4baa6b5c1685c19b020110c5788b0e61ebf1794f05df9237d61ca95f49f00fa0217c6a0935f5674c32f8eb7b9c5fe351d429ba0383761d980b53b1187a9a367f80075c020fcc71a75689a4a9e74b8c6137ab267ce6e697008ff5f8c29af53fd4f97018da197f990181f112be738fd0f57bcfc72a78dda731b64aecdd3f83586f1fcfdbdf2c58eff42b38fed19730a1fd0dd21d248d78a396689a508561590543dbe62b44475bec724f2c13678dc877d6cc15099634c10ee4206b339e45c4829c8d81537c3b57fab792929f79a391c29d4f10f6db8318d9f2bffffdafb3038f005ab5e9ce4bf46960f7cf078cebc4287da0330f6a02555912ce7db52a893e4739af43dd64eb9a70e8b4460da9955c39e5c24d2d35b29999b4c9979ba5acd16abd09427b57ad78db5076b0671fa4bf90a48b863db8a372c99e28a21213246f15cf36470f3a397b9e3cd1b0e4ce4e9ff1f95c5bc4c63c691a960d12e45f452adc7961eaca8dd24a3f51b16c6b15c930500b3c9b9e99638eedbf18332de4f5fa3e948872f46bbe2594de8bf32a1ca36a80a284dc1e055379543bded00cf110bdf5da82f0dc5e5a410c5aa925dd4a7dd2408796f05d3f1ab43c5b30a17e7e392212b0a61eacdfb200a1788e99f85a896e77fe7a86a17e61fdf00da6d9b2aac1935068bb6271a265e6b41aca2662b9beca7005bbbcbb371bb7daf60bf3be282e67ba17cb7d120015a8f7867391197c457e913a9558382f4dfbdd1e8693babf5835707c0142be013ec92869415c772dfc4581c5bb0aaaf0f7dabee7d488ea7e8a8954507e7f04f942429be3dda2317f719f8cf8e68720a91cce20d2406f35347cadb24e4ce3024a70c7938fa14179a9434996f73d666bd9fcb9b0dc02492fdef10a52c39a976058d611d854e6cad01cf85ff380499909638f7ca8f7397cb52b7d3cd9401b5905838b6134e6dbf4a03dff05551043532dbe9833502bba97839964806d55655804eef2317f68caf724f28625bcae7b761fac920d256c78e3843529e4ce7d25861db489128112002df79ee13cdcf5776e0416bdb36c022deba8d5329900da5c9a3bac6d39284691ca2ee684ee7fc9335c5183542b3751199587e9a0a0cad9b7e5365703ceda47d5959548a6a097fe8ff0f1fe8c59ad377db05f11002bacf555d14e99d7ae8cfd7960967384d912ecc3e11cc24cf9849d2592b834fa04ce12e0cb75eb4668ad6d3e43f16cf0dc2c9f54d77877014859b3b74b2bf9489b06682e0bd8f868a7fc34d8e7ff7d2c03435c5a15cc61a0a0791db38a9cc205e9f18e28e1445bfe982e25aeaa196d8dd14d25b65add73760726e15a1f54a414fd9377d2f87d33f87e117731fa6d04377686f33578c8b5481580fd81a74a17e4fb011bdcd9a10ee65ca37ae9eb5e606fde06d95d50880f4732ea6cb81f3d9adc88d43aa15aeadd4e790973f88b0dcdbe18cab73a9309f9e151c21d4025aa1af0fb37fe31ce25ce39c51fdb4e932b45c168111f54398fea71dd3e82ab01499db695e36308bc800246f17393d218a241191086fa02a4d70514e5bb3c5ff229a8320e53e0cd2b605c674dd6006b74a394fcfcf4d8fe0b61c3768b9e53b6601854ebe5dcd210b3db16adf68065a4f60a7dbd3c79776a21f6440cdc263aaa423b11954795d9424e3543b27efbe8dd76e002c30889e872c6d12d82c48933c382347ee89e69cf5d0120b342969312f98a19a8e972a8ef4f78a23701ac80deaf90cbc6b13fddea674790d6e9fe46d054f900b569e624bd50f8c8b20d3cdb29117f0cd28cb3a15c8530fb4827ebd3af739e872b59e6566c928933");
- private IByteBuffer _decoderBuffer = PooledByteBufferAllocator.Default.Buffer(1024 * 1024);
+ private readonly IByteBuffer _decoderBuffer = PooledByteBufferAllocator.Default.Buffer(1024 * 1024);
private NewBlockMessage _outputMessage;
private NewBlockMessageSerializer _newBlockMessageSerializer;
@@ -57,8 +57,8 @@ public void Setup()
public void IterationSetup()
{
_secrets = NetTestVectors.GetSecretsPair();
- FrameCipher frameCipher = new FrameCipher(_secrets.B.AesSecret);
- FrameMacProcessor frameMacProcessor = new FrameMacProcessor(TestItem.IgnoredPublicKey, _secrets.B);
+ FrameCipher frameCipher = new(_secrets.B.AesSecret);
+ FrameMacProcessor frameMacProcessor = new(TestItem.IgnoredPublicKey, _secrets.B);
_zeroDecoder = new TestZeroDecoder(frameCipher, frameMacProcessor);
}
@@ -79,7 +79,8 @@ private void SetupAll(bool useLimboOutput = false)
ResourceLeakDetector.Level = ResourceLeakDetector.DetectionLevel.Paranoid;
}
- private class TestZeroDecoder : ZeroFrameDecoder
+ private class TestZeroDecoder(IFrameCipher frameCipher, FrameMacProcessor frameMacProcessor)
+ : ZeroFrameDecoder(frameCipher, frameMacProcessor)
{
public IByteBuffer Decode(IByteBuffer input)
{
@@ -87,20 +88,10 @@ public IByteBuffer Decode(IByteBuffer input)
base.Decode(null, input, result);
return (IByteBuffer)result[0];
}
-
- public TestZeroDecoder(IFrameCipher frameCipher, FrameMacProcessor frameMacProcessor)
- : base(frameCipher, frameMacProcessor, LimboLogs.Instance)
- {
- }
}
- private class TestZeroMerger : Rlpx.ZeroFrameMerger
+ private class TestZeroMerger() : ZeroFrameMerger(LimboLogs.Instance)
{
- public TestZeroMerger()
- : base(LimboLogs.Instance)
- {
- }
-
public IByteBuffer Decode(IByteBuffer input)
{
var result = new List