diff --git a/src/Nethermind/Nethermind.Blockchain/BlockTree.cs b/src/Nethermind/Nethermind.Blockchain/BlockTree.cs
index 353849ad43b..77f26923bdd 100644
--- a/src/Nethermind/Nethermind.Blockchain/BlockTree.cs
+++ b/src/Nethermind/Nethermind.Blockchain/BlockTree.cs
@@ -1272,7 +1272,7 @@ private ChainLevelInfo UpdateOrCreateLevel(long number, Hash256 hash, BlockInfo
///
private bool ShouldCache(long number)
{
- return number == 0L || Head is null || number <= Head.Number + 1;
+ return number == 0L || Head is null || number >= Head.Number - HeaderStore.CacheSize;
}
public ChainLevelInfo? FindLevel(long number)
diff --git a/src/Nethermind/Nethermind.Blockchain/Headers/HeaderStore.cs b/src/Nethermind/Nethermind.Blockchain/Headers/HeaderStore.cs
index f5f37cc46b2..87eefab12e8 100644
--- a/src/Nethermind/Nethermind.Blockchain/Headers/HeaderStore.cs
+++ b/src/Nethermind/Nethermind.Blockchain/Headers/HeaderStore.cs
@@ -15,8 +15,8 @@ namespace Nethermind.Blockchain.Headers;
public class HeaderStore : IHeaderStore
{
- // SyncProgressResolver MaxLookupBack is 128, add 16 wiggle room
- private const int CacheSize = 128 + 16;
+ // SyncProgressResolver MaxLookupBack is 256, add 16 wiggle room
+ public const int CacheSize = 256 + 16;
private readonly IDb _headerDb;
private readonly IDb _blockNumberDb;
diff --git a/src/Nethermind/Nethermind.Synchronization/FastSync/DetailedProgress.cs b/src/Nethermind/Nethermind.Synchronization/FastSync/DetailedProgress.cs
index febb468f2ad..72cdb8a94c6 100644
--- a/src/Nethermind/Nethermind.Synchronization/FastSync/DetailedProgress.cs
+++ b/src/Nethermind/Nethermind.Synchronization/FastSync/DetailedProgress.cs
@@ -79,7 +79,7 @@ internal void DisplayProgressReport(int pendingRequestsCount, BranchProgress bra
}
if (logger.IsInfo) logger.Info(
- $"State Sync {TimeSpan.FromSeconds(SecondsInSync):dd\\.hh\\:mm\\:ss} | {dataSizeInfo} | branches: {branchProgress.Progress:P2} | kB/s: {savedKBytesPerSecond,5:F0} | accounts {SavedAccounts} | nodes {SavedNodesCount} | diagnostics: {pendingRequestsCount}.{AverageTimeInHandler:f2}ms");
+ $"State Sync {TimeSpan.FromSeconds(SecondsInSync):dd\\.hh\\:mm\\:ss} | {dataSizeInfo} | branches: {branchProgress.Progress:P2} | kB/s: {savedKBytesPerSecond,5:F0} | accounts {SavedAccounts} | nodes {SavedNodesCount} | pending: {pendingRequestsCount,3} | ave: {AverageTimeInHandler:f2}ms");
if (logger.IsDebug && DateTime.UtcNow - LastReportTime.full > TimeSpan.FromSeconds(10))
{
long allChecks = CheckWasInDependencies + CheckWasCached + StateWasThere + StateWasNotThere;
diff --git a/src/Nethermind/Nethermind.Synchronization/FastSync/TreeSync.cs b/src/Nethermind/Nethermind.Synchronization/FastSync/TreeSync.cs
index ba6f6f482c3..61c3821d8a9 100644
--- a/src/Nethermind/Nethermind.Synchronization/FastSync/TreeSync.cs
+++ b/src/Nethermind/Nethermind.Synchronization/FastSync/TreeSync.cs
@@ -308,6 +308,7 @@ shorter than the request */
_data.DisplayProgressReport(_pendingRequests.Count, _branchProgress, _logger);
+ handleWatch.Stop();
long total = handleWatch.ElapsedMilliseconds + _networkWatch.ElapsedMilliseconds;
if (total != 0)
{
@@ -326,9 +327,7 @@ shorter than the request */
Interlocked.Add(ref _handleWatch, handleWatch.ElapsedMilliseconds);
_data.LastDbReads = _data.DbChecks;
- _data.AverageTimeInHandler =
- (_data.AverageTimeInHandler * (_data.ProcessedRequestsCount - 1) +
- _handleWatch) / _data.ProcessedRequestsCount;
+ _data.AverageTimeInHandler = _handleWatch / (decimal)_data.ProcessedRequestsCount;
Interlocked.Add(ref _data.HandledNodesCount, nonEmptyResponses);
return result;
diff --git a/src/Nethermind/Nethermind.Trie/PatriciaTree.cs b/src/Nethermind/Nethermind.Trie/PatriciaTree.cs
index 7293c7c3918..a66779f4a97 100644
--- a/src/Nethermind/Nethermind.Trie/PatriciaTree.cs
+++ b/src/Nethermind/Nethermind.Trie/PatriciaTree.cs
@@ -1077,11 +1077,7 @@ public void Accept(ITreeVisitor visitor, Hash256 rootHash, VisitingOptions? visi
if (!rootHash.Equals(Keccak.EmptyTreeHash))
{
rootRef = RootHash == rootHash ? RootRef : TrieStore.FindCachedOrUnknown(rootHash);
- try
- {
- rootRef!.ResolveNode(TrieStore);
- }
- catch (TrieException)
+ if (!rootRef!.TryResolveNode(TrieStore))
{
visitor.VisitMissingNode(rootHash, trieVisitContext);
return;
diff --git a/src/Nethermind/Nethermind.Trie/Pruning/ITrieNodeResolver.cs b/src/Nethermind/Nethermind.Trie/Pruning/ITrieNodeResolver.cs
index ba5324457b5..3773cb26104 100644
--- a/src/Nethermind/Nethermind.Trie/Pruning/ITrieNodeResolver.cs
+++ b/src/Nethermind/Nethermind.Trie/Pruning/ITrieNodeResolver.cs
@@ -23,5 +23,12 @@ public interface ITrieNodeResolver
///
///
byte[]? LoadRlp(Hash256 hash, ReadFlags flags = ReadFlags.None);
+
+ ///
+ /// Loads RLP of the node.
+ ///
+ ///
+ ///
+ byte[]? TryLoadRlp(Hash256 hash, ReadFlags flags = ReadFlags.None);
}
}
diff --git a/src/Nethermind/Nethermind.Trie/Pruning/NullTrieNodeResolver.cs b/src/Nethermind/Nethermind.Trie/Pruning/NullTrieNodeResolver.cs
index 5eaa828eda1..5856ac64bb4 100644
--- a/src/Nethermind/Nethermind.Trie/Pruning/NullTrieNodeResolver.cs
+++ b/src/Nethermind/Nethermind.Trie/Pruning/NullTrieNodeResolver.cs
@@ -14,5 +14,6 @@ private NullTrieNodeResolver() { }
public TrieNode FindCachedOrUnknown(Hash256 hash) => new(NodeType.Unknown, hash);
public byte[]? LoadRlp(Hash256 hash, ReadFlags flags = ReadFlags.None) => null;
+ public byte[]? TryLoadRlp(Hash256 hash, ReadFlags flags = ReadFlags.None) => null;
}
}
diff --git a/src/Nethermind/Nethermind.Trie/Pruning/NullTrieStore.cs b/src/Nethermind/Nethermind.Trie/Pruning/NullTrieStore.cs
index 9b59b246232..55f77dc2b40 100644
--- a/src/Nethermind/Nethermind.Trie/Pruning/NullTrieStore.cs
+++ b/src/Nethermind/Nethermind.Trie/Pruning/NullTrieStore.cs
@@ -29,6 +29,8 @@ public event EventHandler ReorgBoundaryReached
public TrieNode FindCachedOrUnknown(Hash256 hash) => new(NodeType.Unknown, hash);
+ public byte[] TryLoadRlp(Hash256 hash, ReadFlags flags = ReadFlags.None) => null;
+
public byte[] LoadRlp(Hash256 hash, ReadFlags flags = ReadFlags.None) => Array.Empty();
public bool IsPersisted(in ValueHash256 keccak) => true;
diff --git a/src/Nethermind/Nethermind.Trie/Pruning/ReadOnlyTrieStore.cs b/src/Nethermind/Nethermind.Trie/Pruning/ReadOnlyTrieStore.cs
index f5ccf20926d..86710b26720 100644
--- a/src/Nethermind/Nethermind.Trie/Pruning/ReadOnlyTrieStore.cs
+++ b/src/Nethermind/Nethermind.Trie/Pruning/ReadOnlyTrieStore.cs
@@ -26,6 +26,7 @@ public ReadOnlyTrieStore(TrieStore trieStore, IKeyValueStore? readOnlyStore)
public TrieNode FindCachedOrUnknown(Hash256 hash) =>
_trieStore.FindCachedOrUnknown(hash, true);
+ public byte[]? TryLoadRlp(Hash256 hash, ReadFlags flags) => _trieStore.TryLoadRlp(hash, _readOnlyStore, flags);
public byte[] LoadRlp(Hash256 hash, ReadFlags flags) => _trieStore.LoadRlp(hash, _readOnlyStore, flags);
public bool IsPersisted(in ValueHash256 keccak) => _trieStore.IsPersisted(keccak);
diff --git a/src/Nethermind/Nethermind.Trie/Pruning/TrieStore.cs b/src/Nethermind/Nethermind.Trie/Pruning/TrieStore.cs
index 38c607dbf30..ec18d0c4ec6 100644
--- a/src/Nethermind/Nethermind.Trie/Pruning/TrieStore.cs
+++ b/src/Nethermind/Nethermind.Trie/Pruning/TrieStore.cs
@@ -322,22 +322,32 @@ public void FinishBlockCommit(TrieType trieType, long blockNumber, TrieNode? roo
public event EventHandler? ReorgBoundaryReached;
- public byte[] LoadRlp(Hash256 keccak, IKeyValueStore? keyValueStore, ReadFlags readFlags = ReadFlags.None)
+ public byte[]? TryLoadRlp(Hash256 keccak, IKeyValueStore? keyValueStore, ReadFlags readFlags = ReadFlags.None)
{
keyValueStore ??= _keyValueStore;
byte[]? rlp = keyValueStore.Get(keccak.Bytes, readFlags);
+ if (rlp is not null)
+ {
+ Metrics.LoadedFromDbNodesCount++;
+ }
+
+ return rlp;
+ }
+
+ public byte[] LoadRlp(Hash256 keccak, IKeyValueStore? keyValueStore, ReadFlags readFlags = ReadFlags.None)
+ {
+ byte[]? rlp = TryLoadRlp(keccak, keyValueStore, readFlags);
if (rlp is null)
{
throw new TrieNodeException($"Node {keccak} is missing from the DB", keccak);
}
- Metrics.LoadedFromDbNodesCount++;
-
return rlp;
}
public virtual byte[] LoadRlp(Hash256 keccak, ReadFlags readFlags = ReadFlags.None) => LoadRlp(keccak, null, readFlags);
+ public virtual byte[]? TryLoadRlp(Hash256 keccak, ReadFlags readFlags = ReadFlags.None) => TryLoadRlp(keccak, null, readFlags);
public bool IsPersisted(in ValueHash256 keccak)
{
diff --git a/src/Nethermind/Nethermind.Trie/TrieNode.cs b/src/Nethermind/Nethermind.Trie/TrieNode.cs
index f65a5decc5a..bb229a48678 100644
--- a/src/Nethermind/Nethermind.Trie/TrieNode.cs
+++ b/src/Nethermind/Nethermind.Trie/TrieNode.cs
@@ -304,52 +304,106 @@ public void ResolveNode(ITrieNodeResolver tree, ReadFlags readFlags = ReadFlags.
throw new InvalidAsynchronousStateException($"{nameof(_rlpStream)} is null when {nameof(NodeType)} is {NodeType}");
}
- Metrics.TreeNodeRlpDecodings++;
- _rlpStream.ReadSequenceLength();
+ if (!DecodeRlp(bufferPool, out int numberOfItems))
+ {
+ throw new TrieNodeException($"Unexpected number of items = {numberOfItems} when decoding a node from RLP ({FullRlp.AsSpan().ToHexString()})", Keccak ?? Nethermind.Core.Crypto.Keccak.Zero);
+ }
+ }
+ catch (RlpException rlpException)
+ {
+ throw new TrieNodeException($"Error when decoding node {Keccak}", Keccak ?? Nethermind.Core.Crypto.Keccak.Zero, rlpException);
+ }
+ }
+
+ ///
+ /// Highly optimized
+ ///
+ public bool TryResolveNode(ITrieNodeResolver tree, ReadFlags readFlags = ReadFlags.None, ICappedArrayPool? bufferPool = null)
+ {
+ try
+ {
+ if (NodeType == NodeType.Unknown)
+ {
+ if (FullRlp.IsNull)
+ {
+ if (Keccak is null)
+ {
+ return false;
+ }
- // micro optimization to prevent searches beyond 3 items for branches (search up to three)
- int numberOfItems = _rlpStream.PeekNumberOfItemsRemaining(null, 3);
+ FullRlp = tree.TryLoadRlp(Keccak, readFlags);
+ IsPersisted = true;
- if (numberOfItems > 2)
+ if (FullRlp.IsNull)
+ {
+ return false;
+ }
+ }
+ }
+ else
{
- NodeType = NodeType.Branch;
+ return true;
}
- else if (numberOfItems == 2)
+
+ _rlpStream = FullRlp.AsRlpStream();
+ if (_rlpStream is null)
{
- (byte[] key, bool isLeaf) = HexPrefix.FromBytes(_rlpStream.DecodeByteArraySpan());
+ throw new InvalidAsynchronousStateException($"{nameof(_rlpStream)} is null when {nameof(NodeType)} is {NodeType}");
+ }
- // a hack to set internally and still verify attempts from the outside
- // after the code is ready we should just add proper access control for methods from the outside and inside
- bool isDirtyActual = IsDirty;
- IsDirty = true;
+ return DecodeRlp(bufferPool, out _);
+ }
+ catch (RlpException)
+ {
+ return false;
+ }
+ }
- if (isLeaf)
- {
- NodeType = NodeType.Leaf;
- Key = key;
+ private bool DecodeRlp(ICappedArrayPool bufferPool, out int itemsCount)
+ {
+ Metrics.TreeNodeRlpDecodings++;
+ _rlpStream.ReadSequenceLength();
- ReadOnlySpan valueSpan = _rlpStream.DecodeByteArraySpan();
- CappedArray buffer = bufferPool.SafeRentBuffer(valueSpan.Length);
- valueSpan.CopyTo(buffer.AsSpan());
- Value = buffer;
- }
- else
- {
- NodeType = NodeType.Extension;
- Key = key;
- }
+ // micro optimization to prevent searches beyond 3 items for branches (search up to three)
+ int numberOfItems = itemsCount = _rlpStream.PeekNumberOfItemsRemaining(null, 3);
+
+ if (numberOfItems > 2)
+ {
+ NodeType = NodeType.Branch;
+ }
+ else if (numberOfItems == 2)
+ {
+ (byte[] key, bool isLeaf) = HexPrefix.FromBytes(_rlpStream.DecodeByteArraySpan());
+
+ // a hack to set internally and still verify attempts from the outside
+ // after the code is ready we should just add proper access control for methods from the outside and inside
+ bool isDirtyActual = IsDirty;
+ IsDirty = true;
+
+ if (isLeaf)
+ {
+ NodeType = NodeType.Leaf;
+ Key = key;
- IsDirty = isDirtyActual;
+ ReadOnlySpan valueSpan = _rlpStream.DecodeByteArraySpan();
+ CappedArray buffer = bufferPool.SafeRentBuffer(valueSpan.Length);
+ valueSpan.CopyTo(buffer.AsSpan());
+ Value = buffer;
}
else
{
- throw new TrieNodeException($"Unexpected number of items = {numberOfItems} when decoding a node from RLP ({FullRlp.AsSpan().ToHexString()})", Keccak ?? Nethermind.Core.Crypto.Keccak.Zero);
+ NodeType = NodeType.Extension;
+ Key = key;
}
+
+ IsDirty = isDirtyActual;
}
- catch (RlpException rlpException)
+ else
{
- throw new TrieNodeException($"Error when decoding node {Keccak}", Keccak ?? Nethermind.Core.Crypto.Keccak.Zero, rlpException);
+ return false;
}
+
+ return true;
}
public void ResolveKey(ITrieNodeResolver tree, bool isRoot, ICappedArrayPool? bufferPool = null)
diff --git a/src/Nethermind/Nethermind.Trie/TrieNodeResolverWithReadFlags.cs b/src/Nethermind/Nethermind.Trie/TrieNodeResolverWithReadFlags.cs
index b417aaf5e70..5f96323bf22 100644
--- a/src/Nethermind/Nethermind.Trie/TrieNodeResolverWithReadFlags.cs
+++ b/src/Nethermind/Nethermind.Trie/TrieNodeResolverWithReadFlags.cs
@@ -23,6 +23,16 @@ public TrieNode FindCachedOrUnknown(Hash256 hash)
return _baseResolver.FindCachedOrUnknown(hash);
}
+ public byte[]? TryLoadRlp(Hash256 hash, ReadFlags flags = ReadFlags.None)
+ {
+ if (flags != ReadFlags.None)
+ {
+ return _baseResolver.TryLoadRlp(hash, flags | _defaultFlags);
+ }
+
+ return _baseResolver.TryLoadRlp(hash, _defaultFlags);
+ }
+
public byte[]? LoadRlp(Hash256 hash, ReadFlags flags = ReadFlags.None)
{
if (flags != ReadFlags.None)