Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
using System.Diagnostics;
using System.Linq;
using FluentAssertions;
using Nethermind.Core;
using Nethermind.Core.Collections;
using Nethermind.Core.Crypto;
using Nethermind.Core.Test;
Expand Down Expand Up @@ -90,6 +91,12 @@ public static IEnumerable<TestCaseData> NewBranchesGen()
(new Hash256("cccccccc00000000000000000000000000000000000000000000000000000000"), MakeRandomValue(rng)),
(new Hash256("cccc000000000000000000000000000000000000000000000000000000000000"), MakeRandomValue(rng)),
}).SetName("deep value");

yield return new TestCaseData(new List<(Hash256 key, byte[] value)>()
{
(new Hash256("3333333333333333333333333333333333333333333333333333333333333333"), MakeRandomValue(rng)),
(new Hash256("3333333332222222222222222222222222222222222222222222222222222222"), MakeRandomValue(rng)),
}).SetName("matching long extension");
}

public static IEnumerable<TestCaseData> PreExistingDataGen()
Expand All @@ -109,6 +116,13 @@ public static IEnumerable<TestCaseData> PreExistingDataGen()
(new Hash256("3322222222222222222222222222222222222222222222222222222222222222"), MakeRandomValue(rng)),
}).SetName("one extension");

yield return new TestCaseData(new List<(Hash256 key, byte[] value)>()
{
(new Hash256("3333333332222222222222222222222222222222222222222222222222222222"), MakeRandomValue(rng)),
(new Hash256("3333333333333333333333333333333333333333333333333333333333333333"), MakeRandomValue(rng)),
(new Hash256("3333333344444444444444444444444444444444444444444444444444444444"), MakeRandomValue(rng)),
}).SetName("long extension with branch child");

yield return new TestCaseData(GenRandomOfLength(1000)).SetName("random 1000");
}

Expand All @@ -122,6 +136,10 @@ public static IEnumerable<TestCaseData> BulkSetTestGen()
{
yield return new TestCaseData(existingData.Arguments[0], testCaseData.Arguments[0]).SetName(existingData.TestName + " and " + testCaseData.TestName);
}

List<(Hash256 key, byte[] value)> originalSet = (List<(Hash256 key, byte[] value)>)existingData.Arguments[0];
List<(Hash256 key, byte[] value)> removal = originalSet.Select((kv) => (kv.key, (byte[])null)).ToList();
yield return new TestCaseData(existingData.Arguments[0], removal).SetName(existingData.TestName + " and remove self completely ");
}

yield return new TestCaseData(
Expand Down Expand Up @@ -255,7 +273,7 @@ public void BulkSet(List<(Hash256 key, byte[] value)> existingItems, List<(Hash2
long newWriteCount = 0;
{
TestMemDb db = new TestMemDb();
IScopedTrieStore trieStore = new RawScopedTrieStore(db);
IScopedTrieStore trieStore = new StrictRawScopedTrieStore(new RawScopedTrieStore(db));
PatriciaTree pTree = new PatriciaTree(trieStore, LimboLogs.Instance);
pTree.RootHash = Keccak.EmptyTreeHash;

Expand Down Expand Up @@ -306,7 +324,7 @@ public void BulkSetRootHashUpdated(List<(Hash256 key, byte[] value)> existingIte
(Hash256 root, TimeSpan baselineTime, long baselineWriteCount, string originalDump) = CalculateBaseline(existingItems, items, recordDump);

TestMemDb db = new TestMemDb();
IScopedTrieStore trieStore = new RawScopedTrieStore(db);
IScopedTrieStore trieStore = new StrictRawScopedTrieStore(new RawScopedTrieStore(db));
PatriciaTree pTree = new PatriciaTree(trieStore, LimboLogs.Instance);
pTree.RootHash = Keccak.EmptyTreeHash;

Expand Down Expand Up @@ -338,7 +356,7 @@ public void BulkSetPreSorted(List<(Hash256 key, byte[] value)> existingItems, Li
long preSortedWriteCount;
{
TestMemDb db = new TestMemDb();
IScopedTrieStore trieStore = new RawScopedTrieStore(db);
IScopedTrieStore trieStore = new StrictRawScopedTrieStore(new RawScopedTrieStore(db));
PatriciaTree pTree = new PatriciaTree(trieStore, LimboLogs.Instance);
pTree.RootHash = Keccak.EmptyTreeHash;

Expand Down Expand Up @@ -396,7 +414,7 @@ public void BulkSetOneByOne(List<(Hash256 key, byte[] value)> existingItems, Lis
{
// Just the bulk set one stack
TestMemDb db = new TestMemDb();
IScopedTrieStore trieStore = new RawScopedTrieStore(db);
IScopedTrieStore trieStore = new StrictRawScopedTrieStore(new RawScopedTrieStore(db));
PatriciaTree pTree = new PatriciaTree(trieStore, LimboLogs.Instance);
pTree.RootHash = Keccak.EmptyTreeHash;

Expand Down Expand Up @@ -448,7 +466,7 @@ private static (Hash256, TimeSpan, long, string originalDump) CalculateBaseline(
long baselineWriteCount = 0;
{
TestMemDb db = new TestMemDb();
IScopedTrieStore trieStore = new RawScopedTrieStore(db);
IScopedTrieStore trieStore = new StrictRawScopedTrieStore(new RawScopedTrieStore(db));
PatriciaTree pTree = new PatriciaTree(trieStore, LimboLogs.Instance);
pTree.RootHash = Keccak.EmptyTreeHash;

Expand Down Expand Up @@ -484,7 +502,7 @@ private static (Hash256, TimeSpan, long, string originalDump) CalculateBaseline(
[Test]
public void BulkSet_ShouldThrowOnNonUniqueEntries()
{
IScopedTrieStore trieStore = new RawScopedTrieStore(new TestMemDb());
IScopedTrieStore trieStore = new StrictRawScopedTrieStore(new RawScopedTrieStore(new TestMemDb()));
PatriciaTree pTree = new PatriciaTree(trieStore, LimboLogs.Instance);
pTree.RootHash = Keccak.EmptyTreeHash;

Expand Down Expand Up @@ -715,4 +733,30 @@ public void HexarySearch(int nibIndex, List<ValueHash256> paths, List<ValueHash2
result.ToArray().Should().BeEquivalentTo(expectedResult);

}

public class StrictRawScopedTrieStore(IScopedTrieStore baseTrieStore) : IScopedTrieStore
{
public TrieNode FindCachedOrUnknown(in TreePath path, Hash256 hash)
{
TrieNode node = baseTrieStore.FindCachedOrUnknown(in path, hash);
if (hash != Keccak.EmptyTreeHash)
{
byte[] rlp = LoadRlp(path, hash);
Assert.That(Keccak.Compute(rlp), Is.EqualTo(hash));
}
return node;
}

public byte[] LoadRlp(in TreePath path, Hash256 hash, ReadFlags flags = ReadFlags.None) => baseTrieStore.LoadRlp(in path, hash, flags);

public byte[] TryLoadRlp(in TreePath path, Hash256 hash, ReadFlags flags = ReadFlags.None) => baseTrieStore.TryLoadRlp(in path, hash, flags);

public ITrieNodeResolver GetStorageTrieNodeResolver(Hash256 address) => baseTrieStore.GetStorageTrieNodeResolver(address);

public INodeStorage.KeyScheme Scheme => baseTrieStore.Scheme;

public ICommitter BeginCommit(TrieNode root, WriteFlags writeFlags = WriteFlags.None) => baseTrieStore.BeginCommit(root, writeFlags);

public bool IsPersisted(in TreePath path, in ValueHash256 keccak) => baseTrieStore.IsPersisted(in path, in keccak);
}
}
19 changes: 15 additions & 4 deletions src/Nethermind/Nethermind.Trie/PatriciaTree.cs
Original file line number Diff line number Diff line change
Expand Up @@ -141,13 +141,19 @@ public void Commit(bool skipRoot = false, WriteFlags writeFlags = WriteFlags.Non

_writeBeforeCommit = 0;

using ICommitter committer = TrieStore.BeginCommit(RootRef, writeFlags);
if (RootRef is not null && RootRef.IsDirty)
TrieNode? newRoot = RootRef;
using (ICommitter committer = TrieStore.BeginCommit(RootRef, writeFlags))
{
TreePath path = TreePath.Empty;
RootRef = Commit(committer, ref path, RootRef, skipSelf: skipRoot, maxLevelForConcurrentCommit: maxLevelForConcurrentCommit);
if (RootRef is not null && RootRef.IsDirty)
{
TreePath path = TreePath.Empty;
newRoot = Commit(committer, ref path, RootRef, skipSelf: skipRoot, maxLevelForConcurrentCommit: maxLevelForConcurrentCommit);
}
}

// Need to be after committer dispose so that it can find it in trie store properly
RootRef = newRoot;

// Sometimes RootRef is set to null, so we still need to reset roothash to empty tree hash.
SetRootHash(RootRef?.Keccak, true);
}
Expand Down Expand Up @@ -770,7 +776,9 @@ internal bool ShouldUpdateChild(TrieNode? parent, TrieNode? oldChild, TrieNode?
byte[] extensionKey = [(byte)onlyChildIdx];
if (originalNode is not null && originalNode.IsExtension && Bytes.AreEqual(extensionKey, originalNode.Key))
{
path.AppendMut(onlyChildIdx);
TrieNode? originalChild = originalNode.GetChildWithChildPath(TrieStore, ref path, 0);
path.TruncateOne();
if (!ShouldUpdateChild(originalNode, originalChild, onlyChildNode))
{
return originalNode;
Expand All @@ -790,8 +798,11 @@ internal bool ShouldUpdateChild(TrieNode? parent, TrieNode? oldChild, TrieNode?
{
if (Bytes.AreEqual(newKey, originalNode.Key))
{
int originalLength = path.Length;
path.AppendMut(newKey);
TrieNode? originalChild = originalNode.GetChildWithChildPath(TrieStore, ref path, 0);
TrieNode? newChild = onlyChildNode.GetChildWithChildPath(TrieStore, ref path, 0);
path.TruncateMut(originalLength);
if (!ShouldUpdateChild(originalNode, originalChild, newChild))
{
return originalNode;
Expand Down