diff --git a/Directory.Packages.props b/Directory.Packages.props index 460430443d4..28655cec999 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -72,6 +72,7 @@ + @@ -89,4 +90,4 @@ - + \ No newline at end of file diff --git a/src/Nethermind/Nethermind.Blockchain/CanonicalStateRootFinder.cs b/src/Nethermind/Nethermind.Blockchain/CanonicalStateRootFinder.cs new file mode 100644 index 00000000000..85d46eeb322 --- /dev/null +++ b/src/Nethermind/Nethermind.Blockchain/CanonicalStateRootFinder.cs @@ -0,0 +1,17 @@ +// SPDX-FileCopyrightText: 2025 Demerzel Solutions Limited +// SPDX-License-Identifier: LGPL-3.0-only + +using Nethermind.Core; +using Nethermind.Core.Crypto; +using Nethermind.State.Flat; + +namespace Nethermind.Blockchain; + +public class CanonicalStateRootFinder(IBlockTree blockTree): ICanonicalStateRootFinder +{ + public Hash256? GetCanonicalStateRootAtBlock(long blockNumber) + { + BlockHeader? header = blockTree.FindHeader(blockNumber, BlockTreeLookupOptions.RequireCanonical); + return header?.StateRoot; + } +} diff --git a/src/Nethermind/Nethermind.Consensus/Processing/PrewarmerEnvFactory.cs b/src/Nethermind/Nethermind.Consensus/Processing/PrewarmerEnvFactory.cs index 17b3d97d0a8..8fccf256dd8 100644 --- a/src/Nethermind/Nethermind.Consensus/Processing/PrewarmerEnvFactory.cs +++ b/src/Nethermind/Nethermind.Consensus/Processing/PrewarmerEnvFactory.cs @@ -1,6 +1,7 @@ // SPDX-FileCopyrightText: 2025 Demerzel Solutions Limited // SPDX-License-Identifier: LGPL-3.0-only +using System; using Autofac; using Nethermind.Blockchain; using Nethermind.Core; diff --git a/src/Nethermind/Nethermind.Core.Test/TestMemColumnDb.cs b/src/Nethermind/Nethermind.Core.Test/TestMemColumnDb.cs index 529df1fdfc1..0c73783b4bb 100644 --- a/src/Nethermind/Nethermind.Core.Test/TestMemColumnDb.cs +++ b/src/Nethermind/Nethermind.Core.Test/TestMemColumnDb.cs @@ -1,6 +1,7 @@ // SPDX-FileCopyrightText: 2023 Demerzel Solutions Limited // SPDX-License-Identifier: LGPL-3.0-only +using System; using System.Collections.Generic; using Nethermind.Db; @@ -30,6 +31,12 @@ public IColumnsWriteBatch StartWriteBatch() { return new InMemoryColumnWriteBatch(this); } + + public IColumnDbSnapshot CreateSnapshot() + { + throw new Exception("Snapshot not implemented"); + } + public void Dispose() { } public void Flush(bool onlyWal = false) { } } diff --git a/src/Nethermind/Nethermind.Core/Account.cs b/src/Nethermind/Nethermind.Core/Account.cs index ab7b335ff02..960e424ff73 100644 --- a/src/Nethermind/Nethermind.Core/Account.cs +++ b/src/Nethermind/Nethermind.Core/Account.cs @@ -127,6 +127,11 @@ public Account WithChangedCodeHash(Hash256 newCodeHash) } public AccountStruct ToStruct() => new(Nonce, Balance, StorageRoot, CodeHash); + + public override string ToString() + { + return $"N:{Nonce},B:{Balance},S{StorageRoot},C:{CodeHash}"; + } } public readonly struct AccountStruct : IEquatable diff --git a/src/Nethermind/Nethermind.Core/Address.cs b/src/Nethermind/Nethermind.Core/Address.cs index d20e96be38d..aefc77c27eb 100644 --- a/src/Nethermind/Nethermind.Core/Address.cs +++ b/src/Nethermind/Nethermind.Core/Address.cs @@ -2,6 +2,7 @@ // SPDX-License-Identifier: LGPL-3.0-only using System; +using System.Buffers.Binary; using System.ComponentModel; using System.Diagnostics; using System.Globalization; diff --git a/src/Nethermind/Nethermind.Core/Crypto/Hash256.cs b/src/Nethermind/Nethermind.Core/Crypto/Hash256.cs index 892e20e6c10..f5af01218a8 100644 --- a/src/Nethermind/Nethermind.Core/Crypto/Hash256.cs +++ b/src/Nethermind/Nethermind.Core/Crypto/Hash256.cs @@ -2,6 +2,7 @@ // SPDX-License-Identifier: LGPL-3.0-only using System; +using System.Buffers.Binary; using System.Diagnostics; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; @@ -113,7 +114,12 @@ public readonly struct Hash256AsKey(Hash256 key) : IEquatable, ICo public bool Equals(Hash256AsKey other) => Equals(_key, other._key); public override int GetHashCode() => _key?.GetHashCode() ?? 0; - public int CompareTo(Hash256AsKey other) => _key.CompareTo(other._key); + public int CompareTo(Hash256AsKey other) + { + if (_key is null && other._key is not null) return -1; + if (_key is null && other._key is null) return 0; + return _key!.CompareTo(other._key); + } } [DebuggerStepThrough] diff --git a/src/Nethermind/Nethermind.Core/DevMetric.cs b/src/Nethermind/Nethermind.Core/DevMetric.cs new file mode 100644 index 00000000000..066c6f98da6 --- /dev/null +++ b/src/Nethermind/Nethermind.Core/DevMetric.cs @@ -0,0 +1,12 @@ +// SPDX-FileCopyrightText: 2025 Demerzel Solutions Limited +// SPDX-License-Identifier: LGPL-3.0-only + +using Prometheus; + +namespace Nethermind.Core; + +public static class DevMetric +{ + public static MetricFactory Factory = Prometheus.Metrics.WithCustomRegistry(Prometheus.Metrics.NewCustomRegistry()); // Custom registry does not publish by default + // public static MetricFactory Factory = Prometheus.Metrics.DefaultFactory; +} diff --git a/src/Nethermind/Nethermind.Core/IKeyValueStore.cs b/src/Nethermind/Nethermind.Core/IKeyValueStore.cs index 52a6b97b317..783857d58e9 100644 --- a/src/Nethermind/Nethermind.Core/IKeyValueStore.cs +++ b/src/Nethermind/Nethermind.Core/IKeyValueStore.cs @@ -74,7 +74,7 @@ public interface IMergeableKeyValueStore : IWriteOnlyKeyValueStore void Merge(ReadOnlySpan key, ReadOnlySpan value, WriteFlags flags = WriteFlags.None); } - public interface ISortedKeyValueStore : IKeyValueStore + public interface ISortedKeyValueStore : IReadOnlyKeyValueStore { byte[]? FirstKey { get; } byte[]? LastKey { get; } diff --git a/src/Nethermind/Nethermind.Core/Nethermind.Core.csproj b/src/Nethermind/Nethermind.Core/Nethermind.Core.csproj index 54af4e25feb..a62645e96c8 100644 --- a/src/Nethermind/Nethermind.Core/Nethermind.Core.csproj +++ b/src/Nethermind/Nethermind.Core/Nethermind.Core.csproj @@ -14,6 +14,7 @@ + diff --git a/src/Nethermind/Nethermind.Core/Utils/RefCountingDisposable.cs b/src/Nethermind/Nethermind.Core/Utils/RefCountingDisposable.cs new file mode 100644 index 00000000000..7ae884455cf --- /dev/null +++ b/src/Nethermind/Nethermind.Core/Utils/RefCountingDisposable.cs @@ -0,0 +1,139 @@ +using System; +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; +using System.Runtime.InteropServices; +using System.Threading; + +namespace Nethermind.Core.Utils; + +/// +/// Provides a component that can be disposed multiple times and runs only on the last dispose. +/// +public abstract class RefCountingDisposable : IDisposable +{ + private const int Single = 1; + private const int NoAccessors = 0; + private const int Disposing = -1; + + protected PaddedValue _leases; + + protected RefCountingDisposable(int initialCount = Single) + { + _leases.Value = initialCount; + } + + public void AcquireLease() + { + if (TryAcquireLease() == false) + { + ThrowCouldNotAcquire(); + } + + [DoesNotReturn] + [StackTraceHidden] + static void ThrowCouldNotAcquire() + { + throw new Exception("The lease cannot be acquired"); + } + } + + protected bool TryAcquireLease() + { + // Volatile read for starting value + var current = Volatile.Read(ref _leases.Value); + if (current == Disposing) + { + // Already disposed + return false; + } + + while (true) + { + var prev = Interlocked.CompareExchange(ref _leases.Value, current + Single, current); + if (prev == current) + { + // Successfully acquired + return true; + } + if (prev == Disposing) + { + // Already disposed + return false; + } + + // Try again with new starting value + current = prev; + // Add PAUSE instruction to reduce shared core contention + Thread.SpinWait(1); + } + } + + /// + /// Disposes it once, decreasing the lease count by 1. + /// + public void Dispose() => ReleaseLeaseOnce(); + + private void ReleaseLeaseOnce() + { + // Volatile read for starting value + var current = Volatile.Read(ref _leases.Value); + if (current <= NoAccessors) + { + // Mismatched Acquire/Release + ThrowOverDisposed(); + } + + while (true) + { + var prev = Interlocked.CompareExchange(ref _leases.Value, current - Single, current); + if (prev != current) + { + current = prev; + // Add PAUSE instruction to reduce shared core contention + Thread.SpinWait(1); + continue; + } + if (prev == Single) + { + // Last use, try to dispose underlying + break; + } + if (prev <= NoAccessors) + { + // Mismatched Acquire/Release + ThrowOverDisposed(); + } + + // Successfully released + return; + } + + if (Interlocked.CompareExchange(ref _leases.Value, Disposing, NoAccessors) == NoAccessors) + { + // set to disposed by this Release + CleanUp(); + } + + [DoesNotReturn] + [StackTraceHidden] + static void ThrowOverDisposed() + { + throw new Exception("The lease has already been disposed"); + } + } + + protected abstract void CleanUp(); + + public override string ToString() + { + var leases = Volatile.Read(ref _leases.Value); + return leases == Disposing ? "Disposed" : $"Leases: {leases}"; + } + + [StructLayout(LayoutKind.Explicit, Size = 128)] + protected struct PaddedValue + { + [FieldOffset(64)] + public long Value; + } +} diff --git a/src/Nethermind/Nethermind.Core/Utils/RefCountingDisposableBox.cs b/src/Nethermind/Nethermind.Core/Utils/RefCountingDisposableBox.cs new file mode 100644 index 00000000000..8431f752115 --- /dev/null +++ b/src/Nethermind/Nethermind.Core/Utils/RefCountingDisposableBox.cs @@ -0,0 +1,23 @@ +// SPDX-FileCopyrightText: 2025 Demerzel Solutions Limited +// SPDX-License-Identifier: LGPL-3.0-only + +using System; + +namespace Nethermind.Core.Utils; + +public class RefCountingDisposableBox(T item): RefCountingDisposable + where T : IDisposable +{ + + public T Item => item; + + public bool TryAcquire() + { + return TryAcquireLease(); + } + + protected override void CleanUp() + { + Item.Dispose(); + } +} diff --git a/src/Nethermind/Nethermind.Db.Rocks/ColumnDb.cs b/src/Nethermind/Nethermind.Db.Rocks/ColumnDb.cs index 34713b992b7..4c2890960ec 100644 --- a/src/Nethermind/Nethermind.Db.Rocks/ColumnDb.cs +++ b/src/Nethermind/Nethermind.Db.Rocks/ColumnDb.cs @@ -83,6 +83,19 @@ public IEnumerable GetAllValues(bool ordered = false) return _mainDb.GetAllValuesCore(iterator); } + public IDbSnapshot CreateSnapshot() + { + ReadOptions readOptions = new(); + Snapshot snapshot = _mainDb._db.CreateSnapshot(); + readOptions.SetSnapshot(snapshot); + + return new DbOnTheRocks.DbSnapshot( + _mainDb, + readOptions, + _columnFamily, + snapshot); + } + public IWriteBatch StartWriteBatch() { return new ColumnsDbWriteBatch(this, (DbOnTheRocks.RocksDbWriteBatch)_mainDb.StartWriteBatch()); diff --git a/src/Nethermind/Nethermind.Db.Rocks/ColumnsDb.cs b/src/Nethermind/Nethermind.Db.Rocks/ColumnsDb.cs index 5a83aa1483b..21239cc0371 100644 --- a/src/Nethermind/Nethermind.Db.Rocks/ColumnsDb.cs +++ b/src/Nethermind/Nethermind.Db.Rocks/ColumnsDb.cs @@ -4,8 +4,10 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Runtime.InteropServices; using FastEnumUtility; using Nethermind.Core; +using Nethermind.Core.Extensions; using Nethermind.Db.Rocks.Config; using Nethermind.Logging; using RocksDbSharp; @@ -156,4 +158,32 @@ public void Merge(ReadOnlySpan key, ReadOnlySpan value, WriteFlags f _writeBatch._writeBatch.Merge(key, value, _column._columnFamily, flags); } } + + IColumnDbSnapshot IColumnsDb.CreateSnapshot() + { + Snapshot snapshot = _db.CreateSnapshot(); + return new ColumnDbSnapshot(this, snapshot); + } + + private class ColumnDbSnapshot( + ColumnsDb columnsDb, + Snapshot snapshot + ) : IColumnDbSnapshot + { + public IReadOnlyKeyValueStore GetColumn(T key) + { + ReadOptions options = new ReadOptions(); + options.SetSnapshot(snapshot); + return new DbOnTheRocks.DbSnapshot( + columnsDb, + options, + columnsDb._columnDbs[key]._columnFamily, + snapshot); + } + + public void Dispose() + { + snapshot.Dispose(); + } + } } diff --git a/src/Nethermind/Nethermind.Db.Rocks/Config/DbConfig.cs b/src/Nethermind/Nethermind.Db.Rocks/Config/DbConfig.cs index f0efb6213da..35e454b0f75 100644 --- a/src/Nethermind/Nethermind.Db.Rocks/Config/DbConfig.cs +++ b/src/Nethermind/Nethermind.Db.Rocks/Config/DbConfig.cs @@ -16,10 +16,24 @@ public class DbConfig : IDbConfig public bool EnableDbStatistics { get; set; } = false; public bool EnableMetricsUpdater { get; set; } = false; public uint StatsDumpPeriodSec { get; set; } = 600; + public bool SkipDefaultDbOptions { get; set; } = false; public int? MaxOpenFiles { get; set; } public ulong? ReadAheadSize { get; set; } = (ulong)256.KiB(); + + private const string MinimumBasicOption = + "target_file_size_base=64000000;" + + "max_bytes_for_level_base=256000000;" + + "write_buffer_size=250000000;" + + "min_write_buffer_number_to_merge=1;" + + "max_write_buffer_number=2;" + + "max_compaction_bytes=4000000000;" + + "memtable_whole_key_filtering=true;" + + "memtable_prefix_bloom_size_ratio=0.02;" + + "advise_random_on_open=true;" + + ""; + public string RocksDbOptions { get; set; } = // This section affect the write buffer, or memtable. Note, the size of write buffer affect the size of l0 @@ -41,7 +55,11 @@ public class DbConfig : IDbConfig "max_compaction_bytes=4000000000;" + "compression=kSnappyCompression;" + - "optimize_filters_for_hits=true;" + + + // Note, if this is set, then other Db cannot override it. It save a little bit of space by not creating + // bloom filter for the last level. + // "optimize_filters_for_hits=true;" + "advise_random_on_open=true;" + // Target size of each SST file. Increase to reduce number of file. Default is 64MB. @@ -170,6 +188,8 @@ public class DbConfig : IDbConfig "allow_concurrent_memtable_write=false;"; public string? CodeDbAdditionalRocksDbOptions { get; set; } + public ulong? MetadataDbRowCacheSize { get; set; } + public string BloomDbRocksDbOptions { get; set; } = "max_bytes_for_level_base=16000000;"; public string? BloomDbAdditionalRocksDbOptions { get; set; } @@ -181,7 +201,7 @@ public class DbConfig : IDbConfig public ulong StateDbWriteBufferSize { get; set; } = (ulong)64.MB(); public ulong StateDbWriteBufferNumber { get; set; } = 4; - public bool? StateDbVerifyChecksum { get; set; } + public bool? StateDbVerifyChecksum { get; set; } = true; public ulong? StateDbRowCacheSize { get; set; } public bool StateDbEnableFileWarmer { get; set; } = false; public double StateDbCompressibilityHint { get; set; } = 0.45; @@ -228,11 +248,13 @@ public class DbConfig : IDbConfig // Note: This causes write batch to not be atomic. A concurrent read may read item on start of batch, but not end of batch. // With state, this is fine as writes are done in parallel batch and therefore, not atomic, and the read goes // through triestore first anyway. - "unordered_write=true;" + + // "unordered_write=true;" + // Default is 1 MB. "max_write_batch_group_size_bytes=4000000;" + + "optimize_filters_for_hits=true;" + + ""; public string StateDbLargeMemoryRocksDbOptions { get; set; } = @@ -270,4 +292,224 @@ public class DbConfig : IDbConfig public string L1OriginDbRocksDbOptions { get; set; } = ""; public string? L1OriginDbAdditionalRocksDbOptions { get; set; } + + + public ulong FlatDbWriteBufferSize { get; set; } = (ulong)64.MB(); + public ulong FlatDbWriteBufferNumber { get; set; } = 4; + public bool? FlatDbVerifyChecksum { get; set; } = false; // YOLO + public bool FlatDbEnableFileWarmer { get; set; } + public string FlatDbRocksDbOptions { get; set; } = + MinimumBasicOption + + + // This is basically useless on write only database. However, for halfpath with live pruning, flatdb, or + // (maybe?) full sync where keys are deleted, replaced, or re-inserted, two memtable can merge together + // resulting in a reduced total memtable size to be written. This does seems to reduce sync throughput though. + "min_write_buffer_number_to_merge=2;" + + + // Default value is 16. + // So each block consist of several "restart" and each "restart" is BlockRestartInterval number of key. + // They key within the same restart is delta-encoded with the key before it. This mean a read will have to go + // through potentially "BlockRestartInterval" number of key, probably. That is my understanding. + // Reducing this is likely going to improve CPU usage at the cost of increased uncompressed size, which effect + // cache utilization. + "block_based_table_factory.block_restart_interval=4;" + + + // This adds a hashtable-like index per block (the 32kb block) + // This reduce CPU and therefore latency under high block cache hit scenario. + // It seems to increase disk space use by about 1 GB. + "block_based_table_factory.data_block_index_type=kDataBlockBinaryAndHash;" + + "block_based_table_factory.data_block_hash_table_util_ratio=0.7;" + + + "block_based_table_factory.block_size=16000;" + + + "block_based_table_factory.filter_policy=bloomfilter:15;" + + + // Default is 1 MB. + "max_write_batch_group_size_bytes=4000000;" + + + ""; + public string? FlatDbAdditionalRocksDbOptions { get; set; } + + public string? FlatMetadataDbRocksDbOptions { get; set; } = + MinimumBasicOption + + // This adds a hashtable-like index per block (the 32kb block) + // This reduce CPU and therefore latency under high block cache hit scenario. + // It seems to increase disk space use by about 1 GB. + "block_based_table_factory.data_block_index_type=kDataBlockBinaryAndHash;" + + "block_based_table_factory.data_block_hash_table_util_ratio=0.7;" + + "compression=kNoCompression;" + + + ""; + public ulong? FlatMetadataDbRowCacheSize { get; set; } = (ulong?)1.MiB(); + public string? FlatMetadataDbAdditionalRocksDbOptions { get; set; } + public bool? FlatStateDbVerifyChecksum { get; set; } = false; // YOLO + + public bool FlatStateDbEnableFileWarmer { get; set; } = false; + public ulong FlatStateDbWriteBufferSize { get; set; } = (ulong)64.MiB(); + public ulong FlatStateDbRowCacheSize { get; set; } = 0; + public ulong FlatStateDbWriteBufferNumber { get; set; } = 4; + public bool FlatStateDbSkipDefaultDbOptions { get; set; } = true; + + private const string FlatCommonConfigWithPlainTable = + MinimumBasicOption + + "memtable=prefix_hash:1000;" + + + "min_write_buffer_number_to_merge=2;" + + + // This used to be on trie, but its here now. Attempt to reduce LSM depth at cost of write amp. + "max_bytes_for_level_multiplier=30;" + + "max_bytes_for_level_base=350000000;" + + "plain_table_factory={};" + + ""; + + private const string FlatCommonConfigWithBlockBased = + MinimumBasicOption + + + // Hard to decide. No compression is lower latency. But it means bigger db, which means worst cache is at os + // level. TODO: Measure how much the db size change. + // "compression=kNoCompression;" + + + "compression=kLZ4Compression;" + + + // "memtable=prefix_hash:1000;" + + "memtable=skiplist;" + + "max_write_buffer_number=4;" + + "min_write_buffer_number_to_merge=2;" + + "wal_compression=kZSTD;" + // I think it does not work. + + // This used to be on trie, but its here now. Attempt to reduce LSM depth at cost of write amp. + "max_bytes_for_level_multiplier=20;" + + "max_bytes_for_level_base=250000000;" + + + "block_based_table_factory.metadata_block_size=4096;" + + "block_based_table_factory.block_restart_interval=4;" + + "block_based_table_factory.data_block_index_type=kDataBlockBinaryAndHash;" + + "block_based_table_factory.data_block_hash_table_util_ratio=0.7;" + + "block_based_table_factory.prepopulate_block_cache=kFlushOnly;" + + "block_based_table_factory.pin_l0_filter_and_index_blocks_in_cache=true;" + + "block_based_table_factory.cache_index_and_filter_blocks_with_high_priority=true;" + + "block_based_table_factory.whole_key_filtering=true;" + // should be default. Just in case. + + // Important tunables + + // Further split bloom filter. Reduces memory but add latency. Set to false now + // "block_based_table_factory.partition_filters=true;" + + + // Smaller block size is faster to load slightly. But on most SSD, any value lower than a certain number + // around 16k, does not reduce latency much. The important impact is memory, as lower block size mean more + // hot block can fit in memory, but at the same time, index size become larger, which may take up more memory. + "block_based_table_factory.block_size=4096;" + + + // Two level index means only the top level index points to another index before pointing to block. + // Only top level index is in memory all the time. This adds latency, but reduces memory usage. + // "block_based_table_factory.index_type=kTwoLevelIndexSearch;" + + + // Complete bsearch index in memory + "block_based_table_factory.index_type=kBinarySearch;" + + + // This **should** help given prefix extractor. But further increase index size. + // "block_based_table_factory.index_type=kHashSearch;" + + + // So that last level bloom is kept. Should accelerate miss state check. + "optimize_filters_for_hits=false;" + + ""; + + public const string FlatCommonConfig = FlatCommonConfigWithBlockBased; + + // Note: No prefix extractor for state.Dont forget. + public string? FlatStateDbRocksDbOptions { get; set; } = + FlatCommonConfig + + // "use_direct_reads=true;" + // For testing + // "prefix_extractor=capped:3;" + // I forget why. + "block_based_table_factory.block_cache=64000000;" + + "block_based_table_factory.filter_policy=ribbonfilter:12;" + + ""; + + public string? FlatStateDbAdditionalRocksDbOptions { get; set; } + public bool? FlatStorageDbVerifyChecksum { get; set; } + public bool FlatStorageDbSkipDefaultDbOptions { get; set; } = true; + public bool FlatStorageDbEnableFileWarmer { get; set; } + public ulong FlatStorageDbRowCacheSize { get; set; } = 0; + public ulong FlatStorageDbWriteBufferSize { get; set; }= (ulong)64.MiB(); + public ulong FlatStorageDbWriteBufferNumber { get; set; } = 4; + + public string? FlatStorageDbRocksDbOptions { get; set; } = + FlatCommonConfig + + // v"plain_table_factory={user_key_len=52;};" + + // "block_based_table_factory.index_type=kBinarySearch;" + + // "block_based_table_factory.block_restart_interval=6;" + // For storage the prefix have a lot in common. + // "memtable=skiplist;" + + // "prefix_extractor=capped:23;" + // 20 byte address + 3 byte prefix. + // "use_direct_reads=true;" + // For testing + "block_based_table_factory.block_cache=500000000;" + + "block_based_table_factory.block_size=16000;" + // Using 4kb size is faster, IO wise, but uses additional 500 MB of memory, which if put on block cache is much betterr. + "block_based_table_factory.filter_policy=ribbonfilter:8;" + + ""; + + public string? FlatStorageDbAdditionalRocksDbOptions { get; set; } + + // Largely the same as statedb, but more write focused. + private const string TrieNodeConfig = + + // "use_direct_reads=true;" + + // For trie which is heavy in compaction. Use direct io to prevent taking up space in os cache. + "use_direct_io_for_flush_and_compaction=true;" + + + // LZ4 seems to be slightly faster here + "compression=kLZ4Compression;" + + + "wal_compression=kZSTD;" + + + // Default value is 16. + // So each block consist of several "restart" and each "restart" is BlockRestartInterval number of key. + // They key within the same restart is delta-encoded with the key before it. This mean a read will have to go + // through potentially "BlockRestartInterval" number of key, probably. That is my understanding. + // Reducing this is likely going to improve CPU usage at the cost of increased uncompressed size, which effect + // cache utilization. + "block_based_table_factory.block_restart_interval=8;" + + + // This adds a hashtable-like index per block (the 32kb block) + // This reduce CPU and therefore latency under high block cache hit scenario. + // It seems to increase disk space use by about 1 GB. + "block_based_table_factory.data_block_index_type=kDataBlockBinaryAndHash;" + + "block_based_table_factory.data_block_hash_table_util_ratio=0.75;" + + + "block_based_table_factory.block_size=32000;" + + "block_based_table_factory.filter_policy=ribbonfilter:15;" + + + // Make it + // "optimize_filters_for_hits=true;" + + "optimize_filters_for_hits=true;" + + + ""; + + // Only 1 gig in total, but almost 1/3rd of the writes. + public ulong FlatStateTopNodesDbWriteBufferSize { get; set; } = (ulong)128.MiB(); + public ulong FlatStateTopNodesDbWriteBufferNumber { get; set; } = 4; + public string? FlatStateTopNodesDbRocksDbOptions { get; set; } = + TrieNodeConfig + + ""; + + public ulong FlatStateNodesDbWriteBufferSize { get; set; } = (ulong)128.MiB(); + public ulong FlatStateNodesDbWriteBufferNumber { get; set; } = 4; + public string? FlatStateNodesDbRocksDbOptions { get; set; } = + TrieNodeConfig + + ""; + public string? FlatStateTopNodesDbAdditionalRocksDbOptions { get; set; } + + public string? FlatStateNodesDbAdditionalRocksDbOptions { get; set; } + public ulong FlatStorageNodesDbWriteBufferSize { get; set; } = (ulong)64.MiB(); + public ulong FlatStorageNodesDbWriteBufferNumber { get; set; } = 4; + public string? FlatStorageNodesDbRocksDbOptions { get; set; } = + TrieNodeConfig + + ""; + public string? FlatStorageNodesDbAdditionalRocksDbOptions { get; set; } + + public ulong FlatStorageTopNodesDbWriteBufferSize { get; set; } = (ulong)64.MiB(); + public ulong FlatStorageTopNodesNodesDbWriteBufferNumber { get; set; } = 4; + public string? FlatStorageTopNodesNodesDbRocksDbOptions { get; set; } = + TrieNodeConfig + + ""; + public string? FlatStorageTopNodesNodesDbAdditionalRocksDbOptions { get; set; } + public ulong PreimageDbWriteBufferSize { get; set; } = (ulong)16.MiB(); } diff --git a/src/Nethermind/Nethermind.Db.Rocks/Config/IDbConfig.cs b/src/Nethermind/Nethermind.Db.Rocks/Config/IDbConfig.cs index bdd18bb3afd..dd8a003f273 100644 --- a/src/Nethermind/Nethermind.Db.Rocks/Config/IDbConfig.cs +++ b/src/Nethermind/Nethermind.Db.Rocks/Config/IDbConfig.cs @@ -33,6 +33,7 @@ public interface IDbConfig : IConfig bool EnableFileWarmer { get; set; } double CompressibilityHint { get; set; } bool FlushOnExit { get; set; } + bool SkipDefaultDbOptions { get; set; } string BadBlocksDbRocksDbOptions { get; set; } string? BadBlocksDbAdditionalRocksDbOptions { get; set; } @@ -75,6 +76,7 @@ public interface IDbConfig : IConfig string MetadataDbRocksDbOptions { get; set; } string? MetadataDbAdditionalRocksDbOptions { get; set; } + ulong? MetadataDbRowCacheSize { get; set; } string BloomDbRocksDbOptions { get; set; } string? BloomDbAdditionalRocksDbOptions { get; set; } @@ -101,4 +103,54 @@ public interface IDbConfig : IConfig string L1OriginDbRocksDbOptions { get; set; } string? L1OriginDbAdditionalRocksDbOptions { get; set; } + + ulong FlatDbWriteBufferSize { get; set; } + ulong FlatDbWriteBufferNumber { get; set; } + bool? FlatDbVerifyChecksum { get; set; } + bool FlatDbEnableFileWarmer { get; set; } + string FlatDbRocksDbOptions { get; set; } + string? FlatDbAdditionalRocksDbOptions { get; set; } + + string? FlatMetadataDbRocksDbOptions { get; set; } + ulong? FlatMetadataDbRowCacheSize { get; set; } + string? FlatMetadataDbAdditionalRocksDbOptions { get; set; } + + bool? FlatStateDbVerifyChecksum { get; set; } + bool FlatStateDbSkipDefaultDbOptions { get; set; } + bool FlatStateDbEnableFileWarmer { get; set; } + ulong FlatStateDbWriteBufferSize { get; set; } + ulong FlatStateDbRowCacheSize { get; set; } + ulong FlatStateDbWriteBufferNumber { get; set; } + string? FlatStateDbRocksDbOptions { get; set; } + string? FlatStateDbAdditionalRocksDbOptions { get; set; } + + bool? FlatStorageDbVerifyChecksum { get; set; } + bool FlatStorageDbSkipDefaultDbOptions { get; set; } + bool FlatStorageDbEnableFileWarmer { get; set; } + ulong FlatStorageDbRowCacheSize { get; set; } + ulong FlatStorageDbWriteBufferSize { get; set; } + ulong FlatStorageDbWriteBufferNumber { get; set; } + string? FlatStorageDbRocksDbOptions { get; set; } + string? FlatStorageDbAdditionalRocksDbOptions { get; set; } + + ulong FlatStateNodesDbWriteBufferSize { get; set; } + ulong FlatStateNodesDbWriteBufferNumber { get; set; } + string? FlatStateNodesDbRocksDbOptions { get; set; } + string? FlatStateNodesDbAdditionalRocksDbOptions { get; set; } + + ulong FlatStateTopNodesDbWriteBufferSize { get; set; } + ulong FlatStateTopNodesDbWriteBufferNumber { get; set; } + string? FlatStateTopNodesDbRocksDbOptions { get; set; } + string? FlatStateTopNodesDbAdditionalRocksDbOptions { get; set; } + + ulong FlatStorageNodesDbWriteBufferSize { get; set; } + ulong FlatStorageNodesDbWriteBufferNumber { get; set; } + string? FlatStorageNodesDbRocksDbOptions { get; set; } + string? FlatStorageNodesDbAdditionalRocksDbOptions { get; set; } + + ulong FlatStorageTopNodesDbWriteBufferSize { get; set; } + ulong FlatStorageTopNodesNodesDbWriteBufferNumber { get; set; } + string? FlatStorageTopNodesNodesDbRocksDbOptions { get; set; } + string? FlatStorageTopNodesNodesDbAdditionalRocksDbOptions { get; set; } + ulong PreimageDbWriteBufferSize { get; set; } } diff --git a/src/Nethermind/Nethermind.Db.Rocks/Config/IRocksDbConfig.cs b/src/Nethermind/Nethermind.Db.Rocks/Config/IRocksDbConfig.cs index f70d96006d3..4e10ea15173 100644 --- a/src/Nethermind/Nethermind.Db.Rocks/Config/IRocksDbConfig.cs +++ b/src/Nethermind/Nethermind.Db.Rocks/Config/IRocksDbConfig.cs @@ -7,6 +7,7 @@ public interface IRocksDbConfig { ulong? WriteBufferSize { get; } ulong? WriteBufferNumber { get; } + bool SkipDefaultDbOptions => false; string RocksDbOptions { get; } string AdditionalRocksDbOptions { get; } int? MaxOpenFiles { get; } diff --git a/src/Nethermind/Nethermind.Db.Rocks/Config/PerTableDbConfig.cs b/src/Nethermind/Nethermind.Db.Rocks/Config/PerTableDbConfig.cs index 0efa1fe7553..06c4c4f7bb8 100644 --- a/src/Nethermind/Nethermind.Db.Rocks/Config/PerTableDbConfig.cs +++ b/src/Nethermind/Nethermind.Db.Rocks/Config/PerTableDbConfig.cs @@ -48,12 +48,14 @@ private void EnsureConfigIsAvailable(string propertyName) public ulong? WriteBufferSize => ReadConfig(nameof(WriteBufferSize)); public ulong? WriteBufferNumber => ReadConfig(nameof(WriteBufferNumber)); - public string RocksDbOptions => ReadRocksdbOptions(_dbConfig, nameof(RocksDbOptions), _prefixes); - public string AdditionalRocksDbOptions => ReadRocksdbOptions(_dbConfig, nameof(AdditionalRocksDbOptions), _prefixes); + public string RocksDbOptions => ReadRocksdbOptions(_dbConfig, nameof(RocksDbOptions), _prefixes, SkipDefaultDbOptions); + + public string AdditionalRocksDbOptions => ReadRocksdbOptions(_dbConfig, nameof(AdditionalRocksDbOptions), _prefixes, SkipDefaultDbOptions); public int? MaxOpenFiles => ReadConfig(nameof(MaxOpenFiles)); public bool WriteAheadLogSync => ReadConfig(nameof(WriteAheadLogSync)); public ulong? ReadAheadSize => ReadConfig(nameof(ReadAheadSize)); + public bool SkipDefaultDbOptions => ReadConfig(nameof(SkipDefaultDbOptions)); public bool EnableDbStatistics => _dbConfig.EnableDbStatistics; public uint StatsDumpPeriodSec => _dbConfig.StatsDumpPeriodSec; public bool? VerifyChecksum => ReadConfig(nameof(VerifyChecksum)); @@ -85,12 +87,14 @@ private string[] GetPrefixes() return [string.Concat(_tableName, "Db")]; } - private static string ReadRocksdbOptions(IDbConfig dbConfig, string propertyName, string[] prefixes) + private static string ReadRocksdbOptions(IDbConfig dbConfig, string propertyName, string[] prefixes, bool skipDefault) { Type type = dbConfig.GetType(); PropertyInfo? propertyInfo; - string val = (string)GetProperty(type, propertyName)!.GetValue(dbConfig)!; + string val = skipDefault + ? "" + : (string)GetProperty(type, propertyName)!.GetValue(dbConfig)!; foreach (var prefix in prefixes) { diff --git a/src/Nethermind/Nethermind.Db.Rocks/DbOnTheRocks.cs b/src/Nethermind/Nethermind.Db.Rocks/DbOnTheRocks.cs index a69d62475f0..b96249b08bf 100644 --- a/src/Nethermind/Nethermind.Db.Rocks/DbOnTheRocks.cs +++ b/src/Nethermind/Nethermind.Db.Rocks/DbOnTheRocks.cs @@ -29,7 +29,7 @@ namespace Nethermind.Db.Rocks; -public partial class DbOnTheRocks : IDb, ITunableDb, IReadOnlyNativeKeyValueStore, ISortedKeyValueStore, IMergeableKeyValueStore +public partial class DbOnTheRocks : IDb, ITunableDb, IReadOnlyNativeKeyValueStore, ISnapshottableKeyValueStore, ISortedKeyValueStore, IMergeableKeyValueStore { protected ILogger _logger; @@ -199,6 +199,29 @@ private RocksDb Init(string basePath, string dbPath, IDbConfig dbConfig, ILogMan WarmupFile(_fullPath, db); } + /* + if (Name == "FlatState") + { + Console.Error.WriteLine("Reading all from flat state"); + long keyCount = 0; + using (var it = db.NewIterator()) + { + it.SeekToFirst(); + + while (it.Valid()) + { + keyCount++; + if (keyCount % 1000000 == 0) + { + Console.Error.WriteLine($"{keyCount} keys"); + } + it.Next(); + } + } + Console.Error.WriteLine($"{keyCount} keys"); + } + */ + return db; } catch (DllNotFoundException e) when (e.Message.Contains("libdl")) @@ -215,7 +238,6 @@ private RocksDb Init(string basePath, string dbPath, IDbConfig dbConfig, ILogMan CreateMarkerIfCorrupt(x); throw; } - } private void WarmupFile(string basePath, RocksDb db) @@ -250,15 +272,15 @@ private void WarmupFile(string basePath, RocksDb db) long totalSize = 0; fileMetadatas = fileMetadatas.TakeWhile(metadata => - { - availableMemory -= (long)metadata.metadata.FileSize; - bool take = availableMemory > 0; - if (take) { - totalSize += (long)metadata.metadata.FileSize; - } - return take; - }) + availableMemory -= (long)metadata.metadata.FileSize; + bool take = availableMemory > 0; + if (take) + { + totalSize += (long)metadata.metadata.FileSize; + } + return take; + }) // We reverse them again so that lower level goes last so that it is the freshest. // Not all of the available memory is actually available so we are probably over reading things. .Reverse() @@ -436,7 +458,7 @@ private long GetMemtableSize() return 0; } - [GeneratedRegex("(?[^; ]+)\\=(?[^; ]+);", RegexOptions.Singleline | RegexOptions.NonBacktracking | RegexOptions.ExplicitCapture)] + [GeneratedRegex("(?[A-Za-z0-9_\\.]+)\\=(?[^; ]+);", RegexOptions.Singleline | RegexOptions.NonBacktracking | RegexOptions.ExplicitCapture)] private static partial Regex ExtractDbOptionsRegex(); public static IDictionary ExtractOptions(string dbOptions) @@ -467,8 +489,29 @@ protected virtual void BuildOptions(IRocksDbConfig dbConfig, Options optio _writeBufferSize = ulong.Parse(optionsAsDict["write_buffer_size"]); _maxWriteBufferNumber = int.Parse(optionsAsDict["max_write_buffer_number"]); - BlockBasedTableOptions tableOptions = new(); - options.SetBlockBasedTableFactory(tableOptions); + var isUsingPlainTable = optionsAsDict.ContainsKey("plain_table_factory"); + BlockBasedTableOptions? tableOptions = null; + if (isUsingPlainTable) + { + // It just need to set the default factory. + // settings can be changed via the option string later, but this need to be set first. + options.SetPlainTableFactory( + user_key_len: 0, + bloom_bits_per_key: 10, + hash_table_ratio: 0.75, + index_sparseness: 16, + huge_page_tlb_size: 0, + encoding_type: (char)0, + full_scan_mode: false, + store_index_in_file: true + ); + } + else + { + tableOptions = new(); + options.SetBlockBasedTableFactory(tableOptions); + } + IntPtr optsPtr = Marshal.StringToHGlobalAnsi(dbConfig.RocksDbOptions); try { @@ -485,7 +528,7 @@ protected virtual void BuildOptions(IRocksDbConfig dbConfig, Options optio blockCacheSize = ulong.Parse(blockCacheSizeStr); } - if (sharedCache is not null && blockCacheSize == 0) + if (sharedCache is not null && blockCacheSize == 0 && tableOptions is not null) { tableOptions.SetBlockCache(sharedCache.Value); } @@ -503,6 +546,8 @@ protected virtual void BuildOptions(IRocksDbConfig dbConfig, Options optio } if (_maxWriteBufferNumber < 1) throw new InvalidConfigurationException($"Error initializing {Name} db. Max write buffer number must be more than 1. max write buffer number: {_maxWriteBufferNumber}", ExitCodes.GeneralError); + options.SetAllowConcurrentMemtableWrite(false); + #endregion #region WriteBuffer @@ -707,8 +752,8 @@ public byte[]? this[ReadOnlySpan key] fixed (byte* ptr = &MemoryMarshal.GetReference(key)) { handle = cf is null - ? Native.Instance.rocksdb_get_pinned(db, read_options, ptr, skLength, out errPtr) - : Native.Instance.rocksdb_get_pinned_cf(db, read_options, cf.Handle, ptr, skLength, out errPtr); + ? Native.Instance.rocksdb_get_pinned(db, read_options, ptr, skLength, out errPtr) + : Native.Instance.rocksdb_get_pinned_cf(db, read_options, cf.Handle, ptr, skLength, out errPtr); } if (errPtr != IntPtr.Zero) ThrowRocksDbException(errPtr); @@ -876,7 +921,14 @@ public Span GetSpan(scoped ReadOnlySpan key, ReadFlags flags) return GetSpanWithColumnFamily(key, null, flags); } + internal Span GetSpanWithColumnFamily(scoped ReadOnlySpan key, ColumnFamilyHandle? cf, ReadFlags flags) + { + ReadOptions options = (flags & ReadFlags.HintCacheMiss) != 0 ? _hintCacheMissOptions : _defaultReadOptions; + return GetSpanWithColumnFamily(key, cf, options); + } + + internal Span GetSpanWithColumnFamily(scoped ReadOnlySpan key, ColumnFamilyHandle? cf, ReadOptions readOptions) { ObjectDisposedException.ThrowIf(_isDisposing, this); @@ -884,7 +936,7 @@ internal Span GetSpanWithColumnFamily(scoped ReadOnlySpan key, Colum try { - Span span = _db.GetSpan(key, cf, (flags & ReadFlags.HintCacheMiss) != 0 ? _hintCacheMissOptions : _defaultReadOptions); + Span span = _db.GetSpan(key, cf, readOptions); if (!span.IsNullOrEmpty()) { @@ -1064,6 +1116,19 @@ public IEnumerable GetAllValues(bool ordered = false) return GetAllValuesCore(iterator); } + public IDbSnapshot CreateSnapshot() + { + ReadOptions readOptions = new(); + Snapshot snapshot = _db.CreateSnapshot(); + readOptions.SetSnapshot(snapshot); + + return new DbSnapshot( + this, + readOptions, + null, + snapshot); + } + internal IEnumerable GetAllValuesCore(Iterator iterator) { try @@ -1941,4 +2006,84 @@ internal ISortedView GetViewBetween(ReadOnlySpan firstKey, ReadOnlySpan key, ReadFlags flags = ReadFlags.None) + { + ReadOnlySpan value = default; + try + { + value = GetSpan(key, flags); + + if (value.IsNull()) return null; + return value.ToArray(); + } + finally + { + DangerousReleaseMemory(value); + } + } + + public Span GetSpan(scoped ReadOnlySpan key, ReadFlags flags = ReadFlags.None) + { + return mainDb.GetSpanWithColumnFamily(key, columnFamily, options); + } + + public void DangerousReleaseMemory(in ReadOnlySpan span) + { + mainDb.DangerousReleaseMemory(span); + } + + public byte[]? FirstKey + { + get + { + using Iterator iterator = mainDb.CreateIterator(options); + iterator.SeekToFirst(); + return iterator.Valid() ? iterator.GetKeySpan().ToArray() : null; + } + } + + public byte[]? LastKey + { + get + { + using Iterator iterator = mainDb.CreateIterator(options); + iterator.SeekToLast(); + return iterator.Valid() ? iterator.GetKeySpan().ToArray() : null; + } + } + + public ISortedView GetViewBetween(ReadOnlySpan firstKey, ReadOnlySpan lastKey) + { + ReadOptions readOptions = new ReadOptions(); + readOptions.SetSnapshot(snapshot); + + unsafe + { + IntPtr iterateLowerBound = Marshal.AllocHGlobal(firstKey.Length); + firstKey.CopyTo(new Span(iterateLowerBound.ToPointer(), firstKey.Length)); + Native.Instance.rocksdb_readoptions_set_iterate_lower_bound(readOptions.Handle, iterateLowerBound, (UIntPtr)firstKey.Length); + + IntPtr iterateUpperBound = Marshal.AllocHGlobal(lastKey.Length); + lastKey.CopyTo(new Span(iterateUpperBound.ToPointer(), lastKey.Length)); + Native.Instance.rocksdb_readoptions_set_iterate_upper_bound(readOptions.Handle, iterateUpperBound, (UIntPtr)lastKey.Length); + } + + Iterator iterator = mainDb.CreateIterator(readOptions, columnFamily); + return new RocksdbSortedView(iterator); + } + + public void Dispose() + { + snapshot.Dispose(); + } + } } diff --git a/src/Nethermind/Nethermind.Db.Rocks/FakeColumnsDb.cs b/src/Nethermind/Nethermind.Db.Rocks/FakeColumnsDb.cs new file mode 100644 index 00000000000..42fbad7daab --- /dev/null +++ b/src/Nethermind/Nethermind.Db.Rocks/FakeColumnsDb.cs @@ -0,0 +1,85 @@ +// SPDX-FileCopyrightText: 2025 Demerzel Solutions Limited +// SPDX-License-Identifier: LGPL-3.0-only + +using System.Collections.Generic; +using System.Linq; +using Nethermind.Core; + +namespace Nethermind.Db.Rocks; + +public class FakeColumnsDb( + Dictionary innerDb +): IColumnsDb where T : notnull +{ + public void Flush(bool onlyWal = false) + { + foreach (var keyValuePair in innerDb) + { + keyValuePair.Value.Flush(onlyWal); + } + } + + public void Dispose() + { + } + + public IDb GetColumnDb(T key) + { + return innerDb[key]; + } + + public IEnumerable ColumnKeys => innerDb.Keys; + public IColumnsWriteBatch StartWriteBatch() + { + return new FakeWriteBatch(innerDb); + } + + public IColumnDbSnapshot CreateSnapshot() + { + return new FakeSnapshot(innerDb.ToDictionary((kv) => kv.Key, (kv) => + { + return ((ISnapshottableKeyValueStore)kv.Value).CreateSnapshot(); + })); + } + + private class FakeWriteBatch : IColumnsWriteBatch + { + private Dictionary _innerWriteBatch; + + public FakeWriteBatch(Dictionary innerDb) + { + _innerWriteBatch = innerDb + .ToDictionary((kv) => kv.Key, (kv) => kv.Value.StartWriteBatch()); + } + + public IWriteBatch GetColumnBatch(T key) + { + return _innerWriteBatch[key]; + } + + public void Dispose() + { + foreach (var keyValuePair in _innerWriteBatch) + { + keyValuePair.Value.Dispose(); + } + } + } + + private class FakeSnapshot(Dictionary innerDb) : IColumnDbSnapshot + { + + public IReadOnlyKeyValueStore GetColumn(T key) + { + return innerDb[key]; + } + + public void Dispose() + { + foreach (var keyValuePair in innerDb) + { + keyValuePair.Value.Dispose(); + } + } + } +} diff --git a/src/Nethermind/Nethermind.Db.Rpc/RpcColumnsDb.cs b/src/Nethermind/Nethermind.Db.Rpc/RpcColumnsDb.cs index 8ddcde89941..73e0e1e4beb 100644 --- a/src/Nethermind/Nethermind.Db.Rpc/RpcColumnsDb.cs +++ b/src/Nethermind/Nethermind.Db.Rpc/RpcColumnsDb.cs @@ -45,6 +45,12 @@ public IColumnsWriteBatch StartWriteBatch() { return new InMemoryColumnWriteBatch(this); } + + public IColumnDbSnapshot CreateSnapshot() + { + throw new Exception("Snapshot not implemented"); + } + public void Dispose() { } public void Flush(bool onlyWal = false) { } } diff --git a/src/Nethermind/Nethermind.Db/DbNames.cs b/src/Nethermind/Nethermind.Db/DbNames.cs index e0f96bbc646..15e0145e7db 100644 --- a/src/Nethermind/Nethermind.Db/DbNames.cs +++ b/src/Nethermind/Nethermind.Db/DbNames.cs @@ -7,6 +7,7 @@ public static class DbNames { public const string Storage = "storage"; public const string State = "state"; + public const string Flat = "flat"; public const string Code = "code"; public const string Blocks = "blocks"; public const string Headers = "headers"; @@ -19,5 +20,14 @@ public static class DbNames public const string BlobTransactions = "blobTransactions"; public const string DiscoveryNodes = "discoveryNodes"; public const string PeersDb = "peers"; + public const string Preimage = "preimage"; + + public const string FlatMetadata = "flatMetadata"; + public const string FlatState = "flatState"; + public const string FlatStorage = "flatStorage"; + public const string FlatStateNodes = "flatStateNodes"; + public const string FlatStateTopNodes = "flatStateNodesTop"; + public const string FlatStorageNodes = "flatStorageNodes"; + public const string FlatStorageTopNodes = "flatStorageTopNodes"; } } diff --git a/src/Nethermind/Nethermind.Db/FlatDbConfig.cs b/src/Nethermind/Nethermind.Db/FlatDbConfig.cs new file mode 100644 index 00000000000..f988971b872 --- /dev/null +++ b/src/Nethermind/Nethermind.Db/FlatDbConfig.cs @@ -0,0 +1,72 @@ +// SPDX-FileCopyrightText: 2025 Demerzel Solutions Limited +// SPDX-License-Identifier: LGPL-3.0-only + +using Nethermind.Config; +using Nethermind.Core.Extensions; + +namespace Nethermind.Db; + +public interface IFlatDbConfig: IConfig +{ + [ConfigItem(Description = "Enabled", DefaultValue = "false")] + bool Enabled { get; set; } + + [ConfigItem(Description = "Import from pruning trie state db", DefaultValue = "false")] + bool ImportFromPruningTrieState { get; set; } + + [ConfigItem(Description = "Pruning boundary", DefaultValue = "256")] + int PruningBoundary { get; set; } + + [ConfigItem(Description = "Compact size", DefaultValue = "16")] + int CompactSize { get; set; } + + [ConfigItem(Description = "Compact interval", DefaultValue = "4")] + int CompactInterval { get; set; } + + [ConfigItem(Description = "Max in flight compact job", DefaultValue = "32")] + int MaxInFlightCompactJob { get; set; } + + [ConfigItem(Description = "Read with try", DefaultValue = "false")] + bool ReadWithTrie { get; set; } + + [ConfigItem(Description = "Verify with trie", DefaultValue = "false")] + bool VerifyWithTrie { get; set; } + + [ConfigItem(Description = "Inline compaction", DefaultValue = "false")] + bool InlineCompaction { get; set; } + + [ConfigItem(Description = "Trie cache memory target", DefaultValue = "false")] + long TrieCacheMemoryTarget { get; set; } + + [ConfigItem(Description = "Disable trie warmer", DefaultValue = "false")] + bool DisableTrieWarmer { get; set; } + + [ConfigItem(Description = "Use preimage", DefaultValue = "false")] + FlatLayout Layout { get; set; } +} + +public enum FlatLayout +{ + Flat, + FlatNoSeparateTopStorage, + FlatInTrie, + PreimageFlat +} + +public class FlatDbConfig: IFlatDbConfig +{ + public bool Enabled { get; set; } = false; + public bool ImportFromPruningTrieState { get; set; } = false; + public int PruningBoundary { get; set; } = 256; + public int CompactSize { get; set; } = 16; + public int CompactInterval { get; set; } = 4; + public int MaxInFlightCompactJob { get; set; } = 32; + public bool ReadWithTrie { get; set; } = false; + public bool VerifyWithTrie { get; set; } = false; + public bool InlineCompaction { get; set; } = false; + + // 1 GB is enough for 10% dirty load. 512 MB is pretty good at around 20%. Without it, then the diff layers on its own have around 35% dirty load. + public long TrieCacheMemoryTarget { get; set; } = 512.MiB(); + public bool DisableTrieWarmer { get; set; } = false; + public FlatLayout Layout { get; set; } +} diff --git a/src/Nethermind/Nethermind.Db/IColumnsDb.cs b/src/Nethermind/Nethermind.Db/IColumnsDb.cs index 4f1eddcbd44..5ffddced6ab 100644 --- a/src/Nethermind/Nethermind.Db/IColumnsDb.cs +++ b/src/Nethermind/Nethermind.Db/IColumnsDb.cs @@ -13,10 +13,17 @@ public interface IColumnsDb : IDbMeta, IDisposable IEnumerable ColumnKeys { get; } public IReadOnlyColumnDb CreateReadOnly(bool createInMemWriteStore) => new ReadOnlyColumnsDb(this, createInMemWriteStore); IColumnsWriteBatch StartWriteBatch(); + IColumnDbSnapshot CreateSnapshot(); } public interface IColumnsWriteBatch : IDisposable { IWriteBatch GetColumnBatch(TKey key); } + + + public interface IColumnDbSnapshot : IDisposable + { + IReadOnlyKeyValueStore GetColumn(TKey key); + } } diff --git a/src/Nethermind/Nethermind.Db/IDb.cs b/src/Nethermind/Nethermind.Db/IDb.cs index 73f9e4ba171..9b20b4d7836 100644 --- a/src/Nethermind/Nethermind.Db/IDb.cs +++ b/src/Nethermind/Nethermind.Db/IDb.cs @@ -18,6 +18,15 @@ public interface IDb : IKeyValueStoreWithBatching, IDbMeta, IDisposable public IReadOnlyDb CreateReadOnly(bool createInMemWriteStore) => new ReadOnlyDb(this, createInMemWriteStore); } + public interface ISnapshottableKeyValueStore + { + IDbSnapshot CreateSnapshot(); + } + + public interface IDbSnapshot: IReadOnlyKeyValueStore, IDisposable + { + } + // Some metadata options public interface IDbMeta { diff --git a/src/Nethermind/Nethermind.Db/MemColumnsDb.cs b/src/Nethermind/Nethermind.Db/MemColumnsDb.cs index ea37042e51b..e0bd679cfe1 100644 --- a/src/Nethermind/Nethermind.Db/MemColumnsDb.cs +++ b/src/Nethermind/Nethermind.Db/MemColumnsDb.cs @@ -38,6 +38,12 @@ public IColumnsWriteBatch StartWriteBatch() { return new InMemoryColumnWriteBatch(this); } + + public IColumnDbSnapshot CreateSnapshot() + { + throw new Exception("Snapshot not supported"); + } + public void Dispose() { } public void Flush(bool onlyWal = false) { } } diff --git a/src/Nethermind/Nethermind.Db/ReadOnlyColumnsDb.cs b/src/Nethermind/Nethermind.Db/ReadOnlyColumnsDb.cs index 3b7bafd750d..0c8483d09e7 100644 --- a/src/Nethermind/Nethermind.Db/ReadOnlyColumnsDb.cs +++ b/src/Nethermind/Nethermind.Db/ReadOnlyColumnsDb.cs @@ -10,9 +10,11 @@ namespace Nethermind.Db public class ReadOnlyColumnsDb : IReadOnlyColumnDb, IDisposable { private readonly IDictionary _readOnlyColumns; + private readonly IColumnsDb _baseColumnDb; public ReadOnlyColumnsDb(IColumnsDb baseColumnDb, bool createInMemWriteStore) { + _baseColumnDb = baseColumnDb; _readOnlyColumns = baseColumnDb.ColumnKeys .Select(key => (key, db: baseColumnDb.GetColumnDb(key).CreateReadOnly(createInMemWriteStore))) .ToDictionary(it => it.key, it => it.db); @@ -29,6 +31,11 @@ public IColumnsWriteBatch StartWriteBatch() return new InMemoryColumnWriteBatch(this); } + public IColumnDbSnapshot CreateSnapshot() + { + return _baseColumnDb.CreateSnapshot(); + } + public void ClearTempChanges() { foreach (KeyValuePair readOnlyColumn in _readOnlyColumns) diff --git a/src/Nethermind/Nethermind.Db/SimpleFilePublicKeyDb.cs b/src/Nethermind/Nethermind.Db/SimpleFilePublicKeyDb.cs index d69a532c873..5acf06ba432 100644 --- a/src/Nethermind/Nethermind.Db/SimpleFilePublicKeyDb.cs +++ b/src/Nethermind/Nethermind.Db/SimpleFilePublicKeyDb.cs @@ -116,6 +116,10 @@ public void Clear() public IEnumerable GetAllKeys(bool ordered = false) => _cache.Keys; public IEnumerable GetAllValues(bool ordered = false) => _cache.Values; + public IDbSnapshot CreateSnapshot() + { + throw new NotImplementedException(); + } public IWriteBatch StartWriteBatch() { diff --git a/src/Nethermind/Nethermind.Era1/EraStore.cs b/src/Nethermind/Nethermind.Era1/EraStore.cs index 9086fcd4b06..763a9eaf0f7 100644 --- a/src/Nethermind/Nethermind.Era1/EraStore.cs +++ b/src/Nethermind/Nethermind.Era1/EraStore.cs @@ -136,10 +136,11 @@ private EraReader GetReader(long epoch) return new EraReader(new E2StoreReader(_epochs[epoch])); } - private async ValueTask EnsureEpochVerified(long epoch, EraReader reader, CancellationToken cancellation) + private ValueTask EnsureEpochVerified(long epoch, EraReader reader, CancellationToken cancellation) { if (!(_verifiedEpochs.TryGetValue(epoch, out bool verified) && verified)) { + /* Task checksumTask = Task.Run(() => { ValueHash256 checksum = reader.CalculateChecksum(); @@ -161,9 +162,12 @@ private async ValueTask EnsureEpochVerified(long epoch, EraReader reader, Cancel }); await Task.WhenAll(checksumTask, accumulatorTask); + */ _verifiedEpochs.TryAdd(epoch, true); } + + return ValueTask.CompletedTask; } public long NextEraStart(long blockNumber) diff --git a/src/Nethermind/Nethermind.Evm/State/IWorldStateScopeProvider.cs b/src/Nethermind/Nethermind.Evm/State/IWorldStateScopeProvider.cs index 96f1e1d7de8..73c386de9b5 100644 --- a/src/Nethermind/Nethermind.Evm/State/IWorldStateScopeProvider.cs +++ b/src/Nethermind/Nethermind.Evm/State/IWorldStateScopeProvider.cs @@ -66,6 +66,8 @@ public interface IScope : IDisposable /// first. /// void Commit(long blockNumber); + + void HintSet(Address address); } public interface ICodeDb @@ -89,6 +91,8 @@ public interface IStorageTree /// /// byte[] Get(in ValueHash256 hash); + + void HintSet(in UInt256 index); } public interface IWorldStateWriteBatch : IDisposable diff --git a/src/Nethermind/Nethermind.Init/Modules/FlatWorldStateModule.cs b/src/Nethermind/Nethermind.Init/Modules/FlatWorldStateModule.cs new file mode 100644 index 00000000000..700f876ef41 --- /dev/null +++ b/src/Nethermind/Nethermind.Init/Modules/FlatWorldStateModule.cs @@ -0,0 +1,106 @@ +// SPDX-FileCopyrightText: 2025 Demerzel Solutions Limited +// SPDX-License-Identifier: LGPL-3.0-only + +using System; +using System.Collections.Generic; +using Autofac; +using Nethermind.Api.Steps; +using Nethermind.Blockchain; +using Nethermind.Core; +using Nethermind.Core.Extensions; +using Nethermind.Db; +using Nethermind.Db.Rocks; +using Nethermind.Init.Steps; +using Nethermind.State; +using Nethermind.State.Flat; +using Nethermind.State.Flat.Importer; +using Nethermind.State.Flat.Persistence; +using Nethermind.State.Flat.ScopeProvider; + +namespace Nethermind.Init.Modules; + +public class FlatWorldStateModule(IFlatDbConfig flatDbConfig): Module +{ + protected override void Load(ContainerBuilder builder) + { + builder.AddSingleton(_ => throw new Exception($"{nameof(MainPruningTrieStoreFactory)} disabled.")); + builder.AddSingleton(_ => throw new Exception($"{nameof(PruningTrieStateFactory)} disabled.")); + + builder + .AddSingleton() + .AddSingleton() + .AddSingleton() + .AddSingleton() + .AddSingleton() + .AddColumnDatabase(DbNames.Flat) + .AddSingleton() + + // These fake db are workaround for missing metrics with column db. Probably not a good idea though as + // a failure in writes in one of the DB will break the db. + .AddDatabase(DbNames.Preimage) + .AddDatabase(DbNames.FlatMetadata) + .AddDatabase(DbNames.FlatState) + .AddDatabase(DbNames.FlatStorage) + .AddDatabase(DbNames.FlatStateNodes) + .AddDatabase(DbNames.FlatStateTopNodes) + .AddDatabase(DbNames.FlatStorageNodes) + .AddDatabase(DbNames.FlatStorageTopNodes) + .AddSingleton>((ctx) => + { + return new FakeColumnsDb(new Dictionary() + { + { FlatDbColumns.Metadata, ctx.ResolveKeyed(DbNames.FlatMetadata) }, + { FlatDbColumns.State, ctx.ResolveKeyed(DbNames.FlatState) }, + { FlatDbColumns.Storage, ctx.ResolveKeyed(DbNames.FlatStorage) }, + { FlatDbColumns.StateNodes, ctx.ResolveKeyed(DbNames.FlatStateNodes) }, + { FlatDbColumns.StorageNodes, ctx.ResolveKeyed(DbNames.FlatStorageNodes) }, + { FlatDbColumns.StateTopNodes, ctx.ResolveKeyed(DbNames.FlatStateTopNodes) }, + { FlatDbColumns.StorageTopNodes, ctx.ResolveKeyed(DbNames.FlatStorageTopNodes) }, + }); + }) + + .AddSingleton() + .AddSingleton((config) => new FlatDiffRepository.Configuration() + { + Boundary = config.PruningBoundary, + CompactSize = config.CompactSize, + CompactInterval = config.CompactInterval, + MaxInFlightCompactJob = config.MaxInFlightCompactJob, + ReadWithTrie = config.ReadWithTrie, + VerifyWithTrie = config.VerifyWithTrie, + ConcurrentCompactor = 1, + TrieCacheMemoryTarget = config.TrieCacheMemoryTarget, + InlineCompaction = config.InlineCompaction, + DisableTrieWarmer = config.DisableTrieWarmer + }) + + .AddSingleton((flatDbConfig, ctx) => + { + if ( + flatDbConfig.Layout == FlatLayout.PreimageFlat + || flatDbConfig.Layout == FlatLayout.Flat + || flatDbConfig.Layout == FlatLayout.FlatNoSeparateTopStorage + || flatDbConfig.Layout == FlatLayout.FlatInTrie + ) + { + return ctx.Resolve(); + } + + throw new Exception($"Unsupported layout {flatDbConfig.Layout}"); + }) + + .AddSingleton() + .AddSingleton((config) => new RocksdbPersistence.Configuration() + { + UsePreimage = config.Layout == FlatLayout.PreimageFlat, + FlatInTrie = config.Layout == FlatLayout.FlatInTrie, + SeparateStorageTop = (config.Layout != FlatLayout.FlatInTrie && config.Layout != FlatLayout.FlatNoSeparateTopStorage) + }) + .AddSingleton(); + + if (flatDbConfig.ImportFromPruningTrieState) + { + builder.AddStep(typeof(ImportFlatDb)); + } + } +} diff --git a/src/Nethermind/Nethermind.Init/Modules/NethermindModule.cs b/src/Nethermind/Nethermind.Init/Modules/NethermindModule.cs index 9ff2ea05f82..85102db59f7 100644 --- a/src/Nethermind/Nethermind.Init/Modules/NethermindModule.cs +++ b/src/Nethermind/Nethermind.Init/Modules/NethermindModule.cs @@ -1,6 +1,7 @@ // SPDX-FileCopyrightText: 2025 Demerzel Solutions Limited // SPDX-License-Identifier: LGPL-3.0-only +using System; using System.IO.Abstractions; using Autofac; using Nethermind.Abi; @@ -81,6 +82,9 @@ protected override void Load(ContainerBuilder builder) { builder.AddSingleton(NullBlobTxStorage.Instance); } + + if (configProvider.GetConfig().Enabled) + builder.AddModule(new FlatWorldStateModule(configProvider.GetConfig())); } // Just a wrapper to make it clear, these three are expected to be available at the time of configurations. diff --git a/src/Nethermind/Nethermind.Init/Modules/PrewarmerModule.cs b/src/Nethermind/Nethermind.Init/Modules/PrewarmerModule.cs index 7ee5fe7f62c..dab0a524ecb 100644 --- a/src/Nethermind/Nethermind.Init/Modules/PrewarmerModule.cs +++ b/src/Nethermind/Nethermind.Init/Modules/PrewarmerModule.cs @@ -1,6 +1,7 @@ // SPDX-FileCopyrightText: 2025 Demerzel Solutions Limited // SPDX-License-Identifier: LGPL-3.0-only +using System; using Autofac; using Nethermind.Blockchain; using Nethermind.Config; @@ -46,11 +47,12 @@ protected override void Load(ContainerBuilder builder) .AddDecorator((ctx, worldStateScopeProvider) => { if (worldStateScopeProvider is PrewarmerScopeProvider) return worldStateScopeProvider; // Inner world state - return new PrewarmerScopeProvider( + var worldState = new PrewarmerScopeProvider( worldStateScopeProvider, ctx.Resolve(), populatePreBlockCache: false ); + return worldState; }) .AddDecorator((ctx, originalCodeInfoRepository) => { diff --git a/src/Nethermind/Nethermind.Init/Steps/ImportFlatDb.cs b/src/Nethermind/Nethermind.Init/Steps/ImportFlatDb.cs new file mode 100644 index 00000000000..3b88e3865da --- /dev/null +++ b/src/Nethermind/Nethermind.Init/Steps/ImportFlatDb.cs @@ -0,0 +1,54 @@ +// SPDX-FileCopyrightText: 2025 Demerzel Solutions Limited +// SPDX-License-Identifier: LGPL-3.0-only + +using System.Threading; +using System.Threading.Tasks; +using Nethermind.Api.Steps; +using Nethermind.Blockchain; +using Nethermind.Config; +using Nethermind.Core; +using Nethermind.Logging; +using Nethermind.State.Flat; +using Nethermind.State.Flat.Importer; +using Nethermind.State.Flat.Persistence; +using RocksDbSharp; + +namespace Nethermind.Init.Steps; + +[RunnerStepDependencies( + dependencies: [typeof(InitializeBlockTree)], + dependents: [typeof(InitializeBlockchain)] +)] +public class ImportFlatDb( + IBlockTree blockTree, + IPersistence persistence, + Importer importer, + IProcessExitSource exitSource, + ILogManager logManager +): IStep +{ + ILogger _logger = logManager.GetClassLogger(); + + public Task Execute(CancellationToken cancellationToken) + { + BlockHeader? head = blockTree.Head?.Header; + if (head is null) return Task.CompletedTask; + + using (var reader = persistence.CreateReader()) + { + _logger.Warn($"Current state is {reader.CurrentState}"); + if (reader.CurrentState.blockNumber > 0) + { + _logger.Info("Flat db already exist"); + return Task.CompletedTask; + } + } + + _logger.Info($"Copying state {head.ToString(BlockHeader.Format.Short)} with state root {head.StateRoot}"); + importer.Copy(new StateId(head)); + + exitSource.Exit(0); + + return Task.CompletedTask; + } +} diff --git a/src/Nethermind/Nethermind.Init/Steps/StartMonitoring.cs b/src/Nethermind/Nethermind.Init/Steps/StartMonitoring.cs index 9f4026090f6..7bb34b48e65 100644 --- a/src/Nethermind/Nethermind.Init/Steps/StartMonitoring.cs +++ b/src/Nethermind/Nethermind.Init/Steps/StartMonitoring.cs @@ -31,8 +31,8 @@ public class StartMonitoring( ISyncConfig syncConfig, IServiceStopper serviceStopper, ILogManager logManager, - IMetricsConfig metricsConfig, - ChainHeadInfoProvider chainHeadInfoProvider + IMetricsConfig metricsConfig + // ChainHeadInfoProvider chainHeadInfoProvider ) : IStep { private readonly ILogger _logger = logManager.GetClassLogger(); @@ -112,16 +112,18 @@ private void UpdateDbMetrics() { try { - if (Environment.TickCount64 - _lastDbMetricsUpdate < 60_000) + if (Environment.TickCount64 - _lastDbMetricsUpdate < 5_000) { // Update max every minute return; } + /* if (chainHeadInfoProvider.IsProcessingBlock) { // Do not update db metrics while processing a block return; } + */ foreach (KeyValuePair kv in dbTracker.GetAllDbMeta()) { diff --git a/src/Nethermind/Nethermind.Runner/packages.lock.json b/src/Nethermind/Nethermind.Runner/packages.lock.json index de5f39f1097..1274c31178e 100644 --- a/src/Nethermind/Nethermind.Runner/packages.lock.json +++ b/src/Nethermind/Nethermind.Runner/packages.lock.json @@ -21,7 +21,10 @@ "type": "Direct", "requested": "[10.0.0, )", "resolved": "10.0.0", - "contentHash": "ECaTMB4NdV9W1es9J6tN0yoXRPUHKMi5+2L7hcVZ5k9zVdxccIx6+vMllwEYcdTaO0mCETEmdH4F0KxCqgnPaw==" + "contentHash": "ECaTMB4NdV9W1es9J6tN0yoXRPUHKMi5+2L7hcVZ5k9zVdxccIx6+vMllwEYcdTaO0mCETEmdH4F0KxCqgnPaw==", + "dependencies": { + "Microsoft.Extensions.FileProviders.Abstractions": "10.0.0" + } }, "Microsoft.VisualStudio.Azure.Containers.Tools.Targets": { "type": "Direct", @@ -53,7 +56,10 @@ "AspNetCore.HealthChecks.UI.Core": { "type": "Transitive", "resolved": "9.0.0", - "contentHash": "TVriy4hgYnhfqz6NAzv8qe62Q8wf82iKUL6WV9selqeFZTq1ILi39Sic6sFQegRysvAVcnxKP/vY8z9Fk8x6XQ==" + "contentHash": "TVriy4hgYnhfqz6NAzv8qe62Q8wf82iKUL6WV9selqeFZTq1ILi39Sic6sFQegRysvAVcnxKP/vY8z9Fk8x6XQ==", + "dependencies": { + "Microsoft.Extensions.Diagnostics.HealthChecks": "8.0.11" + } }, "AspNetCore.HealthChecks.UI.Data": { "type": "Transitive", @@ -69,7 +75,8 @@ "resolved": "1.4.0", "contentHash": "1cnkP90c+zNcRyabjKSA3VYJvpYfkGEpXeekfF8KdTFo3VyUUFOioAsANbG8nsMyedGcmUOqHWd1d3fOXke4VA==", "dependencies": { - "NETStandard.Library": "1.6.1" + "NETStandard.Library": "1.6.1", + "System.Buffers": "4.4.0" } }, "Common.Logging": { @@ -77,13 +84,23 @@ "resolved": "3.4.1", "contentHash": "5eZ/vgEOqzLg4PypZqnJ+wMhhgHyckicbZY4iDxqQ4FtOz0CpdYZ0xQ78aszMzeAJZiLLb5VdR9tPfunVQLz6g==", "dependencies": { - "Common.Logging.Core": "3.4.1" + "Common.Logging.Core": "3.4.1", + "Microsoft.CSharp": "4.0.1", + "System.Collections": "4.0.11", + "System.Diagnostics.Debug": "4.0.11", + "System.Globalization": "4.0.11", + "System.Reflection.TypeExtensions": "4.1.0", + "System.Runtime.Extensions": "4.1.0", + "System.Threading": "4.0.11" } }, "Common.Logging.Core": { "type": "Transitive", "resolved": "3.4.1", - "contentHash": "wLHldZHvxsSD6Ahonfj00/SkfHfKqO+YT6jsUwVm8Rch1REL9IArHAcSLXxYxYfu5/4ydGtmXvOtaH3AkVPu0A==" + "contentHash": "wLHldZHvxsSD6Ahonfj00/SkfHfKqO+YT6jsUwVm8Rch1REL9IArHAcSLXxYxYfu5/4ydGtmXvOtaH3AkVPu0A==", + "dependencies": { + "Microsoft.CSharp": "4.0.1" + } }, "FastEnum.Core": { "type": "Transitive", @@ -110,13 +127,17 @@ "resolved": "2.46.6", "contentHash": "ZoRg3KmOJ2urTF4+u3H0b1Yv10xzz2Y/flFWS2tnRmj8dbKLeiJaSRqu4LOBD3ova90evqLkVZ85kUkC4JT4lw==", "dependencies": { - "Grpc.Core.Api": "2.46.6" + "Grpc.Core.Api": "2.46.6", + "System.Memory": "4.5.3" } }, "Grpc.Core.Api": { "type": "Transitive", "resolved": "2.46.6", - "contentHash": "Z7HJGqJYyKb53qfp1jf0wRDYs3sxOnkAFxXAW6q52LLmX/zxzjtFLI9eaWO5UC0weiWjn4iT1FzR+tj9qYZAMg==" + "contentHash": "Z7HJGqJYyKb53qfp1jf0wRDYs3sxOnkAFxXAW6q52LLmX/zxzjtFLI9eaWO5UC0weiWjn4iT1FzR+tj9qYZAMg==", + "dependencies": { + "System.Memory": "4.5.3" + } }, "Humanizer.Core": { "type": "Transitive", @@ -165,6 +186,16 @@ "resolved": "5.0.0", "contentHash": "pg1W2VwaEQMAiTpGK840hZgzavnqjlCMTVSbtVCXVyT+7AX4mc1o89SPv4TBlAjhgCOo9c1Y+jZ5m3ti2YgGgA==" }, + "Microsoft.AspNetCore.Cryptography.Internal": { + "type": "Transitive", + "resolved": "10.0.0", + "contentHash": "jGlm8BsWcN1IIxLaxcHP6s0u2OEiBMa0HPCiWkMK7xox/h4WP2CRMyk7tV0cJC5LdM3JoR5UUqU2cxat6ElwlA==" + }, + "Microsoft.AspNetCore.DataProtection.Abstractions": { + "type": "Transitive", + "resolved": "10.0.0", + "contentHash": "MFwimSi2FH/CMGydm5EnoQFORoaArEX4QG1nijiRN05XUyJqzWwIlYT4AvnhoU1cGett/EvD416f7OnrDisbiA==" + }, "Microsoft.Bcl.AsyncInterfaces": { "type": "Transitive", "resolved": "6.0.0", @@ -173,19 +204,29 @@ "Microsoft.ClearScript.Core": { "type": "Transitive", "resolved": "7.5.0", - "contentHash": "7BfzQZA7LdgpfJGSy/GBzKuURb32UpJGQObH5WAavkUxS/u9h/KaEl8N4812F6f4UWVrtwI5XdRgLMu6ukt38A==" + "contentHash": "7BfzQZA7LdgpfJGSy/GBzKuURb32UpJGQObH5WAavkUxS/u9h/KaEl8N4812F6f4UWVrtwI5XdRgLMu6ukt38A==", + "dependencies": { + "Microsoft.Win32.Registry": "5.0.0" + } }, "Microsoft.ClearScript.V8.ICUData": { "type": "Transitive", "resolved": "7.5.0", - "contentHash": "brX7rIjvZPt/ZDSZOPf36ULxhlsFcEgg2WLeOXCFkexTH7MWgUGy//6vMry/QvTLhSgDrs5z+8SbEU1krnuxRg==" + "contentHash": "brX7rIjvZPt/ZDSZOPf36ULxhlsFcEgg2WLeOXCFkexTH7MWgUGy//6vMry/QvTLhSgDrs5z+8SbEU1krnuxRg==", + "dependencies": { + "System.Text.RegularExpressions": "4.3.1" + } }, "Microsoft.CodeAnalysis.Common": { "type": "Transitive", "resolved": "4.5.0", "contentHash": "lwAbIZNdnY0SUNoDmZHkVUwLO8UyNnyyh1t/4XsbFxi4Ounb3xszIYZaWhyj5ZjyfcwqwmtMbE7fUTVCqQEIdQ==", "dependencies": { - "Microsoft.CodeAnalysis.Analyzers": "3.3.3" + "Microsoft.CodeAnalysis.Analyzers": "3.3.3", + "System.Collections.Immutable": "6.0.0", + "System.Reflection.Metadata": "6.0.1", + "System.Runtime.CompilerServices.Unsafe": "6.0.0", + "System.Text.Encoding.CodePages": "6.0.0" } }, "Microsoft.CodeAnalysis.CSharp.Workspaces": { @@ -207,7 +248,32 @@ "Humanizer.Core": "2.14.1", "Microsoft.Bcl.AsyncInterfaces": "6.0.0", "Microsoft.CodeAnalysis.Common": "[4.5.0]", - "System.Composition": "6.0.0" + "System.Composition": "6.0.0", + "System.IO.Pipelines": "6.0.3", + "System.Threading.Channels": "6.0.0" + } + }, + "Microsoft.CSharp": { + "type": "Transitive", + "resolved": "4.0.1", + "contentHash": "17h8b5mXa87XYKrrVqdgZ38JefSUqLChUQpXgSnpzsM0nDOhE40FTeNWOJ/YmySGV6tG6T8+hjz6vxbknHJr6A==", + "dependencies": { + "System.Collections": "4.0.11", + "System.Diagnostics.Debug": "4.0.11", + "System.Dynamic.Runtime": "4.0.11", + "System.Globalization": "4.0.11", + "System.Linq": "4.1.0", + "System.Linq.Expressions": "4.1.0", + "System.ObjectModel": "4.0.12", + "System.Reflection": "4.1.0", + "System.Reflection.Extensions": "4.0.1", + "System.Reflection.Primitives": "4.0.1", + "System.Reflection.TypeExtensions": "4.1.0", + "System.Resources.ResourceManager": "4.0.1", + "System.Runtime": "4.1.0", + "System.Runtime.Extensions": "4.1.0", + "System.Runtime.InteropServices": "4.1.0", + "System.Threading": "4.0.11" } }, "Microsoft.EntityFrameworkCore": { @@ -216,7 +282,9 @@ "contentHash": "stbjWBTtpQ1HtqXMFyKnXFTr76PvaOHI2b2h85JqBi3eZr00nspvR/a90Zwh8CQ4rVawqLiTG0+0yZQWaav+sQ==", "dependencies": { "Microsoft.EntityFrameworkCore.Abstractions": "8.0.11", - "Microsoft.EntityFrameworkCore.Analyzers": "8.0.11" + "Microsoft.EntityFrameworkCore.Analyzers": "8.0.11", + "Microsoft.Extensions.Caching.Memory": "8.0.1", + "Microsoft.Extensions.Logging": "8.0.1" } }, "Microsoft.EntityFrameworkCore.Abstractions": { @@ -254,14 +322,162 @@ "resolved": "8.0.11", "contentHash": "3TuuW3i5I4Ro0yoaHmi2MqEDGObOVuhLaMEnd/heaLB1fcvm4fu4PevmC4BOWnI0vo176AIlV5o4rEQciLoohw==", "dependencies": { - "Microsoft.EntityFrameworkCore": "8.0.11" + "Microsoft.EntityFrameworkCore": "8.0.11", + "Microsoft.Extensions.Configuration.Abstractions": "8.0.0" + } + }, + "Microsoft.Extensions.Caching.Abstractions": { + "type": "Transitive", + "resolved": "10.0.0", + "contentHash": "Zcoy6H9mSoGyvr7UvlGokEZrlZkcPCICPZr8mCsSt9U/N8eeCwCXwKF5bShdA66R0obxBCwP4AxomQHvVkC/uA==", + "dependencies": { + "Microsoft.Extensions.Primitives": "10.0.0" + } + }, + "Microsoft.Extensions.Configuration": { + "type": "Transitive", + "resolved": "9.0.0", + "contentHash": "YIMO9T3JL8MeEXgVozKt2v79hquo/EFtnY0vgxmLnUvk1Rei/halI7kOWZL2RBeV9FMGzgM9LZA8CVaNwFMaNA==", + "dependencies": { + "Microsoft.Extensions.Configuration.Abstractions": "9.0.0", + "Microsoft.Extensions.Primitives": "9.0.0" + } + }, + "Microsoft.Extensions.Configuration.Abstractions": { + "type": "Transitive", + "resolved": "10.0.0", + "contentHash": "d2kDKnCsJvY7mBVhcjPSp9BkJk48DsaHPg5u+Oy4f8XaOqnEedRy/USyvnpHL92wpJ6DrTPy7htppUUzskbCXQ==", + "dependencies": { + "Microsoft.Extensions.Primitives": "10.0.0" } }, + "Microsoft.Extensions.Configuration.Binder": { + "type": "Transitive", + "resolved": "9.0.0", + "contentHash": "RiScL99DcyngY9zJA2ROrri7Br8tn5N4hP4YNvGdTN/bvg1A3dwvDOxHnNZ3Im7x2SJ5i4LkX1uPiR/MfSFBLQ==", + "dependencies": { + "Microsoft.Extensions.Configuration.Abstractions": "9.0.0" + } + }, + "Microsoft.Extensions.DependencyInjection.Abstractions": { + "type": "Transitive", + "resolved": "10.0.0", + "contentHash": "L3AdmZ1WOK4XXT5YFPEwyt0ep6l8lGIPs7F5OOBZc77Zqeo01Of7XXICy47628sdVl0v/owxYJTe86DTgFwKCA==" + }, "Microsoft.Extensions.DependencyModel": { "type": "Transitive", "resolved": "8.0.2", "contentHash": "mUBDZZRgZrSyFOsJ2qJJ9fXfqd/kXJwf3AiDoqLD9m6TjY5OO/vLNOb9fb4juC0487eq4hcGN/M2Rh/CKS7QYw==" }, + "Microsoft.Extensions.Diagnostics": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "3PZp/YSkIXrF7QK7PfC1bkyRYwqOHpWFad8Qx+4wkuumAeXo1NHaxpS9LboNA9OvNSAu+QOVlXbMyoY+pHSqcw==", + "dependencies": { + "Microsoft.Extensions.Configuration": "8.0.0", + "Microsoft.Extensions.Diagnostics.Abstractions": "8.0.0", + "Microsoft.Extensions.Options.ConfigurationExtensions": "8.0.0" + } + }, + "Microsoft.Extensions.Diagnostics.Abstractions": { + "type": "Transitive", + "resolved": "10.0.0", + "contentHash": "SfK89ytD61S7DgzorFljSkUeluC1ncn6dtZgwc0ot39f/BEYWBl5jpgvodxduoYAs1d9HG8faCDRZxE95UMo2A==", + "dependencies": { + "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.0", + "Microsoft.Extensions.Options": "10.0.0" + } + }, + "Microsoft.Extensions.Diagnostics.HealthChecks": { + "type": "Transitive", + "resolved": "8.0.11", + "contentHash": "zLgN22Zp9pk8RHlwssRTexw4+a6wqOnKWN+VejdPn5Yhjql4XiBhkFo35Nu8mmqHIk/UEmmCnMGLWq75aFfkOw==", + "dependencies": { + "Microsoft.Extensions.Diagnostics.HealthChecks.Abstractions": "8.0.11", + "Microsoft.Extensions.Hosting.Abstractions": "8.0.1", + "Microsoft.Extensions.Logging.Abstractions": "8.0.2", + "Microsoft.Extensions.Options": "8.0.2" + } + }, + "Microsoft.Extensions.Diagnostics.HealthChecks.Abstractions": { + "type": "Transitive", + "resolved": "8.0.11", + "contentHash": "So3JUdRxozRjvQ3cxU6F3nI/i4emDnjane6yMYcJhvTTTu29ltlIdoXjkFGRceIWz8yKvuEpzXItZ0x5GvN2nQ==" + }, + "Microsoft.Extensions.FileProviders.Abstractions": { + "type": "Transitive", + "resolved": "10.0.0", + "contentHash": "/ppSdehKk3fuXjlqCDgSOtjRK/pSHU8eWgzSHfHdwVm5BP4Dgejehkw+PtxKG2j98qTDEHDst2Y99aNsmJldmw==", + "dependencies": { + "Microsoft.Extensions.Primitives": "10.0.0" + } + }, + "Microsoft.Extensions.Hosting.Abstractions": { + "type": "Transitive", + "resolved": "10.0.0", + "contentHash": "KrN6TGFwCwqOkLLk/idW/XtDQh+8In+CL9T4M1Dx+5ScsjTq4TlVbal8q532m82UYrMr6RiQJF2HvYCN0QwVsA==", + "dependencies": { + "Microsoft.Extensions.Configuration.Abstractions": "10.0.0", + "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.0", + "Microsoft.Extensions.Diagnostics.Abstractions": "10.0.0", + "Microsoft.Extensions.FileProviders.Abstractions": "10.0.0", + "Microsoft.Extensions.Logging.Abstractions": "10.0.0" + } + }, + "Microsoft.Extensions.Http": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "cWz4caHwvx0emoYe7NkHPxII/KkTI8R/LC9qdqJqnKv2poTJ4e2qqPGQqvRoQ5kaSA4FU5IV3qFAuLuOhoqULQ==", + "dependencies": { + "Microsoft.Extensions.Configuration.Abstractions": "8.0.0", + "Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.0", + "Microsoft.Extensions.Diagnostics": "8.0.0", + "Microsoft.Extensions.Logging": "8.0.0", + "Microsoft.Extensions.Logging.Abstractions": "8.0.0", + "Microsoft.Extensions.Options": "8.0.0" + } + }, + "Microsoft.Extensions.Logging.Configuration": { + "type": "Transitive", + "resolved": "9.0.0", + "contentHash": "H05HiqaNmg6GjH34ocYE9Wm1twm3Oz2aXZko8GTwGBzM7op2brpAA8pJ5yyD1OpS1mXUtModBYOlcZ/wXeWsSg==", + "dependencies": { + "Microsoft.Extensions.Configuration": "9.0.0", + "Microsoft.Extensions.Configuration.Abstractions": "9.0.0", + "Microsoft.Extensions.Configuration.Binder": "9.0.0", + "Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.0", + "Microsoft.Extensions.Logging": "9.0.0", + "Microsoft.Extensions.Logging.Abstractions": "9.0.0", + "Microsoft.Extensions.Options": "9.0.0", + "Microsoft.Extensions.Options.ConfigurationExtensions": "9.0.0" + } + }, + "Microsoft.Extensions.Options": { + "type": "Transitive", + "resolved": "10.0.0", + "contentHash": "8oCAgXOow5XDrY9HaXX1QmH3ORsyZO/ANVHBlhLyCeWTH5Sg4UuqZeOTWJi6484M+LqSx0RqQXDJtdYy2BNiLQ==", + "dependencies": { + "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.0", + "Microsoft.Extensions.Primitives": "10.0.0" + } + }, + "Microsoft.Extensions.Options.ConfigurationExtensions": { + "type": "Transitive", + "resolved": "9.0.0", + "contentHash": "Ob3FXsXkcSMQmGZi7qP07EQ39kZpSBlTcAZLbJLdI4FIf0Jug8biv2HTavWmnTirchctPlq9bl/26CXtQRguzA==", + "dependencies": { + "Microsoft.Extensions.Configuration.Abstractions": "9.0.0", + "Microsoft.Extensions.Configuration.Binder": "9.0.0", + "Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.0", + "Microsoft.Extensions.Options": "9.0.0", + "Microsoft.Extensions.Primitives": "9.0.0" + } + }, + "Microsoft.Extensions.Primitives": { + "type": "Transitive", + "resolved": "10.0.0", + "contentHash": "inRnbpCS0nwO/RuoZIAqxQUuyjaknOOnCEZB55KSMMjRhl0RQDttSmLSGsUJN3RQ3ocf5NDLFd2mOQViHqMK5w==" + }, "Microsoft.IdentityModel.Abstractions": { "type": "Transitive", "resolved": "8.15.0", @@ -280,13 +496,28 @@ "resolved": "8.15.0", "contentHash": "zUE9ysJXBtXlHHRtcRK3Sp8NzdCI1z/BRDTXJQ2TvBoI0ENRtnufYIep0O5TSCJRJGDwwuLTUx+l/bEYZUxpCA==", "dependencies": { + "Microsoft.Extensions.Logging.Abstractions": "10.0.0", "Microsoft.IdentityModel.Logging": "8.15.0" } }, "Microsoft.NETCore.Platforms": { "type": "Transitive", - "resolved": "1.1.0", - "contentHash": "kz0PEW2lhqygehI/d6XsPCQzD7ff7gUJaVGPVETX611eadGsA3A877GdSlU0LRVMCTH/+P3o2iDTak+S08V2+A==" + "resolved": "5.0.0", + "contentHash": "VyPlqzH2wavqquTcYpkIIAQ6WdenuKoFN0BdYBbCWsclXacSOHNQn66Gt4z5NBqEYW0FAPm5rlvki9ZiCij5xQ==" + }, + "Microsoft.NETCore.Targets": { + "type": "Transitive", + "resolved": "1.1.3", + "contentHash": "3Wrmi0kJDzClwAC+iBdUBpEKmEle8FQNsCs77fkiOIw/9oYA07bL1EZNX0kQ2OMN3xpwvl0vAtOCYY3ndDNlhQ==" + }, + "Microsoft.Win32.Registry": { + "type": "Transitive", + "resolved": "5.0.0", + "contentHash": "dDoKi0PnDz31yAyETfRntsLArTlVAVzUzCIvvEDsDsucrl33Dl8pIJG06ePTJTI3tGpeyHS9Cq7Foc/s4EeKcg==", + "dependencies": { + "System.Security.AccessControl": "5.0.0", + "System.Security.Principal.Windows": "5.0.0" + } }, "Mono.TextTemplating": { "type": "Transitive", @@ -330,13 +561,18 @@ "dependencies": { "Nethermind.DotNetty.Buffers": "1.0.2.76", "Nethermind.DotNetty.Common": "1.0.2.76", - "Nethermind.DotNetty.Transport": "1.0.2.76" + "Nethermind.DotNetty.Transport": "1.0.2.76", + "System.Collections.Immutable": "1.5.0" } }, "Nethermind.DotNetty.Common": { "type": "Transitive", "resolved": "1.0.2.76", - "contentHash": "rraxAOK8Pww3ReW2NkCCr/pwXTp88gI4lXaeA5TriPnp1wZg8jJdZYIj2m2+HKkVtw1C1F1sRA7FzfgBodA3Tw==" + "contentHash": "rraxAOK8Pww3ReW2NkCCr/pwXTp88gI4lXaeA5TriPnp1wZg8jJdZYIj2m2+HKkVtw1C1F1sRA7FzfgBodA3Tw==", + "dependencies": { + "Microsoft.Extensions.Logging": "5.0.0", + "System.Runtime.CompilerServices.Unsafe": "5.0.0" + } }, "Nethermind.Libp2p.Core": { "type": "Transitive", @@ -345,6 +581,9 @@ "dependencies": { "BouncyCastle.Cryptography": "2.4.0", "Google.Protobuf": "3.28.3", + "Microsoft.Extensions.DependencyInjection": "9.0.0", + "Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.0", + "Microsoft.Extensions.Logging.Abstractions": "9.0.0", "Nethermind.Multiformats.Address": "1.1.8", "SimpleBase": "4.0.2" } @@ -355,6 +594,7 @@ "contentHash": "8hEzwS9eYYy1xvmXq+6VcfKcOflEmnPdT5UA8Lhr0cdK2af+wV6tMwHhuvqF0L7N+bdaFMNcUUhBhy17jafutw==", "dependencies": { "Google.Protobuf": "3.28.3", + "Microsoft.Extensions.Logging.Abstractions": "9.0.0", "Nethermind.Libp2p.Core": "1.0.0-preview.45", "Nethermind.Libp2p.Protocols.IpTcp": "1.0.0-preview.45" } @@ -364,6 +604,7 @@ "resolved": "1.0.0-preview.45", "contentHash": "vDoUfrz/45OEKc9TMEs9l0wPWW4r49opS/J+bh3zUTMLaWWf9jl8zkbPh5mz9moBh1JdDVuLRSPT3zRd8/Gvkg==", "dependencies": { + "Microsoft.Extensions.Logging.Abstractions": "9.0.0", "Nethermind.Libp2p.Core": "1.0.0-preview.45" } }, @@ -373,6 +614,7 @@ "contentHash": "NZhHziBoq9mGeqV8o0QMoKOnBLjyPEIhEIcsnL6rdV4nJ1PwRWort8O811dh4aWq8rxVm1dkj66U4Q/ZwEX+JQ==", "dependencies": { "Makaretu.Dns.Multicast": "0.27.0", + "Microsoft.Extensions.Logging.Abstractions": "9.0.0", "Nethermind.Libp2p.Core": "1.0.0-preview.45" } }, @@ -430,6 +672,7 @@ "contentHash": "TfdjpazHxi/Pxfa8oR3wPDoWzgTJJ9L0OHQo5hkozte9mpa8sUim+cfIiEC9qtAjq4D1/MfpvfQaZZzeRh2akQ==", "dependencies": { "BouncyCastle.Cryptography": "2.4.0", + "Microsoft.Extensions.Logging.Abstractions": "9.0.0", "Nethermind.Libp2p.Core": "1.0.0-preview.45" } }, @@ -447,6 +690,7 @@ "resolved": "1.0.0-preview.45", "contentHash": "BJXIfz9T1zPRJjVHGn4qJLvZu2vKnjoSoT9Zd+nYePc+C4ESwhtFuuHHSirnuKqJ/GVY2v8lvhb+fnjYSV3E8w==", "dependencies": { + "Microsoft.Extensions.Logging": "9.0.0", "Nethermind.Libp2p.Core": "1.0.0-preview.45", "Nethermind.Libp2p.Protocols.Quic": "1.0.0-preview.45" } @@ -456,6 +700,7 @@ "resolved": "1.0.0-preview.45", "contentHash": "ElNnTVoTxpHZMGTFbKTndQ1C3jFMMVLQfK1wzJVAw5sD294Yur42UKxdHtrzQJEJ/XHARz5ORxwtWcbprCQLDA==", "dependencies": { + "Microsoft.Extensions.Logging.Abstractions": "9.0.0", "Nethermind.Libp2p.Core": "1.0.0-preview.45" } }, @@ -505,6 +750,9 @@ "contentHash": "fYnHQ8yZcj9W0fPGbzMkZUnE14aGGTFS8WE0Ow2hXiGhJ61Tv71cTi1yuugHxPCLyb87JpWMkq4lix8Rf06vtA==", "dependencies": { "NETStandard.Library": "1.6.1", + "System.Buffers": "4.5.0", + "System.Memory": "4.5.0", + "System.ValueTuple": "4.4.0", "libsodium": "1.0.16" } }, @@ -535,21 +783,42 @@ "resolved": "1.8.5", "contentHash": "EaCgmntbH1sOzemRTqyXSqYjB6pLH7VCYHhhDYZ59guHSD5qPwhIYa7kfy0QUlmTRt9IXhaXdFhNuBUArp70Ng==" }, - "prometheus-net": { - "type": "Transitive", - "resolved": "8.2.1", - "contentHash": "3wVgdEPOCBF752s2xps5T+VH+c9mJK8S8GKEDg49084P6JZMumTZI5Te6aJ9MQpX0sx7om6JOnBpIi7ZBmmiDQ==" - }, "SimpleBase": { "type": "Transitive", "resolved": "4.0.2", - "contentHash": "sNKHP2Qzy4DafamgH44UGg1YeyHFT08AMgHPraxYt4CVBoHHYD5f0MjbBfdmtGca69xikPU5aV8H+MMP7ZnfIg==" + "contentHash": "sNKHP2Qzy4DafamgH44UGg1YeyHFT08AMgHPraxYt4CVBoHHYD5f0MjbBfdmtGca69xikPU5aV8H+MMP7ZnfIg==", + "dependencies": { + "System.Memory": "4.5.5" + } + }, + "System.Buffers": { + "type": "Transitive", + "resolved": "4.5.0", + "contentHash": "pL2ChpaRRWI/p4LXyy4RgeWlYF2sgfj/pnVMvBqwNFr5cXg7CXNnWZWxrOONLg8VGdFB8oB+EG2Qw4MLgTOe+A==" }, "System.CodeDom": { "type": "Transitive", "resolved": "4.4.0", "contentHash": "2sCCb7doXEwtYAbqzbF/8UAeDRMNmPaQbU2q50Psg1J9KzumyVVCgKQY8s53WIPTufNT0DpSe9QRvVjOzfDWBA==" }, + "System.Collections": { + "type": "Transitive", + "resolved": "4.0.11", + "contentHash": "YUJGz6eFKqS0V//mLt25vFGrrCvOnsXjlvFQs+KimpwNxug9x0Pzy4PlFMU3Q2IzqAa9G2L4LsK3+9vCBK7oTg==", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.0.1", + "Microsoft.NETCore.Targets": "1.0.1", + "System.Runtime": "4.1.0" + } + }, + "System.Collections.Immutable": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "l4zZJ1WU2hqpQQHXz1rvC3etVZN+2DLmQMO79FhOTZHMn8tDRr+WU287sbomD0BETlmKDn0ygUgVy9k5xkkJdA==", + "dependencies": { + "System.Runtime.CompilerServices.Unsafe": "6.0.0" + } + }, "System.Composition": { "type": "Transitive", "resolved": "6.0.0", @@ -598,257 +867,608 @@ "System.Composition.Runtime": "6.0.0" } }, - "System.Reactive": { - "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "31kfaW4ZupZzPsI5PVe77VhnvFF55qgma7KZr/E0iFTs6fmdhhG8j0mgEx620iLTey1EynOkEfnyTjtNEpJzGw==" - }, - "Testably.Abstractions.FileSystem.Interface": { + "System.Diagnostics.Debug": { "type": "Transitive", - "resolved": "9.0.0", - "contentHash": "uksk86YlnzAdyfVNu3wICU0X5iXVe9LF7Q3UkngNliHWEvM5gvAlOUr+jmd9JwmbJWISH5+i1vyXE02lEVz7WQ==" + "resolved": "4.0.11", + "contentHash": "w5U95fVKHY4G8ASs/K5iK3J5LY+/dLFd4vKejsnI/ZhBsWS9hQakfx3Zr7lRWKg4tAw9r4iktyvsTagWkqYCiw==", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.0.1", + "Microsoft.NETCore.Targets": "1.0.1", + "System.Runtime": "4.1.0" + } }, - "Tmds.LibC": { + "System.Diagnostics.EventLog": { "type": "Transitive", - "resolved": "0.2.0", - "contentHash": "+RvLuNHOLW7cxzgDe9yHLoayBgjsuH2/gJtJnuVMxweKrxxYT6TwQNAmt06SFWpjwk68aRcwwD4FfMMA6tZvVA==" + "resolved": "10.0.0", + "contentHash": "uaFRda9NjtbJRkdx311eXlAA3n2em7223c1A8d1VWyl+4FL9vkG7y2lpPfBU9HYdj/9KgdRNdn1vFK8ZYCYT/A==" }, - "YamlDotNet": { + "System.Dynamic.Runtime": { "type": "Transitive", - "resolved": "16.3.0", - "contentHash": "SgMOdxbz8X65z8hraIs6hOEdnkH6hESTAIUa7viEngHOYaH+6q5XJmwr1+yb9vJpNQ19hCQY69xbFsLtXpobQA==" - }, - "nethermind.abi": { - "type": "Project", + "resolved": "4.0.11", + "contentHash": "db34f6LHYM0U0JpE+sOmjar27BnqTVkbLJhgfwMpTdgTigG/Hna3m2MYVwnFzGGKnEJk2UXFuoVTr8WUbU91/A==", "dependencies": { - "MathNet.Numerics.FSharp": "[5.0.0, )", - "Nethermind.Core": "[1.36.0-unstable, )" + "System.Collections": "4.0.11", + "System.Diagnostics.Debug": "4.0.11", + "System.Globalization": "4.0.11", + "System.Linq": "4.1.0", + "System.Linq.Expressions": "4.1.0", + "System.ObjectModel": "4.0.12", + "System.Reflection": "4.1.0", + "System.Reflection.Emit": "4.0.1", + "System.Reflection.Emit.ILGeneration": "4.0.1", + "System.Reflection.Primitives": "4.0.1", + "System.Reflection.TypeExtensions": "4.1.0", + "System.Resources.ResourceManager": "4.0.1", + "System.Runtime": "4.1.0", + "System.Runtime.Extensions": "4.1.0", + "System.Threading": "4.0.11" } }, - "nethermind.api": { - "type": "Project", + "System.Globalization": { + "type": "Transitive", + "resolved": "4.0.11", + "contentHash": "B95h0YLEL2oSnwF/XjqSWKnwKOy/01VWkNlsCeMTFJLLabflpGV26nK164eRs5GiaRSBGpOxQ3pKoSnnyZN5pg==", "dependencies": { - "Nethermind.Blockchain": "[1.36.0-unstable, )", - "Nethermind.Facade": "[1.36.0-unstable, )", - "Nethermind.Grpc": "[1.36.0-unstable, )", - "Nethermind.History": "[1.36.0-unstable, )", - "Nethermind.JsonRpc": "[1.36.0-unstable, )", - "Nethermind.Monitoring": "[1.36.0-unstable, )", - "Nethermind.Network": "[1.36.0-unstable, )", - "Nethermind.Sockets": "[1.36.0-unstable, )" + "Microsoft.NETCore.Platforms": "1.0.1", + "Microsoft.NETCore.Targets": "1.0.1", + "System.Runtime": "4.1.0" } }, - "nethermind.blockchain": { - "type": "Project", + "System.IO": { + "type": "Transitive", + "resolved": "4.1.0", + "contentHash": "3KlTJceQc3gnGIaHZ7UBZO26SHL1SHE4ddrmiwumFnId+CEHP+O8r386tZKaE6zlk5/mF8vifMBzHj9SaXN+mQ==", "dependencies": { - "Microsoft.ClearScript.V8.Native.linux-arm64": "[7.5.0, )", - "Microsoft.ClearScript.V8.Native.linux-x64": "[7.5.0, )", - "Microsoft.ClearScript.V8.Native.osx-arm64": "[7.5.0, )", - "Microsoft.ClearScript.V8.Native.osx-x64": "[7.5.0, )", - "Microsoft.ClearScript.V8.Native.win-x64": "[7.5.0, )", - "Nethermind.Abi": "[1.36.0-unstable, )", - "Nethermind.Core": "[1.36.0-unstable, )", - "Nethermind.Db": "[1.36.0-unstable, )", - "Nethermind.Evm": "[1.36.0-unstable, )", - "Nethermind.Evm.Precompiles": "[1.36.0-unstable, )", - "Nethermind.Network.Stats": "[1.36.0-unstable, )", - "Nethermind.Specs": "[1.36.0-unstable, )", - "Nethermind.State": "[1.36.0-unstable, )", - "Nethermind.TxPool": "[1.36.0-unstable, )", - "Polly": "[8.6.4, )" + "Microsoft.NETCore.Platforms": "1.0.1", + "Microsoft.NETCore.Targets": "1.0.1", + "System.Runtime": "4.1.0", + "System.Text.Encoding": "4.0.11", + "System.Threading.Tasks": "4.0.11" } }, - "nethermind.config": { - "type": "Project", + "System.IO.Pipelines": { + "type": "Transitive", + "resolved": "6.0.3", + "contentHash": "ryTgF+iFkpGZY1vRQhfCzX0xTdlV3pyaTTqRu2ETbEv+HlV7O6y7hyQURnghNIXvctl5DuZ//Dpks6HdL/Txgw==" + }, + "System.Linq": { + "type": "Transitive", + "resolved": "4.1.0", + "contentHash": "bQ0iYFOQI0nuTnt+NQADns6ucV4DUvMdwN6CbkB1yj8i7arTGiTN5eok1kQwdnnNWSDZfIUySQY+J3d5KjWn0g==", "dependencies": { - "Nethermind.Core": "[1.36.0-unstable, )", - "NonBlocking": "[2.1.2, )", - "System.Configuration.ConfigurationManager": "[10.0.0, )" + "System.Collections": "4.0.11", + "System.Diagnostics.Debug": "4.0.11", + "System.Resources.ResourceManager": "4.0.1", + "System.Runtime": "4.1.0", + "System.Runtime.Extensions": "4.1.0" } }, - "nethermind.consensus": { - "type": "Project", + "System.Linq.Expressions": { + "type": "Transitive", + "resolved": "4.1.0", + "contentHash": "I+y02iqkgmCAyfbqOmSDOgqdZQ5tTj80Akm5BPSS8EeB0VGWdy6X1KCoYe8Pk6pwDoAKZUOdLVxnTJcExiv5zw==", "dependencies": { - "Nethermind.Blockchain": "[1.36.0-unstable, )", - "Nethermind.Config": "[1.36.0-unstable, )", - "Nethermind.Core": "[1.36.0-unstable, )", - "Nethermind.Crypto": "[1.36.0-unstable, )", - "Nethermind.Evm": "[1.36.0-unstable, )", - "Nethermind.TxPool": "[1.36.0-unstable, )" + "System.Collections": "4.0.11", + "System.Diagnostics.Debug": "4.0.11", + "System.Globalization": "4.0.11", + "System.IO": "4.1.0", + "System.Linq": "4.1.0", + "System.ObjectModel": "4.0.12", + "System.Reflection": "4.1.0", + "System.Reflection.Emit": "4.0.1", + "System.Reflection.Emit.ILGeneration": "4.0.1", + "System.Reflection.Emit.Lightweight": "4.0.1", + "System.Reflection.Extensions": "4.0.1", + "System.Reflection.Primitives": "4.0.1", + "System.Reflection.TypeExtensions": "4.1.0", + "System.Resources.ResourceManager": "4.0.1", + "System.Runtime": "4.1.0", + "System.Runtime.Extensions": "4.1.0", + "System.Threading": "4.0.11" } }, - "nethermind.consensus.aura": { - "type": "Project", + "System.Memory": { + "type": "Transitive", + "resolved": "4.5.5", + "contentHash": "XIWiDvKPXaTveaB7HVganDlOCRoj03l+jrwNvcge/t8vhGYKvqV+dMv6G4SAX2NoNmN0wZfVPTAlFwZcZvVOUw==" + }, + "System.ObjectModel": { + "type": "Transitive", + "resolved": "4.0.12", + "contentHash": "tAgJM1xt3ytyMoW4qn4wIqgJYm7L7TShRZG4+Q4Qsi2PCcj96pXN7nRywS9KkB3p/xDUjc2HSwP9SROyPYDYKQ==", "dependencies": { - "BouncyCastle.Cryptography": "[2.6.2, )", - "Nethermind.Abi": "[1.36.0-unstable, )", - "Nethermind.Api": "[1.36.0-unstable, )", - "Nethermind.Blockchain": "[1.36.0-unstable, )", - "Nethermind.Facade": "[1.36.0-unstable, )", - "Nethermind.Init": "[1.36.0-unstable, )", - "Nethermind.Specs": "[1.36.0-unstable, )", - "Nethermind.Synchronization": "[1.36.0-unstable, )", - "Nito.Collections.Deque": "[1.2.1, )" + "System.Collections": "4.0.11", + "System.Diagnostics.Debug": "4.0.11", + "System.Resources.ResourceManager": "4.0.1", + "System.Runtime": "4.1.0", + "System.Threading": "4.0.11" } }, - "nethermind.consensus.clique": { - "type": "Project", + "System.Reactive": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "31kfaW4ZupZzPsI5PVe77VhnvFF55qgma7KZr/E0iFTs6fmdhhG8j0mgEx620iLTey1EynOkEfnyTjtNEpJzGw==" + }, + "System.Reflection": { + "type": "Transitive", + "resolved": "4.1.0", + "contentHash": "JCKANJ0TI7kzoQzuwB/OoJANy1Lg338B6+JVacPl4TpUwi3cReg3nMLplMq2uqYfHFQpKIlHAUVAJlImZz/4ng==", "dependencies": { - "Nethermind.Api": "[1.36.0-unstable, )", - "Nethermind.Blockchain": "[1.36.0-unstable, )", - "Nethermind.Consensus": "[1.36.0-unstable, )", - "Nethermind.JsonRpc": "[1.36.0-unstable, )" + "Microsoft.NETCore.Platforms": "1.0.1", + "Microsoft.NETCore.Targets": "1.0.1", + "System.IO": "4.1.0", + "System.Reflection.Primitives": "4.0.1", + "System.Runtime": "4.1.0" } }, - "nethermind.consensus.ethash": { - "type": "Project", + "System.Reflection.Emit": { + "type": "Transitive", + "resolved": "4.0.1", + "contentHash": "P2wqAj72fFjpP6wb9nSfDqNBMab+2ovzSDzUZK7MVIm54tBJEPr9jWfSjjoTpPwj1LeKcmX3vr0ttyjSSFM47g==", "dependencies": { - "Nethermind.Api": "[1.36.0-unstable, )", - "Nethermind.Blockchain": "[1.36.0-unstable, )", - "Nethermind.Consensus": "[1.36.0-unstable, )", - "Nethermind.Core": "[1.36.0-unstable, )", - "Nethermind.Crypto": "[1.36.0-unstable, )", - "Nethermind.Serialization.Rlp": "[1.36.0-unstable, )", - "Nethermind.Specs": "[1.36.0-unstable, )" + "System.IO": "4.1.0", + "System.Reflection": "4.1.0", + "System.Reflection.Emit.ILGeneration": "4.0.1", + "System.Reflection.Primitives": "4.0.1", + "System.Runtime": "4.1.0" } }, - "nethermind.core": { - "type": "Project", + "System.Reflection.Emit.ILGeneration": { + "type": "Transitive", + "resolved": "4.0.1", + "contentHash": "Ov6dU8Bu15Bc7zuqttgHF12J5lwSWyTf1S+FJouUXVMSqImLZzYaQ+vRr1rQ0OZ0HqsrwWl4dsKHELckQkVpgA==", "dependencies": { - "Autofac": "[9.0.0, )", - "Autofac.Extensions.DependencyInjection": "[10.0.0, )", - "FastEnum": "[2.0.6, )", - "Microsoft.IO.RecyclableMemoryStream": "[3.0.1, )", - "Microsoft.IdentityModel.JsonWebTokens": "[8.15.0, )", - "Nethermind.Crypto.SecP256k1": "[1.5.0, )", - "Nethermind.Logging": "[1.36.0-unstable, )", - "Nethermind.Numerics.Int256": "[1.3.6, )", - "NonBlocking": "[2.1.2, )", - "TestableIO.System.IO.Abstractions.Wrappers": "[22.0.16, )" + "System.Reflection": "4.1.0", + "System.Reflection.Primitives": "4.0.1", + "System.Runtime": "4.1.0" } }, - "nethermind.crypto": { - "type": "Project", + "System.Reflection.Emit.Lightweight": { + "type": "Transitive", + "resolved": "4.0.1", + "contentHash": "sSzHHXueZ5Uh0OLpUQprhr+ZYJrLPA2Cmr4gn0wj9+FftNKXx8RIMKvO9qnjk2ebPYUjZ+F2ulGdPOsvj+MEjA==", "dependencies": { - "BouncyCastle.Cryptography": "[2.6.2, )", - "Ckzg.Bindings": "[2.1.5.1542, )", - "Nethermind.Core": "[1.36.0-unstable, )", - "Nethermind.Crypto.Bls": "[1.0.5, )", - "Nethermind.Serialization.Rlp": "[1.36.0-unstable, )", - "System.Security.Cryptography.ProtectedData": "[10.0.0, )" + "System.Reflection": "4.1.0", + "System.Reflection.Emit.ILGeneration": "4.0.1", + "System.Reflection.Primitives": "4.0.1", + "System.Runtime": "4.1.0" } }, - "nethermind.db": { - "type": "Project", + "System.Reflection.Extensions": { + "type": "Transitive", + "resolved": "4.0.1", + "contentHash": "GYrtRsZcMuHF3sbmRHfMYpvxZoIN2bQGrYGerUiWLEkqdEUQZhH3TRSaC/oI4wO0II1RKBPlpIa1TOMxIcOOzQ==", "dependencies": { - "Nethermind.Config": "[1.36.0-unstable, )", - "Nethermind.Serialization.Rlp": "[1.36.0-unstable, )", - "NonBlocking": "[2.1.2, )" + "Microsoft.NETCore.Platforms": "1.0.1", + "Microsoft.NETCore.Targets": "1.0.1", + "System.Reflection": "4.1.0", + "System.Runtime": "4.1.0" } }, - "nethermind.db.rocks": { - "type": "Project", + "System.Reflection.Metadata": { + "type": "Transitive", + "resolved": "6.0.1", + "contentHash": "III/lNMSn0ZRBuM9m5Cgbiho5j81u0FAEagFX5ta2DKbljZ3T0IpD8j+BIiHQPeKqJppWS9bGEp6JnKnWKze0g==", "dependencies": { - "ConcurrentHashSet": "[1.3.0, )", - "Nethermind.Api": "[1.36.0-unstable, )", - "Nethermind.Db": "[1.36.0-unstable, )", - "NonBlocking": "[2.1.2, )", - "RocksDB": "[10.4.2.62659, 10.4.2.62659]" + "System.Collections.Immutable": "6.0.0" } }, - "nethermind.db.rpc": { - "type": "Project", + "System.Reflection.Primitives": { + "type": "Transitive", + "resolved": "4.0.1", + "contentHash": "4inTox4wTBaDhB7V3mPvp9XlCbeGYWVEM9/fXALd52vNEAVisc1BoVWQPuUuD0Ga//dNbA/WeMy9u9mzLxGTHQ==", "dependencies": { - "Nethermind.Db": "[1.36.0-unstable, )", - "Nethermind.JsonRpc": "[1.36.0-unstable, )", - "Nethermind.Serialization.Json": "[1.36.0-unstable, )", - "Nethermind.State": "[1.36.0-unstable, )" + "Microsoft.NETCore.Platforms": "1.0.1", + "Microsoft.NETCore.Targets": "1.0.1", + "System.Runtime": "4.1.0" } }, - "nethermind.era1": { - "type": "Project", + "System.Reflection.TypeExtensions": { + "type": "Transitive", + "resolved": "4.1.0", + "contentHash": "tsQ/ptQ3H5FYfON8lL4MxRk/8kFyE0A+tGPXmVP967cT/gzLHYxIejIYSxp4JmIeFHVP78g/F2FE1mUUTbDtrg==", "dependencies": { - "CommunityToolkit.HighPerformance": "[8.4.0, )", - "Nethermind.Api": "[1.36.0-unstable, )", - "Nethermind.Blockchain": "[1.36.0-unstable, )", - "Nethermind.Consensus": "[1.36.0-unstable, )", - "Nethermind.Core": "[1.36.0-unstable, )", - "Nethermind.JsonRpc": "[1.36.0-unstable, )", - "Nethermind.Merkleization": "[1.36.0-unstable, )", - "Nethermind.Serialization.Rlp": "[1.36.0-unstable, )", - "Nethermind.Serialization.Ssz": "[1.36.0-unstable, )", - "Nethermind.State": "[1.36.0-unstable, )", - "Snappier": "[1.2.0, )" + "System.Reflection": "4.1.0", + "System.Runtime": "4.1.0" } }, - "nethermind.ethstats": { - "type": "Project", + "System.Resources.ResourceManager": { + "type": "Transitive", + "resolved": "4.0.1", + "contentHash": "TxwVeUNoTgUOdQ09gfTjvW411MF+w9MBYL7AtNVc+HtBCFlutPLhUCdZjNkjbhj3bNQWMdHboF0KIWEOjJssbA==", "dependencies": { - "Nethermind.Api": "[1.36.0-unstable, )", - "Nethermind.Blockchain": "[1.36.0-unstable, )", - "Nethermind.Core": "[1.36.0-unstable, )", - "Nethermind.Init": "[1.36.0-unstable, )", - "Nethermind.JsonRpc": "[1.36.0-unstable, )", - "Nethermind.Logging": "[1.36.0-unstable, )", - "Nethermind.Network": "[1.36.0-unstable, )", - "Websocket.Client": "[5.3.0, )" + "Microsoft.NETCore.Platforms": "1.0.1", + "Microsoft.NETCore.Targets": "1.0.1", + "System.Globalization": "4.0.11", + "System.Reflection": "4.1.0", + "System.Runtime": "4.1.0" } }, - "nethermind.evm": { - "type": "Project", + "System.Runtime": { + "type": "Transitive", + "resolved": "4.3.1", + "contentHash": "abhfv1dTK6NXOmu4bgHIONxHyEqFjW8HwXPmpY9gmll+ix9UNo4XDcmzJn6oLooftxNssVHdJC1pGT9jkSynQg==", "dependencies": { - "Nethermind.Core": "[1.36.0-unstable, )", - "Nethermind.Crypto": "[1.36.0-unstable, )", - "Nethermind.Serialization.Rlp": "[1.36.0-unstable, )", - "Nethermind.Specs": "[1.36.0-unstable, )" + "Microsoft.NETCore.Platforms": "1.1.1", + "Microsoft.NETCore.Targets": "1.1.3" } }, - "nethermind.evm.precompiles": { - "type": "Project", + "System.Runtime.CompilerServices.Unsafe": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "/iUeP3tq1S0XdNNoMz5C9twLSrM/TH+qElHkXWaPvuNOt+99G75NrV0OS2EqHx5wMN7popYjpc8oTjC1y16DLg==" + }, + "System.Runtime.Extensions": { + "type": "Transitive", + "resolved": "4.1.0", + "contentHash": "CUOHjTT/vgP0qGW22U4/hDlOqXmcPq5YicBaXdUR2UiUoLwBT+olO6we4DVbq57jeX5uXH2uerVZhf0qGj+sVQ==", "dependencies": { - "Nethermind.Core": "[1.36.0-unstable, )", - "Nethermind.Crypto": "[1.36.0-unstable, )", - "Nethermind.Crypto.Bls": "[1.0.5, )", - "Nethermind.Crypto.SecP256r1": "[1.0.0-preview.6, )", - "Nethermind.Evm": "[1.36.0-unstable, )", - "Nethermind.GmpBindings": "[1.0.3, )", - "Nethermind.MclBindings": "[1.0.3, )", - "Nethermind.Serialization.Rlp": "[1.36.0-unstable, )", - "Nethermind.Specs": "[1.36.0-unstable, )" + "Microsoft.NETCore.Platforms": "1.0.1", + "Microsoft.NETCore.Targets": "1.0.1", + "System.Runtime": "4.1.0" } }, - "nethermind.externalsigner.plugin": { - "type": "Project", + "System.Runtime.Handles": { + "type": "Transitive", + "resolved": "4.0.1", + "contentHash": "nCJvEKguXEvk2ymk1gqj625vVnlK3/xdGzx0vOKicQkoquaTBJTP13AIYkocSUwHCLNBwUbXTqTWGDxBTWpt7g==", "dependencies": { - "Nethermind.Api": "[1.36.0-unstable, )", - "Nethermind.JsonRpc": "[1.36.0-unstable, )" + "Microsoft.NETCore.Platforms": "1.0.1", + "Microsoft.NETCore.Targets": "1.0.1", + "System.Runtime": "4.1.0" } }, - "nethermind.facade": { - "type": "Project", + "System.Runtime.InteropServices": { + "type": "Transitive", + "resolved": "4.1.0", + "contentHash": "16eu3kjHS633yYdkjwShDHZLRNMKVi/s0bY8ODiqJ2RfMhDMAwxZaUaWVnZ2P71kr/or+X9o/xFWtNqz8ivieQ==", "dependencies": { - "Nethermind.Consensus": "[1.36.0-unstable, )", - "Nethermind.Core": "[1.36.0-unstable, )", - "Nethermind.Crypto": "[1.36.0-unstable, )", - "Nethermind.Synchronization": "[1.36.0-unstable, )", - "NonBlocking": "[2.1.2, )" + "Microsoft.NETCore.Platforms": "1.0.1", + "Microsoft.NETCore.Targets": "1.0.1", + "System.Reflection": "4.1.0", + "System.Reflection.Primitives": "4.0.1", + "System.Runtime": "4.1.0", + "System.Runtime.Handles": "4.0.1" } }, - "nethermind.flashbots": { - "type": "Project", + "System.Security.AccessControl": { + "type": "Transitive", + "resolved": "5.0.0", + "contentHash": "dagJ1mHZO3Ani8GH0PHpPEe/oYO+rVdbQjvjJkBRNQkX4t0r1iaeGn8+/ybkSLEan3/slM0t59SVdHzuHf2jmw==", "dependencies": { - "Nethermind.Merge.Plugin": "[1.36.0-unstable, )" + "Microsoft.NETCore.Platforms": "5.0.0", + "System.Security.Principal.Windows": "5.0.0" } }, - "nethermind.grpc": { - "type": "Project", - "dependencies": { + "System.Security.Cryptography.Pkcs": { + "type": "Transitive", + "resolved": "10.0.0", + "contentHash": "UPWqLSygJlFerRi9XNIuM0a1VC8gHUIufyP24xQ0sc+XimqUAEcjpOz9DhKpyDjH+5B/wO3RpC0KpkEeDj/ddg==" + }, + "System.Security.Cryptography.Xml": { + "type": "Transitive", + "resolved": "10.0.0", + "contentHash": "Vb1vrLKtg0icvKk0GCzCErclNRH6NB7TMZ9BgXH9xqQ0lFlByQeoVwbewteJDuOPh1774GuVLD7rQrxhrpCCuA==", + "dependencies": { + "System.Security.Cryptography.Pkcs": "10.0.0" + } + }, + "System.Security.Principal.Windows": { + "type": "Transitive", + "resolved": "5.0.0", + "contentHash": "t0MGLukB5WAVU9bO3MGzvlGnyJPgUlcwerXn1kzBRjwLKixT96XV0Uza41W49gVd8zEMFu9vQEFlv0IOrytICA==" + }, + "System.Text.Encoding": { + "type": "Transitive", + "resolved": "4.0.11", + "contentHash": "U3gGeMlDZXxCEiY4DwVLSacg+DFWCvoiX+JThA/rvw37Sqrku7sEFeVBBBMBnfB6FeZHsyDx85HlKL19x0HtZA==", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.0.1", + "Microsoft.NETCore.Targets": "1.0.1", + "System.Runtime": "4.1.0" + } + }, + "System.Text.Encoding.CodePages": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "ZFCILZuOvtKPauZ/j/swhvw68ZRi9ATCfvGbk1QfydmcXBkIWecWKn/250UH7rahZ5OoDBaiAudJtPvLwzw85A==", + "dependencies": { + "System.Runtime.CompilerServices.Unsafe": "6.0.0" + } + }, + "System.Text.RegularExpressions": { + "type": "Transitive", + "resolved": "4.3.1", + "contentHash": "N0kNRrWe4+nXOWlpLT4LAY5brb8caNFlUuIRpraCVMDLYutKkol1aV079rQjLuSxKMJT2SpBQsYX9xbcTMmzwg==", + "dependencies": { + "System.Runtime": "4.3.1" + } + }, + "System.Threading": { + "type": "Transitive", + "resolved": "4.0.11", + "contentHash": "N+3xqIcg3VDKyjwwCGaZ9HawG9aC6cSDI+s7ROma310GQo8vilFZa86hqKppwTHleR/G0sfOzhvgnUxWCR/DrQ==", + "dependencies": { + "System.Runtime": "4.1.0", + "System.Threading.Tasks": "4.0.11" + } + }, + "System.Threading.Channels": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "TY8/9+tI0mNaUMgntOxxaq2ndTkdXqLSxvPmas7XEqOlv9lQtB7wLjYGd756lOaO7Dvb5r/WXhluM+0Xe87v5Q==" + }, + "System.Threading.Tasks": { + "type": "Transitive", + "resolved": "4.0.11", + "contentHash": "k1S4Gc6IGwtHGT8188RSeGaX86Qw/wnrgNLshJvsdNUOPP9etMmo8S07c+UlOAx4K/xLuN9ivA1bD0LVurtIxQ==", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.0.1", + "Microsoft.NETCore.Targets": "1.0.1", + "System.Runtime": "4.1.0" + } + }, + "System.Threading.Tasks.Dataflow": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "7V0I8tPa9V7UxMx/+7DIwkhls5ouaEMQx6l/GwGm1Y8kJQ61On9B/PxCXFLbgu5/C47g0BP2CUYs+nMv1+Oaqw==" + }, + "System.ValueTuple": { + "type": "Transitive", + "resolved": "4.4.0", + "contentHash": "BahUww/+mdP4ARCAh2RQhQTg13wYLVrBb9SYVgW8ZlrwjraGCXHGjo0oIiUfZ34LUZkMMR+RAzR7dEY4S1HeQQ==" + }, + "Testably.Abstractions.FileSystem.Interface": { + "type": "Transitive", + "resolved": "9.0.0", + "contentHash": "uksk86YlnzAdyfVNu3wICU0X5iXVe9LF7Q3UkngNliHWEvM5gvAlOUr+jmd9JwmbJWISH5+i1vyXE02lEVz7WQ==" + }, + "Tmds.LibC": { + "type": "Transitive", + "resolved": "0.2.0", + "contentHash": "+RvLuNHOLW7cxzgDe9yHLoayBgjsuH2/gJtJnuVMxweKrxxYT6TwQNAmt06SFWpjwk68aRcwwD4FfMMA6tZvVA==" + }, + "YamlDotNet": { + "type": "Transitive", + "resolved": "16.3.0", + "contentHash": "SgMOdxbz8X65z8hraIs6hOEdnkH6hESTAIUa7viEngHOYaH+6q5XJmwr1+yb9vJpNQ19hCQY69xbFsLtXpobQA==" + }, + "nethermind.abi": { + "type": "Project", + "dependencies": { + "MathNet.Numerics.FSharp": "[5.0.0, )", + "Nethermind.Core": "[1.37.0-unstable, )" + } + }, + "nethermind.api": { + "type": "Project", + "dependencies": { + "Nethermind.Blockchain": "[1.37.0-unstable, )", + "Nethermind.Facade": "[1.37.0-unstable, )", + "Nethermind.Grpc": "[1.37.0-unstable, )", + "Nethermind.History": "[1.37.0-unstable, )", + "Nethermind.JsonRpc": "[1.37.0-unstable, )", + "Nethermind.Monitoring": "[1.37.0-unstable, )", + "Nethermind.Network": "[1.37.0-unstable, )", + "Nethermind.Sockets": "[1.37.0-unstable, )" + } + }, + "nethermind.blockchain": { + "type": "Project", + "dependencies": { + "Microsoft.ClearScript.V8.Native.linux-arm64": "[7.5.0, )", + "Microsoft.ClearScript.V8.Native.linux-x64": "[7.5.0, )", + "Microsoft.ClearScript.V8.Native.osx-arm64": "[7.5.0, )", + "Microsoft.ClearScript.V8.Native.osx-x64": "[7.5.0, )", + "Microsoft.ClearScript.V8.Native.win-x64": "[7.5.0, )", + "Nethermind.Abi": "[1.37.0-unstable, )", + "Nethermind.Core": "[1.37.0-unstable, )", + "Nethermind.Db": "[1.37.0-unstable, )", + "Nethermind.Evm": "[1.37.0-unstable, )", + "Nethermind.Evm.Precompiles": "[1.37.0-unstable, )", + "Nethermind.Network.Stats": "[1.37.0-unstable, )", + "Nethermind.Specs": "[1.37.0-unstable, )", + "Nethermind.State": "[1.37.0-unstable, )", + "Nethermind.TxPool": "[1.37.0-unstable, )", + "Polly": "[8.6.4, )" + } + }, + "nethermind.config": { + "type": "Project", + "dependencies": { + "Nethermind.Core": "[1.37.0-unstable, )", + "NonBlocking": "[2.1.2, )", + "System.Configuration.ConfigurationManager": "[10.0.0, )" + } + }, + "nethermind.consensus": { + "type": "Project", + "dependencies": { + "Nethermind.Blockchain": "[1.37.0-unstable, )", + "Nethermind.Config": "[1.37.0-unstable, )", + "Nethermind.Core": "[1.37.0-unstable, )", + "Nethermind.Crypto": "[1.37.0-unstable, )", + "Nethermind.Evm": "[1.37.0-unstable, )", + "Nethermind.TxPool": "[1.37.0-unstable, )" + } + }, + "nethermind.consensus.aura": { + "type": "Project", + "dependencies": { + "BouncyCastle.Cryptography": "[2.6.2, )", + "Nethermind.Abi": "[1.37.0-unstable, )", + "Nethermind.Api": "[1.37.0-unstable, )", + "Nethermind.Blockchain": "[1.37.0-unstable, )", + "Nethermind.Facade": "[1.37.0-unstable, )", + "Nethermind.Init": "[1.37.0-unstable, )", + "Nethermind.Specs": "[1.37.0-unstable, )", + "Nethermind.Synchronization": "[1.37.0-unstable, )", + "Nito.Collections.Deque": "[1.2.1, )" + } + }, + "nethermind.consensus.clique": { + "type": "Project", + "dependencies": { + "Nethermind.Api": "[1.37.0-unstable, )", + "Nethermind.Blockchain": "[1.37.0-unstable, )", + "Nethermind.Consensus": "[1.37.0-unstable, )", + "Nethermind.JsonRpc": "[1.37.0-unstable, )" + } + }, + "nethermind.consensus.ethash": { + "type": "Project", + "dependencies": { + "Nethermind.Api": "[1.37.0-unstable, )", + "Nethermind.Blockchain": "[1.37.0-unstable, )", + "Nethermind.Consensus": "[1.37.0-unstable, )", + "Nethermind.Core": "[1.37.0-unstable, )", + "Nethermind.Crypto": "[1.37.0-unstable, )", + "Nethermind.Serialization.Rlp": "[1.37.0-unstable, )", + "Nethermind.Specs": "[1.37.0-unstable, )" + } + }, + "nethermind.core": { + "type": "Project", + "dependencies": { + "Autofac": "[9.0.0, )", + "Autofac.Extensions.DependencyInjection": "[10.0.0, )", + "FastEnum": "[2.0.6, )", + "Microsoft.Extensions.DependencyInjection": "[10.0.0, )", + "Microsoft.Extensions.ObjectPool": "[10.0.0, )", + "Microsoft.IO.RecyclableMemoryStream": "[3.0.1, )", + "Microsoft.IdentityModel.JsonWebTokens": "[8.15.0, )", + "Nethermind.Crypto.SecP256k1": "[1.5.0, )", + "Nethermind.Logging": "[1.37.0-unstable, )", + "Nethermind.Numerics.Int256": "[1.3.6, )", + "NonBlocking": "[2.1.2, )", + "TestableIO.System.IO.Abstractions.Wrappers": "[22.0.16, )", + "prometheus-net": "[8.2.1, )" + } + }, + "nethermind.crypto": { + "type": "Project", + "dependencies": { + "BouncyCastle.Cryptography": "[2.6.2, )", + "Ckzg.Bindings": "[2.1.5.1542, )", + "Microsoft.AspNetCore.DataProtection": "[10.0.0, )", + "Microsoft.AspNetCore.DataProtection.Extensions": "[10.0.0, )", + "Nethermind.Core": "[1.37.0-unstable, )", + "Nethermind.Crypto.Bls": "[1.0.5, )", + "Nethermind.Serialization.Rlp": "[1.37.0-unstable, )", + "System.Security.Cryptography.ProtectedData": "[10.0.0, )" + } + }, + "nethermind.db": { + "type": "Project", + "dependencies": { + "Nethermind.Config": "[1.37.0-unstable, )", + "Nethermind.Serialization.Rlp": "[1.37.0-unstable, )", + "NonBlocking": "[2.1.2, )" + } + }, + "nethermind.db.rocks": { + "type": "Project", + "dependencies": { + "ConcurrentHashSet": "[1.3.0, )", + "Nethermind.Api": "[1.37.0-unstable, )", + "Nethermind.Db": "[1.37.0-unstable, )", + "NonBlocking": "[2.1.2, )", + "RocksDB": "[10.4.2.62659, 10.4.2.62659]" + } + }, + "nethermind.db.rpc": { + "type": "Project", + "dependencies": { + "Nethermind.Db": "[1.37.0-unstable, )", + "Nethermind.JsonRpc": "[1.37.0-unstable, )", + "Nethermind.Serialization.Json": "[1.37.0-unstable, )", + "Nethermind.State": "[1.37.0-unstable, )" + } + }, + "nethermind.era1": { + "type": "Project", + "dependencies": { + "CommunityToolkit.HighPerformance": "[8.4.0, )", + "Nethermind.Api": "[1.37.0-unstable, )", + "Nethermind.Blockchain": "[1.37.0-unstable, )", + "Nethermind.Consensus": "[1.37.0-unstable, )", + "Nethermind.Core": "[1.37.0-unstable, )", + "Nethermind.JsonRpc": "[1.37.0-unstable, )", + "Nethermind.Merkleization": "[1.37.0-unstable, )", + "Nethermind.Serialization.Rlp": "[1.37.0-unstable, )", + "Nethermind.Serialization.Ssz": "[1.37.0-unstable, )", + "Nethermind.State": "[1.37.0-unstable, )", + "Snappier": "[1.2.0, )" + } + }, + "nethermind.ethstats": { + "type": "Project", + "dependencies": { + "Nethermind.Api": "[1.37.0-unstable, )", + "Nethermind.Blockchain": "[1.37.0-unstable, )", + "Nethermind.Core": "[1.37.0-unstable, )", + "Nethermind.Init": "[1.37.0-unstable, )", + "Nethermind.JsonRpc": "[1.37.0-unstable, )", + "Nethermind.Logging": "[1.37.0-unstable, )", + "Nethermind.Network": "[1.37.0-unstable, )", + "Websocket.Client": "[5.3.0, )" + } + }, + "nethermind.evm": { + "type": "Project", + "dependencies": { + "Nethermind.Core": "[1.37.0-unstable, )", + "Nethermind.Crypto": "[1.37.0-unstable, )", + "Nethermind.Serialization.Rlp": "[1.37.0-unstable, )", + "Nethermind.Specs": "[1.37.0-unstable, )" + } + }, + "nethermind.evm.precompiles": { + "type": "Project", + "dependencies": { + "Nethermind.Core": "[1.37.0-unstable, )", + "Nethermind.Crypto": "[1.37.0-unstable, )", + "Nethermind.Crypto.Bls": "[1.0.5, )", + "Nethermind.Crypto.SecP256r1": "[1.0.0-preview.6, )", + "Nethermind.Evm": "[1.37.0-unstable, )", + "Nethermind.GmpBindings": "[1.0.3, )", + "Nethermind.MclBindings": "[1.0.3, )", + "Nethermind.Serialization.Rlp": "[1.37.0-unstable, )", + "Nethermind.Specs": "[1.37.0-unstable, )" + } + }, + "nethermind.externalsigner.plugin": { + "type": "Project", + "dependencies": { + "Nethermind.Api": "[1.37.0-unstable, )", + "Nethermind.JsonRpc": "[1.37.0-unstable, )" + } + }, + "nethermind.facade": { + "type": "Project", + "dependencies": { + "Nethermind.Consensus": "[1.37.0-unstable, )", + "Nethermind.Core": "[1.37.0-unstable, )", + "Nethermind.Crypto": "[1.37.0-unstable, )", + "Nethermind.Synchronization": "[1.37.0-unstable, )", + "NonBlocking": "[2.1.2, )" + } + }, + "nethermind.flashbots": { + "type": "Project", + "dependencies": { + "Nethermind.Merge.Plugin": "[1.37.0-unstable, )" + } + }, + "nethermind.grpc": { + "type": "Project", + "dependencies": { "Google.Protobuf": "[3.33.1, )", "Google.Protobuf.Tools": "[3.33.1, )", "Grpc": "[2.46.6, )", - "Nethermind.Config": "[1.36.0-unstable, )", - "Nethermind.Core": "[1.36.0-unstable, )", - "Nethermind.Serialization.Json": "[1.36.0-unstable, )" + "Nethermind.Config": "[1.37.0-unstable, )", + "Nethermind.Core": "[1.37.0-unstable, )", + "Nethermind.Serialization.Json": "[1.37.0-unstable, )" } }, "nethermind.healthchecks": { @@ -857,74 +1477,74 @@ "AspNetCore.HealthChecks.UI": "[9.0.0, )", "AspNetCore.HealthChecks.UI.InMemory.Storage": "[9.0.0, )", "KubernetesClient": "[18.0.5, )", - "Nethermind.Api": "[1.36.0-unstable, )", - "Nethermind.Merge.Plugin": "[1.36.0-unstable, )" + "Nethermind.Api": "[1.37.0-unstable, )", + "Nethermind.Merge.Plugin": "[1.37.0-unstable, )" } }, "nethermind.history": { "type": "Project", "dependencies": { - "Nethermind.Consensus": "[1.36.0-unstable, )" + "Nethermind.Consensus": "[1.37.0-unstable, )" } }, "nethermind.hive": { "type": "Project", "dependencies": { - "Nethermind.Api": "[1.36.0-unstable, )", - "Nethermind.Init": "[1.36.0-unstable, )" + "Nethermind.Api": "[1.37.0-unstable, )", + "Nethermind.Init": "[1.37.0-unstable, )" } }, "nethermind.init": { "type": "Project", "dependencies": { - "Nethermind.Api": "[1.36.0-unstable, )", - "Nethermind.Db.Rocks": "[1.36.0-unstable, )", - "Nethermind.Db.Rpc": "[1.36.0-unstable, )", - "Nethermind.Era1": "[1.36.0-unstable, )", - "Nethermind.Network.Discovery": "[1.36.0-unstable, )", - "Nethermind.Network.Dns": "[1.36.0-unstable, )", - "Nethermind.Network.Enr": "[1.36.0-unstable, )", - "Nethermind.Specs": "[1.36.0-unstable, )" + "Nethermind.Api": "[1.37.0-unstable, )", + "Nethermind.Db.Rocks": "[1.37.0-unstable, )", + "Nethermind.Db.Rpc": "[1.37.0-unstable, )", + "Nethermind.Era1": "[1.37.0-unstable, )", + "Nethermind.Network.Discovery": "[1.37.0-unstable, )", + "Nethermind.Network.Dns": "[1.37.0-unstable, )", + "Nethermind.Network.Enr": "[1.37.0-unstable, )", + "Nethermind.Specs": "[1.37.0-unstable, )" } }, "nethermind.init.snapshot": { "type": "Project", "dependencies": { - "Nethermind.Api": "[1.36.0-unstable, )", - "Nethermind.Init": "[1.36.0-unstable, )" + "Nethermind.Api": "[1.37.0-unstable, )", + "Nethermind.Init": "[1.37.0-unstable, )" } }, "nethermind.jsonrpc": { "type": "Project", "dependencies": { - "Nethermind.Abi": "[1.36.0-unstable, )", - "Nethermind.Blockchain": "[1.36.0-unstable, )", - "Nethermind.Config": "[1.36.0-unstable, )", - "Nethermind.Consensus": "[1.36.0-unstable, )", - "Nethermind.Core": "[1.36.0-unstable, )", - "Nethermind.Crypto": "[1.36.0-unstable, )", - "Nethermind.Evm": "[1.36.0-unstable, )", - "Nethermind.Facade": "[1.36.0-unstable, )", - "Nethermind.Network.Dns": "[1.36.0-unstable, )", - "Nethermind.Sockets": "[1.36.0-unstable, )", - "Nethermind.Synchronization": "[1.36.0-unstable, )", - "Nethermind.Wallet": "[1.36.0-unstable, )" + "Nethermind.Abi": "[1.37.0-unstable, )", + "Nethermind.Blockchain": "[1.37.0-unstable, )", + "Nethermind.Config": "[1.37.0-unstable, )", + "Nethermind.Consensus": "[1.37.0-unstable, )", + "Nethermind.Core": "[1.37.0-unstable, )", + "Nethermind.Crypto": "[1.37.0-unstable, )", + "Nethermind.Evm": "[1.37.0-unstable, )", + "Nethermind.Facade": "[1.37.0-unstable, )", + "Nethermind.Network.Dns": "[1.37.0-unstable, )", + "Nethermind.Sockets": "[1.37.0-unstable, )", + "Nethermind.Synchronization": "[1.37.0-unstable, )", + "Nethermind.Wallet": "[1.37.0-unstable, )" } }, "nethermind.jsonrpc.tracestore": { "type": "Project", "dependencies": { - "Nethermind.Api": "[1.36.0-unstable, )", - "Nethermind.Init": "[1.36.0-unstable, )" + "Nethermind.Api": "[1.37.0-unstable, )", + "Nethermind.Init": "[1.37.0-unstable, )" } }, "nethermind.keystore": { "type": "Project", "dependencies": { - "Nethermind.Config": "[1.36.0-unstable, )", - "Nethermind.Core": "[1.36.0-unstable, )", - "Nethermind.Crypto": "[1.36.0-unstable, )", - "Nethermind.Serialization.Json": "[1.36.0-unstable, )", + "Nethermind.Config": "[1.37.0-unstable, )", + "Nethermind.Core": "[1.37.0-unstable, )", + "Nethermind.Crypto": "[1.37.0-unstable, )", + "Nethermind.Serialization.Json": "[1.37.0-unstable, )", "SCrypt": "[2.0.0.2, )" } }, @@ -935,41 +1555,41 @@ "type": "Project", "dependencies": { "NLog": "[5.5.1, )", - "Nethermind.Logging": "[1.36.0-unstable, )" + "Nethermind.Logging": "[1.37.0-unstable, )" } }, "nethermind.merge.aura": { "type": "Project", "dependencies": { - "Nethermind.Blockchain": "[1.36.0-unstable, )", - "Nethermind.Consensus": "[1.36.0-unstable, )", - "Nethermind.Consensus.AuRa": "[1.36.0-unstable, )", - "Nethermind.Core": "[1.36.0-unstable, )", - "Nethermind.Db": "[1.36.0-unstable, )", - "Nethermind.Evm": "[1.36.0-unstable, )", - "Nethermind.Merge.Plugin": "[1.36.0-unstable, )", - "Nethermind.Specs": "[1.36.0-unstable, )", - "Nethermind.State": "[1.36.0-unstable, )" + "Nethermind.Blockchain": "[1.37.0-unstable, )", + "Nethermind.Consensus": "[1.37.0-unstable, )", + "Nethermind.Consensus.AuRa": "[1.37.0-unstable, )", + "Nethermind.Core": "[1.37.0-unstable, )", + "Nethermind.Db": "[1.37.0-unstable, )", + "Nethermind.Evm": "[1.37.0-unstable, )", + "Nethermind.Merge.Plugin": "[1.37.0-unstable, )", + "Nethermind.Specs": "[1.37.0-unstable, )", + "Nethermind.State": "[1.37.0-unstable, )" } }, "nethermind.merge.plugin": { "type": "Project", "dependencies": { - "Nethermind.Api": "[1.36.0-unstable, )" + "Nethermind.Api": "[1.37.0-unstable, )" } }, "nethermind.merkleization": { "type": "Project", "dependencies": { - "Nethermind.Core": "[1.36.0-unstable, )", - "Nethermind.Serialization.Ssz": "[1.36.0-unstable, )" + "Nethermind.Core": "[1.37.0-unstable, )", + "Nethermind.Serialization.Ssz": "[1.37.0-unstable, )" } }, "nethermind.monitoring": { "type": "Project", "dependencies": { - "Nethermind.Config": "[1.36.0-unstable, )", - "Nethermind.Logging": "[1.36.0-unstable, )", + "Nethermind.Config": "[1.37.0-unstable, )", + "Nethermind.Logging": "[1.37.0-unstable, )", "prometheus-net.AspNetCore": "[8.2.1, )" } }, @@ -977,31 +1597,32 @@ "type": "Project", "dependencies": { "Crc32.NET": "[1.2.0, )", - "Nethermind.Blockchain": "[1.36.0-unstable, )", - "Nethermind.Config": "[1.36.0-unstable, )", - "Nethermind.Core": "[1.36.0-unstable, )", - "Nethermind.Crypto": "[1.36.0-unstable, )", + "Nethermind.Blockchain": "[1.37.0-unstable, )", + "Nethermind.Config": "[1.37.0-unstable, )", + "Nethermind.Core": "[1.37.0-unstable, )", + "Nethermind.Crypto": "[1.37.0-unstable, )", "Nethermind.DotNetty.Handlers": "[1.0.2.76, )", - "Nethermind.Network.Contract": "[1.36.0-unstable, )", - "Nethermind.Network.Stats": "[1.36.0-unstable, )", - "Nethermind.Synchronization": "[1.36.0-unstable, )", + "Nethermind.Network.Contract": "[1.37.0-unstable, )", + "Nethermind.Network.Stats": "[1.37.0-unstable, )", + "Nethermind.Synchronization": "[1.37.0-unstable, )", "Snappier": "[1.2.0, )" } }, "nethermind.network.contract": { "type": "Project", "dependencies": { - "Nethermind.Config": "[1.36.0-unstable, )" + "Nethermind.Config": "[1.37.0-unstable, )" } }, "nethermind.network.discovery": { "type": "Project", "dependencies": { - "Nethermind.Api": "[1.36.0-unstable, )", - "Nethermind.Crypto": "[1.36.0-unstable, )", - "Nethermind.Facade": "[1.36.0-unstable, )", - "Nethermind.Network": "[1.36.0-unstable, )", - "Nethermind.Network.Enr": "[1.36.0-unstable, )", + "Microsoft.Extensions.Caching.Memory": "[10.0.0, )", + "Nethermind.Api": "[1.37.0-unstable, )", + "Nethermind.Crypto": "[1.37.0-unstable, )", + "Nethermind.Facade": "[1.37.0-unstable, )", + "Nethermind.Network": "[1.37.0-unstable, )", + "Nethermind.Network.Enr": "[1.37.0-unstable, )", "PierTwo.Lantern.Discv5.WireProtocol": "[1.0.0-preview.6, )" } }, @@ -1009,147 +1630,149 @@ "type": "Project", "dependencies": { "DnsClient": "[1.8.0, )", - "Nethermind.Network": "[1.36.0-unstable, )", - "Nethermind.Network.Enr": "[1.36.0-unstable, )" + "Nethermind.Network": "[1.37.0-unstable, )", + "Nethermind.Network.Enr": "[1.37.0-unstable, )" } }, "nethermind.network.enr": { "type": "Project", "dependencies": { - "Nethermind.Crypto": "[1.36.0-unstable, )", - "Nethermind.Network": "[1.36.0-unstable, )" + "Nethermind.Crypto": "[1.37.0-unstable, )", + "Nethermind.Network": "[1.37.0-unstable, )" } }, "nethermind.network.stats": { "type": "Project", "dependencies": { - "Nethermind.Config": "[1.36.0-unstable, )", - "Nethermind.Core": "[1.36.0-unstable, )", - "Nethermind.Logging": "[1.36.0-unstable, )", - "Nethermind.Network.Contract": "[1.36.0-unstable, )" + "Nethermind.Config": "[1.37.0-unstable, )", + "Nethermind.Core": "[1.37.0-unstable, )", + "Nethermind.Logging": "[1.37.0-unstable, )", + "Nethermind.Network.Contract": "[1.37.0-unstable, )" } }, "nethermind.optimism": { "type": "Project", "dependencies": { "Google.Protobuf": "[3.33.1, )", - "Nethermind.Api": "[1.36.0-unstable, )", - "Nethermind.Blockchain": "[1.36.0-unstable, )", - "Nethermind.Consensus": "[1.36.0-unstable, )", - "Nethermind.Core": "[1.36.0-unstable, )", - "Nethermind.Init": "[1.36.0-unstable, )", - "Nethermind.JsonRpc": "[1.36.0-unstable, )", + "Nethermind.Api": "[1.37.0-unstable, )", + "Nethermind.Blockchain": "[1.37.0-unstable, )", + "Nethermind.Consensus": "[1.37.0-unstable, )", + "Nethermind.Core": "[1.37.0-unstable, )", + "Nethermind.Init": "[1.37.0-unstable, )", + "Nethermind.JsonRpc": "[1.37.0-unstable, )", "Nethermind.Libp2p": "[1.0.0-preview.45, )", "Nethermind.Libp2p.Protocols.PubsubPeerDiscovery": "[1.0.0-preview.45, )", - "Nethermind.Merge.Plugin": "[1.36.0-unstable, )", + "Nethermind.Merge.Plugin": "[1.37.0-unstable, )", "Snappier": "[1.2.0, )" } }, "nethermind.seq": { "type": "Project", "dependencies": { - "Nethermind.Config": "[1.36.0-unstable, )" + "Nethermind.Config": "[1.37.0-unstable, )" } }, "nethermind.serialization.json": { "type": "Project", "dependencies": { "Microsoft.ClearScript.V8": "[7.5.0, )", - "Nethermind.Core": "[1.36.0-unstable, )" + "Nethermind.Core": "[1.37.0-unstable, )" } }, "nethermind.serialization.rlp": { "type": "Project", "dependencies": { - "Nethermind.Core": "[1.36.0-unstable, )", + "Nethermind.Core": "[1.37.0-unstable, )", "Nethermind.DotNetty.Buffers": "[1.0.2.76, )" } }, "nethermind.serialization.ssz": { "type": "Project", "dependencies": { - "Nethermind.Core": "[1.36.0-unstable, )" + "Nethermind.Core": "[1.37.0-unstable, )" } }, "nethermind.shutter": { "type": "Project", "dependencies": { "Google.Protobuf": "[3.33.1, )", - "Nethermind.Blockchain": "[1.36.0-unstable, )", - "Nethermind.Consensus": "[1.36.0-unstable, )", - "Nethermind.Core": "[1.36.0-unstable, )", - "Nethermind.Crypto": "[1.36.0-unstable, )", - "Nethermind.Init": "[1.36.0-unstable, )", + "Nethermind.Blockchain": "[1.37.0-unstable, )", + "Nethermind.Consensus": "[1.37.0-unstable, )", + "Nethermind.Core": "[1.37.0-unstable, )", + "Nethermind.Crypto": "[1.37.0-unstable, )", + "Nethermind.Init": "[1.37.0-unstable, )", "Nethermind.Libp2p": "[1.0.0-preview.45, )", "Nethermind.Libp2p.Protocols.PubsubPeerDiscovery": "[1.0.0-preview.45, )", - "Nethermind.Merge.Plugin": "[1.36.0-unstable, )", - "Nethermind.Merkleization": "[1.36.0-unstable, )", - "Nethermind.Network.Discovery": "[1.36.0-unstable, )", - "Nethermind.Serialization.Ssz": "[1.36.0-unstable, )", - "Nethermind.Specs": "[1.36.0-unstable, )" + "Nethermind.Merge.Plugin": "[1.37.0-unstable, )", + "Nethermind.Merkleization": "[1.37.0-unstable, )", + "Nethermind.Network.Discovery": "[1.37.0-unstable, )", + "Nethermind.Serialization.Ssz": "[1.37.0-unstable, )", + "Nethermind.Specs": "[1.37.0-unstable, )" } }, "nethermind.sockets": { "type": "Project", "dependencies": { - "Nethermind.Core": "[1.36.0-unstable, )", - "Nethermind.Logging": "[1.36.0-unstable, )", - "Nethermind.Serialization.Json": "[1.36.0-unstable, )" + "Nethermind.Core": "[1.37.0-unstable, )", + "Nethermind.Logging": "[1.37.0-unstable, )", + "Nethermind.Serialization.Json": "[1.37.0-unstable, )" } }, "nethermind.specs": { "type": "Project", "dependencies": { - "Nethermind.Config": "[1.36.0-unstable, )", - "Nethermind.Core": "[1.36.0-unstable, )", - "Nethermind.Serialization.Json": "[1.36.0-unstable, )", + "Nethermind.Config": "[1.37.0-unstable, )", + "Nethermind.Core": "[1.37.0-unstable, )", + "Nethermind.Serialization.Json": "[1.37.0-unstable, )", "ZstdSharp.Port": "[0.8.6, )" } }, "nethermind.state": { "type": "Project", "dependencies": { - "Nethermind.Core": "[1.36.0-unstable, )", - "Nethermind.Db": "[1.36.0-unstable, )", - "Nethermind.Evm": "[1.36.0-unstable, )", - "Nethermind.Serialization.Rlp": "[1.36.0-unstable, )", - "Nethermind.Trie": "[1.36.0-unstable, )" + "Nethermind.Core": "[1.37.0-unstable, )", + "Nethermind.Db": "[1.37.0-unstable, )", + "Nethermind.Evm": "[1.37.0-unstable, )", + "Nethermind.Serialization.Rlp": "[1.37.0-unstable, )", + "Nethermind.Trie": "[1.37.0-unstable, )", + "ZstdSharp.Port": "[0.8.6, )", + "prometheus-net": "[8.2.1, )" } }, "nethermind.synchronization": { "type": "Project", "dependencies": { "ConcurrentHashSet": "[1.3.0, )", - "Nethermind.Consensus": "[1.36.0-unstable, )", - "Nethermind.History": "[1.36.0-unstable, )", - "Nethermind.Logging": "[1.36.0-unstable, )", - "Nethermind.Network.Contract": "[1.36.0-unstable, )", - "Nethermind.Trie": "[1.36.0-unstable, )", + "Nethermind.Consensus": "[1.37.0-unstable, )", + "Nethermind.History": "[1.37.0-unstable, )", + "Nethermind.Logging": "[1.37.0-unstable, )", + "Nethermind.Network.Contract": "[1.37.0-unstable, )", + "Nethermind.Trie": "[1.37.0-unstable, )", "NonBlocking": "[2.1.2, )" } }, "nethermind.taiko": { "type": "Project", "dependencies": { - "Nethermind.Api": "[1.36.0-unstable, )", - "Nethermind.Blockchain": "[1.36.0-unstable, )", - "Nethermind.Consensus": "[1.36.0-unstable, )", - "Nethermind.Core": "[1.36.0-unstable, )", - "Nethermind.Evm": "[1.36.0-unstable, )", - "Nethermind.Evm.Precompiles": "[1.36.0-unstable, )", - "Nethermind.Init": "[1.36.0-unstable, )", - "Nethermind.JsonRpc": "[1.36.0-unstable, )", - "Nethermind.Logging": "[1.36.0-unstable, )", - "Nethermind.Merge.Plugin": "[1.36.0-unstable, )", - "Nethermind.Serialization.Json": "[1.36.0-unstable, )" + "Nethermind.Api": "[1.37.0-unstable, )", + "Nethermind.Blockchain": "[1.37.0-unstable, )", + "Nethermind.Consensus": "[1.37.0-unstable, )", + "Nethermind.Core": "[1.37.0-unstable, )", + "Nethermind.Evm": "[1.37.0-unstable, )", + "Nethermind.Evm.Precompiles": "[1.37.0-unstable, )", + "Nethermind.Init": "[1.37.0-unstable, )", + "Nethermind.JsonRpc": "[1.37.0-unstable, )", + "Nethermind.Logging": "[1.37.0-unstable, )", + "Nethermind.Merge.Plugin": "[1.37.0-unstable, )", + "Nethermind.Serialization.Json": "[1.37.0-unstable, )" } }, "nethermind.trie": { "type": "Project", "dependencies": { - "Nethermind.Core": "[1.36.0-unstable, )", - "Nethermind.Db": "[1.36.0-unstable, )", - "Nethermind.Serialization.Rlp": "[1.36.0-unstable, )", + "Nethermind.Core": "[1.37.0-unstable, )", + "Nethermind.Db": "[1.37.0-unstable, )", + "Nethermind.Serialization.Rlp": "[1.37.0-unstable, )", "NonBlocking": "[2.1.2, )" } }, @@ -1157,39 +1780,39 @@ "type": "Project", "dependencies": { "Collections.Pooled": "[1.0.82, )", - "Nethermind.Config": "[1.36.0-unstable, )", - "Nethermind.Core": "[1.36.0-unstable, )", - "Nethermind.Crypto": "[1.36.0-unstable, )", - "Nethermind.Db": "[1.36.0-unstable, )", - "Nethermind.Evm": "[1.36.0-unstable, )", - "Nethermind.Network.Contract": "[1.36.0-unstable, )", - "Nethermind.State": "[1.36.0-unstable, )", + "Nethermind.Config": "[1.37.0-unstable, )", + "Nethermind.Core": "[1.37.0-unstable, )", + "Nethermind.Crypto": "[1.37.0-unstable, )", + "Nethermind.Db": "[1.37.0-unstable, )", + "Nethermind.Evm": "[1.37.0-unstable, )", + "Nethermind.Network.Contract": "[1.37.0-unstable, )", + "Nethermind.State": "[1.37.0-unstable, )", "NonBlocking": "[2.1.2, )" } }, "nethermind.upnp.plugin": { "type": "Project", "dependencies": { - "Nethermind.Api": "[1.36.0-unstable, )", + "Nethermind.Api": "[1.37.0-unstable, )", "Open.NAT.Core": "[2.1.0.5, )" } }, "nethermind.wallet": { "type": "Project", "dependencies": { - "Nethermind.Core": "[1.36.0-unstable, )", - "Nethermind.KeyStore": "[1.36.0-unstable, )", - "Nethermind.Serialization.Rlp": "[1.36.0-unstable, )", - "Nethermind.TxPool": "[1.36.0-unstable, )" + "Nethermind.Core": "[1.37.0-unstable, )", + "Nethermind.KeyStore": "[1.37.0-unstable, )", + "Nethermind.Serialization.Rlp": "[1.37.0-unstable, )", + "Nethermind.TxPool": "[1.37.0-unstable, )" } }, "nethermind.xdc": { "type": "Project", "dependencies": { - "Nethermind.Api": "[1.36.0-unstable, )", - "Nethermind.Consensus": "[1.36.0-unstable, )", - "Nethermind.Core": "[1.36.0-unstable, )", - "Nethermind.Init": "[1.36.0-unstable, )" + "Nethermind.Api": "[1.37.0-unstable, )", + "Nethermind.Consensus": "[1.37.0-unstable, )", + "Nethermind.Core": "[1.37.0-unstable, )", + "Nethermind.Init": "[1.37.0-unstable, )" } }, "AspNetCore.HealthChecks.UI": { @@ -1200,7 +1823,8 @@ "dependencies": { "AspNetCore.HealthChecks.UI.Data": "9.0.0", "KubernetesClient": "15.0.1", - "Microsoft.EntityFrameworkCore.Design": "8.0.11" + "Microsoft.EntityFrameworkCore.Design": "8.0.11", + "Microsoft.Extensions.Http": "8.0.0" } }, "AspNetCore.HealthChecks.UI.InMemory.Storage": { @@ -1225,7 +1849,8 @@ "resolved": "10.0.0", "contentHash": "ZjR/onUlP7BzQ7VBBigQepWLAyAzi3VRGX3pP6sBqkPRiT61fsTZqbTpRUKxo30TMgbs1o3y6bpLbETix4SJog==", "dependencies": { - "Autofac": "8.1.0" + "Autofac": "8.1.0", + "Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.1" } }, "BouncyCastle.Cryptography": { @@ -1324,6 +1949,31 @@ "MathNet.Numerics": "5.0.0" } }, + "Microsoft.AspNetCore.DataProtection": { + "type": "CentralTransitive", + "requested": "[10.0.0, )", + "resolved": "10.0.0", + "contentHash": "RKU345im2k3hqboK+2ZcBa6oReAUr1m4c/8kf/6/rATNjxVFvWmCMLIP4U1lHhYat+Zmv1TpOlCw+8/7xATRhA==", + "dependencies": { + "Microsoft.AspNetCore.Cryptography.Internal": "10.0.0", + "Microsoft.AspNetCore.DataProtection.Abstractions": "10.0.0", + "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.0", + "Microsoft.Extensions.Hosting.Abstractions": "10.0.0", + "Microsoft.Extensions.Logging.Abstractions": "10.0.0", + "Microsoft.Extensions.Options": "10.0.0", + "System.Security.Cryptography.Xml": "10.0.0" + } + }, + "Microsoft.AspNetCore.DataProtection.Extensions": { + "type": "CentralTransitive", + "requested": "[10.0.0, )", + "resolved": "10.0.0", + "contentHash": "FRBizffEDpUVIkb0RnIgSzQmSuFa9mLEvbROakrkSzRythB/qubj8j4U0vuxyfAXrLSuQdsbogMHlCTPC/4QeQ==", + "dependencies": { + "Microsoft.AspNetCore.DataProtection": "10.0.0", + "Microsoft.Extensions.DependencyInjection": "10.0.0" + } + }, "Microsoft.ClearScript.V8": { "type": "CentralTransitive", "requested": "[7.5.0, )", @@ -1380,6 +2030,67 @@ "Microsoft.CodeAnalysis.Common": "[4.5.0]" } }, + "Microsoft.Extensions.Caching.Memory": { + "type": "CentralTransitive", + "requested": "[10.0.0, )", + "resolved": "10.0.0", + "contentHash": "krK19MKp0BNiR9rpBDW7PKSrTMLVlifS9am3CVc4O1Jq6GWz0o4F+sw5OSL4L3mVd56W8l6JRgghUa2KB51vOw==", + "dependencies": { + "Microsoft.Extensions.Caching.Abstractions": "10.0.0", + "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.0", + "Microsoft.Extensions.Logging.Abstractions": "10.0.0", + "Microsoft.Extensions.Options": "10.0.0", + "Microsoft.Extensions.Primitives": "10.0.0" + } + }, + "Microsoft.Extensions.DependencyInjection": { + "type": "CentralTransitive", + "requested": "[10.0.0, )", + "resolved": "10.0.0", + "contentHash": "f0RBabswJq+gRu5a+hWIobrLWiUYPKMhCD9WO3sYBAdSy3FFH14LMvLVFZc2kPSCimBLxSuitUhsd6tb0TAY6A==", + "dependencies": { + "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.0" + } + }, + "Microsoft.Extensions.Logging": { + "type": "CentralTransitive", + "requested": "[8.0.0, )", + "resolved": "9.0.0", + "contentHash": "crjWyORoug0kK7RSNJBTeSE6VX8IQgLf3nUpTB9m62bPXp/tzbnOsnbe8TXEG0AASNaKZddnpHKw7fET8E++Pg==", + "dependencies": { + "Microsoft.Extensions.DependencyInjection": "9.0.0", + "Microsoft.Extensions.Logging.Abstractions": "9.0.0", + "Microsoft.Extensions.Options": "9.0.0" + } + }, + "Microsoft.Extensions.Logging.Abstractions": { + "type": "CentralTransitive", + "requested": "[10.0.0, )", + "resolved": "10.0.0", + "contentHash": "FU/IfjDfwaMuKr414SSQNTIti/69bHEMb+QKrskRb26oVqpx3lNFXMjs/RC9ZUuhBhcwDM2BwOgoMw+PZ+beqQ==", + "dependencies": { + "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.0" + } + }, + "Microsoft.Extensions.Logging.Console": { + "type": "CentralTransitive", + "requested": "[10.0.0, )", + "resolved": "9.0.0", + "contentHash": "yDZ4zsjl7N0K+R/1QTNpXBd79Kaf4qNLHtjk4NaG82UtNg2Z6etJywwv6OarOv3Rp7ocU7uIaRY4CrzHRO/d3w==", + "dependencies": { + "Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.0", + "Microsoft.Extensions.Logging": "9.0.0", + "Microsoft.Extensions.Logging.Abstractions": "9.0.0", + "Microsoft.Extensions.Logging.Configuration": "9.0.0", + "Microsoft.Extensions.Options": "9.0.0" + } + }, + "Microsoft.Extensions.ObjectPool": { + "type": "CentralTransitive", + "requested": "[10.0.0, )", + "resolved": "10.0.0", + "contentHash": "bpeCq0IYmVLACyEUMzFIOQX+zZUElG1t+nu1lSxthe7B+1oNYking7b91305+jNB6iwojp9fqTY9O+Nh7ULQxg==" + }, "Microsoft.IdentityModel.JsonWebTokens": { "type": "CentralTransitive", "requested": "[8.15.0, )", @@ -1419,7 +2130,8 @@ "resolved": "1.0.2.76", "contentHash": "bI9wc+xazOgsgibMvPaMWtQ4dNWktBBCo3fZeUVFgEsDBMwagl3GqIozx4D1I1GbozkhCpUD55Q2KGx0CuDHHQ==", "dependencies": { - "Nethermind.DotNetty.Common": "1.0.2.76" + "Nethermind.DotNetty.Common": "1.0.2.76", + "System.Runtime.CompilerServices.Unsafe": "5.0.0" } }, "Nethermind.DotNetty.Handlers": { @@ -1456,6 +2168,9 @@ "resolved": "1.0.0-preview.45", "contentHash": "OLV+fEkqbG+kFfQnFgkOloX8a1QPCJnTZfTkviOKgWo7vVlOnmePIvy0Tk963LVpmRmjreMCTvLLOh3yB1ZCJQ==", "dependencies": { + "Microsoft.Extensions.DependencyInjection": "9.0.0", + "Microsoft.Extensions.Logging": "9.0.0", + "Microsoft.Extensions.Logging.Console": "9.0.0", "Nethermind.Libp2p.Core": "1.0.0-preview.45", "Nethermind.Libp2p.Protocols.Identify": "1.0.0-preview.45", "Nethermind.Libp2p.Protocols.MDns": "1.0.0-preview.45", @@ -1477,6 +2192,7 @@ "contentHash": "VK0g5fehGQbarfAzqwCFfNLhU7BBQBEkVypjt/ToOD619FhDMjqJJ1FuBPC0EP6Gmf2whG55cYnIBoGQdrNgtA==", "dependencies": { "Makaretu.Dns.Multicast": "0.27.0", + "Microsoft.Extensions.Logging.Abstractions": "9.0.0", "Nethermind.Libp2p.Core": "1.0.0-preview.45", "Nethermind.Libp2p.Protocols.Pubsub": "1.0.0-preview.45" } @@ -1509,7 +2225,10 @@ "type": "CentralTransitive", "requested": "[2.1.2, )", "resolved": "2.1.2", - "contentHash": "yTP24PcuFmUw1RxQgYmIMxvpAJ1ciT/zv8Sb7OZHTuM/x9Tupz+DvEqeu9HykSYmI3/bGuy1ZZ7k/rZgfuIAuw==" + "contentHash": "yTP24PcuFmUw1RxQgYmIMxvpAJ1ciT/zv8Sb7OZHTuM/x9Tupz+DvEqeu9HykSYmI3/bGuy1ZZ7k/rZgfuIAuw==", + "dependencies": { + "System.Runtime.CompilerServices.Unsafe": "4.3.0" + } }, "Open.NAT.Core": { "type": "CentralTransitive", @@ -1524,9 +2243,15 @@ "contentHash": "WjVYiDxyZ3z00kuJJXuWJwRqkWfTrZF1v7qWz4mMASRP6AEhDCF4jMdCuAWkH1uPj00kkluONOc426Z//FcjDw==", "dependencies": { "BouncyCastle.Cryptography": "2.4.0", + "Microsoft.Extensions.Caching.Memory": "8.0.0", + "Microsoft.Extensions.DependencyInjection": "8.0.0", + "Microsoft.Extensions.Logging": "8.0.0", + "Microsoft.Extensions.Logging.Console": "8.0.0", + "Microsoft.Extensions.Options": "8.0.2", "NBitcoin.Secp256k1": "3.1.5", "PierTwo.Lantern.Discv5.Enr": "1.0.0-preview.6", - "PierTwo.Lantern.Discv5.Rlp": "1.0.0-preview.6" + "PierTwo.Lantern.Discv5.Rlp": "1.0.0-preview.6", + "System.Threading.Tasks.Dataflow": "8.0.0" } }, "Polly": { @@ -1538,6 +2263,16 @@ "Polly.Core": "8.6.4" } }, + "prometheus-net": { + "type": "CentralTransitive", + "requested": "[8.2.1, )", + "resolved": "8.2.1", + "contentHash": "3wVgdEPOCBF752s2xps5T+VH+c9mJK8S8GKEDg49084P6JZMumTZI5Te6aJ9MQpX0sx7om6JOnBpIi7ZBmmiDQ==", + "dependencies": { + "Microsoft.Extensions.Http": "3.1.0", + "Microsoft.Extensions.ObjectPool": "7.0.0" + } + }, "prometheus-net.AspNetCore": { "type": "CentralTransitive", "requested": "[8.2.1, )", @@ -1571,6 +2306,7 @@ "resolved": "10.0.0", "contentHash": "0B+BzJ6pPMrRzJrVsttKf9MfDj6Syw9xoY+agcS9VssYQali1446+jTf5v1K94AMFUBxLXqDZlaTjO5edaI3jA==", "dependencies": { + "System.Diagnostics.EventLog": "10.0.0", "System.Security.Cryptography.ProtectedData": "10.0.0" } }, @@ -1595,6 +2331,7 @@ "resolved": "5.3.0", "contentHash": "uhdDM+gruCEhHRCKCoyali1HJp0wSS/HBs5X9XZwULNKM2y5ML188TsvcEgWEFOx0NOaHfGNtfoC0cd1p2NOIg==", "dependencies": { + "Microsoft.Extensions.Logging.Abstractions": "8.0.0", "Microsoft.IO.RecyclableMemoryStream": "3.0.0", "System.Reactive": "6.0.0" } @@ -1612,7 +2349,8 @@ "resolved": "2.46.6", "contentHash": "ZoRg3KmOJ2urTF4+u3H0b1Yv10xzz2Y/flFWS2tnRmj8dbKLeiJaSRqu4LOBD3ova90evqLkVZ85kUkC4JT4lw==", "dependencies": { - "Grpc.Core.Api": "2.46.6" + "Grpc.Core.Api": "2.46.6", + "System.Memory": "4.5.3" } }, "libsodium": { @@ -1620,11 +2358,1343 @@ "resolved": "1.0.20", "contentHash": "fMO6HpAbvLagobzBH6eU36riWF01lCAweX34D5eugqjuXA+WS5MnV1ngE+2Sw3LvGvxZlmyLp9416t57dMZ5og==" }, - "Tmds.LibC": { + "Microsoft.Win32.Registry": { "type": "Transitive", - "resolved": "0.2.0", - "contentHash": "+RvLuNHOLW7cxzgDe9yHLoayBgjsuH2/gJtJnuVMxweKrxxYT6TwQNAmt06SFWpjwk68aRcwwD4FfMMA6tZvVA==" - }, + "resolved": "5.0.0", + "contentHash": "dDoKi0PnDz31yAyETfRntsLArTlVAVzUzCIvvEDsDsucrl33Dl8pIJG06ePTJTI3tGpeyHS9Cq7Foc/s4EeKcg==", + "dependencies": { + "System.Security.AccessControl": "5.0.0", + "System.Security.Principal.Windows": "5.0.0" + } + }, + "runtime.any.System.Collections": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "23g6rqftKmovn2cLeGsuHUYm0FD7pdutb0uQMJpZ3qTvq+zHkgmt6J65VtRry4WDGYlmkMa4xDACtaQ94alNag==", + "dependencies": { + "System.Runtime": "4.3.0" + } + }, + "runtime.any.System.Globalization": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "sMDBnad4rp4t7GY442Jux0MCUuKL4otn5BK6Ni0ARTXTSpRNBzZ7hpMfKSvnVSED5kYJm96YOWsqV0JH0d2uuw==" + }, + "runtime.any.System.IO": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "SDZ5AD1DtyRoxYtEcqQ3HDlcrorMYXZeCt7ZhG9US9I5Vva+gpIWDGMkcwa5XiKL0ceQKRZIX2x0XEjLX7PDzQ==" + }, + "runtime.any.System.Reflection": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "hLC3A3rI8jipR5d9k7+f0MgRCW6texsAp0MWkN/ci18FMtQ9KH7E2vDn/DH2LkxsszlpJpOn9qy6Z6/69rH6eQ==" + }, + "runtime.any.System.Reflection.Extensions": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "cPhT+Vqu52+cQQrDai/V91gubXUnDKNRvlBnH+hOgtGyHdC17aQIU64EaehwAQymd7kJA5rSrVRNfDYrbhnzyA==" + }, + "runtime.any.System.Reflection.Primitives": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "Nrm1p3armp6TTf2xuvaa+jGTTmncALWFq22CpmwRvhDf6dE9ZmH40EbOswD4GnFLrMRS0Ki6Kx5aUPmKK/hZBg==" + }, + "runtime.any.System.Resources.ResourceManager": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "Lxb89SMvf8w9p9+keBLyL6H6x/TEmc6QVsIIA0T36IuyOY3kNvIdyGddA2qt35cRamzxF8K5p0Opq4G4HjNbhQ==" + }, + "runtime.any.System.Runtime": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "fRS7zJgaG9NkifaAxGGclDDoRn9HC7hXACl52Or06a/fxdzDajWb5wov3c6a+gVSlekRoexfjwQSK9sh5um5LQ==", + "dependencies": { + "System.Private.Uri": "4.3.0" + } + }, + "runtime.any.System.Runtime.Handles": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "GG84X6vufoEzqx8PbeBKheE4srOhimv+yLtGb/JkR3Y2FmoqmueLNFU4Xx8Y67plFpltQSdK74x0qlEhIpv/CQ==" + }, + "runtime.any.System.Runtime.InteropServices": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "lBoFeQfxe/4eqjPi46E0LU/YaCMdNkQ8B4MZu/mkzdIAZh8RQ1NYZSj0egrQKdgdvlPFtP4STtob40r4o2DBAw==" + }, + "runtime.any.System.Text.Encoding": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "+ihI5VaXFCMVPJNstG4O4eo1CfbrByLxRrQQTqOTp1ttK0kUKDqOdBSTaCB2IBk/QtjDrs6+x4xuezyMXdm0HQ==" + }, + "runtime.any.System.Threading.Tasks": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "OhBAVBQG5kFj1S+hCEQ3TUHBAEtZ3fbEMgZMRNdN8A0Pj4x+5nTELEqL59DU0TjKVE6II3dqKw4Dklb3szT65w==" + }, + "runtime.debian.8-x64.runtime.native.System.Security.Cryptography.OpenSsl": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "HdSSp5MnJSsg08KMfZThpuLPJpPwE5hBXvHwoKWosyHHfe8Mh5WKT0ylEOf6yNzX6Ngjxe4Whkafh5q7Ymac4Q==" + }, + "runtime.fedora.23-x64.runtime.native.System.Security.Cryptography.OpenSsl": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "+yH1a49wJMy8Zt4yx5RhJrxO/DBDByAiCzNwiETI+1S4mPdCu0OY4djdciC7Vssk0l22wQaDLrXxXkp+3+7bVA==" + }, + "runtime.fedora.24-x64.runtime.native.System.Security.Cryptography.OpenSsl": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "c3YNH1GQJbfIPJeCnr4avseugSqPrxwIqzthYyZDN6EuOyNOzq+y2KSUfRcXauya1sF4foESTgwM5e1A8arAKw==" + }, + "runtime.native.System": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "c/qWt2LieNZIj1jGnVNsE2Kl23Ya2aSTBuXMD6V7k9KWr6l16Tqdwq+hJScEpWER9753NWC8h96PaVNY5Ld7Jw==", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.1.0", + "Microsoft.NETCore.Targets": "1.1.0" + } + }, + "runtime.native.System.Security.Cryptography.OpenSsl": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "NS1U+700m4KFRHR5o4vo9DSlTmlCKu/u7dtE5sUHVIPB+xpXxYQvgBgA6wEIeCz6Yfn0Z52/72WYsToCEPJnrw==", + "dependencies": { + "runtime.debian.8-x64.runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0", + "runtime.fedora.23-x64.runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0", + "runtime.fedora.24-x64.runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0", + "runtime.opensuse.13.2-x64.runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0", + "runtime.opensuse.42.1-x64.runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0", + "runtime.osx.10.10-x64.runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0", + "runtime.rhel.7-x64.runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0", + "runtime.ubuntu.14.04-x64.runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0", + "runtime.ubuntu.16.04-x64.runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0", + "runtime.ubuntu.16.10-x64.runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0" + } + }, + "runtime.opensuse.13.2-x64.runtime.native.System.Security.Cryptography.OpenSsl": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "b3pthNgxxFcD+Pc0WSEoC0+md3MyhRS6aCEeenvNE3Fdw1HyJ18ZhRFVJJzIeR/O/jpxPboB805Ho0T3Ul7w8A==" + }, + "runtime.opensuse.42.1-x64.runtime.native.System.Security.Cryptography.OpenSsl": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "KeLz4HClKf+nFS7p/6Fi/CqyLXh81FpiGzcmuS8DGi9lUqSnZ6Es23/gv2O+1XVGfrbNmviF7CckBpavkBoIFQ==" + }, + "runtime.osx.10.10-x64.runtime.native.System.Security.Cryptography.OpenSsl": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "X7IdhILzr4ROXd8mI1BUCQMSHSQwelUlBjF1JyTKCjXaOGn2fB4EKBxQbCK2VjO3WaWIdlXZL3W6TiIVnrhX4g==" + }, + "runtime.rhel.7-x64.runtime.native.System.Security.Cryptography.OpenSsl": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "nyFNiCk/r+VOiIqreLix8yN+q3Wga9+SE8BCgkf+2BwEKiNx6DyvFjCgkfV743/grxv8jHJ8gUK4XEQw7yzRYg==" + }, + "runtime.ubuntu.14.04-x64.runtime.native.System.Security.Cryptography.OpenSsl": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "ytoewC6wGorL7KoCAvRfsgoJPJbNq+64k2SqW6JcOAebWsFUvCCYgfzQMrnpvPiEl4OrblUlhF2ji+Q1+SVLrQ==" + }, + "runtime.ubuntu.16.04-x64.runtime.native.System.Security.Cryptography.OpenSsl": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "I8bKw2I8k58Wx7fMKQJn2R8lamboCAiHfHeV/pS65ScKWMMI0+wJkLYlEKvgW1D/XvSl/221clBoR2q9QNNM7A==" + }, + "runtime.ubuntu.16.10-x64.runtime.native.System.Security.Cryptography.OpenSsl": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "VB5cn/7OzUfzdnC8tqAIMQciVLiq2epm2NrAm1E9OjNRyG4lVhfR61SMcLizejzQP8R8Uf/0l5qOIbUEi+RdEg==" + }, + "runtime.unix.System.Diagnostics.Debug": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "WV8KLRHWVUVUDduFnvGMHt0FsEt2wK6xPl1EgDKlaMx2KnZ43A/O0GzP8wIuvAC7mq4T9V1mm90r+PXkL9FPdQ==", + "dependencies": { + "runtime.native.System": "4.3.0" + } + }, + "runtime.unix.System.Private.Uri": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "ooWzobr5RAq34r9uan1r/WPXJYG1XWy9KanrxNvEnBzbFdQbMG7Y3bVi4QxR7xZMNLOxLLTAyXvnSkfj5boZSg==", + "dependencies": { + "runtime.native.System": "4.3.0" + } + }, + "runtime.unix.System.Runtime.Extensions": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "zQiTBVpiLftTQZW8GFsV0gjYikB1WMkEPIxF5O6RkUrSV/OgvRRTYgeFQha/0keBpuS0HYweraGRwhfhJ7dj7w==", + "dependencies": { + "System.Private.Uri": "4.3.0", + "runtime.native.System": "4.3.0", + "runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0" + } + }, + "System.Collections": { + "type": "Transitive", + "resolved": "4.0.11", + "contentHash": "YUJGz6eFKqS0V//mLt25vFGrrCvOnsXjlvFQs+KimpwNxug9x0Pzy4PlFMU3Q2IzqAa9G2L4LsK3+9vCBK7oTg==", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.0.1", + "Microsoft.NETCore.Targets": "1.0.1", + "System.Runtime": "4.1.0", + "runtime.any.System.Collections": "4.3.0" + } + }, + "System.Diagnostics.Debug": { + "type": "Transitive", + "resolved": "4.0.11", + "contentHash": "w5U95fVKHY4G8ASs/K5iK3J5LY+/dLFd4vKejsnI/ZhBsWS9hQakfx3Zr7lRWKg4tAw9r4iktyvsTagWkqYCiw==", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.0.1", + "Microsoft.NETCore.Targets": "1.0.1", + "System.Runtime": "4.1.0", + "runtime.unix.System.Diagnostics.Debug": "4.3.0" + } + }, + "System.Diagnostics.EventLog": { + "type": "Transitive", + "resolved": "10.0.0", + "contentHash": "uaFRda9NjtbJRkdx311eXlAA3n2em7223c1A8d1VWyl+4FL9vkG7y2lpPfBU9HYdj/9KgdRNdn1vFK8ZYCYT/A==" + }, + "System.Globalization": { + "type": "Transitive", + "resolved": "4.0.11", + "contentHash": "B95h0YLEL2oSnwF/XjqSWKnwKOy/01VWkNlsCeMTFJLLabflpGV26nK164eRs5GiaRSBGpOxQ3pKoSnnyZN5pg==", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.0.1", + "Microsoft.NETCore.Targets": "1.0.1", + "System.Runtime": "4.1.0", + "runtime.any.System.Globalization": "4.3.0" + } + }, + "System.IO": { + "type": "Transitive", + "resolved": "4.1.0", + "contentHash": "3KlTJceQc3gnGIaHZ7UBZO26SHL1SHE4ddrmiwumFnId+CEHP+O8r386tZKaE6zlk5/mF8vifMBzHj9SaXN+mQ==", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.0.1", + "Microsoft.NETCore.Targets": "1.0.1", + "System.Runtime": "4.1.0", + "System.Text.Encoding": "4.0.11", + "System.Threading.Tasks": "4.0.11", + "runtime.any.System.IO": "4.3.0" + } + }, + "System.Private.Uri": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "I4SwANiUGho1esj4V4oSlPllXjzCZDE+5XXso2P03LW2vOda2Enzh8DWOxwN6hnrJyp314c7KuVu31QYhRzOGg==", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.1.0", + "Microsoft.NETCore.Targets": "1.1.0", + "runtime.unix.System.Private.Uri": "4.3.0" + } + }, + "System.Reflection": { + "type": "Transitive", + "resolved": "4.1.0", + "contentHash": "JCKANJ0TI7kzoQzuwB/OoJANy1Lg338B6+JVacPl4TpUwi3cReg3nMLplMq2uqYfHFQpKIlHAUVAJlImZz/4ng==", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.0.1", + "Microsoft.NETCore.Targets": "1.0.1", + "System.IO": "4.1.0", + "System.Reflection.Primitives": "4.0.1", + "System.Runtime": "4.1.0", + "runtime.any.System.Reflection": "4.3.0" + } + }, + "System.Reflection.Extensions": { + "type": "Transitive", + "resolved": "4.0.1", + "contentHash": "GYrtRsZcMuHF3sbmRHfMYpvxZoIN2bQGrYGerUiWLEkqdEUQZhH3TRSaC/oI4wO0II1RKBPlpIa1TOMxIcOOzQ==", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.0.1", + "Microsoft.NETCore.Targets": "1.0.1", + "System.Reflection": "4.1.0", + "System.Runtime": "4.1.0", + "runtime.any.System.Reflection.Extensions": "4.3.0" + } + }, + "System.Reflection.Primitives": { + "type": "Transitive", + "resolved": "4.0.1", + "contentHash": "4inTox4wTBaDhB7V3mPvp9XlCbeGYWVEM9/fXALd52vNEAVisc1BoVWQPuUuD0Ga//dNbA/WeMy9u9mzLxGTHQ==", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.0.1", + "Microsoft.NETCore.Targets": "1.0.1", + "System.Runtime": "4.1.0", + "runtime.any.System.Reflection.Primitives": "4.3.0" + } + }, + "System.Resources.ResourceManager": { + "type": "Transitive", + "resolved": "4.0.1", + "contentHash": "TxwVeUNoTgUOdQ09gfTjvW411MF+w9MBYL7AtNVc+HtBCFlutPLhUCdZjNkjbhj3bNQWMdHboF0KIWEOjJssbA==", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.0.1", + "Microsoft.NETCore.Targets": "1.0.1", + "System.Globalization": "4.0.11", + "System.Reflection": "4.1.0", + "System.Runtime": "4.1.0", + "runtime.any.System.Resources.ResourceManager": "4.3.0" + } + }, + "System.Runtime": { + "type": "Transitive", + "resolved": "4.3.1", + "contentHash": "abhfv1dTK6NXOmu4bgHIONxHyEqFjW8HwXPmpY9gmll+ix9UNo4XDcmzJn6oLooftxNssVHdJC1pGT9jkSynQg==", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.1.1", + "Microsoft.NETCore.Targets": "1.1.3", + "runtime.any.System.Runtime": "4.3.0" + } + }, + "System.Runtime.Extensions": { + "type": "Transitive", + "resolved": "4.1.0", + "contentHash": "CUOHjTT/vgP0qGW22U4/hDlOqXmcPq5YicBaXdUR2UiUoLwBT+olO6we4DVbq57jeX5uXH2uerVZhf0qGj+sVQ==", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.0.1", + "Microsoft.NETCore.Targets": "1.0.1", + "System.Runtime": "4.1.0", + "runtime.unix.System.Runtime.Extensions": "4.3.0" + } + }, + "System.Runtime.Handles": { + "type": "Transitive", + "resolved": "4.0.1", + "contentHash": "nCJvEKguXEvk2ymk1gqj625vVnlK3/xdGzx0vOKicQkoquaTBJTP13AIYkocSUwHCLNBwUbXTqTWGDxBTWpt7g==", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.0.1", + "Microsoft.NETCore.Targets": "1.0.1", + "System.Runtime": "4.1.0", + "runtime.any.System.Runtime.Handles": "4.3.0" + } + }, + "System.Runtime.InteropServices": { + "type": "Transitive", + "resolved": "4.1.0", + "contentHash": "16eu3kjHS633yYdkjwShDHZLRNMKVi/s0bY8ODiqJ2RfMhDMAwxZaUaWVnZ2P71kr/or+X9o/xFWtNqz8ivieQ==", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.0.1", + "Microsoft.NETCore.Targets": "1.0.1", + "System.Reflection": "4.1.0", + "System.Reflection.Primitives": "4.0.1", + "System.Runtime": "4.1.0", + "System.Runtime.Handles": "4.0.1", + "runtime.any.System.Runtime.InteropServices": "4.3.0" + } + }, + "System.Security.AccessControl": { + "type": "Transitive", + "resolved": "5.0.0", + "contentHash": "dagJ1mHZO3Ani8GH0PHpPEe/oYO+rVdbQjvjJkBRNQkX4t0r1iaeGn8+/ybkSLEan3/slM0t59SVdHzuHf2jmw==", + "dependencies": { + "Microsoft.NETCore.Platforms": "5.0.0", + "System.Security.Principal.Windows": "5.0.0" + } + }, + "System.Security.Cryptography.Pkcs": { + "type": "Transitive", + "resolved": "10.0.0", + "contentHash": "UPWqLSygJlFerRi9XNIuM0a1VC8gHUIufyP24xQ0sc+XimqUAEcjpOz9DhKpyDjH+5B/wO3RpC0KpkEeDj/ddg==" + }, + "System.Security.Principal.Windows": { + "type": "Transitive", + "resolved": "5.0.0", + "contentHash": "t0MGLukB5WAVU9bO3MGzvlGnyJPgUlcwerXn1kzBRjwLKixT96XV0Uza41W49gVd8zEMFu9vQEFlv0IOrytICA==" + }, + "System.Text.Encoding": { + "type": "Transitive", + "resolved": "4.0.11", + "contentHash": "U3gGeMlDZXxCEiY4DwVLSacg+DFWCvoiX+JThA/rvw37Sqrku7sEFeVBBBMBnfB6FeZHsyDx85HlKL19x0HtZA==", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.0.1", + "Microsoft.NETCore.Targets": "1.0.1", + "System.Runtime": "4.1.0", + "runtime.any.System.Text.Encoding": "4.3.0" + } + }, + "System.Text.Encoding.CodePages": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "ZFCILZuOvtKPauZ/j/swhvw68ZRi9ATCfvGbk1QfydmcXBkIWecWKn/250UH7rahZ5OoDBaiAudJtPvLwzw85A==", + "dependencies": { + "System.Runtime.CompilerServices.Unsafe": "6.0.0" + } + }, + "System.Threading.Tasks": { + "type": "Transitive", + "resolved": "4.0.11", + "contentHash": "k1S4Gc6IGwtHGT8188RSeGaX86Qw/wnrgNLshJvsdNUOPP9etMmo8S07c+UlOAx4K/xLuN9ivA1bD0LVurtIxQ==", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.0.1", + "Microsoft.NETCore.Targets": "1.0.1", + "System.Runtime": "4.1.0", + "runtime.any.System.Threading.Tasks": "4.3.0" + } + }, + "Tmds.LibC": { + "type": "Transitive", + "resolved": "0.2.0", + "contentHash": "+RvLuNHOLW7cxzgDe9yHLoayBgjsuH2/gJtJnuVMxweKrxxYT6TwQNAmt06SFWpjwk68aRcwwD4FfMMA6tZvVA==" + }, + "Ckzg.Bindings": { + "type": "CentralTransitive", + "requested": "[2.1.5.1542, )", + "resolved": "2.1.5.1542", + "contentHash": "GFXmOjg5fZ8s+836/HdKiyXbJ+J73wVX6hNmUE6Isb1rA8dI2SMLeW1m48s+ZNIPN9ENJb8Daq1GwmMjBGjUVQ==" + }, + "Microsoft.ClearScript.V8.Native.linux-arm64": { + "type": "CentralTransitive", + "requested": "[7.5.0, )", + "resolved": "7.5.0", + "contentHash": "UU+3Bef3UnwQgP8hKobT09ucYuYubVFiseAsuRUvmjvOBVu7yHRES+nXBNYSvDi88fMTp/HBUknpYQdrfoDemQ==" + }, + "Microsoft.ClearScript.V8.Native.linux-x64": { + "type": "CentralTransitive", + "requested": "[7.5.0, )", + "resolved": "7.5.0", + "contentHash": "snoN9oRwKqShA32IsuCanLjNtP8hros2WOrOBL7g+ED3AV40qwrsbfKwWq37BzogrfsF1aEVoDkBpE19Az7DVQ==" + }, + "Microsoft.ClearScript.V8.Native.osx-arm64": { + "type": "CentralTransitive", + "requested": "[7.5.0, )", + "resolved": "7.5.0", + "contentHash": "CkMgeX0I0+bXUzoaVoJdV86/k0H2PEukqCoZ8zQ28msB6YHeRX6FJTfvOQ0l6UTX5HaBHGG3CWUI04uBYe6M+A==" + }, + "Microsoft.ClearScript.V8.Native.osx-x64": { + "type": "CentralTransitive", + "requested": "[7.5.0, )", + "resolved": "7.5.0", + "contentHash": "yXoXLWKJJgW5V6ez1aMa+ZS2nCef0X4iTYzPS9bTSYl9y7D4R2Ie2KrfR8nLO2rhOKimIMx3MH49Zh1CYruN/g==" + }, + "Microsoft.ClearScript.V8.Native.win-x64": { + "type": "CentralTransitive", + "requested": "[7.5.0, )", + "resolved": "7.5.0", + "contentHash": "DKMxDLboTNflYkwDQ/ELrSf1vXTpew5UZ8xzrXSVKYFBU570VA6NKh1etEGhufuCuDyU7Je5L2g6H+19Dbl+tA==" + }, + "Nethermind.Crypto.Bls": { + "type": "CentralTransitive", + "requested": "[1.0.5, )", + "resolved": "1.0.5", + "contentHash": "SYdQOFVVcC3R3VAm9Dv+u4Mc1yqHZETxt4tN3a+AFeOnEtUmpcjwVwYkkiiUIIrr6vQVVOUuwsDmaa9l3u45IQ==" + }, + "Nethermind.Crypto.SecP256k1": { + "type": "CentralTransitive", + "requested": "[1.5.0, )", + "resolved": "1.5.0", + "contentHash": "+mNlEgN1gYDB6f4jRcYssaE6/AlSoPr7eLDQHQoX+tXcnGRzgnArezPwz82TsWxruQGDh5h9Qfowa0xt4Xz59g==" + }, + "Nethermind.Crypto.SecP256r1": { + "type": "CentralTransitive", + "requested": "[1.0.0-preview.6, )", + "resolved": "1.0.0-preview.6", + "contentHash": "wFfpg1ofZz5nsjN8TAKUg0mdUCskmOUO0lFk3LcoeRkVnQ5Rw2rYzsJxgPFfnxAABH/EPPs62S7oF8E0Ayjjeg==" + }, + "Nethermind.GmpBindings": { + "type": "CentralTransitive", + "requested": "[1.0.3, )", + "resolved": "1.0.3", + "contentHash": "EE12z2k4ku0ugfI01utaQR8EbBoEMLI4QAKKGrfz5Fvbw/YtXTqDDzvKtBTleOB9YBH7oTpH9T9ZFtKgKZMj2g==" + }, + "Nethermind.MclBindings": { + "type": "CentralTransitive", + "requested": "[1.0.3, )", + "resolved": "1.0.3", + "contentHash": "hgT2oiMFqItNXv5vzIbhhlgqPJK4qnOHaYmUiR4jJsaWiqDRH05YtqYeMQq2+oyBOf8REtuGOW5RZ7+agRSEbg==" + }, + "RocksDB": { + "type": "CentralTransitive", + "requested": "[10.4.2.62659, 10.4.2.62659]", + "resolved": "10.4.2.62659", + "contentHash": "+ZY7koKclaRz7+3QiCbXprWK4++Cwh0Hhqj+5Z5fcZpQvoIoo+iM9iAdCo+W5ha9XOLeI0YWbi9nZt12dNVBMg==" + } + }, + "net10.0/linux-x64": { + "Grpc.Core": { + "type": "Transitive", + "resolved": "2.46.6", + "contentHash": "ZoRg3KmOJ2urTF4+u3H0b1Yv10xzz2Y/flFWS2tnRmj8dbKLeiJaSRqu4LOBD3ova90evqLkVZ85kUkC4JT4lw==", + "dependencies": { + "Grpc.Core.Api": "2.46.6", + "System.Memory": "4.5.3" + } + }, + "libsodium": { + "type": "Transitive", + "resolved": "1.0.20", + "contentHash": "fMO6HpAbvLagobzBH6eU36riWF01lCAweX34D5eugqjuXA+WS5MnV1ngE+2Sw3LvGvxZlmyLp9416t57dMZ5og==" + }, + "Microsoft.Win32.Registry": { + "type": "Transitive", + "resolved": "5.0.0", + "contentHash": "dDoKi0PnDz31yAyETfRntsLArTlVAVzUzCIvvEDsDsucrl33Dl8pIJG06ePTJTI3tGpeyHS9Cq7Foc/s4EeKcg==", + "dependencies": { + "System.Security.AccessControl": "5.0.0", + "System.Security.Principal.Windows": "5.0.0" + } + }, + "runtime.any.System.Collections": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "23g6rqftKmovn2cLeGsuHUYm0FD7pdutb0uQMJpZ3qTvq+zHkgmt6J65VtRry4WDGYlmkMa4xDACtaQ94alNag==", + "dependencies": { + "System.Runtime": "4.3.0" + } + }, + "runtime.any.System.Globalization": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "sMDBnad4rp4t7GY442Jux0MCUuKL4otn5BK6Ni0ARTXTSpRNBzZ7hpMfKSvnVSED5kYJm96YOWsqV0JH0d2uuw==" + }, + "runtime.any.System.IO": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "SDZ5AD1DtyRoxYtEcqQ3HDlcrorMYXZeCt7ZhG9US9I5Vva+gpIWDGMkcwa5XiKL0ceQKRZIX2x0XEjLX7PDzQ==" + }, + "runtime.any.System.Reflection": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "hLC3A3rI8jipR5d9k7+f0MgRCW6texsAp0MWkN/ci18FMtQ9KH7E2vDn/DH2LkxsszlpJpOn9qy6Z6/69rH6eQ==" + }, + "runtime.any.System.Reflection.Extensions": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "cPhT+Vqu52+cQQrDai/V91gubXUnDKNRvlBnH+hOgtGyHdC17aQIU64EaehwAQymd7kJA5rSrVRNfDYrbhnzyA==" + }, + "runtime.any.System.Reflection.Primitives": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "Nrm1p3armp6TTf2xuvaa+jGTTmncALWFq22CpmwRvhDf6dE9ZmH40EbOswD4GnFLrMRS0Ki6Kx5aUPmKK/hZBg==" + }, + "runtime.any.System.Resources.ResourceManager": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "Lxb89SMvf8w9p9+keBLyL6H6x/TEmc6QVsIIA0T36IuyOY3kNvIdyGddA2qt35cRamzxF8K5p0Opq4G4HjNbhQ==" + }, + "runtime.any.System.Runtime": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "fRS7zJgaG9NkifaAxGGclDDoRn9HC7hXACl52Or06a/fxdzDajWb5wov3c6a+gVSlekRoexfjwQSK9sh5um5LQ==", + "dependencies": { + "System.Private.Uri": "4.3.0" + } + }, + "runtime.any.System.Runtime.Handles": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "GG84X6vufoEzqx8PbeBKheE4srOhimv+yLtGb/JkR3Y2FmoqmueLNFU4Xx8Y67plFpltQSdK74x0qlEhIpv/CQ==" + }, + "runtime.any.System.Runtime.InteropServices": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "lBoFeQfxe/4eqjPi46E0LU/YaCMdNkQ8B4MZu/mkzdIAZh8RQ1NYZSj0egrQKdgdvlPFtP4STtob40r4o2DBAw==" + }, + "runtime.any.System.Text.Encoding": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "+ihI5VaXFCMVPJNstG4O4eo1CfbrByLxRrQQTqOTp1ttK0kUKDqOdBSTaCB2IBk/QtjDrs6+x4xuezyMXdm0HQ==" + }, + "runtime.any.System.Threading.Tasks": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "OhBAVBQG5kFj1S+hCEQ3TUHBAEtZ3fbEMgZMRNdN8A0Pj4x+5nTELEqL59DU0TjKVE6II3dqKw4Dklb3szT65w==" + }, + "runtime.debian.8-x64.runtime.native.System.Security.Cryptography.OpenSsl": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "HdSSp5MnJSsg08KMfZThpuLPJpPwE5hBXvHwoKWosyHHfe8Mh5WKT0ylEOf6yNzX6Ngjxe4Whkafh5q7Ymac4Q==" + }, + "runtime.fedora.23-x64.runtime.native.System.Security.Cryptography.OpenSsl": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "+yH1a49wJMy8Zt4yx5RhJrxO/DBDByAiCzNwiETI+1S4mPdCu0OY4djdciC7Vssk0l22wQaDLrXxXkp+3+7bVA==" + }, + "runtime.fedora.24-x64.runtime.native.System.Security.Cryptography.OpenSsl": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "c3YNH1GQJbfIPJeCnr4avseugSqPrxwIqzthYyZDN6EuOyNOzq+y2KSUfRcXauya1sF4foESTgwM5e1A8arAKw==" + }, + "runtime.native.System": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "c/qWt2LieNZIj1jGnVNsE2Kl23Ya2aSTBuXMD6V7k9KWr6l16Tqdwq+hJScEpWER9753NWC8h96PaVNY5Ld7Jw==", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.1.0", + "Microsoft.NETCore.Targets": "1.1.0" + } + }, + "runtime.native.System.Security.Cryptography.OpenSsl": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "NS1U+700m4KFRHR5o4vo9DSlTmlCKu/u7dtE5sUHVIPB+xpXxYQvgBgA6wEIeCz6Yfn0Z52/72WYsToCEPJnrw==", + "dependencies": { + "runtime.debian.8-x64.runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0", + "runtime.fedora.23-x64.runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0", + "runtime.fedora.24-x64.runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0", + "runtime.opensuse.13.2-x64.runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0", + "runtime.opensuse.42.1-x64.runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0", + "runtime.osx.10.10-x64.runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0", + "runtime.rhel.7-x64.runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0", + "runtime.ubuntu.14.04-x64.runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0", + "runtime.ubuntu.16.04-x64.runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0", + "runtime.ubuntu.16.10-x64.runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0" + } + }, + "runtime.opensuse.13.2-x64.runtime.native.System.Security.Cryptography.OpenSsl": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "b3pthNgxxFcD+Pc0WSEoC0+md3MyhRS6aCEeenvNE3Fdw1HyJ18ZhRFVJJzIeR/O/jpxPboB805Ho0T3Ul7w8A==" + }, + "runtime.opensuse.42.1-x64.runtime.native.System.Security.Cryptography.OpenSsl": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "KeLz4HClKf+nFS7p/6Fi/CqyLXh81FpiGzcmuS8DGi9lUqSnZ6Es23/gv2O+1XVGfrbNmviF7CckBpavkBoIFQ==" + }, + "runtime.osx.10.10-x64.runtime.native.System.Security.Cryptography.OpenSsl": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "X7IdhILzr4ROXd8mI1BUCQMSHSQwelUlBjF1JyTKCjXaOGn2fB4EKBxQbCK2VjO3WaWIdlXZL3W6TiIVnrhX4g==" + }, + "runtime.rhel.7-x64.runtime.native.System.Security.Cryptography.OpenSsl": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "nyFNiCk/r+VOiIqreLix8yN+q3Wga9+SE8BCgkf+2BwEKiNx6DyvFjCgkfV743/grxv8jHJ8gUK4XEQw7yzRYg==" + }, + "runtime.ubuntu.14.04-x64.runtime.native.System.Security.Cryptography.OpenSsl": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "ytoewC6wGorL7KoCAvRfsgoJPJbNq+64k2SqW6JcOAebWsFUvCCYgfzQMrnpvPiEl4OrblUlhF2ji+Q1+SVLrQ==" + }, + "runtime.ubuntu.16.04-x64.runtime.native.System.Security.Cryptography.OpenSsl": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "I8bKw2I8k58Wx7fMKQJn2R8lamboCAiHfHeV/pS65ScKWMMI0+wJkLYlEKvgW1D/XvSl/221clBoR2q9QNNM7A==" + }, + "runtime.ubuntu.16.10-x64.runtime.native.System.Security.Cryptography.OpenSsl": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "VB5cn/7OzUfzdnC8tqAIMQciVLiq2epm2NrAm1E9OjNRyG4lVhfR61SMcLizejzQP8R8Uf/0l5qOIbUEi+RdEg==" + }, + "runtime.unix.System.Diagnostics.Debug": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "WV8KLRHWVUVUDduFnvGMHt0FsEt2wK6xPl1EgDKlaMx2KnZ43A/O0GzP8wIuvAC7mq4T9V1mm90r+PXkL9FPdQ==", + "dependencies": { + "runtime.native.System": "4.3.0" + } + }, + "runtime.unix.System.Private.Uri": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "ooWzobr5RAq34r9uan1r/WPXJYG1XWy9KanrxNvEnBzbFdQbMG7Y3bVi4QxR7xZMNLOxLLTAyXvnSkfj5boZSg==", + "dependencies": { + "runtime.native.System": "4.3.0" + } + }, + "runtime.unix.System.Runtime.Extensions": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "zQiTBVpiLftTQZW8GFsV0gjYikB1WMkEPIxF5O6RkUrSV/OgvRRTYgeFQha/0keBpuS0HYweraGRwhfhJ7dj7w==", + "dependencies": { + "System.Private.Uri": "4.3.0", + "runtime.native.System": "4.3.0", + "runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0" + } + }, + "System.Collections": { + "type": "Transitive", + "resolved": "4.0.11", + "contentHash": "YUJGz6eFKqS0V//mLt25vFGrrCvOnsXjlvFQs+KimpwNxug9x0Pzy4PlFMU3Q2IzqAa9G2L4LsK3+9vCBK7oTg==", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.0.1", + "Microsoft.NETCore.Targets": "1.0.1", + "System.Runtime": "4.1.0", + "runtime.any.System.Collections": "4.3.0" + } + }, + "System.Diagnostics.Debug": { + "type": "Transitive", + "resolved": "4.0.11", + "contentHash": "w5U95fVKHY4G8ASs/K5iK3J5LY+/dLFd4vKejsnI/ZhBsWS9hQakfx3Zr7lRWKg4tAw9r4iktyvsTagWkqYCiw==", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.0.1", + "Microsoft.NETCore.Targets": "1.0.1", + "System.Runtime": "4.1.0", + "runtime.unix.System.Diagnostics.Debug": "4.3.0" + } + }, + "System.Diagnostics.EventLog": { + "type": "Transitive", + "resolved": "10.0.0", + "contentHash": "uaFRda9NjtbJRkdx311eXlAA3n2em7223c1A8d1VWyl+4FL9vkG7y2lpPfBU9HYdj/9KgdRNdn1vFK8ZYCYT/A==" + }, + "System.Globalization": { + "type": "Transitive", + "resolved": "4.0.11", + "contentHash": "B95h0YLEL2oSnwF/XjqSWKnwKOy/01VWkNlsCeMTFJLLabflpGV26nK164eRs5GiaRSBGpOxQ3pKoSnnyZN5pg==", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.0.1", + "Microsoft.NETCore.Targets": "1.0.1", + "System.Runtime": "4.1.0", + "runtime.any.System.Globalization": "4.3.0" + } + }, + "System.IO": { + "type": "Transitive", + "resolved": "4.1.0", + "contentHash": "3KlTJceQc3gnGIaHZ7UBZO26SHL1SHE4ddrmiwumFnId+CEHP+O8r386tZKaE6zlk5/mF8vifMBzHj9SaXN+mQ==", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.0.1", + "Microsoft.NETCore.Targets": "1.0.1", + "System.Runtime": "4.1.0", + "System.Text.Encoding": "4.0.11", + "System.Threading.Tasks": "4.0.11", + "runtime.any.System.IO": "4.3.0" + } + }, + "System.Private.Uri": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "I4SwANiUGho1esj4V4oSlPllXjzCZDE+5XXso2P03LW2vOda2Enzh8DWOxwN6hnrJyp314c7KuVu31QYhRzOGg==", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.1.0", + "Microsoft.NETCore.Targets": "1.1.0", + "runtime.unix.System.Private.Uri": "4.3.0" + } + }, + "System.Reflection": { + "type": "Transitive", + "resolved": "4.1.0", + "contentHash": "JCKANJ0TI7kzoQzuwB/OoJANy1Lg338B6+JVacPl4TpUwi3cReg3nMLplMq2uqYfHFQpKIlHAUVAJlImZz/4ng==", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.0.1", + "Microsoft.NETCore.Targets": "1.0.1", + "System.IO": "4.1.0", + "System.Reflection.Primitives": "4.0.1", + "System.Runtime": "4.1.0", + "runtime.any.System.Reflection": "4.3.0" + } + }, + "System.Reflection.Extensions": { + "type": "Transitive", + "resolved": "4.0.1", + "contentHash": "GYrtRsZcMuHF3sbmRHfMYpvxZoIN2bQGrYGerUiWLEkqdEUQZhH3TRSaC/oI4wO0II1RKBPlpIa1TOMxIcOOzQ==", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.0.1", + "Microsoft.NETCore.Targets": "1.0.1", + "System.Reflection": "4.1.0", + "System.Runtime": "4.1.0", + "runtime.any.System.Reflection.Extensions": "4.3.0" + } + }, + "System.Reflection.Primitives": { + "type": "Transitive", + "resolved": "4.0.1", + "contentHash": "4inTox4wTBaDhB7V3mPvp9XlCbeGYWVEM9/fXALd52vNEAVisc1BoVWQPuUuD0Ga//dNbA/WeMy9u9mzLxGTHQ==", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.0.1", + "Microsoft.NETCore.Targets": "1.0.1", + "System.Runtime": "4.1.0", + "runtime.any.System.Reflection.Primitives": "4.3.0" + } + }, + "System.Resources.ResourceManager": { + "type": "Transitive", + "resolved": "4.0.1", + "contentHash": "TxwVeUNoTgUOdQ09gfTjvW411MF+w9MBYL7AtNVc+HtBCFlutPLhUCdZjNkjbhj3bNQWMdHboF0KIWEOjJssbA==", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.0.1", + "Microsoft.NETCore.Targets": "1.0.1", + "System.Globalization": "4.0.11", + "System.Reflection": "4.1.0", + "System.Runtime": "4.1.0", + "runtime.any.System.Resources.ResourceManager": "4.3.0" + } + }, + "System.Runtime": { + "type": "Transitive", + "resolved": "4.3.1", + "contentHash": "abhfv1dTK6NXOmu4bgHIONxHyEqFjW8HwXPmpY9gmll+ix9UNo4XDcmzJn6oLooftxNssVHdJC1pGT9jkSynQg==", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.1.1", + "Microsoft.NETCore.Targets": "1.1.3", + "runtime.any.System.Runtime": "4.3.0" + } + }, + "System.Runtime.Extensions": { + "type": "Transitive", + "resolved": "4.1.0", + "contentHash": "CUOHjTT/vgP0qGW22U4/hDlOqXmcPq5YicBaXdUR2UiUoLwBT+olO6we4DVbq57jeX5uXH2uerVZhf0qGj+sVQ==", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.0.1", + "Microsoft.NETCore.Targets": "1.0.1", + "System.Runtime": "4.1.0", + "runtime.unix.System.Runtime.Extensions": "4.3.0" + } + }, + "System.Runtime.Handles": { + "type": "Transitive", + "resolved": "4.0.1", + "contentHash": "nCJvEKguXEvk2ymk1gqj625vVnlK3/xdGzx0vOKicQkoquaTBJTP13AIYkocSUwHCLNBwUbXTqTWGDxBTWpt7g==", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.0.1", + "Microsoft.NETCore.Targets": "1.0.1", + "System.Runtime": "4.1.0", + "runtime.any.System.Runtime.Handles": "4.3.0" + } + }, + "System.Runtime.InteropServices": { + "type": "Transitive", + "resolved": "4.1.0", + "contentHash": "16eu3kjHS633yYdkjwShDHZLRNMKVi/s0bY8ODiqJ2RfMhDMAwxZaUaWVnZ2P71kr/or+X9o/xFWtNqz8ivieQ==", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.0.1", + "Microsoft.NETCore.Targets": "1.0.1", + "System.Reflection": "4.1.0", + "System.Reflection.Primitives": "4.0.1", + "System.Runtime": "4.1.0", + "System.Runtime.Handles": "4.0.1", + "runtime.any.System.Runtime.InteropServices": "4.3.0" + } + }, + "System.Security.AccessControl": { + "type": "Transitive", + "resolved": "5.0.0", + "contentHash": "dagJ1mHZO3Ani8GH0PHpPEe/oYO+rVdbQjvjJkBRNQkX4t0r1iaeGn8+/ybkSLEan3/slM0t59SVdHzuHf2jmw==", + "dependencies": { + "Microsoft.NETCore.Platforms": "5.0.0", + "System.Security.Principal.Windows": "5.0.0" + } + }, + "System.Security.Cryptography.Pkcs": { + "type": "Transitive", + "resolved": "10.0.0", + "contentHash": "UPWqLSygJlFerRi9XNIuM0a1VC8gHUIufyP24xQ0sc+XimqUAEcjpOz9DhKpyDjH+5B/wO3RpC0KpkEeDj/ddg==" + }, + "System.Security.Principal.Windows": { + "type": "Transitive", + "resolved": "5.0.0", + "contentHash": "t0MGLukB5WAVU9bO3MGzvlGnyJPgUlcwerXn1kzBRjwLKixT96XV0Uza41W49gVd8zEMFu9vQEFlv0IOrytICA==" + }, + "System.Text.Encoding": { + "type": "Transitive", + "resolved": "4.0.11", + "contentHash": "U3gGeMlDZXxCEiY4DwVLSacg+DFWCvoiX+JThA/rvw37Sqrku7sEFeVBBBMBnfB6FeZHsyDx85HlKL19x0HtZA==", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.0.1", + "Microsoft.NETCore.Targets": "1.0.1", + "System.Runtime": "4.1.0", + "runtime.any.System.Text.Encoding": "4.3.0" + } + }, + "System.Text.Encoding.CodePages": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "ZFCILZuOvtKPauZ/j/swhvw68ZRi9ATCfvGbk1QfydmcXBkIWecWKn/250UH7rahZ5OoDBaiAudJtPvLwzw85A==", + "dependencies": { + "System.Runtime.CompilerServices.Unsafe": "6.0.0" + } + }, + "System.Threading.Tasks": { + "type": "Transitive", + "resolved": "4.0.11", + "contentHash": "k1S4Gc6IGwtHGT8188RSeGaX86Qw/wnrgNLshJvsdNUOPP9etMmo8S07c+UlOAx4K/xLuN9ivA1bD0LVurtIxQ==", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.0.1", + "Microsoft.NETCore.Targets": "1.0.1", + "System.Runtime": "4.1.0", + "runtime.any.System.Threading.Tasks": "4.3.0" + } + }, + "Tmds.LibC": { + "type": "Transitive", + "resolved": "0.2.0", + "contentHash": "+RvLuNHOLW7cxzgDe9yHLoayBgjsuH2/gJtJnuVMxweKrxxYT6TwQNAmt06SFWpjwk68aRcwwD4FfMMA6tZvVA==" + }, + "Ckzg.Bindings": { + "type": "CentralTransitive", + "requested": "[2.1.5.1542, )", + "resolved": "2.1.5.1542", + "contentHash": "GFXmOjg5fZ8s+836/HdKiyXbJ+J73wVX6hNmUE6Isb1rA8dI2SMLeW1m48s+ZNIPN9ENJb8Daq1GwmMjBGjUVQ==" + }, + "Microsoft.ClearScript.V8.Native.linux-arm64": { + "type": "CentralTransitive", + "requested": "[7.5.0, )", + "resolved": "7.5.0", + "contentHash": "UU+3Bef3UnwQgP8hKobT09ucYuYubVFiseAsuRUvmjvOBVu7yHRES+nXBNYSvDi88fMTp/HBUknpYQdrfoDemQ==" + }, + "Microsoft.ClearScript.V8.Native.linux-x64": { + "type": "CentralTransitive", + "requested": "[7.5.0, )", + "resolved": "7.5.0", + "contentHash": "snoN9oRwKqShA32IsuCanLjNtP8hros2WOrOBL7g+ED3AV40qwrsbfKwWq37BzogrfsF1aEVoDkBpE19Az7DVQ==" + }, + "Microsoft.ClearScript.V8.Native.osx-arm64": { + "type": "CentralTransitive", + "requested": "[7.5.0, )", + "resolved": "7.5.0", + "contentHash": "CkMgeX0I0+bXUzoaVoJdV86/k0H2PEukqCoZ8zQ28msB6YHeRX6FJTfvOQ0l6UTX5HaBHGG3CWUI04uBYe6M+A==" + }, + "Microsoft.ClearScript.V8.Native.osx-x64": { + "type": "CentralTransitive", + "requested": "[7.5.0, )", + "resolved": "7.5.0", + "contentHash": "yXoXLWKJJgW5V6ez1aMa+ZS2nCef0X4iTYzPS9bTSYl9y7D4R2Ie2KrfR8nLO2rhOKimIMx3MH49Zh1CYruN/g==" + }, + "Microsoft.ClearScript.V8.Native.win-x64": { + "type": "CentralTransitive", + "requested": "[7.5.0, )", + "resolved": "7.5.0", + "contentHash": "DKMxDLboTNflYkwDQ/ELrSf1vXTpew5UZ8xzrXSVKYFBU570VA6NKh1etEGhufuCuDyU7Je5L2g6H+19Dbl+tA==" + }, + "Nethermind.Crypto.Bls": { + "type": "CentralTransitive", + "requested": "[1.0.5, )", + "resolved": "1.0.5", + "contentHash": "SYdQOFVVcC3R3VAm9Dv+u4Mc1yqHZETxt4tN3a+AFeOnEtUmpcjwVwYkkiiUIIrr6vQVVOUuwsDmaa9l3u45IQ==" + }, + "Nethermind.Crypto.SecP256k1": { + "type": "CentralTransitive", + "requested": "[1.5.0, )", + "resolved": "1.5.0", + "contentHash": "+mNlEgN1gYDB6f4jRcYssaE6/AlSoPr7eLDQHQoX+tXcnGRzgnArezPwz82TsWxruQGDh5h9Qfowa0xt4Xz59g==" + }, + "Nethermind.Crypto.SecP256r1": { + "type": "CentralTransitive", + "requested": "[1.0.0-preview.6, )", + "resolved": "1.0.0-preview.6", + "contentHash": "wFfpg1ofZz5nsjN8TAKUg0mdUCskmOUO0lFk3LcoeRkVnQ5Rw2rYzsJxgPFfnxAABH/EPPs62S7oF8E0Ayjjeg==" + }, + "Nethermind.GmpBindings": { + "type": "CentralTransitive", + "requested": "[1.0.3, )", + "resolved": "1.0.3", + "contentHash": "EE12z2k4ku0ugfI01utaQR8EbBoEMLI4QAKKGrfz5Fvbw/YtXTqDDzvKtBTleOB9YBH7oTpH9T9ZFtKgKZMj2g==" + }, + "Nethermind.MclBindings": { + "type": "CentralTransitive", + "requested": "[1.0.3, )", + "resolved": "1.0.3", + "contentHash": "hgT2oiMFqItNXv5vzIbhhlgqPJK4qnOHaYmUiR4jJsaWiqDRH05YtqYeMQq2+oyBOf8REtuGOW5RZ7+agRSEbg==" + }, + "RocksDB": { + "type": "CentralTransitive", + "requested": "[10.4.2.62659, 10.4.2.62659]", + "resolved": "10.4.2.62659", + "contentHash": "+ZY7koKclaRz7+3QiCbXprWK4++Cwh0Hhqj+5Z5fcZpQvoIoo+iM9iAdCo+W5ha9XOLeI0YWbi9nZt12dNVBMg==" + } + }, + "net10.0/osx-arm64": { + "Grpc.Core": { + "type": "Transitive", + "resolved": "2.46.6", + "contentHash": "ZoRg3KmOJ2urTF4+u3H0b1Yv10xzz2Y/flFWS2tnRmj8dbKLeiJaSRqu4LOBD3ova90evqLkVZ85kUkC4JT4lw==", + "dependencies": { + "Grpc.Core.Api": "2.46.6", + "System.Memory": "4.5.3" + } + }, + "libsodium": { + "type": "Transitive", + "resolved": "1.0.20", + "contentHash": "fMO6HpAbvLagobzBH6eU36riWF01lCAweX34D5eugqjuXA+WS5MnV1ngE+2Sw3LvGvxZlmyLp9416t57dMZ5og==" + }, + "Microsoft.Win32.Registry": { + "type": "Transitive", + "resolved": "5.0.0", + "contentHash": "dDoKi0PnDz31yAyETfRntsLArTlVAVzUzCIvvEDsDsucrl33Dl8pIJG06ePTJTI3tGpeyHS9Cq7Foc/s4EeKcg==", + "dependencies": { + "System.Security.AccessControl": "5.0.0", + "System.Security.Principal.Windows": "5.0.0" + } + }, + "runtime.any.System.Collections": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "23g6rqftKmovn2cLeGsuHUYm0FD7pdutb0uQMJpZ3qTvq+zHkgmt6J65VtRry4WDGYlmkMa4xDACtaQ94alNag==", + "dependencies": { + "System.Runtime": "4.3.0" + } + }, + "runtime.any.System.Globalization": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "sMDBnad4rp4t7GY442Jux0MCUuKL4otn5BK6Ni0ARTXTSpRNBzZ7hpMfKSvnVSED5kYJm96YOWsqV0JH0d2uuw==" + }, + "runtime.any.System.IO": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "SDZ5AD1DtyRoxYtEcqQ3HDlcrorMYXZeCt7ZhG9US9I5Vva+gpIWDGMkcwa5XiKL0ceQKRZIX2x0XEjLX7PDzQ==" + }, + "runtime.any.System.Reflection": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "hLC3A3rI8jipR5d9k7+f0MgRCW6texsAp0MWkN/ci18FMtQ9KH7E2vDn/DH2LkxsszlpJpOn9qy6Z6/69rH6eQ==" + }, + "runtime.any.System.Reflection.Extensions": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "cPhT+Vqu52+cQQrDai/V91gubXUnDKNRvlBnH+hOgtGyHdC17aQIU64EaehwAQymd7kJA5rSrVRNfDYrbhnzyA==" + }, + "runtime.any.System.Reflection.Primitives": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "Nrm1p3armp6TTf2xuvaa+jGTTmncALWFq22CpmwRvhDf6dE9ZmH40EbOswD4GnFLrMRS0Ki6Kx5aUPmKK/hZBg==" + }, + "runtime.any.System.Resources.ResourceManager": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "Lxb89SMvf8w9p9+keBLyL6H6x/TEmc6QVsIIA0T36IuyOY3kNvIdyGddA2qt35cRamzxF8K5p0Opq4G4HjNbhQ==" + }, + "runtime.any.System.Runtime": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "fRS7zJgaG9NkifaAxGGclDDoRn9HC7hXACl52Or06a/fxdzDajWb5wov3c6a+gVSlekRoexfjwQSK9sh5um5LQ==", + "dependencies": { + "System.Private.Uri": "4.3.0" + } + }, + "runtime.any.System.Runtime.Handles": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "GG84X6vufoEzqx8PbeBKheE4srOhimv+yLtGb/JkR3Y2FmoqmueLNFU4Xx8Y67plFpltQSdK74x0qlEhIpv/CQ==" + }, + "runtime.any.System.Runtime.InteropServices": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "lBoFeQfxe/4eqjPi46E0LU/YaCMdNkQ8B4MZu/mkzdIAZh8RQ1NYZSj0egrQKdgdvlPFtP4STtob40r4o2DBAw==" + }, + "runtime.any.System.Text.Encoding": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "+ihI5VaXFCMVPJNstG4O4eo1CfbrByLxRrQQTqOTp1ttK0kUKDqOdBSTaCB2IBk/QtjDrs6+x4xuezyMXdm0HQ==" + }, + "runtime.any.System.Threading.Tasks": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "OhBAVBQG5kFj1S+hCEQ3TUHBAEtZ3fbEMgZMRNdN8A0Pj4x+5nTELEqL59DU0TjKVE6II3dqKw4Dklb3szT65w==" + }, + "runtime.debian.8-x64.runtime.native.System.Security.Cryptography.OpenSsl": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "HdSSp5MnJSsg08KMfZThpuLPJpPwE5hBXvHwoKWosyHHfe8Mh5WKT0ylEOf6yNzX6Ngjxe4Whkafh5q7Ymac4Q==" + }, + "runtime.fedora.23-x64.runtime.native.System.Security.Cryptography.OpenSsl": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "+yH1a49wJMy8Zt4yx5RhJrxO/DBDByAiCzNwiETI+1S4mPdCu0OY4djdciC7Vssk0l22wQaDLrXxXkp+3+7bVA==" + }, + "runtime.fedora.24-x64.runtime.native.System.Security.Cryptography.OpenSsl": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "c3YNH1GQJbfIPJeCnr4avseugSqPrxwIqzthYyZDN6EuOyNOzq+y2KSUfRcXauya1sF4foESTgwM5e1A8arAKw==" + }, + "runtime.native.System": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "c/qWt2LieNZIj1jGnVNsE2Kl23Ya2aSTBuXMD6V7k9KWr6l16Tqdwq+hJScEpWER9753NWC8h96PaVNY5Ld7Jw==", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.1.0", + "Microsoft.NETCore.Targets": "1.1.0" + } + }, + "runtime.native.System.Security.Cryptography.OpenSsl": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "NS1U+700m4KFRHR5o4vo9DSlTmlCKu/u7dtE5sUHVIPB+xpXxYQvgBgA6wEIeCz6Yfn0Z52/72WYsToCEPJnrw==", + "dependencies": { + "runtime.debian.8-x64.runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0", + "runtime.fedora.23-x64.runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0", + "runtime.fedora.24-x64.runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0", + "runtime.opensuse.13.2-x64.runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0", + "runtime.opensuse.42.1-x64.runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0", + "runtime.osx.10.10-x64.runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0", + "runtime.rhel.7-x64.runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0", + "runtime.ubuntu.14.04-x64.runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0", + "runtime.ubuntu.16.04-x64.runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0", + "runtime.ubuntu.16.10-x64.runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0" + } + }, + "runtime.opensuse.13.2-x64.runtime.native.System.Security.Cryptography.OpenSsl": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "b3pthNgxxFcD+Pc0WSEoC0+md3MyhRS6aCEeenvNE3Fdw1HyJ18ZhRFVJJzIeR/O/jpxPboB805Ho0T3Ul7w8A==" + }, + "runtime.opensuse.42.1-x64.runtime.native.System.Security.Cryptography.OpenSsl": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "KeLz4HClKf+nFS7p/6Fi/CqyLXh81FpiGzcmuS8DGi9lUqSnZ6Es23/gv2O+1XVGfrbNmviF7CckBpavkBoIFQ==" + }, + "runtime.osx.10.10-x64.runtime.native.System.Security.Cryptography.OpenSsl": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "X7IdhILzr4ROXd8mI1BUCQMSHSQwelUlBjF1JyTKCjXaOGn2fB4EKBxQbCK2VjO3WaWIdlXZL3W6TiIVnrhX4g==" + }, + "runtime.rhel.7-x64.runtime.native.System.Security.Cryptography.OpenSsl": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "nyFNiCk/r+VOiIqreLix8yN+q3Wga9+SE8BCgkf+2BwEKiNx6DyvFjCgkfV743/grxv8jHJ8gUK4XEQw7yzRYg==" + }, + "runtime.ubuntu.14.04-x64.runtime.native.System.Security.Cryptography.OpenSsl": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "ytoewC6wGorL7KoCAvRfsgoJPJbNq+64k2SqW6JcOAebWsFUvCCYgfzQMrnpvPiEl4OrblUlhF2ji+Q1+SVLrQ==" + }, + "runtime.ubuntu.16.04-x64.runtime.native.System.Security.Cryptography.OpenSsl": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "I8bKw2I8k58Wx7fMKQJn2R8lamboCAiHfHeV/pS65ScKWMMI0+wJkLYlEKvgW1D/XvSl/221clBoR2q9QNNM7A==" + }, + "runtime.ubuntu.16.10-x64.runtime.native.System.Security.Cryptography.OpenSsl": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "VB5cn/7OzUfzdnC8tqAIMQciVLiq2epm2NrAm1E9OjNRyG4lVhfR61SMcLizejzQP8R8Uf/0l5qOIbUEi+RdEg==" + }, + "runtime.unix.System.Diagnostics.Debug": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "WV8KLRHWVUVUDduFnvGMHt0FsEt2wK6xPl1EgDKlaMx2KnZ43A/O0GzP8wIuvAC7mq4T9V1mm90r+PXkL9FPdQ==", + "dependencies": { + "runtime.native.System": "4.3.0" + } + }, + "runtime.unix.System.Private.Uri": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "ooWzobr5RAq34r9uan1r/WPXJYG1XWy9KanrxNvEnBzbFdQbMG7Y3bVi4QxR7xZMNLOxLLTAyXvnSkfj5boZSg==", + "dependencies": { + "runtime.native.System": "4.3.0" + } + }, + "runtime.unix.System.Runtime.Extensions": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "zQiTBVpiLftTQZW8GFsV0gjYikB1WMkEPIxF5O6RkUrSV/OgvRRTYgeFQha/0keBpuS0HYweraGRwhfhJ7dj7w==", + "dependencies": { + "System.Private.Uri": "4.3.0", + "runtime.native.System": "4.3.0", + "runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0" + } + }, + "System.Collections": { + "type": "Transitive", + "resolved": "4.0.11", + "contentHash": "YUJGz6eFKqS0V//mLt25vFGrrCvOnsXjlvFQs+KimpwNxug9x0Pzy4PlFMU3Q2IzqAa9G2L4LsK3+9vCBK7oTg==", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.0.1", + "Microsoft.NETCore.Targets": "1.0.1", + "System.Runtime": "4.1.0", + "runtime.any.System.Collections": "4.3.0" + } + }, + "System.Diagnostics.Debug": { + "type": "Transitive", + "resolved": "4.0.11", + "contentHash": "w5U95fVKHY4G8ASs/K5iK3J5LY+/dLFd4vKejsnI/ZhBsWS9hQakfx3Zr7lRWKg4tAw9r4iktyvsTagWkqYCiw==", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.0.1", + "Microsoft.NETCore.Targets": "1.0.1", + "System.Runtime": "4.1.0", + "runtime.unix.System.Diagnostics.Debug": "4.3.0" + } + }, + "System.Diagnostics.EventLog": { + "type": "Transitive", + "resolved": "10.0.0", + "contentHash": "uaFRda9NjtbJRkdx311eXlAA3n2em7223c1A8d1VWyl+4FL9vkG7y2lpPfBU9HYdj/9KgdRNdn1vFK8ZYCYT/A==" + }, + "System.Globalization": { + "type": "Transitive", + "resolved": "4.0.11", + "contentHash": "B95h0YLEL2oSnwF/XjqSWKnwKOy/01VWkNlsCeMTFJLLabflpGV26nK164eRs5GiaRSBGpOxQ3pKoSnnyZN5pg==", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.0.1", + "Microsoft.NETCore.Targets": "1.0.1", + "System.Runtime": "4.1.0", + "runtime.any.System.Globalization": "4.3.0" + } + }, + "System.IO": { + "type": "Transitive", + "resolved": "4.1.0", + "contentHash": "3KlTJceQc3gnGIaHZ7UBZO26SHL1SHE4ddrmiwumFnId+CEHP+O8r386tZKaE6zlk5/mF8vifMBzHj9SaXN+mQ==", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.0.1", + "Microsoft.NETCore.Targets": "1.0.1", + "System.Runtime": "4.1.0", + "System.Text.Encoding": "4.0.11", + "System.Threading.Tasks": "4.0.11", + "runtime.any.System.IO": "4.3.0" + } + }, + "System.Private.Uri": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "I4SwANiUGho1esj4V4oSlPllXjzCZDE+5XXso2P03LW2vOda2Enzh8DWOxwN6hnrJyp314c7KuVu31QYhRzOGg==", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.1.0", + "Microsoft.NETCore.Targets": "1.1.0", + "runtime.unix.System.Private.Uri": "4.3.0" + } + }, + "System.Reflection": { + "type": "Transitive", + "resolved": "4.1.0", + "contentHash": "JCKANJ0TI7kzoQzuwB/OoJANy1Lg338B6+JVacPl4TpUwi3cReg3nMLplMq2uqYfHFQpKIlHAUVAJlImZz/4ng==", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.0.1", + "Microsoft.NETCore.Targets": "1.0.1", + "System.IO": "4.1.0", + "System.Reflection.Primitives": "4.0.1", + "System.Runtime": "4.1.0", + "runtime.any.System.Reflection": "4.3.0" + } + }, + "System.Reflection.Extensions": { + "type": "Transitive", + "resolved": "4.0.1", + "contentHash": "GYrtRsZcMuHF3sbmRHfMYpvxZoIN2bQGrYGerUiWLEkqdEUQZhH3TRSaC/oI4wO0II1RKBPlpIa1TOMxIcOOzQ==", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.0.1", + "Microsoft.NETCore.Targets": "1.0.1", + "System.Reflection": "4.1.0", + "System.Runtime": "4.1.0", + "runtime.any.System.Reflection.Extensions": "4.3.0" + } + }, + "System.Reflection.Primitives": { + "type": "Transitive", + "resolved": "4.0.1", + "contentHash": "4inTox4wTBaDhB7V3mPvp9XlCbeGYWVEM9/fXALd52vNEAVisc1BoVWQPuUuD0Ga//dNbA/WeMy9u9mzLxGTHQ==", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.0.1", + "Microsoft.NETCore.Targets": "1.0.1", + "System.Runtime": "4.1.0", + "runtime.any.System.Reflection.Primitives": "4.3.0" + } + }, + "System.Resources.ResourceManager": { + "type": "Transitive", + "resolved": "4.0.1", + "contentHash": "TxwVeUNoTgUOdQ09gfTjvW411MF+w9MBYL7AtNVc+HtBCFlutPLhUCdZjNkjbhj3bNQWMdHboF0KIWEOjJssbA==", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.0.1", + "Microsoft.NETCore.Targets": "1.0.1", + "System.Globalization": "4.0.11", + "System.Reflection": "4.1.0", + "System.Runtime": "4.1.0", + "runtime.any.System.Resources.ResourceManager": "4.3.0" + } + }, + "System.Runtime": { + "type": "Transitive", + "resolved": "4.3.1", + "contentHash": "abhfv1dTK6NXOmu4bgHIONxHyEqFjW8HwXPmpY9gmll+ix9UNo4XDcmzJn6oLooftxNssVHdJC1pGT9jkSynQg==", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.1.1", + "Microsoft.NETCore.Targets": "1.1.3", + "runtime.any.System.Runtime": "4.3.0" + } + }, + "System.Runtime.Extensions": { + "type": "Transitive", + "resolved": "4.1.0", + "contentHash": "CUOHjTT/vgP0qGW22U4/hDlOqXmcPq5YicBaXdUR2UiUoLwBT+olO6we4DVbq57jeX5uXH2uerVZhf0qGj+sVQ==", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.0.1", + "Microsoft.NETCore.Targets": "1.0.1", + "System.Runtime": "4.1.0", + "runtime.unix.System.Runtime.Extensions": "4.3.0" + } + }, + "System.Runtime.Handles": { + "type": "Transitive", + "resolved": "4.0.1", + "contentHash": "nCJvEKguXEvk2ymk1gqj625vVnlK3/xdGzx0vOKicQkoquaTBJTP13AIYkocSUwHCLNBwUbXTqTWGDxBTWpt7g==", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.0.1", + "Microsoft.NETCore.Targets": "1.0.1", + "System.Runtime": "4.1.0", + "runtime.any.System.Runtime.Handles": "4.3.0" + } + }, + "System.Runtime.InteropServices": { + "type": "Transitive", + "resolved": "4.1.0", + "contentHash": "16eu3kjHS633yYdkjwShDHZLRNMKVi/s0bY8ODiqJ2RfMhDMAwxZaUaWVnZ2P71kr/or+X9o/xFWtNqz8ivieQ==", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.0.1", + "Microsoft.NETCore.Targets": "1.0.1", + "System.Reflection": "4.1.0", + "System.Reflection.Primitives": "4.0.1", + "System.Runtime": "4.1.0", + "System.Runtime.Handles": "4.0.1", + "runtime.any.System.Runtime.InteropServices": "4.3.0" + } + }, + "System.Security.AccessControl": { + "type": "Transitive", + "resolved": "5.0.0", + "contentHash": "dagJ1mHZO3Ani8GH0PHpPEe/oYO+rVdbQjvjJkBRNQkX4t0r1iaeGn8+/ybkSLEan3/slM0t59SVdHzuHf2jmw==", + "dependencies": { + "Microsoft.NETCore.Platforms": "5.0.0", + "System.Security.Principal.Windows": "5.0.0" + } + }, + "System.Security.Cryptography.Pkcs": { + "type": "Transitive", + "resolved": "10.0.0", + "contentHash": "UPWqLSygJlFerRi9XNIuM0a1VC8gHUIufyP24xQ0sc+XimqUAEcjpOz9DhKpyDjH+5B/wO3RpC0KpkEeDj/ddg==" + }, + "System.Security.Principal.Windows": { + "type": "Transitive", + "resolved": "5.0.0", + "contentHash": "t0MGLukB5WAVU9bO3MGzvlGnyJPgUlcwerXn1kzBRjwLKixT96XV0Uza41W49gVd8zEMFu9vQEFlv0IOrytICA==" + }, + "System.Text.Encoding": { + "type": "Transitive", + "resolved": "4.0.11", + "contentHash": "U3gGeMlDZXxCEiY4DwVLSacg+DFWCvoiX+JThA/rvw37Sqrku7sEFeVBBBMBnfB6FeZHsyDx85HlKL19x0HtZA==", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.0.1", + "Microsoft.NETCore.Targets": "1.0.1", + "System.Runtime": "4.1.0", + "runtime.any.System.Text.Encoding": "4.3.0" + } + }, + "System.Text.Encoding.CodePages": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "ZFCILZuOvtKPauZ/j/swhvw68ZRi9ATCfvGbk1QfydmcXBkIWecWKn/250UH7rahZ5OoDBaiAudJtPvLwzw85A==", + "dependencies": { + "System.Runtime.CompilerServices.Unsafe": "6.0.0" + } + }, + "System.Threading.Tasks": { + "type": "Transitive", + "resolved": "4.0.11", + "contentHash": "k1S4Gc6IGwtHGT8188RSeGaX86Qw/wnrgNLshJvsdNUOPP9etMmo8S07c+UlOAx4K/xLuN9ivA1bD0LVurtIxQ==", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.0.1", + "Microsoft.NETCore.Targets": "1.0.1", + "System.Runtime": "4.1.0", + "runtime.any.System.Threading.Tasks": "4.3.0" + } + }, + "Tmds.LibC": { + "type": "Transitive", + "resolved": "0.2.0", + "contentHash": "+RvLuNHOLW7cxzgDe9yHLoayBgjsuH2/gJtJnuVMxweKrxxYT6TwQNAmt06SFWpjwk68aRcwwD4FfMMA6tZvVA==" + }, "Ckzg.Bindings": { "type": "CentralTransitive", "requested": "[2.1.5.1542, )", @@ -1685,32 +3755,415 @@ "resolved": "1.0.3", "contentHash": "EE12z2k4ku0ugfI01utaQR8EbBoEMLI4QAKKGrfz5Fvbw/YtXTqDDzvKtBTleOB9YBH7oTpH9T9ZFtKgKZMj2g==" }, - "Nethermind.MclBindings": { - "type": "CentralTransitive", - "requested": "[1.0.3, )", - "resolved": "1.0.3", - "contentHash": "hgT2oiMFqItNXv5vzIbhhlgqPJK4qnOHaYmUiR4jJsaWiqDRH05YtqYeMQq2+oyBOf8REtuGOW5RZ7+agRSEbg==" + "Nethermind.MclBindings": { + "type": "CentralTransitive", + "requested": "[1.0.3, )", + "resolved": "1.0.3", + "contentHash": "hgT2oiMFqItNXv5vzIbhhlgqPJK4qnOHaYmUiR4jJsaWiqDRH05YtqYeMQq2+oyBOf8REtuGOW5RZ7+agRSEbg==" + }, + "RocksDB": { + "type": "CentralTransitive", + "requested": "[10.4.2.62659, 10.4.2.62659]", + "resolved": "10.4.2.62659", + "contentHash": "+ZY7koKclaRz7+3QiCbXprWK4++Cwh0Hhqj+5Z5fcZpQvoIoo+iM9iAdCo+W5ha9XOLeI0YWbi9nZt12dNVBMg==" + } + }, + "net10.0/osx-x64": { + "Grpc.Core": { + "type": "Transitive", + "resolved": "2.46.6", + "contentHash": "ZoRg3KmOJ2urTF4+u3H0b1Yv10xzz2Y/flFWS2tnRmj8dbKLeiJaSRqu4LOBD3ova90evqLkVZ85kUkC4JT4lw==", + "dependencies": { + "Grpc.Core.Api": "2.46.6", + "System.Memory": "4.5.3" + } + }, + "libsodium": { + "type": "Transitive", + "resolved": "1.0.20", + "contentHash": "fMO6HpAbvLagobzBH6eU36riWF01lCAweX34D5eugqjuXA+WS5MnV1ngE+2Sw3LvGvxZlmyLp9416t57dMZ5og==" + }, + "Microsoft.Win32.Registry": { + "type": "Transitive", + "resolved": "5.0.0", + "contentHash": "dDoKi0PnDz31yAyETfRntsLArTlVAVzUzCIvvEDsDsucrl33Dl8pIJG06ePTJTI3tGpeyHS9Cq7Foc/s4EeKcg==", + "dependencies": { + "System.Security.AccessControl": "5.0.0", + "System.Security.Principal.Windows": "5.0.0" + } + }, + "runtime.any.System.Collections": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "23g6rqftKmovn2cLeGsuHUYm0FD7pdutb0uQMJpZ3qTvq+zHkgmt6J65VtRry4WDGYlmkMa4xDACtaQ94alNag==", + "dependencies": { + "System.Runtime": "4.3.0" + } + }, + "runtime.any.System.Globalization": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "sMDBnad4rp4t7GY442Jux0MCUuKL4otn5BK6Ni0ARTXTSpRNBzZ7hpMfKSvnVSED5kYJm96YOWsqV0JH0d2uuw==" + }, + "runtime.any.System.IO": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "SDZ5AD1DtyRoxYtEcqQ3HDlcrorMYXZeCt7ZhG9US9I5Vva+gpIWDGMkcwa5XiKL0ceQKRZIX2x0XEjLX7PDzQ==" + }, + "runtime.any.System.Reflection": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "hLC3A3rI8jipR5d9k7+f0MgRCW6texsAp0MWkN/ci18FMtQ9KH7E2vDn/DH2LkxsszlpJpOn9qy6Z6/69rH6eQ==" + }, + "runtime.any.System.Reflection.Extensions": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "cPhT+Vqu52+cQQrDai/V91gubXUnDKNRvlBnH+hOgtGyHdC17aQIU64EaehwAQymd7kJA5rSrVRNfDYrbhnzyA==" + }, + "runtime.any.System.Reflection.Primitives": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "Nrm1p3armp6TTf2xuvaa+jGTTmncALWFq22CpmwRvhDf6dE9ZmH40EbOswD4GnFLrMRS0Ki6Kx5aUPmKK/hZBg==" + }, + "runtime.any.System.Resources.ResourceManager": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "Lxb89SMvf8w9p9+keBLyL6H6x/TEmc6QVsIIA0T36IuyOY3kNvIdyGddA2qt35cRamzxF8K5p0Opq4G4HjNbhQ==" + }, + "runtime.any.System.Runtime": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "fRS7zJgaG9NkifaAxGGclDDoRn9HC7hXACl52Or06a/fxdzDajWb5wov3c6a+gVSlekRoexfjwQSK9sh5um5LQ==", + "dependencies": { + "System.Private.Uri": "4.3.0" + } + }, + "runtime.any.System.Runtime.Handles": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "GG84X6vufoEzqx8PbeBKheE4srOhimv+yLtGb/JkR3Y2FmoqmueLNFU4Xx8Y67plFpltQSdK74x0qlEhIpv/CQ==" + }, + "runtime.any.System.Runtime.InteropServices": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "lBoFeQfxe/4eqjPi46E0LU/YaCMdNkQ8B4MZu/mkzdIAZh8RQ1NYZSj0egrQKdgdvlPFtP4STtob40r4o2DBAw==" + }, + "runtime.any.System.Text.Encoding": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "+ihI5VaXFCMVPJNstG4O4eo1CfbrByLxRrQQTqOTp1ttK0kUKDqOdBSTaCB2IBk/QtjDrs6+x4xuezyMXdm0HQ==" + }, + "runtime.any.System.Threading.Tasks": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "OhBAVBQG5kFj1S+hCEQ3TUHBAEtZ3fbEMgZMRNdN8A0Pj4x+5nTELEqL59DU0TjKVE6II3dqKw4Dklb3szT65w==" + }, + "runtime.debian.8-x64.runtime.native.System.Security.Cryptography.OpenSsl": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "HdSSp5MnJSsg08KMfZThpuLPJpPwE5hBXvHwoKWosyHHfe8Mh5WKT0ylEOf6yNzX6Ngjxe4Whkafh5q7Ymac4Q==" + }, + "runtime.fedora.23-x64.runtime.native.System.Security.Cryptography.OpenSsl": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "+yH1a49wJMy8Zt4yx5RhJrxO/DBDByAiCzNwiETI+1S4mPdCu0OY4djdciC7Vssk0l22wQaDLrXxXkp+3+7bVA==" + }, + "runtime.fedora.24-x64.runtime.native.System.Security.Cryptography.OpenSsl": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "c3YNH1GQJbfIPJeCnr4avseugSqPrxwIqzthYyZDN6EuOyNOzq+y2KSUfRcXauya1sF4foESTgwM5e1A8arAKw==" + }, + "runtime.native.System": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "c/qWt2LieNZIj1jGnVNsE2Kl23Ya2aSTBuXMD6V7k9KWr6l16Tqdwq+hJScEpWER9753NWC8h96PaVNY5Ld7Jw==", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.1.0", + "Microsoft.NETCore.Targets": "1.1.0" + } + }, + "runtime.native.System.Security.Cryptography.OpenSsl": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "NS1U+700m4KFRHR5o4vo9DSlTmlCKu/u7dtE5sUHVIPB+xpXxYQvgBgA6wEIeCz6Yfn0Z52/72WYsToCEPJnrw==", + "dependencies": { + "runtime.debian.8-x64.runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0", + "runtime.fedora.23-x64.runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0", + "runtime.fedora.24-x64.runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0", + "runtime.opensuse.13.2-x64.runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0", + "runtime.opensuse.42.1-x64.runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0", + "runtime.osx.10.10-x64.runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0", + "runtime.rhel.7-x64.runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0", + "runtime.ubuntu.14.04-x64.runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0", + "runtime.ubuntu.16.04-x64.runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0", + "runtime.ubuntu.16.10-x64.runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0" + } + }, + "runtime.opensuse.13.2-x64.runtime.native.System.Security.Cryptography.OpenSsl": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "b3pthNgxxFcD+Pc0WSEoC0+md3MyhRS6aCEeenvNE3Fdw1HyJ18ZhRFVJJzIeR/O/jpxPboB805Ho0T3Ul7w8A==" + }, + "runtime.opensuse.42.1-x64.runtime.native.System.Security.Cryptography.OpenSsl": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "KeLz4HClKf+nFS7p/6Fi/CqyLXh81FpiGzcmuS8DGi9lUqSnZ6Es23/gv2O+1XVGfrbNmviF7CckBpavkBoIFQ==" + }, + "runtime.osx.10.10-x64.runtime.native.System.Security.Cryptography.OpenSsl": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "X7IdhILzr4ROXd8mI1BUCQMSHSQwelUlBjF1JyTKCjXaOGn2fB4EKBxQbCK2VjO3WaWIdlXZL3W6TiIVnrhX4g==" + }, + "runtime.rhel.7-x64.runtime.native.System.Security.Cryptography.OpenSsl": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "nyFNiCk/r+VOiIqreLix8yN+q3Wga9+SE8BCgkf+2BwEKiNx6DyvFjCgkfV743/grxv8jHJ8gUK4XEQw7yzRYg==" + }, + "runtime.ubuntu.14.04-x64.runtime.native.System.Security.Cryptography.OpenSsl": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "ytoewC6wGorL7KoCAvRfsgoJPJbNq+64k2SqW6JcOAebWsFUvCCYgfzQMrnpvPiEl4OrblUlhF2ji+Q1+SVLrQ==" + }, + "runtime.ubuntu.16.04-x64.runtime.native.System.Security.Cryptography.OpenSsl": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "I8bKw2I8k58Wx7fMKQJn2R8lamboCAiHfHeV/pS65ScKWMMI0+wJkLYlEKvgW1D/XvSl/221clBoR2q9QNNM7A==" + }, + "runtime.ubuntu.16.10-x64.runtime.native.System.Security.Cryptography.OpenSsl": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "VB5cn/7OzUfzdnC8tqAIMQciVLiq2epm2NrAm1E9OjNRyG4lVhfR61SMcLizejzQP8R8Uf/0l5qOIbUEi+RdEg==" + }, + "runtime.unix.System.Diagnostics.Debug": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "WV8KLRHWVUVUDduFnvGMHt0FsEt2wK6xPl1EgDKlaMx2KnZ43A/O0GzP8wIuvAC7mq4T9V1mm90r+PXkL9FPdQ==", + "dependencies": { + "runtime.native.System": "4.3.0" + } + }, + "runtime.unix.System.Private.Uri": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "ooWzobr5RAq34r9uan1r/WPXJYG1XWy9KanrxNvEnBzbFdQbMG7Y3bVi4QxR7xZMNLOxLLTAyXvnSkfj5boZSg==", + "dependencies": { + "runtime.native.System": "4.3.0" + } + }, + "runtime.unix.System.Runtime.Extensions": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "zQiTBVpiLftTQZW8GFsV0gjYikB1WMkEPIxF5O6RkUrSV/OgvRRTYgeFQha/0keBpuS0HYweraGRwhfhJ7dj7w==", + "dependencies": { + "System.Private.Uri": "4.3.0", + "runtime.native.System": "4.3.0", + "runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0" + } + }, + "System.Collections": { + "type": "Transitive", + "resolved": "4.0.11", + "contentHash": "YUJGz6eFKqS0V//mLt25vFGrrCvOnsXjlvFQs+KimpwNxug9x0Pzy4PlFMU3Q2IzqAa9G2L4LsK3+9vCBK7oTg==", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.0.1", + "Microsoft.NETCore.Targets": "1.0.1", + "System.Runtime": "4.1.0", + "runtime.any.System.Collections": "4.3.0" + } + }, + "System.Diagnostics.Debug": { + "type": "Transitive", + "resolved": "4.0.11", + "contentHash": "w5U95fVKHY4G8ASs/K5iK3J5LY+/dLFd4vKejsnI/ZhBsWS9hQakfx3Zr7lRWKg4tAw9r4iktyvsTagWkqYCiw==", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.0.1", + "Microsoft.NETCore.Targets": "1.0.1", + "System.Runtime": "4.1.0", + "runtime.unix.System.Diagnostics.Debug": "4.3.0" + } + }, + "System.Diagnostics.EventLog": { + "type": "Transitive", + "resolved": "10.0.0", + "contentHash": "uaFRda9NjtbJRkdx311eXlAA3n2em7223c1A8d1VWyl+4FL9vkG7y2lpPfBU9HYdj/9KgdRNdn1vFK8ZYCYT/A==" + }, + "System.Globalization": { + "type": "Transitive", + "resolved": "4.0.11", + "contentHash": "B95h0YLEL2oSnwF/XjqSWKnwKOy/01VWkNlsCeMTFJLLabflpGV26nK164eRs5GiaRSBGpOxQ3pKoSnnyZN5pg==", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.0.1", + "Microsoft.NETCore.Targets": "1.0.1", + "System.Runtime": "4.1.0", + "runtime.any.System.Globalization": "4.3.0" + } + }, + "System.IO": { + "type": "Transitive", + "resolved": "4.1.0", + "contentHash": "3KlTJceQc3gnGIaHZ7UBZO26SHL1SHE4ddrmiwumFnId+CEHP+O8r386tZKaE6zlk5/mF8vifMBzHj9SaXN+mQ==", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.0.1", + "Microsoft.NETCore.Targets": "1.0.1", + "System.Runtime": "4.1.0", + "System.Text.Encoding": "4.0.11", + "System.Threading.Tasks": "4.0.11", + "runtime.any.System.IO": "4.3.0" + } + }, + "System.Private.Uri": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "I4SwANiUGho1esj4V4oSlPllXjzCZDE+5XXso2P03LW2vOda2Enzh8DWOxwN6hnrJyp314c7KuVu31QYhRzOGg==", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.1.0", + "Microsoft.NETCore.Targets": "1.1.0", + "runtime.unix.System.Private.Uri": "4.3.0" + } + }, + "System.Reflection": { + "type": "Transitive", + "resolved": "4.1.0", + "contentHash": "JCKANJ0TI7kzoQzuwB/OoJANy1Lg338B6+JVacPl4TpUwi3cReg3nMLplMq2uqYfHFQpKIlHAUVAJlImZz/4ng==", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.0.1", + "Microsoft.NETCore.Targets": "1.0.1", + "System.IO": "4.1.0", + "System.Reflection.Primitives": "4.0.1", + "System.Runtime": "4.1.0", + "runtime.any.System.Reflection": "4.3.0" + } + }, + "System.Reflection.Extensions": { + "type": "Transitive", + "resolved": "4.0.1", + "contentHash": "GYrtRsZcMuHF3sbmRHfMYpvxZoIN2bQGrYGerUiWLEkqdEUQZhH3TRSaC/oI4wO0II1RKBPlpIa1TOMxIcOOzQ==", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.0.1", + "Microsoft.NETCore.Targets": "1.0.1", + "System.Reflection": "4.1.0", + "System.Runtime": "4.1.0", + "runtime.any.System.Reflection.Extensions": "4.3.0" + } + }, + "System.Reflection.Primitives": { + "type": "Transitive", + "resolved": "4.0.1", + "contentHash": "4inTox4wTBaDhB7V3mPvp9XlCbeGYWVEM9/fXALd52vNEAVisc1BoVWQPuUuD0Ga//dNbA/WeMy9u9mzLxGTHQ==", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.0.1", + "Microsoft.NETCore.Targets": "1.0.1", + "System.Runtime": "4.1.0", + "runtime.any.System.Reflection.Primitives": "4.3.0" + } + }, + "System.Resources.ResourceManager": { + "type": "Transitive", + "resolved": "4.0.1", + "contentHash": "TxwVeUNoTgUOdQ09gfTjvW411MF+w9MBYL7AtNVc+HtBCFlutPLhUCdZjNkjbhj3bNQWMdHboF0KIWEOjJssbA==", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.0.1", + "Microsoft.NETCore.Targets": "1.0.1", + "System.Globalization": "4.0.11", + "System.Reflection": "4.1.0", + "System.Runtime": "4.1.0", + "runtime.any.System.Resources.ResourceManager": "4.3.0" + } + }, + "System.Runtime": { + "type": "Transitive", + "resolved": "4.3.1", + "contentHash": "abhfv1dTK6NXOmu4bgHIONxHyEqFjW8HwXPmpY9gmll+ix9UNo4XDcmzJn6oLooftxNssVHdJC1pGT9jkSynQg==", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.1.1", + "Microsoft.NETCore.Targets": "1.1.3", + "runtime.any.System.Runtime": "4.3.0" + } + }, + "System.Runtime.Extensions": { + "type": "Transitive", + "resolved": "4.1.0", + "contentHash": "CUOHjTT/vgP0qGW22U4/hDlOqXmcPq5YicBaXdUR2UiUoLwBT+olO6we4DVbq57jeX5uXH2uerVZhf0qGj+sVQ==", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.0.1", + "Microsoft.NETCore.Targets": "1.0.1", + "System.Runtime": "4.1.0", + "runtime.unix.System.Runtime.Extensions": "4.3.0" + } + }, + "System.Runtime.Handles": { + "type": "Transitive", + "resolved": "4.0.1", + "contentHash": "nCJvEKguXEvk2ymk1gqj625vVnlK3/xdGzx0vOKicQkoquaTBJTP13AIYkocSUwHCLNBwUbXTqTWGDxBTWpt7g==", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.0.1", + "Microsoft.NETCore.Targets": "1.0.1", + "System.Runtime": "4.1.0", + "runtime.any.System.Runtime.Handles": "4.3.0" + } + }, + "System.Runtime.InteropServices": { + "type": "Transitive", + "resolved": "4.1.0", + "contentHash": "16eu3kjHS633yYdkjwShDHZLRNMKVi/s0bY8ODiqJ2RfMhDMAwxZaUaWVnZ2P71kr/or+X9o/xFWtNqz8ivieQ==", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.0.1", + "Microsoft.NETCore.Targets": "1.0.1", + "System.Reflection": "4.1.0", + "System.Reflection.Primitives": "4.0.1", + "System.Runtime": "4.1.0", + "System.Runtime.Handles": "4.0.1", + "runtime.any.System.Runtime.InteropServices": "4.3.0" + } + }, + "System.Security.AccessControl": { + "type": "Transitive", + "resolved": "5.0.0", + "contentHash": "dagJ1mHZO3Ani8GH0PHpPEe/oYO+rVdbQjvjJkBRNQkX4t0r1iaeGn8+/ybkSLEan3/slM0t59SVdHzuHf2jmw==", + "dependencies": { + "Microsoft.NETCore.Platforms": "5.0.0", + "System.Security.Principal.Windows": "5.0.0" + } + }, + "System.Security.Cryptography.Pkcs": { + "type": "Transitive", + "resolved": "10.0.0", + "contentHash": "UPWqLSygJlFerRi9XNIuM0a1VC8gHUIufyP24xQ0sc+XimqUAEcjpOz9DhKpyDjH+5B/wO3RpC0KpkEeDj/ddg==" + }, + "System.Security.Principal.Windows": { + "type": "Transitive", + "resolved": "5.0.0", + "contentHash": "t0MGLukB5WAVU9bO3MGzvlGnyJPgUlcwerXn1kzBRjwLKixT96XV0Uza41W49gVd8zEMFu9vQEFlv0IOrytICA==" }, - "RocksDB": { - "type": "CentralTransitive", - "requested": "[10.4.2.62659, 10.4.2.62659]", - "resolved": "10.4.2.62659", - "contentHash": "+ZY7koKclaRz7+3QiCbXprWK4++Cwh0Hhqj+5Z5fcZpQvoIoo+iM9iAdCo+W5ha9XOLeI0YWbi9nZt12dNVBMg==" - } - }, - "net10.0/linux-x64": { - "Grpc.Core": { + "System.Text.Encoding": { "type": "Transitive", - "resolved": "2.46.6", - "contentHash": "ZoRg3KmOJ2urTF4+u3H0b1Yv10xzz2Y/flFWS2tnRmj8dbKLeiJaSRqu4LOBD3ova90evqLkVZ85kUkC4JT4lw==", + "resolved": "4.0.11", + "contentHash": "U3gGeMlDZXxCEiY4DwVLSacg+DFWCvoiX+JThA/rvw37Sqrku7sEFeVBBBMBnfB6FeZHsyDx85HlKL19x0HtZA==", "dependencies": { - "Grpc.Core.Api": "2.46.6" + "Microsoft.NETCore.Platforms": "1.0.1", + "Microsoft.NETCore.Targets": "1.0.1", + "System.Runtime": "4.1.0", + "runtime.any.System.Text.Encoding": "4.3.0" } }, - "libsodium": { + "System.Text.Encoding.CodePages": { "type": "Transitive", - "resolved": "1.0.20", - "contentHash": "fMO6HpAbvLagobzBH6eU36riWF01lCAweX34D5eugqjuXA+WS5MnV1ngE+2Sw3LvGvxZlmyLp9416t57dMZ5og==" + "resolved": "6.0.0", + "contentHash": "ZFCILZuOvtKPauZ/j/swhvw68ZRi9ATCfvGbk1QfydmcXBkIWecWKn/250UH7rahZ5OoDBaiAudJtPvLwzw85A==", + "dependencies": { + "System.Runtime.CompilerServices.Unsafe": "6.0.0" + } + }, + "System.Threading.Tasks": { + "type": "Transitive", + "resolved": "4.0.11", + "contentHash": "k1S4Gc6IGwtHGT8188RSeGaX86Qw/wnrgNLshJvsdNUOPP9etMmo8S07c+UlOAx4K/xLuN9ivA1bD0LVurtIxQ==", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.0.1", + "Microsoft.NETCore.Targets": "1.0.1", + "System.Runtime": "4.1.0", + "runtime.any.System.Threading.Tasks": "4.3.0" + } }, "Tmds.LibC": { "type": "Transitive", @@ -1790,13 +4243,14 @@ "contentHash": "+ZY7koKclaRz7+3QiCbXprWK4++Cwh0Hhqj+5Z5fcZpQvoIoo+iM9iAdCo+W5ha9XOLeI0YWbi9nZt12dNVBMg==" } }, - "net10.0/osx-arm64": { + "net10.0/win-x64": { "Grpc.Core": { "type": "Transitive", "resolved": "2.46.6", "contentHash": "ZoRg3KmOJ2urTF4+u3H0b1Yv10xzz2Y/flFWS2tnRmj8dbKLeiJaSRqu4LOBD3ova90evqLkVZ85kUkC4JT4lw==", "dependencies": { - "Grpc.Core.Api": "2.46.6" + "Grpc.Core.Api": "2.46.6", + "System.Memory": "4.5.3" } }, "libsodium": { @@ -1804,189 +4258,297 @@ "resolved": "1.0.20", "contentHash": "fMO6HpAbvLagobzBH6eU36riWF01lCAweX34D5eugqjuXA+WS5MnV1ngE+2Sw3LvGvxZlmyLp9416t57dMZ5og==" }, - "Tmds.LibC": { + "Microsoft.Win32.Registry": { "type": "Transitive", - "resolved": "0.2.0", - "contentHash": "+RvLuNHOLW7cxzgDe9yHLoayBgjsuH2/gJtJnuVMxweKrxxYT6TwQNAmt06SFWpjwk68aRcwwD4FfMMA6tZvVA==" + "resolved": "5.0.0", + "contentHash": "dDoKi0PnDz31yAyETfRntsLArTlVAVzUzCIvvEDsDsucrl33Dl8pIJG06ePTJTI3tGpeyHS9Cq7Foc/s4EeKcg==", + "dependencies": { + "System.Security.AccessControl": "5.0.0", + "System.Security.Principal.Windows": "5.0.0" + } }, - "Ckzg.Bindings": { - "type": "CentralTransitive", - "requested": "[2.1.5.1542, )", - "resolved": "2.1.5.1542", - "contentHash": "GFXmOjg5fZ8s+836/HdKiyXbJ+J73wVX6hNmUE6Isb1rA8dI2SMLeW1m48s+ZNIPN9ENJb8Daq1GwmMjBGjUVQ==" + "runtime.any.System.Collections": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "23g6rqftKmovn2cLeGsuHUYm0FD7pdutb0uQMJpZ3qTvq+zHkgmt6J65VtRry4WDGYlmkMa4xDACtaQ94alNag==", + "dependencies": { + "System.Runtime": "4.3.0" + } }, - "Microsoft.ClearScript.V8.Native.linux-arm64": { - "type": "CentralTransitive", - "requested": "[7.5.0, )", - "resolved": "7.5.0", - "contentHash": "UU+3Bef3UnwQgP8hKobT09ucYuYubVFiseAsuRUvmjvOBVu7yHRES+nXBNYSvDi88fMTp/HBUknpYQdrfoDemQ==" + "runtime.any.System.Globalization": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "sMDBnad4rp4t7GY442Jux0MCUuKL4otn5BK6Ni0ARTXTSpRNBzZ7hpMfKSvnVSED5kYJm96YOWsqV0JH0d2uuw==" }, - "Microsoft.ClearScript.V8.Native.linux-x64": { - "type": "CentralTransitive", - "requested": "[7.5.0, )", - "resolved": "7.5.0", - "contentHash": "snoN9oRwKqShA32IsuCanLjNtP8hros2WOrOBL7g+ED3AV40qwrsbfKwWq37BzogrfsF1aEVoDkBpE19Az7DVQ==" + "runtime.any.System.IO": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "SDZ5AD1DtyRoxYtEcqQ3HDlcrorMYXZeCt7ZhG9US9I5Vva+gpIWDGMkcwa5XiKL0ceQKRZIX2x0XEjLX7PDzQ==" }, - "Microsoft.ClearScript.V8.Native.osx-arm64": { - "type": "CentralTransitive", - "requested": "[7.5.0, )", - "resolved": "7.5.0", - "contentHash": "CkMgeX0I0+bXUzoaVoJdV86/k0H2PEukqCoZ8zQ28msB6YHeRX6FJTfvOQ0l6UTX5HaBHGG3CWUI04uBYe6M+A==" + "runtime.any.System.Reflection": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "hLC3A3rI8jipR5d9k7+f0MgRCW6texsAp0MWkN/ci18FMtQ9KH7E2vDn/DH2LkxsszlpJpOn9qy6Z6/69rH6eQ==" }, - "Microsoft.ClearScript.V8.Native.osx-x64": { - "type": "CentralTransitive", - "requested": "[7.5.0, )", - "resolved": "7.5.0", - "contentHash": "yXoXLWKJJgW5V6ez1aMa+ZS2nCef0X4iTYzPS9bTSYl9y7D4R2Ie2KrfR8nLO2rhOKimIMx3MH49Zh1CYruN/g==" + "runtime.any.System.Reflection.Extensions": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "cPhT+Vqu52+cQQrDai/V91gubXUnDKNRvlBnH+hOgtGyHdC17aQIU64EaehwAQymd7kJA5rSrVRNfDYrbhnzyA==" }, - "Microsoft.ClearScript.V8.Native.win-x64": { - "type": "CentralTransitive", - "requested": "[7.5.0, )", - "resolved": "7.5.0", - "contentHash": "DKMxDLboTNflYkwDQ/ELrSf1vXTpew5UZ8xzrXSVKYFBU570VA6NKh1etEGhufuCuDyU7Je5L2g6H+19Dbl+tA==" + "runtime.any.System.Reflection.Primitives": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "Nrm1p3armp6TTf2xuvaa+jGTTmncALWFq22CpmwRvhDf6dE9ZmH40EbOswD4GnFLrMRS0Ki6Kx5aUPmKK/hZBg==" }, - "Nethermind.Crypto.Bls": { - "type": "CentralTransitive", - "requested": "[1.0.5, )", - "resolved": "1.0.5", - "contentHash": "SYdQOFVVcC3R3VAm9Dv+u4Mc1yqHZETxt4tN3a+AFeOnEtUmpcjwVwYkkiiUIIrr6vQVVOUuwsDmaa9l3u45IQ==" + "runtime.any.System.Resources.ResourceManager": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "Lxb89SMvf8w9p9+keBLyL6H6x/TEmc6QVsIIA0T36IuyOY3kNvIdyGddA2qt35cRamzxF8K5p0Opq4G4HjNbhQ==" }, - "Nethermind.Crypto.SecP256k1": { - "type": "CentralTransitive", - "requested": "[1.5.0, )", - "resolved": "1.5.0", - "contentHash": "+mNlEgN1gYDB6f4jRcYssaE6/AlSoPr7eLDQHQoX+tXcnGRzgnArezPwz82TsWxruQGDh5h9Qfowa0xt4Xz59g==" + "runtime.any.System.Runtime": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "fRS7zJgaG9NkifaAxGGclDDoRn9HC7hXACl52Or06a/fxdzDajWb5wov3c6a+gVSlekRoexfjwQSK9sh5um5LQ==", + "dependencies": { + "System.Private.Uri": "4.3.0" + } }, - "Nethermind.Crypto.SecP256r1": { - "type": "CentralTransitive", - "requested": "[1.0.0-preview.6, )", - "resolved": "1.0.0-preview.6", - "contentHash": "wFfpg1ofZz5nsjN8TAKUg0mdUCskmOUO0lFk3LcoeRkVnQ5Rw2rYzsJxgPFfnxAABH/EPPs62S7oF8E0Ayjjeg==" + "runtime.any.System.Runtime.Handles": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "GG84X6vufoEzqx8PbeBKheE4srOhimv+yLtGb/JkR3Y2FmoqmueLNFU4Xx8Y67plFpltQSdK74x0qlEhIpv/CQ==" }, - "Nethermind.GmpBindings": { - "type": "CentralTransitive", - "requested": "[1.0.3, )", - "resolved": "1.0.3", - "contentHash": "EE12z2k4ku0ugfI01utaQR8EbBoEMLI4QAKKGrfz5Fvbw/YtXTqDDzvKtBTleOB9YBH7oTpH9T9ZFtKgKZMj2g==" + "runtime.any.System.Runtime.InteropServices": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "lBoFeQfxe/4eqjPi46E0LU/YaCMdNkQ8B4MZu/mkzdIAZh8RQ1NYZSj0egrQKdgdvlPFtP4STtob40r4o2DBAw==" }, - "Nethermind.MclBindings": { - "type": "CentralTransitive", - "requested": "[1.0.3, )", - "resolved": "1.0.3", - "contentHash": "hgT2oiMFqItNXv5vzIbhhlgqPJK4qnOHaYmUiR4jJsaWiqDRH05YtqYeMQq2+oyBOf8REtuGOW5RZ7+agRSEbg==" + "runtime.any.System.Text.Encoding": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "+ihI5VaXFCMVPJNstG4O4eo1CfbrByLxRrQQTqOTp1ttK0kUKDqOdBSTaCB2IBk/QtjDrs6+x4xuezyMXdm0HQ==" }, - "RocksDB": { - "type": "CentralTransitive", - "requested": "[10.4.2.62659, 10.4.2.62659]", - "resolved": "10.4.2.62659", - "contentHash": "+ZY7koKclaRz7+3QiCbXprWK4++Cwh0Hhqj+5Z5fcZpQvoIoo+iM9iAdCo+W5ha9XOLeI0YWbi9nZt12dNVBMg==" - } - }, - "net10.0/osx-x64": { - "Grpc.Core": { + "runtime.any.System.Threading.Tasks": { "type": "Transitive", - "resolved": "2.46.6", - "contentHash": "ZoRg3KmOJ2urTF4+u3H0b1Yv10xzz2Y/flFWS2tnRmj8dbKLeiJaSRqu4LOBD3ova90evqLkVZ85kUkC4JT4lw==", + "resolved": "4.3.0", + "contentHash": "OhBAVBQG5kFj1S+hCEQ3TUHBAEtZ3fbEMgZMRNdN8A0Pj4x+5nTELEqL59DU0TjKVE6II3dqKw4Dklb3szT65w==" + }, + "runtime.win.System.Diagnostics.Debug": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "hHHP0WCStene2jjeYcuDkETozUYF/3sHVRHAEOgS3L15hlip24ssqCTnJC28Z03Wpo078oMcJd0H4egD2aJI8g==" + }, + "runtime.win.System.Runtime.Extensions": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "RkgHVhUPvzZxuUubiZe8yr/6CypRVXj0VBzaR8hsqQ8f+rUo7e4PWrHTLOCjd8fBMGWCrY//fi7Ku3qXD7oHRw==", "dependencies": { - "Grpc.Core.Api": "2.46.6" + "System.Private.Uri": "4.3.0" } }, - "libsodium": { + "System.Collections": { "type": "Transitive", - "resolved": "1.0.20", - "contentHash": "fMO6HpAbvLagobzBH6eU36riWF01lCAweX34D5eugqjuXA+WS5MnV1ngE+2Sw3LvGvxZlmyLp9416t57dMZ5og==" + "resolved": "4.0.11", + "contentHash": "YUJGz6eFKqS0V//mLt25vFGrrCvOnsXjlvFQs+KimpwNxug9x0Pzy4PlFMU3Q2IzqAa9G2L4LsK3+9vCBK7oTg==", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.0.1", + "Microsoft.NETCore.Targets": "1.0.1", + "System.Runtime": "4.1.0", + "runtime.any.System.Collections": "4.3.0" + } }, - "Tmds.LibC": { + "System.Diagnostics.Debug": { "type": "Transitive", - "resolved": "0.2.0", - "contentHash": "+RvLuNHOLW7cxzgDe9yHLoayBgjsuH2/gJtJnuVMxweKrxxYT6TwQNAmt06SFWpjwk68aRcwwD4FfMMA6tZvVA==" + "resolved": "4.0.11", + "contentHash": "w5U95fVKHY4G8ASs/K5iK3J5LY+/dLFd4vKejsnI/ZhBsWS9hQakfx3Zr7lRWKg4tAw9r4iktyvsTagWkqYCiw==", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.0.1", + "Microsoft.NETCore.Targets": "1.0.1", + "System.Runtime": "4.1.0", + "runtime.win.System.Diagnostics.Debug": "4.3.0" + } }, - "Ckzg.Bindings": { - "type": "CentralTransitive", - "requested": "[2.1.5.1542, )", - "resolved": "2.1.5.1542", - "contentHash": "GFXmOjg5fZ8s+836/HdKiyXbJ+J73wVX6hNmUE6Isb1rA8dI2SMLeW1m48s+ZNIPN9ENJb8Daq1GwmMjBGjUVQ==" + "System.Diagnostics.EventLog": { + "type": "Transitive", + "resolved": "10.0.0", + "contentHash": "uaFRda9NjtbJRkdx311eXlAA3n2em7223c1A8d1VWyl+4FL9vkG7y2lpPfBU9HYdj/9KgdRNdn1vFK8ZYCYT/A==" }, - "Microsoft.ClearScript.V8.Native.linux-arm64": { - "type": "CentralTransitive", - "requested": "[7.5.0, )", - "resolved": "7.5.0", - "contentHash": "UU+3Bef3UnwQgP8hKobT09ucYuYubVFiseAsuRUvmjvOBVu7yHRES+nXBNYSvDi88fMTp/HBUknpYQdrfoDemQ==" + "System.Globalization": { + "type": "Transitive", + "resolved": "4.0.11", + "contentHash": "B95h0YLEL2oSnwF/XjqSWKnwKOy/01VWkNlsCeMTFJLLabflpGV26nK164eRs5GiaRSBGpOxQ3pKoSnnyZN5pg==", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.0.1", + "Microsoft.NETCore.Targets": "1.0.1", + "System.Runtime": "4.1.0", + "runtime.any.System.Globalization": "4.3.0" + } }, - "Microsoft.ClearScript.V8.Native.linux-x64": { - "type": "CentralTransitive", - "requested": "[7.5.0, )", - "resolved": "7.5.0", - "contentHash": "snoN9oRwKqShA32IsuCanLjNtP8hros2WOrOBL7g+ED3AV40qwrsbfKwWq37BzogrfsF1aEVoDkBpE19Az7DVQ==" + "System.IO": { + "type": "Transitive", + "resolved": "4.1.0", + "contentHash": "3KlTJceQc3gnGIaHZ7UBZO26SHL1SHE4ddrmiwumFnId+CEHP+O8r386tZKaE6zlk5/mF8vifMBzHj9SaXN+mQ==", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.0.1", + "Microsoft.NETCore.Targets": "1.0.1", + "System.Runtime": "4.1.0", + "System.Text.Encoding": "4.0.11", + "System.Threading.Tasks": "4.0.11", + "runtime.any.System.IO": "4.3.0" + } }, - "Microsoft.ClearScript.V8.Native.osx-arm64": { - "type": "CentralTransitive", - "requested": "[7.5.0, )", - "resolved": "7.5.0", - "contentHash": "CkMgeX0I0+bXUzoaVoJdV86/k0H2PEukqCoZ8zQ28msB6YHeRX6FJTfvOQ0l6UTX5HaBHGG3CWUI04uBYe6M+A==" + "System.Private.Uri": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "I4SwANiUGho1esj4V4oSlPllXjzCZDE+5XXso2P03LW2vOda2Enzh8DWOxwN6hnrJyp314c7KuVu31QYhRzOGg==", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.1.0", + "Microsoft.NETCore.Targets": "1.1.0" + } }, - "Microsoft.ClearScript.V8.Native.osx-x64": { - "type": "CentralTransitive", - "requested": "[7.5.0, )", - "resolved": "7.5.0", - "contentHash": "yXoXLWKJJgW5V6ez1aMa+ZS2nCef0X4iTYzPS9bTSYl9y7D4R2Ie2KrfR8nLO2rhOKimIMx3MH49Zh1CYruN/g==" + "System.Reflection": { + "type": "Transitive", + "resolved": "4.1.0", + "contentHash": "JCKANJ0TI7kzoQzuwB/OoJANy1Lg338B6+JVacPl4TpUwi3cReg3nMLplMq2uqYfHFQpKIlHAUVAJlImZz/4ng==", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.0.1", + "Microsoft.NETCore.Targets": "1.0.1", + "System.IO": "4.1.0", + "System.Reflection.Primitives": "4.0.1", + "System.Runtime": "4.1.0", + "runtime.any.System.Reflection": "4.3.0" + } }, - "Microsoft.ClearScript.V8.Native.win-x64": { - "type": "CentralTransitive", - "requested": "[7.5.0, )", - "resolved": "7.5.0", - "contentHash": "DKMxDLboTNflYkwDQ/ELrSf1vXTpew5UZ8xzrXSVKYFBU570VA6NKh1etEGhufuCuDyU7Je5L2g6H+19Dbl+tA==" + "System.Reflection.Extensions": { + "type": "Transitive", + "resolved": "4.0.1", + "contentHash": "GYrtRsZcMuHF3sbmRHfMYpvxZoIN2bQGrYGerUiWLEkqdEUQZhH3TRSaC/oI4wO0II1RKBPlpIa1TOMxIcOOzQ==", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.0.1", + "Microsoft.NETCore.Targets": "1.0.1", + "System.Reflection": "4.1.0", + "System.Runtime": "4.1.0", + "runtime.any.System.Reflection.Extensions": "4.3.0" + } }, - "Nethermind.Crypto.Bls": { - "type": "CentralTransitive", - "requested": "[1.0.5, )", - "resolved": "1.0.5", - "contentHash": "SYdQOFVVcC3R3VAm9Dv+u4Mc1yqHZETxt4tN3a+AFeOnEtUmpcjwVwYkkiiUIIrr6vQVVOUuwsDmaa9l3u45IQ==" + "System.Reflection.Primitives": { + "type": "Transitive", + "resolved": "4.0.1", + "contentHash": "4inTox4wTBaDhB7V3mPvp9XlCbeGYWVEM9/fXALd52vNEAVisc1BoVWQPuUuD0Ga//dNbA/WeMy9u9mzLxGTHQ==", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.0.1", + "Microsoft.NETCore.Targets": "1.0.1", + "System.Runtime": "4.1.0", + "runtime.any.System.Reflection.Primitives": "4.3.0" + } }, - "Nethermind.Crypto.SecP256k1": { - "type": "CentralTransitive", - "requested": "[1.5.0, )", - "resolved": "1.5.0", - "contentHash": "+mNlEgN1gYDB6f4jRcYssaE6/AlSoPr7eLDQHQoX+tXcnGRzgnArezPwz82TsWxruQGDh5h9Qfowa0xt4Xz59g==" + "System.Resources.ResourceManager": { + "type": "Transitive", + "resolved": "4.0.1", + "contentHash": "TxwVeUNoTgUOdQ09gfTjvW411MF+w9MBYL7AtNVc+HtBCFlutPLhUCdZjNkjbhj3bNQWMdHboF0KIWEOjJssbA==", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.0.1", + "Microsoft.NETCore.Targets": "1.0.1", + "System.Globalization": "4.0.11", + "System.Reflection": "4.1.0", + "System.Runtime": "4.1.0", + "runtime.any.System.Resources.ResourceManager": "4.3.0" + } }, - "Nethermind.Crypto.SecP256r1": { - "type": "CentralTransitive", - "requested": "[1.0.0-preview.6, )", - "resolved": "1.0.0-preview.6", - "contentHash": "wFfpg1ofZz5nsjN8TAKUg0mdUCskmOUO0lFk3LcoeRkVnQ5Rw2rYzsJxgPFfnxAABH/EPPs62S7oF8E0Ayjjeg==" + "System.Runtime": { + "type": "Transitive", + "resolved": "4.3.1", + "contentHash": "abhfv1dTK6NXOmu4bgHIONxHyEqFjW8HwXPmpY9gmll+ix9UNo4XDcmzJn6oLooftxNssVHdJC1pGT9jkSynQg==", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.1.1", + "Microsoft.NETCore.Targets": "1.1.3", + "runtime.any.System.Runtime": "4.3.0" + } }, - "Nethermind.GmpBindings": { - "type": "CentralTransitive", - "requested": "[1.0.3, )", - "resolved": "1.0.3", - "contentHash": "EE12z2k4ku0ugfI01utaQR8EbBoEMLI4QAKKGrfz5Fvbw/YtXTqDDzvKtBTleOB9YBH7oTpH9T9ZFtKgKZMj2g==" + "System.Runtime.Extensions": { + "type": "Transitive", + "resolved": "4.1.0", + "contentHash": "CUOHjTT/vgP0qGW22U4/hDlOqXmcPq5YicBaXdUR2UiUoLwBT+olO6we4DVbq57jeX5uXH2uerVZhf0qGj+sVQ==", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.0.1", + "Microsoft.NETCore.Targets": "1.0.1", + "System.Runtime": "4.1.0", + "runtime.win.System.Runtime.Extensions": "4.3.0" + } }, - "Nethermind.MclBindings": { - "type": "CentralTransitive", - "requested": "[1.0.3, )", - "resolved": "1.0.3", - "contentHash": "hgT2oiMFqItNXv5vzIbhhlgqPJK4qnOHaYmUiR4jJsaWiqDRH05YtqYeMQq2+oyBOf8REtuGOW5RZ7+agRSEbg==" + "System.Runtime.Handles": { + "type": "Transitive", + "resolved": "4.0.1", + "contentHash": "nCJvEKguXEvk2ymk1gqj625vVnlK3/xdGzx0vOKicQkoquaTBJTP13AIYkocSUwHCLNBwUbXTqTWGDxBTWpt7g==", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.0.1", + "Microsoft.NETCore.Targets": "1.0.1", + "System.Runtime": "4.1.0", + "runtime.any.System.Runtime.Handles": "4.3.0" + } }, - "RocksDB": { - "type": "CentralTransitive", - "requested": "[10.4.2.62659, 10.4.2.62659]", - "resolved": "10.4.2.62659", - "contentHash": "+ZY7koKclaRz7+3QiCbXprWK4++Cwh0Hhqj+5Z5fcZpQvoIoo+iM9iAdCo+W5ha9XOLeI0YWbi9nZt12dNVBMg==" - } - }, - "net10.0/win-x64": { - "Grpc.Core": { + "System.Runtime.InteropServices": { "type": "Transitive", - "resolved": "2.46.6", - "contentHash": "ZoRg3KmOJ2urTF4+u3H0b1Yv10xzz2Y/flFWS2tnRmj8dbKLeiJaSRqu4LOBD3ova90evqLkVZ85kUkC4JT4lw==", + "resolved": "4.1.0", + "contentHash": "16eu3kjHS633yYdkjwShDHZLRNMKVi/s0bY8ODiqJ2RfMhDMAwxZaUaWVnZ2P71kr/or+X9o/xFWtNqz8ivieQ==", "dependencies": { - "Grpc.Core.Api": "2.46.6" + "Microsoft.NETCore.Platforms": "1.0.1", + "Microsoft.NETCore.Targets": "1.0.1", + "System.Reflection": "4.1.0", + "System.Reflection.Primitives": "4.0.1", + "System.Runtime": "4.1.0", + "System.Runtime.Handles": "4.0.1", + "runtime.any.System.Runtime.InteropServices": "4.3.0" } }, - "libsodium": { + "System.Security.AccessControl": { "type": "Transitive", - "resolved": "1.0.20", - "contentHash": "fMO6HpAbvLagobzBH6eU36riWF01lCAweX34D5eugqjuXA+WS5MnV1ngE+2Sw3LvGvxZlmyLp9416t57dMZ5og==" + "resolved": "5.0.0", + "contentHash": "dagJ1mHZO3Ani8GH0PHpPEe/oYO+rVdbQjvjJkBRNQkX4t0r1iaeGn8+/ybkSLEan3/slM0t59SVdHzuHf2jmw==", + "dependencies": { + "Microsoft.NETCore.Platforms": "5.0.0", + "System.Security.Principal.Windows": "5.0.0" + } + }, + "System.Security.Cryptography.Pkcs": { + "type": "Transitive", + "resolved": "10.0.0", + "contentHash": "UPWqLSygJlFerRi9XNIuM0a1VC8gHUIufyP24xQ0sc+XimqUAEcjpOz9DhKpyDjH+5B/wO3RpC0KpkEeDj/ddg==" + }, + "System.Security.Principal.Windows": { + "type": "Transitive", + "resolved": "5.0.0", + "contentHash": "t0MGLukB5WAVU9bO3MGzvlGnyJPgUlcwerXn1kzBRjwLKixT96XV0Uza41W49gVd8zEMFu9vQEFlv0IOrytICA==" + }, + "System.Text.Encoding": { + "type": "Transitive", + "resolved": "4.0.11", + "contentHash": "U3gGeMlDZXxCEiY4DwVLSacg+DFWCvoiX+JThA/rvw37Sqrku7sEFeVBBBMBnfB6FeZHsyDx85HlKL19x0HtZA==", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.0.1", + "Microsoft.NETCore.Targets": "1.0.1", + "System.Runtime": "4.1.0", + "runtime.any.System.Text.Encoding": "4.3.0" + } + }, + "System.Text.Encoding.CodePages": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "ZFCILZuOvtKPauZ/j/swhvw68ZRi9ATCfvGbk1QfydmcXBkIWecWKn/250UH7rahZ5OoDBaiAudJtPvLwzw85A==", + "dependencies": { + "System.Runtime.CompilerServices.Unsafe": "6.0.0" + } + }, + "System.Threading.Tasks": { + "type": "Transitive", + "resolved": "4.0.11", + "contentHash": "k1S4Gc6IGwtHGT8188RSeGaX86Qw/wnrgNLshJvsdNUOPP9etMmo8S07c+UlOAx4K/xLuN9ivA1bD0LVurtIxQ==", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.0.1", + "Microsoft.NETCore.Targets": "1.0.1", + "System.Runtime": "4.1.0", + "runtime.any.System.Threading.Tasks": "4.3.0" + } }, "Tmds.LibC": { "type": "Transitive", diff --git a/src/Nethermind/Nethermind.State.Test/Nethermind.State.Test.csproj b/src/Nethermind/Nethermind.State.Test/Nethermind.State.Test.csproj index 4d0fd2ba57d..c1a4530f927 100644 --- a/src/Nethermind/Nethermind.State.Test/Nethermind.State.Test.csproj +++ b/src/Nethermind/Nethermind.State.Test/Nethermind.State.Test.csproj @@ -1,7 +1,7 @@ - + Nethermind.Store.Test diff --git a/src/Nethermind/Nethermind.State.Test/SpmcRingBufferTests.cs b/src/Nethermind/Nethermind.State.Test/SpmcRingBufferTests.cs new file mode 100644 index 00000000000..84f10cf8057 --- /dev/null +++ b/src/Nethermind/Nethermind.State.Test/SpmcRingBufferTests.cs @@ -0,0 +1,55 @@ +// SPDX-FileCopyrightText: 2025 Demerzel Solutions Limited +// SPDX-License-Identifier: LGPL-3.0-only + +using FluentAssertions; +using Nethermind.State; +using NUnit.Framework; + +namespace Nethermind.Store.Test; + +public class SpmcRingBufferTests +{ + + [Test] + public void SmokeTest() + { + SpmcRingBuffer jobQueue = new SpmcRingBuffer(16); + + jobQueue.TryEnqueue(1); + jobQueue.TryEnqueue(2); + jobQueue.TryEnqueue(3); + jobQueue.TryEnqueue(4); + jobQueue.TryEnqueue(5); + + jobQueue.TryDequeue(out int j).Should().BeTrue(); + j.Should().Be(1); + jobQueue.TryDequeue(out j).Should().BeTrue(); + j.Should().Be(2); + jobQueue.TryDequeue(out j).Should().BeTrue(); + j.Should().Be(3); + jobQueue.TryDequeue(out j).Should().BeTrue(); + j.Should().Be(4); + jobQueue.TryDequeue(out j).Should().BeTrue(); + j.Should().Be(5); + } + + [Test] + public void RollingSmokeTest() + { + SpmcRingBuffer jobQueue = new SpmcRingBuffer(16); + + jobQueue.TryEnqueue(1); + jobQueue.TryEnqueue(2); + jobQueue.TryEnqueue(3); + jobQueue.TryEnqueue(4); + jobQueue.TryEnqueue(5); + + int j = 0; + for (int i = 0; i < 100; i++) + { + jobQueue.TryDequeue(out j).Should().BeTrue(); + j.Should().Be(i + 1); + jobQueue.TryEnqueue(i + 5 + 1).Should().BeTrue(); + } + } +} diff --git a/src/Nethermind/Nethermind.State.Test/StorageProviderTests.cs b/src/Nethermind/Nethermind.State.Test/StorageProviderTests.cs index cc25eea210a..d359da04eb0 100644 --- a/src/Nethermind/Nethermind.State.Test/StorageProviderTests.cs +++ b/src/Nethermind/Nethermind.State.Test/StorageProviderTests.cs @@ -760,6 +760,11 @@ public void Commit(long blockNumber) { baseScope.Commit(blockNumber); } + + public void HintSet(Address address) + { + + } } private class WriteBatchDecorator( diff --git a/src/Nethermind/Nethermind.State/Flat/CachedResource.cs b/src/Nethermind/Nethermind.State/Flat/CachedResource.cs new file mode 100644 index 00000000000..1c3b82f3305 --- /dev/null +++ b/src/Nethermind/Nethermind.State/Flat/CachedResource.cs @@ -0,0 +1,25 @@ +// SPDX-FileCopyrightText: 2025 Demerzel Solutions Limited +// SPDX-License-Identifier: LGPL-3.0-only + +using System.Collections.Concurrent; +using Nethermind.Core; +using Nethermind.Core.Collections; +using Nethermind.Core.Crypto; +using Nethermind.Int256; +using Nethermind.Trie; + +namespace Nethermind.State.Flat; + +public record CachedResource( + ConcurrentDictionary TrieWarmerLoadedNodes, + ConcurrentDictionary<(Hash256AsKey, TreePath), TrieNode> LoadedStorageNodes, + ConcurrentDictionary<(AddressAsKey, UInt256?), bool> PrewarmedAddresses +) +{ + public void Clear() + { + TrieWarmerLoadedNodes.NoResizeClear(); + LoadedStorageNodes.NoResizeClear(); + PrewarmedAddresses.NoResizeClear(); + } +} diff --git a/src/Nethermind/Nethermind.State/Flat/FlatDbColumns.cs b/src/Nethermind/Nethermind.State/Flat/FlatDbColumns.cs new file mode 100644 index 00000000000..2d94c7a14cf --- /dev/null +++ b/src/Nethermind/Nethermind.State/Flat/FlatDbColumns.cs @@ -0,0 +1,15 @@ +// SPDX-FileCopyrightText: 2025 Demerzel Solutions Limited +// SPDX-License-Identifier: LGPL-3.0-only + +namespace Nethermind.State.Flat; + +public enum FlatDbColumns +{ + Metadata, + State, + Storage, + StateNodes, + StateTopNodes, + StorageNodes, + StorageTopNodes, +} diff --git a/src/Nethermind/Nethermind.State/Flat/FlatDiffRepository.cs b/src/Nethermind/Nethermind.State/Flat/FlatDiffRepository.cs new file mode 100644 index 00000000000..33e1ca6757b --- /dev/null +++ b/src/Nethermind/Nethermind.State/Flat/FlatDiffRepository.cs @@ -0,0 +1,906 @@ +// SPDX-FileCopyrightText: 2025 Demerzel Solutions Limited +// SPDX-License-Identifier: LGPL-3.0-only + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using System.Threading; +using System.Threading.Channels; +using System.Threading.Tasks; +using Nethermind.Config; +using Nethermind.Core; +using Nethermind.Core.Collections; +using Nethermind.Core.Crypto; +using Nethermind.Core.Extensions; +using Nethermind.Int256; +using Nethermind.Logging; +using Nethermind.State.Flat.Persistence; +using Nethermind.State.Flat.ScopeProvider; +using Nethermind.Trie; +using Nethermind.Trie.Pruning; +using Prometheus; + +namespace Nethermind.State.Flat; + +public class FlatDiffRepository : IFlatDiffRepository, IAsyncDisposable +{ + private ReaderWriterLockSlim _repoLock = new ReaderWriterLockSlim(); // Note: lock is for proteccting in memory and compacted states only + private readonly ICanonicalStateRootFinder _stateRootFinder; + private Dictionary _compactedKnownStates = new(); + private InMemorySnapshotStore _inMemorySnapshotStore; + private ResourcePool _resourcePool; + private List<(Hash256AsKey, TreePath)> _trieNodesSortBuffer = new List<(Hash256AsKey, TreePath)>(); // Presort make it faster + private readonly Task _compactorTask; + + private Lock _readerCacheLock = new Lock(); + private RefCountingPersistenceReader? _cachedReader = null; + private readonly TrieNodeCache _trieNodeCache; + private readonly Task _persistenceTask; + + private IPersistence _persistence; + private int _boundary; + + private Channel<(StateId, CachedResource)> _compactorJobs; + private Channel _persistenceJob; + private long _compactSize; + private long _compactEveryBlockNum; + private readonly bool _inlineCompaction; + private ILogger _logger; + private StateId _currentPersistedState; + + private static Histogram _flatdiffimes = DevMetric.Factory.CreateHistogram("flatdiff_times", "aha", new HistogramConfiguration() + { + LabelNames = new[] { "category", "type" }, + // Buckets = Histogram.PowersOfTenDividedBuckets(2, 12, 5) + Buckets = [1] + }); + + private static Gauge _knownStatesMemory = DevMetric.Factory.CreateGauge("flatdiff_knownstates_memory", "memory", "category"); + private static Gauge _compactedMemory = DevMetric.Factory.CreateGauge("flatdiff_compacted_memory", "memory", "category"); + + public event EventHandler? ReorgBoundaryReached; + + public record Configuration( + int MaxInFlightCompactJob = 32, + int CompactSize = 64, + int CompactInterval = 4, + int ConcurrentCompactor = 4, + int Boundary = 128, + long TrieCacheMemoryTarget = 2_000_000_000, + bool VerifyWithTrie = false, + bool ReadWithTrie = false, + bool InlineCompaction = false, + bool DisableTrieWarmer = false + ) + { + } + + public FlatDiffRepository( + IProcessExitSource exitSource, + ICanonicalStateRootFinder stateRootFinder, + IPersistence persistedPersistence, + ResourcePool resourcePool, + ILogManager logManager, + Configuration? config = null) + { + if (config is null) config = new Configuration(); + _inMemorySnapshotStore = new InMemorySnapshotStore(); + _persistence = persistedPersistence; + _compactSize = config.CompactSize; + _compactEveryBlockNum = config.CompactInterval; + _inlineCompaction = config.InlineCompaction; + _stateRootFinder = stateRootFinder; + _resourcePool = resourcePool; + _logger = logManager.GetClassLogger(); + + _compactorJobs = Channel.CreateBounded<(StateId, CachedResource)>(config.MaxInFlightCompactJob); + _persistenceJob = Channel.CreateBounded(config.MaxInFlightCompactJob); + _boundary = config.Boundary; + + using var reader = LeaseReader(); + _currentPersistedState = reader.CurrentState; + _trieNodeCache = new TrieNodeCache(config.TrieCacheMemoryTarget, logManager); + + _compactorTask = RunCompactor(exitSource.Token); + _persistenceTask = RunPersistence(exitSource.Token); + } + + + public IPersistence.IPersistenceReader LeaseReader() + { + using var _ = _readerCacheLock.EnterScope(); + var cachedReader = _cachedReader; + if (cachedReader is null) + { + _cachedReader = cachedReader = new RefCountingPersistenceReader( + _persistence.CreateReader(), + _logger + ); + } + + cachedReader.AcquireLease(); + return cachedReader; + } + + private void ClearReaderCache() + { + using var _ = _readerCacheLock.EnterScope(); + RefCountingPersistenceReader? cachedReader = _cachedReader; + _cachedReader = null; + cachedReader?.Dispose(); + } + + private async Task RunCompactor(CancellationToken cancellationToken) + { + try + { + await foreach (var (stateId, cachedResource) in _compactorJobs.Reader.ReadAllAsync(cancellationToken)) + { + try + { + CompactLevel(stateId, cachedResource); + if (stateId.blockNumber % _compactSize == 0) + { + await _persistenceJob.Writer.WriteAsync(stateId, cancellationToken); + } + } + catch (Exception ex) + { + _logger.Error("Compact job failed", ex); + throw; + } + } + } + catch (OperationCanceledException) + { + } + } + + private async Task RunPersistence(CancellationToken cancellationToken) + { + try + { + await foreach (var _ in _persistenceJob.Reader.ReadAllAsync(cancellationToken)) + { + try + { + await PersistIfNeeded(); + } + catch (Exception ex) + { + _logger.Error("Persistence job failed", ex); + throw; + } + } + } + catch (OperationCanceledException) + { + } + } + + private async Task NotifyWhenSlow(string name, Action closure) + { + Task jobTask = Task.Run(() => + { + try + { + closure(); + } + catch (Exception ex) + { + _logger.Error($"job {name} failed", ex); + Environment.Exit(1); + throw; + } + }); + Task waiterTask = Task.Run(async () => + { + Stopwatch sw = Stopwatch.StartNew(); + while (true) + { + await Task.Delay(1000); + if (jobTask.IsCompleted) break; + _logger.Info($"Task {name} took {sw.Elapsed}"); + } + }); + + await Task.WhenAny(jobTask, waiterTask); + } + + private void RunCompactJob(StateId stateId, CachedResource cachedResource) + { + CompactLevel(stateId, cachedResource); + PersistIfNeeded().Wait(); + } + + private RepolockReadExiter EnterRepolockReadOnly() + { + _repoLock.EnterReadLock(); + + return new RepolockReadExiter(_repoLock, true); + } + + private RepolockReadExiter EnterRepolock() + { + _repoLock.EnterWriteLock(); + + return new RepolockReadExiter(_repoLock, false); + } + + private ref struct RepolockReadExiter(ReaderWriterLockSlim @lock, bool read) : IDisposable + { + public void Dispose() + { + if (read) + { + @lock.ExitReadLock(); + } + else + { + @lock.ExitWriteLock(); + } + } + } + + private void CompactLevel(StateId stateId, CachedResource cachedResource) + { + try + { + if (PopulateTrieNodeCache(cachedResource)) return; + + if (_compactSize <= 1) return; // Disabled + long blockNumber = stateId.blockNumber; + if (blockNumber == 0) return; + if (blockNumber % _compactSize != 0) + { + using (EnterRepolockReadOnly()) + { + StateId? last = _inMemorySnapshotStore.GetLast(); + if (last != null && last.Value.blockNumber - blockNumber > 1) + { + // To slow. Just skip this block number. + return; + } + } + + if (blockNumber % _compactEveryBlockNum != 0) return; + } + + long startingBlockNumber = ((blockNumber - 1) / _compactSize) * _compactSize; + + using SnapshotBundle gatheredCache = GatherCache(stateId, IFlatDiffRepository.SnapshotBundleUsage.Compactor, startingBlockNumber); + if (gatheredCache.SnapshotCount == 1) + { + return; + } + + if (_logger.IsDebug) _logger.Debug($"Compacting {stateId}"); + long sw = Stopwatch.GetTimestamp(); + Snapshot snapshot = gatheredCache.CompactToKnownState(); + _flatdiffimes.WithLabels("compaction", "compact_to_known_state").Observe(Stopwatch.GetTimestamp() - sw); + sw = Stopwatch.GetTimestamp(); + Dictionary memory = snapshot.EstimateMemory(); + + using (EnterRepolock()) + { + _flatdiffimes.WithLabels("compaction", "add_repolock").Observe(Stopwatch.GetTimestamp() - sw); + sw = Stopwatch.GetTimestamp(); + + if (_logger.IsDebug) _logger.Debug($"Compacted {gatheredCache.SnapshotCount} to {stateId}"); + + if (_compactedKnownStates.TryAdd(stateId, snapshot)) + { + foreach (var keyValuePair in memory) + { + _compactedMemory.WithLabels(keyValuePair.Key.ToString()).Inc(keyValuePair.Value); + } + _compactedMemory.WithLabels("count").Inc(1); + } + else + { + snapshot.Dispose(); + } + + _flatdiffimes.WithLabels("compaction", "add_and_measure").Observe(Stopwatch.GetTimestamp() - sw); + sw = Stopwatch.GetTimestamp(); + + if (stateId.blockNumber % _compactSize != 0) + { + // Save memory + foreach (var id in _inMemorySnapshotStore.GetStatesAtBlockNumber(stateId.blockNumber - _compactSize)) + { + RemoveAndReleaseCompactedKnownState(id); + } + } + + _flatdiffimes.WithLabels("compaction", "cleanup_compacted").Observe(Stopwatch.GetTimestamp() - sw); + } + } + catch (Exception e) + { + _logger.Error($"Compactor failed {e}"); + } + } + + private bool PopulateTrieNodeCache(CachedResource cachedResource) + { + Snapshot lastSnapshot; + using (EnterRepolockReadOnly()) + { + StateId? last = _inMemorySnapshotStore.GetLast(); + if (last == null) return true; + if (!TryLeaseState(last.Value, out lastSnapshot)) return true; + } + + try + { + var memory = lastSnapshot.EstimateMemory(); // Note: This is slow, do it outside. + foreach (var keyValuePair in memory) + { + _knownStatesMemory.WithLabels(keyValuePair.Key.ToString()).Inc(keyValuePair.Value); + } + _knownStatesMemory.WithLabels("count").Inc(1); + + long sw = Stopwatch.GetTimestamp(); + _trieNodeCache.Add(lastSnapshot, cachedResource); + _flatdiffimes.WithLabels("compaction", "add_to_trienode_cache").Observe(Stopwatch.GetTimestamp() - sw); + _resourcePool.ReturnCachedResource(IFlatDiffRepository.SnapshotBundleUsage.MainBlockProcessing, cachedResource); + return false; + } + finally + { + lastSnapshot.Dispose(); + } + } + + public SnapshotBundle? GatherReaderAtBaseBlock(StateId baseBlock, IFlatDiffRepository.SnapshotBundleUsage usage) + { + // TODO: Throw if not enough or return null + return GatherCache(baseBlock, usage, null); + } + + private static Histogram _knownStatesSize = DevMetric.Factory.CreateHistogram("flatdiff_known_state_size", "timer", + new HistogramConfiguration() + { + LabelNames = ["part"], + // Buckets = Histogram.LinearBuckets(0, 1, 100) + Buckets = [1] + }); + + private SnapshotBundle GatherCache(StateId baseBlock, IFlatDiffRepository.SnapshotBundleUsage usage, long? earliestExclusive = null) { + long sw = Stopwatch.GetTimestamp(); + using var _ = EnterRepolockReadOnly(); + _flatdiffimes.WithLabels("gather_cache", "repolock").Observe(Stopwatch.GetTimestamp() - sw); + sw = Stopwatch.GetTimestamp(); + + ArrayPoolList knownStates = new(Math.Max(1, (int)(_inMemorySnapshotStore.KnownStatesCount / _compactSize))); + + if (_logger.IsTrace) _logger.Trace($"Gathering {baseBlock}. Earliest is {earliestExclusive}"); + + StateId bigCacheState = _currentPersistedState; + + // TODO: Determine if using a linked list of snapshot make more sense. Measure the impact of this loop and the + // dispose loop. + string exitReason = ""; + StateId current = baseBlock; + while(TryLeaseCompactedState(current, out var entry) || TryLeaseState(current, out entry)) + { + Snapshot state = entry; + if (_logger.IsTrace) _logger.Trace($"Got {state.From} -> {state.To}"); + knownStates.Add(state); + if (state.From == current) { + exitReason = "cycle"; + break; // Some test commit two block with the same id, so we dont know the parent anymore. + } + current = state.From; + + if (state.To.blockNumber <= bigCacheState.blockNumber) + { + exitReason = $"First {state.From} to {bigCacheState}"; + break; // Or equal? + } + if (state.From.blockNumber <= earliestExclusive) break; + } + + _flatdiffimes.WithLabels("gather_cache", "gather").Observe(Stopwatch.GetTimestamp() - sw); + sw = Stopwatch.GetTimestamp(); + _knownStatesSize.Observe(knownStates.Count); + + // Note: By the time the previous loop finished checking all state, the big cache may have added new state and removed some + // entry in `_inMemorySnapshotStore`. Meaning, this need to be here instead oof before the loop. + IPersistence.IPersistenceReader bigCacheReader = LeaseReader(); + if (current != baseBlock && earliestExclusive is null && bigCacheReader.CurrentState.blockNumber != -1 && current.blockNumber > bigCacheReader.CurrentState.blockNumber) + { + throw new Exception($"Non consecutive snappshots. Current {current} vs {bigCacheReader.CurrentState}, {bigCacheState}, {baseBlock}, {_inMemorySnapshotStore.TryGetValue(current, out var snapshot)}, {exitReason}"); + } + + if (bigCacheReader.CurrentState.blockNumber > baseBlock.blockNumber) + { + _logger.Warn("Big cache too early"); + bigCacheReader.Dispose(); + bigCacheReader = new NoopPersistenceReader(); + } + + // TODO: Measure this + knownStates.Reverse(); + _flatdiffimes.WithLabels("gather_cache", "reverse").Observe(Stopwatch.GetTimestamp() - sw); + sw = Stopwatch.GetTimestamp(); + + if (_logger.IsTrace) _logger.Trace($"Gathered {baseBlock}. Earliest is {earliestExclusive}, Got {knownStates.Count} known states, {_currentPersistedState}"); + var res = new SnapshotBundle( + knownStates, + bigCacheReader, + _trieNodeCache, + _resourcePool, + usage: usage); + _flatdiffimes.WithLabels("gather_cache", "done").Observe(Stopwatch.GetTimestamp() - sw); + return res; + } + + public bool TryLeaseCompactedState(StateId stateId, out Snapshot entry) + { + if (!_compactedKnownStates.TryGetValue(stateId, out entry)) return false; + if (!entry.TryAcquire()) return false; + return true; + } + + public bool TryLeaseState(StateId stateId, out Snapshot entry) + { + if (!_inMemorySnapshotStore.TryGetValue(stateId, out entry)) return false; + if (!entry.TryAcquire()) return false; + return true; + } + + public void AddSnapshot(Snapshot snapshot, CachedResource cachedResource) + { + long sw = Stopwatch.GetTimestamp(); + + StateId startingBlock = snapshot.From; + StateId endBlock = snapshot.To; + using (EnterRepolock()) + { + _flatdiffimes.WithLabels("add_snapshot", "repolock").Observe(Stopwatch.GetTimestamp() - sw); + sw = Stopwatch.GetTimestamp(); + + if (_logger.IsTrace) _logger.Trace($"Registering {startingBlock.blockNumber} to {endBlock.blockNumber}"); + if (endBlock.blockNumber <= _currentPersistedState.blockNumber) + { + _logger.Warn( + $"Cannot register snapshot earlier than bigcache. Snapshot number {endBlock.blockNumber}, bigcache number: {_currentPersistedState}"); + return; + } + + // snapshot should have 2 lease here + _inMemorySnapshotStore.AddBlock(endBlock, snapshot); + + _flatdiffimes.WithLabels("add_snapshot", "add_block").Observe(Stopwatch.GetTimestamp() - sw); + sw = Stopwatch.GetTimestamp(); + } + + if (_inlineCompaction) + { + RunCompactJob(endBlock, cachedResource); + } + else + { + if (!_compactorJobs.Writer.TryWrite((endBlock, cachedResource))) + { + _flatdiffimes.WithLabels("add_snapshot", "try_write_failed").Observe(Stopwatch.GetTimestamp() - sw); + sw = Stopwatch.GetTimestamp(); + _logger.Warn("Compactor job stall!"); + _compactorJobs.Writer.WriteAsync((endBlock, cachedResource)).AsTask().Wait(); + _flatdiffimes.WithLabels("add_snapshot", "write_async").Observe(Stopwatch.GetTimestamp() - sw); + } + else + { + _flatdiffimes.WithLabels("add_snapshot", "try_write_ok").Observe(Stopwatch.GetTimestamp() - sw); + } + } + } + + private async Task PersistIfNeeded() + { + await NotifyWhenSlow("add to bigcache", () => AddToPersistence()); + } + + private void AddToPersistence() + { + // Attempt to add snapshots into bigcache + while (true) + { + Snapshot pickedState; + StateId? pickedSnapshot = null; + List toRemoveStates = new List(); + long sw = Stopwatch.GetTimestamp(); + using (EnterRepolock()) + { + _flatdiffimes.WithLabels("add_to_persistence", "repolock").Observe(Stopwatch.GetTimestamp() - sw); + sw = Stopwatch.GetTimestamp(); + long lastSnapshotNumber = _inMemorySnapshotStore.GetLast()?.blockNumber ?? 0; + StateId currentState = _currentPersistedState; + if (lastSnapshotNumber - currentState.blockNumber <= (_boundary + _compactSize)) + { + break; + } + + List candidateToAdd = new List(); + + long? blockNumber = null; + bool persistCompactedStates = false; + // Note: Need to verify that this is finalized + foreach (var stateId in _inMemorySnapshotStore.GetStatesAfterBlock(currentState.blockNumber + _compactSize - 1)) + { + if (stateId.blockNumber > currentState.blockNumber + _compactSize) + { + break; + } + if (_compactedKnownStates.TryGetValue(stateId, out var existingState)) + { + if (blockNumber is null) + { + if (existingState.From != currentState) + { + if (_logger.IsDebug) _logger.Debug($"Not using compacted state. Mismatch. {existingState.From}, query {stateId} vs {currentState}"); + break; + } + + if (_logger.IsDebug) _logger.Debug($"Setting compacted state"); + persistCompactedStates = true; + blockNumber = stateId.blockNumber; + candidateToAdd.Add(stateId); + } + else if (blockNumber == stateId.blockNumber) + { + candidateToAdd.Add(stateId); + } + else + { + break; + } + } + else + { + if (_logger.IsDebug) _logger.Debug($"Cancelling setting compacted state, {stateId}"); + persistCompactedStates = false; + candidateToAdd.Clear(); + blockNumber = null; + break; + } + } + + if (persistCompactedStates) + { + if (_logger.IsDebug) _logger.Debug($"Using compacted state. {blockNumber}, vs {currentState}"); + } + + if (blockNumber is null) + { + foreach (var stateId in _inMemorySnapshotStore.GetStatesAfterBlock(currentState.blockNumber)) + { + if (blockNumber is null) + { + blockNumber = stateId.blockNumber; + candidateToAdd.Add(stateId); + } + else if (blockNumber == stateId.blockNumber) + { + candidateToAdd.Add(stateId); + } + else + { + break; + } + } + } + + Debug.Assert(candidateToAdd.Count > 0); + + if (candidateToAdd.Count > 1) + { + Hash256? canonicalStateRoot = _stateRootFinder.GetCanonicalStateRootAtBlock(blockNumber.Value); + if (canonicalStateRoot is null) + { + _logger.Warn($"Canonical state root for block {blockNumber} not known"); + return; + } + + foreach (var stateId in candidateToAdd) + { + if (stateId.stateRoot == canonicalStateRoot) + { + pickedSnapshot = stateId; + } + } + } + else + { + pickedSnapshot = candidateToAdd[0]; + } + + if (!pickedSnapshot.HasValue) + { + // Ah, probably filter the compacted state here instead + _logger.Warn($"Unable to determine canonicaal snapshot"); + return; + } + + // Remove non-canon snapshots + foreach (var stateId in candidateToAdd) + { + if (stateId != pickedSnapshot) + { + RemoveAndReleaseCompactedKnownState(stateId); + RemoveAndReleaseKnownState(stateId); + } + } + + if (persistCompactedStates) + { + _compactedKnownStates.TryGetValue(pickedSnapshot.Value, out pickedState); + pickedState.AcquireLease(); + if (_logger.IsDebug) _logger.Debug($"Picking compacted state {pickedState.From} to {pickedState.To}"); + + foreach (var stateId in _inMemorySnapshotStore.GetStatesAfterBlock(currentState.blockNumber)) + { + if (stateId.blockNumber < pickedSnapshot.Value.blockNumber) + { + toRemoveStates.Add(stateId); + } + } + } + else + { + _inMemorySnapshotStore.TryGetValue(pickedSnapshot.Value, out pickedState); + pickedState.AcquireLease(); + } + } + _flatdiffimes.WithLabels("add_to_persistence", "state_picked").Observe(Stopwatch.GetTimestamp() - sw); + + // Add the canon snapshot + Add(pickedState); + pickedState.Dispose(); + + sw = Stopwatch.GetTimestamp(); + // And we remove it + using (EnterRepolock()) + { + RemoveAndReleaseCompactedKnownState(pickedSnapshot.Value); + RemoveAndReleaseKnownState(pickedSnapshot.Value); + + foreach (var stateId in toRemoveStates) + { + RemoveAndReleaseCompactedKnownState(stateId); + RemoveAndReleaseKnownState(stateId); + } + } + _flatdiffimes.WithLabels("add_to_persistence", "cleanup").Observe(Stopwatch.GetTimestamp() - sw); + + sw = Stopwatch.GetTimestamp(); + + ReorgBoundaryReached?.Invoke(this, new ReorgBoundaryReached(pickedSnapshot.Value.blockNumber)); + + _flatdiffimes.WithLabels("add_to_persistence", "reorg_boundary").Observe(Stopwatch.GetTimestamp() - sw); + } + } + + private void RemoveAndReleaseCompactedKnownState(StateId stateId) + { + if (_compactedKnownStates.Remove(stateId, out var existingState)) + { + var memory = existingState.EstimateMemory(); + foreach (var keyValuePair in memory) + { + _compactedMemory.WithLabels(keyValuePair.Key.ToString()).Dec(keyValuePair.Value); + } + _compactedMemory.WithLabels("count").Dec(1); + + existingState.Dispose(); + } + } + + private void RemoveAndReleaseKnownState(StateId stateId) + { + if (!_repoLock.IsWriteLockHeld) throw new InvalidOperationException("Must hold write lock to repolock to change snapshot store"); + if (_inMemorySnapshotStore.TryGetValue(stateId, out var existingState)) + { + _inMemorySnapshotStore.Remove(stateId); + var memory = existingState.EstimateMemory(); + foreach (var keyValuePair in memory) + { + _knownStatesMemory.WithLabels(keyValuePair.Key.ToString()).Dec(keyValuePair.Value); + } + _knownStatesMemory.WithLabels("count").Dec(); + + existingState.Dispose(); // After memory + } + } + + public void Add(Snapshot snapshot) + { + if (snapshot.To.blockNumber - snapshot.From.blockNumber != _compactSize) _logger.Warn($"Snapshot size write is {snapshot.To.blockNumber - snapshot.From.blockNumber}"); + long sw = Stopwatch.GetTimestamp(); + using (var batch = _persistence.CreateWriteBatch(snapshot.From, snapshot.To)) + { + _flatdiffimes.WithLabels("persistence", "start_batch").Observe(Stopwatch.GetTimestamp() - sw); + sw = Stopwatch.GetTimestamp(); + int counter = 0; + foreach (var toSelfDestructStorage in snapshot.SelfDestructedStorageAddresses) + { + if (toSelfDestructStorage.Value) + { + /* + int deleted = batch.SelfDestruct(toSelfDestructStorage.Key.Value); + if (toSelfDestructStorage.Key.Value == FlatWorldStateScope.DebugAddress) + { + Console.Error.WriteLine($"Selfdestruct should skip {toSelfDestructStorage.Key}"); + } + if (deleted > 0) + { + _logger.Warn($"Should selfdestruct {toSelfDestructStorage.Key}. Deleted {deleted}. Snapshot range {snapshot.From} {snapshot.To}"); + throw new Exception($"Should sefl destruct not called properly {toSelfDestructStorage.Key}"); + } + */ + continue; + } + + int num = batch.SelfDestruct(toSelfDestructStorage.Key.Value); + if (toSelfDestructStorage.Key.Value == FlatWorldStateScope.DebugAddress) + { + using var r = LeaseReader(); + bool _ = r.TryGetSlot(FlatWorldStateScope.DebugAddress, FlatWorldStateScope.DebugSlot, out var value); + Console.Error.WriteLine($"Selfdestructed {toSelfDestructStorage.Key} {num}, {value?.ToHexString()}"); + } + counter++; + } + _flatdiffimes.WithLabels("persistence", "self_destruct").Observe(Stopwatch.GetTimestamp() - sw); + sw = Stopwatch.GetTimestamp(); + + foreach (var kv in snapshot.Accounts) + { + (Address addr, Account? account) = kv; + if (account is null) + batch.RemoveAccount(addr); + else + batch.SetAccount(addr, account); + } + _flatdiffimes.WithLabels("persistence", "accounts").Observe(Stopwatch.GetTimestamp() - sw); + sw = Stopwatch.GetTimestamp(); + + foreach (var kv in snapshot.Storages) + { + ((Address addr, UInt256 slot), byte[] value) = kv; + + if (value is null || Bytes.AreEqual(value, StorageTree.ZeroBytes)) + { + batch.RemoveStorage(addr, slot); + } + else + { + batch.SetStorage(addr, slot, value); + } + } + _flatdiffimes.WithLabels("persistence", "storages").Observe(Stopwatch.GetTimestamp() - sw); + sw = Stopwatch.GetTimestamp(); + + _trieNodesSortBuffer.Clear(); + _trieNodesSortBuffer.AddRange(snapshot.StateNodeKeys.Select((path) => (null, path))); + _trieNodesSortBuffer.Sort(); + _flatdiffimes.WithLabels("persistence", "trienode_sort_state").Observe(Stopwatch.GetTimestamp() - sw); + sw = Stopwatch.GetTimestamp(); + + // foreach (var tn in snapshot.TrieNodes) + foreach (var k in _trieNodesSortBuffer) + { + (_, TreePath path) = k; + + snapshot.TryGetStateNode(path, out TrieNode node); + + if (node.FullRlp.Length == 0) + { + // TODO: Need to double check this case. Does it need a rewrite or not? + if (node.NodeType == NodeType.Unknown) + { + continue; + } + } + + // Note: Even if the node already marked as persisted, we still re-persist it + batch.SetTrieNodes(null, path, node); + + node.IsPersisted = true; + } + _flatdiffimes.WithLabels("persistence", "trienodes").Observe(Stopwatch.GetTimestamp() - sw); + + _trieNodesSortBuffer.Clear(); + _trieNodesSortBuffer.AddRange(snapshot.StorageTrieNodeKeys); + _trieNodesSortBuffer.Sort(); + _flatdiffimes.WithLabels("persistence", "trienode_sort").Observe(Stopwatch.GetTimestamp() - sw); + sw = Stopwatch.GetTimestamp(); + + // foreach (var tn in snapshot.TrieNodes) + foreach (var k in _trieNodesSortBuffer) + { + (Hash256AsKey address, TreePath path) = k; + + snapshot.TryGetStorageNode(address, path, out TrieNode node); + + if (node.FullRlp.Length == 0) + { + // TODO: Need to double check this case. Does it need a rewrite or not? + if (node.NodeType == NodeType.Unknown) + { + continue; + } + } + + // Note: Even if the node already marked as persisted, we still re-persist it + batch.SetTrieNodes(address, path, node); + + node.IsPersisted = true; + } + _flatdiffimes.WithLabels("persistence", "trienodes").Observe(Stopwatch.GetTimestamp() - sw); + + sw = Stopwatch.GetTimestamp(); + } + _flatdiffimes.WithLabels("persistence", "dispose").Observe(Stopwatch.GetTimestamp() - sw); + + _currentPersistedState = snapshot.To; + ClearReaderCache(); + } + + + public void FlushCache(CancellationToken cancellationToken) + { + Console.Error.WriteLine("Flush cache not implemented"); + } + + public bool HasStateForBlock(StateId stateId) + { + if (_inMemorySnapshotStore.TryGetValue(stateId, out var snapshot)) + { + return true; + } + + if (_currentPersistedState == stateId) return true; + return false; + } + + public StateId? FindStateIdForStateRoot(Hash256 stateRoot) + { + using (EnterRepolockReadOnly()) + { + foreach (var stateId in _inMemorySnapshotStore.GetKeysBetween(new StateId(0, Hash256.Zero), new StateId(long.MaxValue, Keccak.MaxValue))) + { + if (stateId.stateRoot == stateRoot) return stateId; + } + + if (_currentPersistedState.stateRoot == stateRoot) + { + return _currentPersistedState; + } + } + + return null; + } + + public StateId? FindLatestAvailableState() + { + using (EnterRepolockReadOnly()) + { + StateId? lastInMemory = _inMemorySnapshotStore.GetLast(); + if (lastInMemory != null) return lastInMemory; + + return _currentPersistedState; + } + } + + public async ValueTask DisposeAsync() + { + await _compactorTask; + await _persistenceTask; + ClearReaderCache(); + + return; + } +} diff --git a/src/Nethermind/Nethermind.State/Flat/FlatStateReader.cs b/src/Nethermind/Nethermind.State/Flat/FlatStateReader.cs new file mode 100644 index 00000000000..e2f15e4015e --- /dev/null +++ b/src/Nethermind/Nethermind.State/Flat/FlatStateReader.cs @@ -0,0 +1,94 @@ +// SPDX-FileCopyrightText: 2025 Demerzel Solutions Limited +// SPDX-License-Identifier: LGPL-3.0-only + +using System; +using Autofac.Features.AttributeFilters; +using Nethermind.Core; +using Nethermind.Core.Crypto; +using Nethermind.Core.Utils; +using Nethermind.Db; +using Nethermind.Int256; +using Nethermind.Logging; +using Nethermind.State.Flat.ScopeProvider; +using Nethermind.Trie; + +namespace Nethermind.State.Flat; + +public class FlatStateReader( + [KeyFilter(DbNames.Code)] IDb codeDb, + ReadonlyReaderRepository readonlyReaderRepositor, + IFlatDiffRepository flatDiffRepository, + ILogManager logManager +): IStateReader +{ + public bool TryGetAccount(BlockHeader? baseBlock, Address address, out AccountStruct account) + { + using RefCountingDisposableBox readerBox = readonlyReaderRepositor.GatherReadOnlyReaderAtBaseBlock(new StateId(baseBlock)); + if (readerBox is null) + { + account = default; + return false; + } + + SnapshotBundle reader = readerBox.Item; + if (reader.TryGetAccount(address, out Account? accountCls) && accountCls != null) + { + account = accountCls.ToStruct(); + return true; + } + + account = default; + return false; + } + + // TODO: Why is it return span? How is it suppose to dispose itself? + public ReadOnlySpan GetStorage(BlockHeader? baseBlock, Address address, in UInt256 index) + { + using RefCountingDisposableBox readerBox = readonlyReaderRepositor.GatherReadOnlyReaderAtBaseBlock(new StateId(baseBlock)); + if (readerBox is null) + { + return Array.Empty(); + } + + SnapshotBundle reader = readerBox.Item; + if (reader.TryGetSlot(address, index, reader.DetermineSelfDestructSnapshotIdx(address), out byte[] value)) + { + return value; + } + + return Array.Empty(); + } + + public byte[]? GetCode(Hash256 codeHash) => codeHash == Keccak.OfAnEmptyString ? [] : codeDb[codeHash.Bytes]; + + public byte[]? GetCode(in ValueHash256 codeHash) => codeHash == Keccak.OfAnEmptyString.ValueHash256 ? [] : codeDb[codeHash.Bytes]; + + public void RunTreeVisitor(ITreeVisitor treeVisitor, Hash256 stateRoot, VisitingOptions? visitingOptions = null) where TCtx : struct, INodeContext + { + StateId? stateId = flatDiffRepository.FindStateIdForStateRoot(stateRoot); + if (stateId is null) + { + throw new InvalidOperationException($"State root {stateRoot} not found"); + } + + using RefCountingDisposableBox readerBox = readonlyReaderRepositor.GatherReadOnlyReaderAtBaseBlock(stateId.Value); + if (readerBox is null) + { + throw new InvalidOperationException($"State root {stateRoot} not found"); + } + + SnapshotBundle reader = readerBox.Item; + StateTrieStoreAdapter trieStoreAdapter = new StateTrieStoreAdapter( + reader, + new ConcurrencyQuota(), + false); + + PatriciaTree patriciaTree = new PatriciaTree(trieStoreAdapter, logManager); + patriciaTree.Accept(treeVisitor, stateRoot, visitingOptions); + } + + public bool HasStateForBlock(BlockHeader? baseBlock) + { + return flatDiffRepository.HasStateForBlock(new StateId(baseBlock)); + } +} diff --git a/src/Nethermind/Nethermind.State/Flat/FlatVerifyTrieVisitor.cs b/src/Nethermind/Nethermind.State/Flat/FlatVerifyTrieVisitor.cs new file mode 100644 index 00000000000..4d1d36617af --- /dev/null +++ b/src/Nethermind/Nethermind.State/Flat/FlatVerifyTrieVisitor.cs @@ -0,0 +1,338 @@ +// SPDX-FileCopyrightText: 2025 Demerzel Solutions Limited +// SPDX-License-Identifier: LGPL-3.0-only + +using System; +using System.Text; +using System.Threading; +using Nethermind.Core; +using Nethermind.Core.Caching; +using Nethermind.Core.Crypto; +using Nethermind.Core.Extensions; +using Nethermind.Logging; +using Nethermind.Serialization.Rlp; +using Nethermind.State.Flat.Persistence; +using Nethermind.Trie; + +namespace Nethermind.State.Flat; + +public class FlatVerifyTrieVisitor : ITreeVisitor +{ + private readonly ClockCache _existingCodeHash = new ClockCache(1024 * 8); + private readonly IKeyValueStore _codeKeyValueStore; + private long _lastAccountNodeCount = 0; + + private readonly ILogger _logger; + private readonly CancellationToken _cancellationToken; + private readonly IPersistence.IPersistenceReader _persistenceReader; + + // Combine both `TreePathContextWithStorage` and `OldStyleTrieVisitContext` + public struct Context : INodeContext + { + private TreePathContextWithStorage PathContext; + private OldStyleTrieVisitContext OldStyleTrieVisitContext; + + public readonly Hash256? Storage => PathContext.Storage; + public readonly TreePath Path => PathContext.Path; + public readonly bool IsStorage => OldStyleTrieVisitContext.IsStorage; + public readonly int Level => OldStyleTrieVisitContext.Level; + + public readonly Context Add(ReadOnlySpan nibblePath) + { + return new Context() + { + PathContext = PathContext.Add(nibblePath), + OldStyleTrieVisitContext = OldStyleTrieVisitContext.Add(nibblePath) + }; + } + + public readonly Context Add(byte nibble) + { + return new Context() + { + PathContext = PathContext.Add(nibble), + OldStyleTrieVisitContext = OldStyleTrieVisitContext.Add(nibble) + }; + } + + public readonly Context AddStorage(in ValueHash256 storage) + { + return new Context() + { + PathContext = PathContext.AddStorage(storage), + OldStyleTrieVisitContext = OldStyleTrieVisitContext.AddStorage(storage) + }; + } + } + + public bool ExpectAccounts { get; } + + public FlatVerifyTrieVisitor( + IKeyValueStore codeKeyValueStore, + IPersistence.IPersistenceReader persistenceReader, + ILogManager logManager, + CancellationToken cancellationToken = default, + bool expectAccounts = true) + { + _persistenceReader = persistenceReader; + _codeKeyValueStore = codeKeyValueStore ?? throw new ArgumentNullException(nameof(codeKeyValueStore)); + _logger = logManager.GetClassLogger(); + ExpectAccounts = expectAccounts; + _cancellationToken = cancellationToken; + } + + public TrieStats Stats { get; } = new(); + + public bool IsFullDbScan => true; + public void VisitTree(in Context nodeContext, in ValueHash256 rootHash) + { + } + + public bool ShouldVisit(in Context nodeContext, in ValueHash256 nextNode) + { + return true; + } + + public void VisitMissingNode(in Context nodeContext, in ValueHash256 nodeHash) + { + if (nodeContext.IsStorage) + { + if (_logger.IsWarn) _logger.Warn($"Missing node. Storage: {nodeContext.Storage} Path: {nodeContext.Path} Hash: {nodeHash}"); + Interlocked.Increment(ref Stats._missingStorage); + } + else + { + if (_logger.IsWarn) _logger.Warn($"Missing node. Path: {nodeContext.Path} Hash: {nodeHash}"); + Interlocked.Increment(ref Stats._missingState); + } + + IncrementLevel(nodeContext); + } + + public void VisitBranch(in Context nodeContext, TrieNode node) + { + _cancellationToken.ThrowIfCancellationRequested(); + + if (nodeContext.IsStorage) + { + Interlocked.Add(ref Stats._storageSize, node.FullRlp.Length); + Interlocked.Increment(ref Stats._storageBranchCount); + } + else + { + Interlocked.Add(ref Stats._stateSize, node.FullRlp.Length); + Interlocked.Increment(ref Stats._stateBranchCount); + } + + IncrementLevel(nodeContext); + } + + public void VisitExtension(in Context nodeContext, TrieNode node) + { + if (nodeContext.IsStorage) + { + Interlocked.Add(ref Stats._storageSize, node.FullRlp.Length); + Interlocked.Increment(ref Stats._storageExtensionCount); + } + else + { + Interlocked.Add(ref Stats._stateSize, node.FullRlp.Length); + Interlocked.Increment(ref Stats._stateExtensionCount); + } + + IncrementLevel(nodeContext); + } + + public void VisitLeaf(in Context nodeContext, TrieNode node) + { + long lastAccountNodeCount = _lastAccountNodeCount; + long currentNodeCount = Stats.NodesCount; + if (currentNodeCount - lastAccountNodeCount > 1_000_000 && Interlocked.CompareExchange(ref _lastAccountNodeCount, currentNodeCount, lastAccountNodeCount) == lastAccountNodeCount) + { + _logger.Warn($"Collected info from {Stats.NodesCount} nodes. Missing CODE {Stats.MissingCode} STATE {Stats.MissingState} STORAGE {Stats.MissingStorage}"); + } + + if (nodeContext.IsStorage) + { + Interlocked.Add(ref Stats._storageSize, node.FullRlp.Length); + Interlocked.Increment(ref Stats._storageLeafCount); + + Hash256 fullPath = nodeContext.Path.Append(node.Key).Path.ToHash256(); + byte[]? nodeSlot = State.StorageTree.ZeroBytes; + if (node.Value.IsNotNull) + { + Rlp.ValueDecoderContext ctx = node.Value.Span.AsRlpValueContext(); + nodeSlot = ctx.DecodeByteArray(); + } + + byte[]? flatSlot = _persistenceReader.GetStorageRaw(nodeContext.Storage, fullPath); + if (!Bytes.AreEqual(flatSlot, nodeSlot)) + { + if (_logger.IsWarn) _logger.Warn($"Mismatched slot. AddressHash: {nodeContext.Storage}. SlotHash {fullPath}. Trie slot: {nodeSlot.ToHexString() ?? ""}, Flat slot; {flatSlot?.ToHexString()}"); + Interlocked.Increment(ref Stats._mismatchedSlot); + } + + } + else + { + Interlocked.Add(ref Stats._stateSize, node.FullRlp.Length); + Interlocked.Increment(ref Stats._accountCount); + + Hash256 addrHash = nodeContext.Path.Append(node.Key).Path.ToHash256(); + byte[]? rawAccountBytes = _persistenceReader.GetAccountRaw(addrHash); + Rlp.ValueDecoderContext ctx = new Rlp.ValueDecoderContext(rawAccountBytes); + Account? flatAccount = AccountDecoder.Instance.Decode(ref ctx); + + ctx = node.Value.Span.AsRlpValueContext(); + Account? nodeAccount = AccountDecoder.Instance.Decode(ref ctx); + + if (nodeAccount != flatAccount) + { + if (_logger.IsWarn) _logger.Warn($"Mismatched account. AddressHash: {addrHash}. Trie account: {nodeAccount}, Flat account; {flatAccount}"); + Interlocked.Increment(ref Stats._mismatchedAccount); + } + } + + IncrementLevel(nodeContext); + } + + public void VisitAccount(in Context nodeContext, TrieNode node, in AccountStruct account) + { + if (!account.HasCode) return; + ValueHash256 key = account.CodeHash; + bool codeExist = _existingCodeHash.TryGet(key, out int codeLength); + if (!codeExist) + { + byte[] code = _codeKeyValueStore[key.Bytes]; + codeExist = code is not null; + if (codeExist) + { + codeLength = code.Length; + _existingCodeHash.Set(key, codeLength); + } + } + + if (codeExist) + { + Interlocked.Add(ref Stats._codeSize, codeLength); + Interlocked.Increment(ref Stats._codeCount); + } + else + { + if (_logger.IsWarn) _logger.Warn($"Missing code. Hash: {account.CodeHash}"); + Interlocked.Increment(ref Stats._missingCode); + } + + IncrementLevel(nodeContext, Stats._codeLevels); + } + + private void IncrementLevel(Context context) + { + long[] levels = context.IsStorage ? Stats._storageLevels : Stats._stateLevels; + IncrementLevel(context, levels); + } + + private static void IncrementLevel(Context context, long[] levels) + { + Interlocked.Increment(ref levels[context.Level]); + } + + public class TrieStats + { + private const int Levels = 128; + internal long _stateBranchCount; + internal long _stateExtensionCount; + internal long _accountCount; + internal long _storageBranchCount; + internal long _storageExtensionCount; + internal long _storageLeafCount; + internal long _codeCount; + internal long _missingState; + internal long _missingCode; + internal long _missingStorage; + internal long _mismatchedAccount; + internal long _mismatchedSlot; + internal long _storageSize; + internal long _codeSize; + internal long _stateSize; + internal readonly long[] _stateLevels = new long[Levels]; + internal readonly long[] _storageLevels = new long[Levels]; + internal readonly long[] _codeLevels = new long[Levels]; + + public long StateBranchCount => _stateBranchCount; + + public long StateExtensionCount => _stateExtensionCount; + + public long AccountCount => _accountCount; + + public long StorageBranchCount => _storageBranchCount; + + public long StorageExtensionCount => _storageExtensionCount; + + public long StorageLeafCount => _storageLeafCount; + + public long CodeCount => _codeCount; + + public long MissingState => _missingState; + + public long MissingCode => _missingCode; + + public long MissingStorage => _missingStorage; + + public long MismatchedSlot => _mismatchedSlot; + + public long MismatchedAccount => _mismatchedAccount; + + public long MissingNodes => MissingCode + MissingState + MissingStorage; + + public long StorageCount => StorageLeafCount + StorageExtensionCount + StorageBranchCount; + + public long StateCount => AccountCount + StateExtensionCount + StateBranchCount; + + public long NodesCount => StorageCount + StateCount + CodeCount; + + public long StorageSize => _storageSize; + + public long CodeSize => _codeSize; + + public long StateSize => _stateSize; + + public long Size => StateSize + StorageSize + CodeSize; + + // public List MissingNodes { get; set; } = new List(); + + public long[] StateLevels => _stateLevels; + public long[] StorageLevels => _storageLevels; + public long[] CodeLevels => _codeLevels; + public long[] AllLevels + { + get + { + long[] result = new long[Levels]; + for (int i = 0; i < result.Length; i++) + { + result[i] = _stateLevels[i] + _storageLevels[i] + _codeLevels[i]; + } + + return result; + } + } + + public override string ToString() + { + StringBuilder builder = new(); + builder.AppendLine("TRIE STATS"); + builder.AppendLine($" SIZE {Size} (STATE {StateSize}, CODE {CodeSize}, STORAGE {StorageSize})"); + builder.AppendLine($" ALL NODES {NodesCount} ({StateBranchCount + StorageBranchCount}|{StateExtensionCount + StorageExtensionCount}|{AccountCount + StorageLeafCount})"); + builder.AppendLine($" STATE NODES {StateCount} ({StateBranchCount}|{StateExtensionCount}|{AccountCount})"); + builder.AppendLine($" STORAGE NODES {StorageCount} ({StorageBranchCount}|{StorageExtensionCount}|{StorageLeafCount})"); + builder.AppendLine($" ACCOUNTS {AccountCount} OF WHICH ({CodeCount}) ARE CONTRACTS"); + builder.AppendLine($" MISSING {MissingNodes} (STATE {MissingState}, CODE {MissingCode}, STORAGE {MissingStorage})"); + builder.AppendLine($" MISMATCHED (ACCOUNT {MismatchedAccount}) (SLOT {MismatchedSlot})"); + builder.AppendLine($" ALL LEVELS {string.Join(" | ", AllLevels)}"); + builder.AppendLine($" STATE LEVELS {string.Join(" | ", StateLevels)}"); + builder.AppendLine($" STORAGE LEVELS {string.Join(" | ", StorageLevels)}"); + builder.AppendLine($" CODE LEVELS {string.Join(" | ", CodeLevels)}"); + return builder.ToString(); + } + } +} diff --git a/src/Nethermind/Nethermind.State/Flat/ICanonicalStateRootFinder.cs b/src/Nethermind/Nethermind.State/Flat/ICanonicalStateRootFinder.cs new file mode 100644 index 00000000000..5226ebc5cbd --- /dev/null +++ b/src/Nethermind/Nethermind.State/Flat/ICanonicalStateRootFinder.cs @@ -0,0 +1,11 @@ +// SPDX-FileCopyrightText: 2025 Demerzel Solutions Limited +// SPDX-License-Identifier: LGPL-3.0-only + +using Nethermind.Core.Crypto; + +namespace Nethermind.State.Flat; + +public interface ICanonicalStateRootFinder +{ + public Hash256? GetCanonicalStateRootAtBlock(long blockNumber); +} diff --git a/src/Nethermind/Nethermind.State/Flat/IFlatDiffRepository.cs b/src/Nethermind/Nethermind.State/Flat/IFlatDiffRepository.cs new file mode 100644 index 00000000000..58ea35df523 --- /dev/null +++ b/src/Nethermind/Nethermind.State/Flat/IFlatDiffRepository.cs @@ -0,0 +1,31 @@ +// SPDX-FileCopyrightText: 2025 Demerzel Solutions Limited +// SPDX-License-Identifier: LGPL-3.0-only + +using System; +using System.Threading; +using Nethermind.Core.Crypto; +using Nethermind.Core.Utils; +using Nethermind.State.Flat.Persistence; +using Nethermind.Trie.Pruning; + +namespace Nethermind.State.Flat; + +public interface IFlatDiffRepository +{ + event EventHandler? ReorgBoundaryReached; + SnapshotBundle? GatherReaderAtBaseBlock(StateId baseBlock, SnapshotBundleUsage usage); + void AddSnapshot(Snapshot snapshot, CachedResource cachedResource); + void FlushCache(CancellationToken cancellationToken); + bool HasStateForBlock(StateId stateId); + StateId? FindStateIdForStateRoot(Hash256 stateRoot); // Ugh... + StateId? FindLatestAvailableState(); + IPersistence.IPersistenceReader LeaseReader(); + + enum SnapshotBundleUsage + { + MainBlockProcessing, + StateReader, + ReadOnlyProcessingEnv, + Compactor, + } +} diff --git a/src/Nethermind/Nethermind.State/Flat/Importer/Importer.cs b/src/Nethermind/Nethermind.State/Flat/Importer/Importer.cs new file mode 100644 index 00000000000..8175b51e8a8 --- /dev/null +++ b/src/Nethermind/Nethermind.State/Flat/Importer/Importer.cs @@ -0,0 +1,164 @@ +// SPDX-FileCopyrightText: 2025 Demerzel Solutions Limited +// SPDX-License-Identifier: LGPL-3.0-only + +using System; +using System.Threading.Channels; +using System.Threading.Tasks; +using Nethermind.Core; +using Nethermind.Core.Crypto; +using Nethermind.Logging; +using Nethermind.Serialization.Rlp; +using Nethermind.State.Flat.Persistence; +using Nethermind.Trie; +using Nethermind.Trie.Pruning; + +namespace Nethermind.State.Flat.Importer; + +public class Importer( + INodeStorage nodeStorage, + IPersistence persistence, + ILogManager logManager +) +{ + ILogger _logger = logManager.GetClassLogger(); + internal AccountDecoder _accountDecoder = AccountDecoder.Instance; + + private record Entry(Hash256? address, TreePath path, TrieNode node); + + public void Copy(StateId to) + { + StateId from = new StateId(); + using (var reader = persistence.CreateReader()) + { + from = reader.CurrentState; + } + + ITrieStore trieStore = new RawTrieStore(nodeStorage); + PatriciaTree tree = new PatriciaTree(trieStore, logManager); + tree.RootHash = to.stateRoot.ToHash256(); + + Channel channel = Channel.CreateBounded(1_000_000); + + Task visitTask = Task.Run(() => + { + try + { + tree.Accept(new Visitor(channel.Writer), to.stateRoot.ToHash256(), new VisitingOptions() + { + MaxDegreeOfParallelism = 1 // The writer is single threaded. Its better to write sorted then. + }); + } + finally + { + channel.Writer.Complete(); + } + }); + + Task ingestTask = Task.Run(async () => + { + long totalNodes = 0; + int batchSize = 4_000_000; + int currentBatchSize = 0; + + StateId lastState = from; + var writeBatch = persistence.CreateWriteBatch(lastState, to); + await foreach (var entry in channel.Reader.ReadAllAsync()) + { + // Write it + + TrieNode node = entry.node; + writeBatch.SetTrieNodes(entry.address, entry.path, node); + if (node.IsLeaf) + { + ValueHash256 fullPath = entry.path.Append(node.Key).Path; + if (entry.address is null) + { + Account acc = _accountDecoder.Decode(node.Value.Span); + writeBatch.SetAccountRaw(fullPath.ToHash256(), acc); + } + else + { + + ReadOnlySpan value = node.Value.Span; + byte[] toWrite; + + if (value.IsEmpty) + { + toWrite = StorageTree.ZeroBytes; + } + else + { + Rlp.ValueDecoderContext rlp = value.AsRlpValueContext(); + toWrite = rlp.DecodeByteArray(); + } + + writeBatch.SetStorageRaw(entry.address, fullPath.ToHash256(), toWrite); + } + } + + currentBatchSize += 1; + totalNodes += 1; + if (currentBatchSize >= batchSize) + { + _logger.Info($"Wrote {totalNodes:N} nodes."); + writeBatch.Dispose(); + lastState = to; + writeBatch = persistence.CreateWriteBatch(lastState, to); + currentBatchSize = 0; + } + } + + writeBatch.Dispose(); + _logger.Info($"Flat db copy completed. Wrote {totalNodes} nodes."); + }); + + Task.WaitAll(ingestTask, visitTask); + } + + private class Visitor(ChannelWriter channelWriter) : ITreeVisitor + { + public bool IsFullDbScan => true; + public bool ExpectAccounts => true; + + public bool ShouldVisit(in TreePathContextWithStorage nodeContext, in ValueHash256 nextNode) + { + return true; + } + + public void VisitTree(in TreePathContextWithStorage nodeContext, in ValueHash256 rootHash) + { + } + + public void VisitMissingNode(in TreePathContextWithStorage nodeContext, in ValueHash256 nodeHash) + { + throw new Exception("Missing node is not expected"); + } + + private void Write(in TreePathContextWithStorage nodeContext, TrieNode node) + { + while (!channelWriter.TryWrite(new Entry(nodeContext.Storage, nodeContext.Path, node))) + { + channelWriter.WaitToWriteAsync().AsTask().Wait(); + } + } + + public void VisitBranch(in TreePathContextWithStorage nodeContext, TrieNode node) + { + Write(nodeContext, node); + } + + public void VisitExtension(in TreePathContextWithStorage nodeContext, TrieNode node) + { + Write(nodeContext, node); + } + + public void VisitLeaf(in TreePathContextWithStorage nodeContext, TrieNode node) + { + Write(nodeContext, node); + } + + public void VisitAccount(in TreePathContextWithStorage nodeContext, TrieNode node, in AccountStruct account) + { + } + } +} diff --git a/src/Nethermind/Nethermind.State/Flat/InMemorySnapshotStore.cs b/src/Nethermind/Nethermind.State/Flat/InMemorySnapshotStore.cs new file mode 100644 index 00000000000..8680ce374c4 --- /dev/null +++ b/src/Nethermind/Nethermind.State/Flat/InMemorySnapshotStore.cs @@ -0,0 +1,69 @@ +// SPDX-FileCopyrightText: 2025 Demerzel Solutions Limited +// SPDX-License-Identifier: LGPL-3.0-only + +using System.Collections.Generic; +using System.Linq; +using Nethermind.Core.Crypto; +using Nethermind.State.Flat; + +namespace Nethermind.State.Flat; + +public class InMemorySnapshotStore +{ + private Dictionary _knownStates = new(); + private SortedSet _sortedKnownStates = new(); + + internal int KnownStatesCount => _knownStates.Count; + + public bool TryGetValue(StateId current, out Snapshot value) + { + return _knownStates.TryGetValue(current, out value); + } + + public void AddBlock(StateId endBlock, Snapshot snapshot) + { + _knownStates[endBlock] = snapshot; + _sortedKnownStates.Add(endBlock); + } + + public (StateId, Snapshot) GetFirst() + { + var firstKey = _sortedKnownStates.First(); + var snapshot = _knownStates[firstKey]; + return (firstKey, snapshot); + } + + public void Remove(StateId firstKey) + { + _knownStates.Remove(firstKey); + _sortedKnownStates.Remove(firstKey); + } + + public List GetKeysBetween(StateId start, StateId end) + { + return _sortedKnownStates.GetViewBetween(start, end).ToList(); + } + + public List GetStatesAtBlockNumber(long blockNumber) + { + StateId min = new StateId(blockNumber, ValueKeccak.Zero); + StateId max = new StateId(blockNumber, ValueKeccak.MaxValue); + + return GetKeysBetween(min, max).ToList(); + } + + public IEnumerable GetStatesAfterBlock(long startingBlockNumber) + { + return GetKeysBetween( + new StateId(startingBlockNumber + 1, ValueKeccak.Zero), + new StateId(long.MaxValue, ValueKeccak.Zero) + ); + } + + public StateId? GetLast() + { + if (_sortedKnownStates.Count == 0) + return null; + return _sortedKnownStates.Last(); + } +} diff --git a/src/Nethermind/Nethermind.State/Flat/Persistence/IPersistence.cs b/src/Nethermind/Nethermind.State/Flat/Persistence/IPersistence.cs new file mode 100644 index 00000000000..c4721b79766 --- /dev/null +++ b/src/Nethermind/Nethermind.State/Flat/Persistence/IPersistence.cs @@ -0,0 +1,40 @@ +// SPDX-FileCopyrightText: 2025 Demerzel Solutions Limited +// SPDX-License-Identifier: LGPL-3.0-only + +using System; +using Nethermind.Core; +using Nethermind.Core.Crypto; +using Nethermind.Int256; +using Nethermind.Trie; + +namespace Nethermind.State.Flat.Persistence; + +public interface IPersistence +{ + IPersistenceReader CreateReader(); + IWriteBatch CreateWriteBatch(StateId from, StateId to); + + public interface IPersistenceReader: IDisposable + { + bool TryGetAccount(Address address, out Account? acc); + bool TryGetSlot(Address address, in UInt256 index, out byte[] value); + StateId CurrentState { get; } + byte[]? TryLoadRlp(Hash256? address, in TreePath path, Hash256 hash, ReadFlags flags); + + byte[]? GetAccountRaw(Hash256? addrHash); + byte[]? GetStorageRaw(Hash256? addrHash, Hash256 slotHash); + } + + public interface IWriteBatch: IDisposable + { + int SelfDestruct(Address addr); + void RemoveAccount(Address addr); + void SetAccount(Address addr, Account account); + void SetStorage(Address addr, UInt256 slot, ReadOnlySpan value); + void SetTrieNodes(Hash256? address, TreePath path, TrieNode tnValue); + void RemoveStorage(Address addr, UInt256 slot); + + void SetStorageRaw(Hash256? addrHash, Hash256 slotHash, ReadOnlySpan value); + void SetAccountRaw(Hash256? addrHash, Account account); + } +} diff --git a/src/Nethermind/Nethermind.State/Flat/Persistence/NoopPersistence.cs b/src/Nethermind/Nethermind.State/Flat/Persistence/NoopPersistence.cs new file mode 100644 index 00000000000..3cb1a8bfe3f --- /dev/null +++ b/src/Nethermind/Nethermind.State/Flat/Persistence/NoopPersistence.cs @@ -0,0 +1,44 @@ +// SPDX-FileCopyrightText: 2025 Demerzel Solutions Limited +// SPDX-License-Identifier: LGPL-3.0-only + +using Nethermind.Core; +using Nethermind.Core.Crypto; +using Nethermind.Int256; +using Nethermind.Trie; + +namespace Nethermind.State.Flat.Persistence; + +public class NoopPersistenceReader: IPersistence.IPersistenceReader +{ + public void Dispose() + { + } + + public bool TryGetAccount(Address address, out Account? acc) + { + acc = null; + return false; + } + + public bool TryGetSlot(Address address, in UInt256 index, out byte[] value) + { + value = null; + return false; + } + + public StateId CurrentState => new StateId(0, Keccak.EmptyTreeHash); + public byte[]? TryLoadRlp(Hash256? address, in TreePath path, Hash256 hash, ReadFlags flags) + { + return null; + } + + public byte[]? GetAccountRaw(Hash256? addrHash) + { + return null; + } + + public byte[]? GetStorageRaw(Hash256? addrHash, Hash256 slotHash) + { + return null; + } +} diff --git a/src/Nethermind/Nethermind.State/Flat/Persistence/RefCountingPersistenceReader.cs b/src/Nethermind/Nethermind.State/Flat/Persistence/RefCountingPersistenceReader.cs new file mode 100644 index 00000000000..abaad7304ea --- /dev/null +++ b/src/Nethermind/Nethermind.State/Flat/Persistence/RefCountingPersistenceReader.cs @@ -0,0 +1,70 @@ +// SPDX-FileCopyrightText: 2025 Demerzel Solutions Limited +// SPDX-License-Identifier: LGPL-3.0-only + +using System; +using System.Threading.Tasks; +using Nethermind.Core; +using Nethermind.Core.Crypto; +using Nethermind.Core.Utils; +using Nethermind.Int256; +using Nethermind.Logging; +using Nethermind.Trie; +using Prometheus; + +namespace Nethermind.State.Flat.Persistence; + +public class RefCountingPersistenceReader : RefCountingDisposable, IPersistence.IPersistenceReader +{ + private readonly IPersistence.IPersistenceReader _innerReader; + private bool _isDisposed = false; + + public RefCountingPersistenceReader(IPersistence.IPersistenceReader innerReader, ILogger logger) + { + _innerReader = innerReader; + + string sTrace = Environment.StackTrace; + _ = Task.Run(async () => + { + // Reader should be re-created every block unless something holds it for very long. + // It prevent database compaction, so this need to be closed eventually. + await Task.Delay(60_000); + if (!_isDisposed) + { + if (logger.IsWarn) logger.Warn($"Unexpected old snapshot created. Lease count {_leases.Value} at {sTrace}"); + } + }); + } + + public bool TryGetAccount(Address address, out Account? acc) + { + return _innerReader.TryGetAccount(address, out acc); + } + + public bool TryGetSlot(Address address, in UInt256 index, out byte[] value) + { + return _innerReader.TryGetSlot(address, in index, out value); + } + + public StateId CurrentState => _innerReader.CurrentState; + + public byte[]? TryLoadRlp(Hash256? address, in TreePath path, Hash256 hash, ReadFlags flags) + { + return _innerReader.TryLoadRlp(address, in path, hash, flags); + } + + public byte[]? GetAccountRaw(Hash256? addrHash) + { + return _innerReader.GetAccountRaw(addrHash); + } + + public byte[]? GetStorageRaw(Hash256? addrHash, Hash256 slotHash) + { + return _innerReader.GetStorageRaw(addrHash, slotHash); + } + + protected override void CleanUp() + { + _isDisposed = true; + _innerReader.Dispose(); + } +} diff --git a/src/Nethermind/Nethermind.State/Flat/Persistence/RocksdbPersistence.cs b/src/Nethermind/Nethermind.State/Flat/Persistence/RocksdbPersistence.cs new file mode 100644 index 00000000000..dd5bc6c4b6d --- /dev/null +++ b/src/Nethermind/Nethermind.State/Flat/Persistence/RocksdbPersistence.cs @@ -0,0 +1,668 @@ +// SPDX-FileCopyrightText: 2025 Demerzel Solutions Limited +// SPDX-License-Identifier: LGPL-3.0-only + +using System; +using System.Buffers.Binary; +using System.Collections.Generic; +using System.IO; +using System.Reflection; +using Autofac.Features.AttributeFilters; +using Nethermind.Core; +using Nethermind.Core.Crypto; +using Nethermind.Core.Extensions; +using Nethermind.Db; +using Nethermind.Int256; +using Nethermind.Serialization.Rlp; +using Nethermind.State.Flat.ScopeProvider; +using Nethermind.Trie; +using Prometheus; +using ZstdSharp; + +namespace Nethermind.State.Flat.Persistence; + +public class RocksdbPersistence : IPersistence +{ + private readonly IColumnsDb _db; + private static byte[] CurrentStateKey = Keccak.Compute("CurrentState").BytesToArray(); + + private const int StateKeyPrefixLength = 20; + + private const int StorageHashPrefixLength = 20; // Store prefix of the 32 byte of the storage. Reduces index size. + private const int StorageSlotKeySize = 32; + private const int StorageKeyLength = StorageHashPrefixLength + StorageSlotKeySize; + private const int FullPathLength = 32; + private const int PathLengthLength = 1; + + private const int StateNodesKeyLength = FullPathLength + PathLengthLength; + private const int StateNodesTopThreshold = 5; + private const int StateNodesTopPathLength = 3; + private const int StateNodesTopKeyLength = StateNodesTopPathLength + PathLengthLength; + + private const int StorageNodesKeyLength = StorageHashPrefixLength + FullPathLength + PathLengthLength; + private const int StorageNodesTopThreshold = 3; + private const int StorageNodesTopPathLength = 2; + private const int StorageNodesTopKeyLength = StorageHashPrefixLength + StorageNodesTopPathLength + PathLengthLength; + + internal AccountDecoder _accountDecoder = AccountDecoder.Instance; + private readonly IKeyValueStoreWithBatching _preimageDb; + + // Compress accounts here instead of in rocksdb, allowing disabling rocksdb's compression. + // Unfortunately, only works well with accounts due to many redundant hash. + // TODO: Does not help with latency, or anything. Overall use slightly more disk space. Maybe remove. + private byte[] _zstdDictionary; + private readonly Configuration _configuration; + private readonly Histogram.Child _rocksdBPersistenceTimesSlotHit; + private readonly Histogram.Child _rocksdBPersistenceTimesSlotMiss; + private readonly Histogram.Child _rocksdBPersistenceTimesSlotCompareTime; + private readonly Histogram.Child _rocksdBPersistenceTimesAddressHash; + + public record Configuration( + bool UsePreimage = false, + bool FlatInTrie = false, + bool SeparateStorageTop = true + ) + { + } + + private static Histogram _rocksdBPersistenceTimes = DevMetric.Factory.CreateHistogram("rocksdb_persistence_times", "aha", new HistogramConfiguration() + { + LabelNames = new[] { "type" }, + // Buckets = Histogram.PowersOfTenDividedBuckets(2, 12, 5) + Buckets = [1] + }); + + public RocksdbPersistence( + IColumnsDb db, + [KeyFilter(DbNames.Preimage)] IDb preimageDb, + Configuration configuration) + { + _configuration = configuration; + _db = db; + _preimageDb = preimageDb; + + LoadZstdDictionary(); + + _rocksdBPersistenceTimesAddressHash = _rocksdBPersistenceTimes.WithLabels("address_hash"); + _rocksdBPersistenceTimesSlotHit = _rocksdBPersistenceTimes.WithLabels("slot_hash_hit"); + _rocksdBPersistenceTimesSlotMiss = _rocksdBPersistenceTimes.WithLabels("slot_hash_miss"); + _rocksdBPersistenceTimesSlotCompareTime = _rocksdBPersistenceTimes.WithLabels("slot_hash_compare_time"); + // TrainDictionary(); + } + + /* + private Decompressor CreateDecompressor() + { + Decompressor decomp = new Decompressor(); + decomp.LoadDictionary(_zstdDictionary); + return decomp; + } + */ + + private void LoadZstdDictionary() + { + var assembly = Assembly.GetExecutingAssembly(); + const string resourceName = "Nethermind.State.Flat.Persistence.zstddictionary.bin"; + using Stream? stream = assembly.GetManifestResourceStream(resourceName) + ?? throw new InvalidOperationException($"Resource '{resourceName}' not found."); + + using var ms = new MemoryStream(); + stream.CopyTo(ms); + _zstdDictionary = ms.ToArray(); + Console.Error.WriteLine($"Dictionary size is {_zstdDictionary.Length}"); + } + + internal void TrainDictionary() + { + using var snapshot = _db.CreateSnapshot(); + + List data = new List(); + + FlatDbColumns[] columnsToTest = + [ + FlatDbColumns.State, + // FlatDbColumns.Storage, + // FlatDbColumns.StateTopNodes, + // FlatDbColumns.StateNodes, + // FlatDbColumns.StorageTopNodes, + // FlatDbColumns.StorageNodes, + ]; + + Random rand = new Random(0); + + byte[] key = new byte[32]; + byte[] maxKey = new byte[32]; + Keccak.MaxValue.Bytes.CopyTo(maxKey); + + int totalSize = 0; + + foreach (FlatDbColumns column in columnsToTest) + { + ISortedKeyValueStore col = snapshot.GetColumn(column) as ISortedKeyValueStore; + for (int i = 0; i < 10000; i++) + { + rand.NextBytes(key); + + using ISortedView view = col.GetViewBetween(key, maxKey); + + if (view.MoveNext()) + { + data.Add(view.CurrentValue.ToArray()); + totalSize += view.CurrentValue.Length; + } + } + } + + Console.Error.WriteLine($"Training dictionary"); + byte[] dictionary = DictBuilder.TrainFromBuffer(data, 1024 * 2); + Console.Error.WriteLine($"Trained a dictionary of size {dictionary.Length} from {data.Count} samples of total size {totalSize}"); + + File.WriteAllBytes(Path.Combine(Directory.GetCurrentDirectory(), "flatdictionary.bin"), dictionary); + + using Compressor compressor = new Compressor(); + compressor.LoadDictionary(dictionary); + + foreach (FlatDbColumns column in columnsToTest) + { + int compressed = 0; + int uncompressed = 0; + + ISortedKeyValueStore col = snapshot.GetColumn(column) as ISortedKeyValueStore; + for (int i = 0; i < 10000; i++) + { + rand.NextBytes(key); + + using ISortedView view = col.GetViewBetween(key, maxKey); + + if (view.MoveNext()) + { + data.Add(view.CurrentValue.ToArray()); + totalSize += view.CurrentValue.Length; + } + } + for (int i = 0; i < 10000; i++) + { + rand.NextBytes(key); + + using ISortedView view = col.GetViewBetween(key, maxKey); + + if (view.MoveNext()) + { + uncompressed += view.CurrentValue.Length; + compressed += compressor.Wrap(view.CurrentValue).Length; + } + } + + Console.Error.WriteLine($"Expected ratio for {column} {(double)compressed / uncompressed}. Comppressed {compressed:N}, Uncompressed {uncompressed:N}"); + } + } + + internal static StateId ReadCurrentState(IReadOnlyKeyValueStore kv) + { + byte[] bytes = kv.Get(CurrentStateKey); + if (bytes is null || bytes.Length == 0) + { + return new StateId(-1, Keccak.EmptyTreeHash); + } + + long blockNumber = BinaryPrimitives.ReadInt64BigEndian(bytes); + Hash256 stateHash = new Hash256(bytes[8..]); + return new StateId(blockNumber, stateHash); + } + + internal static void SetCurrentState(IWriteOnlyKeyValueStore kv, StateId stateId) + { + Span bytes = stackalloc byte[8 + 32]; + BinaryPrimitives.WriteInt64BigEndian(bytes[..8], stateId.blockNumber); + stateId.stateRoot.BytesAsSpan.CopyTo(bytes[8..]); + + kv.PutSpan(CurrentStateKey, bytes); + } + + private ReadOnlySpan EncodeAccountKey(Span buffer, in Address addr) + { + if (_configuration.UsePreimage) + { + addr.Bytes.CopyTo(buffer); + return buffer[..StateKeyPrefixLength]; + } + else + { + ValueHash256 hashBuffer = ValueKeccak.Zero; + hashBuffer = addr.ToAccountPath; + hashBuffer.Bytes[..StorageHashPrefixLength].CopyTo(buffer); + return buffer[..StateKeyPrefixLength]; + } + } + + internal ReadOnlySpan EncodeStorageKey(Span buffer, in Address addr, in UInt256 slot) + { + if (_configuration.UsePreimage) + { + addr.Bytes.CopyTo(buffer); + slot.ToBigEndian(buffer[StorageHashPrefixLength..]); + return buffer[..StorageKeyLength]; + } + else + { + ValueHash256 hashBuffer = ValueKeccak.Zero; + hashBuffer = addr.ToAccountPath; // 75ns on average + hashBuffer.Bytes[..StorageHashPrefixLength].CopyTo(buffer); + + // around 300ns on average. 30% keccak cache hit rate. + StorageTree.ComputeKeyWithLookup(slot, buffer[StorageHashPrefixLength..(StorageHashPrefixLength + StorageSlotKeySize)]); + + return buffer[..StorageKeyLength]; + } + } + + internal ReadOnlySpan EncodeStorageKeyHashed(Span buffer, in ValueHash256 addrHash, in ValueHash256 slotHash) + { + addrHash.Bytes[..StorageHashPrefixLength].CopyTo(buffer); + slotHash.Bytes.CopyTo(buffer[StorageHashPrefixLength..(StorageHashPrefixLength + StorageSlotKeySize)]); + return buffer[..StorageKeyLength]; + } + + internal static ReadOnlySpan EncodeStateNodeKey(Span buffer, in TreePath path) + { + path.Path.Bytes.CopyTo(buffer); + buffer[FullPathLength] = (byte)path.Length; + return buffer[..StateNodesKeyLength]; + } + + internal static ReadOnlySpan EncodeStateTopNodeKey(Span buffer, in TreePath path) + { + path.Path.Bytes[0..StateNodesTopPathLength].CopyTo(buffer); + buffer[StateNodesTopPathLength] = (byte)path.Length; + return buffer[..StateNodesTopKeyLength]; + } + + internal static ReadOnlySpan EncodeStorageNodeKey(Span buffer, Hash256 addr, in TreePath path) + { + addr.Bytes[..StorageHashPrefixLength].CopyTo(buffer); + path.Path.Bytes.CopyTo(buffer[StorageHashPrefixLength..]); + buffer[StorageHashPrefixLength + FullPathLength] = (byte)path.Length; + return buffer[..StorageNodesKeyLength]; + } + + internal static ReadOnlySpan EncodeStorageNodeTopKey(Span buffer, Hash256 addr, in TreePath path) + { + addr.Bytes[..StorageHashPrefixLength].CopyTo(buffer); + path.Path.Bytes[..StorageNodesTopPathLength].CopyTo(buffer[StorageHashPrefixLength..]); + buffer[StorageHashPrefixLength + StorageNodesTopPathLength] = (byte)path.Length; + return buffer[..StorageNodesTopKeyLength]; + } + + public IPersistence.IPersistenceReader CreateReader() + { + return new PersistenceReader(_db.CreateSnapshot(), this); + } + + public IPersistence.IWriteBatch CreateWriteBatch(StateId from, StateId to) + { + var dbSnap = _db.CreateSnapshot(); + var currentState = ReadCurrentState(dbSnap.GetColumn(FlatDbColumns.Metadata)); + if (currentState != from) + { + dbSnap.Dispose(); + throw new InvalidOperationException( + $"Attempted to apply snapshot on top of wrong state. Snapshot from: {from}, Db state: {currentState}"); + } + + Compressor compressor = new Compressor(); + compressor.LoadDictionary(_zstdDictionary); + + return new WriteBatch(this, _preimageDb.StartWriteBatch(), _db.StartWriteBatch(), dbSnap, compressor, to); + } + + private class WriteBatch : IPersistence.IWriteBatch + { + private IWriteOnlyKeyValueStore state; + private IWriteOnlyKeyValueStore storage; + private IWriteOnlyKeyValueStore stateNodes; + private IWriteOnlyKeyValueStore stateTopNodes; + private IWriteOnlyKeyValueStore storageNodes; + private IWriteOnlyKeyValueStore storageTopNodes; + + private ISortedKeyValueStore storageSnap; + private ISortedKeyValueStore storageNodesSnap; + private ISortedKeyValueStore storageTopNodesSnap; + + private AccountDecoder _accountDecoder = AccountDecoder.Instance; + + WriteFlags _flags = WriteFlags.None; + private readonly RocksdbPersistence _mainDb; + private readonly IWriteBatch _preimageWriteBatch; + private readonly IColumnsWriteBatch _batch; + private readonly IColumnDbSnapshot _dbSnap; + private readonly Compressor _compressor; + private readonly StateId _to; + private readonly bool _flatInTrie; + private readonly bool _separateStorageTop; + + public WriteBatch(RocksdbPersistence mainDb, + IWriteBatch preimageWriteBatch, + IColumnsWriteBatch batch, + IColumnDbSnapshot dbSnap, + Compressor compressor, + StateId to) + { + _mainDb = mainDb; + _preimageWriteBatch = preimageWriteBatch; + _batch = batch; + _dbSnap = dbSnap; + _compressor = compressor; + _to = to; + + _flatInTrie = mainDb._configuration.FlatInTrie; + _separateStorageTop = mainDb._configuration.SeparateStorageTop; + if (mainDb._configuration.FlatInTrie) + { + state = batch.GetColumnBatch(FlatDbColumns.StateNodes); + storage = batch.GetColumnBatch(FlatDbColumns.StorageNodes); + } + else + { + state = batch.GetColumnBatch(FlatDbColumns.State); + storage = batch.GetColumnBatch(FlatDbColumns.Storage); + } + + stateNodes = batch.GetColumnBatch(FlatDbColumns.StateNodes); + stateTopNodes = batch.GetColumnBatch(FlatDbColumns.StateTopNodes); + storageNodes = batch.GetColumnBatch(FlatDbColumns.StorageNodes); + storageTopNodes = batch.GetColumnBatch(FlatDbColumns.StorageTopNodes); + + storageSnap = ((ISortedKeyValueStore) dbSnap.GetColumn(FlatDbColumns.Storage)); + storageNodesSnap = ((ISortedKeyValueStore) dbSnap.GetColumn(FlatDbColumns.StorageNodes)); + storageTopNodesSnap = ((ISortedKeyValueStore)dbSnap.GetColumn(FlatDbColumns.StorageTopNodes)); + } + + public void Dispose() + { + SetCurrentState(_batch.GetColumnBatch(FlatDbColumns.Metadata), _to); + _batch.Dispose(); + _dbSnap.Dispose(); + _preimageWriteBatch.Dispose(); + _compressor.Dispose(); + } + + public int SelfDestruct(Address addr) + { + ValueHash256 accountPath = addr.ToAccountPath; + Span firstKey = stackalloc byte[StorageHashPrefixLength]; // Because slot 0 is a thing, its just the address prefix. + Span lastKey = stackalloc byte[StorageNodesKeyLength]; + firstKey.Fill(0x00); + lastKey.Fill(0xff); + accountPath.Bytes[..StorageHashPrefixLength].CopyTo(firstKey); + accountPath.Bytes[..StorageHashPrefixLength].CopyTo(lastKey); + + int removedEntry = 0; + using (ISortedView storageNodeReader = storageNodesSnap.GetViewBetween(firstKey, lastKey)) + { + var storageNodeWriter = storageNodes; + while (storageNodeReader.MoveNext()) + { + storageNodeWriter.Remove(storageNodeReader.CurrentKey); + removedEntry++; + } + } + + if (_separateStorageTop) + { + using (ISortedView storageNodeReader = storageTopNodesSnap.GetViewBetween(firstKey, lastKey)) + { + var storageNodeWriter = storageNodes; + while (storageNodeReader.MoveNext()) + { + storageNodeWriter.Remove(storageNodeReader.CurrentKey); + removedEntry++; + } + } + } + + if (!_flatInTrie) + { + removedEntry = 0; // Debug + // for storage the prefix might change depending on the encoding + firstKey.Fill(0x00); + lastKey.Fill(0xff); + _mainDb.EncodeAccountKey(firstKey, addr); + _mainDb.EncodeAccountKey(lastKey, addr); + using (ISortedView storageReader = storageSnap.GetViewBetween(firstKey, lastKey)) + { + IWriteOnlyKeyValueStore? storageWriter = storage; + while (storageReader.MoveNext()) + { + storageWriter.Remove(storageReader.CurrentKey); + removedEntry++; + } + } + } + + return removedEntry; + } + + public void RemoveAccount(Address addr) + { + state.Remove(_mainDb.EncodeAccountKey(stackalloc byte[StateKeyPrefixLength], addr)); + } + + public void SetAccount(Address addr, Account account) + { + using var stream = _accountDecoder.EncodeToNewNettyStream(account); + state.PutSpan(_mainDb.EncodeAccountKey(stackalloc byte[StateKeyPrefixLength], addr), stream.AsSpan()); + } + + public void SetStorage(Address addr, UInt256 slot, ReadOnlySpan value) + { + ValueHash256 hash256 = ValueKeccak.Zero; + StorageTree.ComputeKeyWithLookup(slot, hash256.BytesAsSpan); + _preimageWriteBatch.PutSpan(hash256.Bytes, slot.ToBigEndian()); + + ReadOnlySpan theKey = _mainDb.EncodeStorageKey(stackalloc byte[StorageKeyLength], addr, slot); + storage.PutSpan(theKey, value, _flags); + } + + public void RemoveStorage(Address addr, UInt256 slot) + { + ReadOnlySpan theKey = _mainDb.EncodeStorageKey(stackalloc byte[StorageKeyLength], addr, slot); + storage.Remove(theKey); + } + + public void SetStorageRaw(Hash256 addrHash, Hash256 slotHash, ReadOnlySpan value) + { + if (_mainDb._configuration.UsePreimage) throw new InvalidOperationException("Cannot set raw when using preimage"); + + storage.PutSpan(_mainDb.EncodeStorageKeyHashed(stackalloc byte[StorageKeyLength], addrHash.ValueHash256, slotHash.ValueHash256), value, _flags); + } + + public void SetAccountRaw(Hash256 addrHash, Account account) + { + if (_mainDb._configuration.UsePreimage) throw new InvalidOperationException("Cannot set raw when using preimage"); + using var stream = _accountDecoder.EncodeToNewNettyStream(account); + + state.PutSpan(addrHash.Bytes[..StateKeyPrefixLength], stream.AsSpan(), _flags); + } + + public void SetTrieNodes(Hash256? address, TreePath path, TrieNode tn) + { + if (address is null) + { + if (path.Length <= StateNodesTopThreshold) + { + stateTopNodes.PutSpan(EncodeStateTopNodeKey(stackalloc byte[StateNodesTopKeyLength], path), tn.FullRlp.Span, _flags); + } + else + { + stateNodes.PutSpan(EncodeStateNodeKey(stackalloc byte[StateNodesKeyLength], path), tn.FullRlp.Span, _flags); + } + } + else + { + if (_separateStorageTop && path.Length <= StorageNodesTopThreshold) + { + storageTopNodes.PutSpan(EncodeStorageNodeTopKey(stackalloc byte[StorageNodesTopKeyLength], address, path), tn.FullRlp.Span, _flags); + } + else + { + storageNodes.PutSpan(EncodeStorageNodeKey(stackalloc byte[StorageNodesKeyLength], address, path), tn.FullRlp.Span, _flags); + } + } + } + } + + private class PersistenceReader : IPersistence.IPersistenceReader + { + private readonly IColumnDbSnapshot _db; + private readonly IReadOnlyKeyValueStore _state; + private readonly IReadOnlyKeyValueStore _storage; + private readonly IReadOnlyKeyValueStore _stateNodes; + private readonly IReadOnlyKeyValueStore _stateTopNodes; + private readonly IReadOnlyKeyValueStore _storageNodes; + private readonly IReadOnlyKeyValueStore _storageTopNodes; + private readonly RocksdbPersistence _mainDb; + private readonly bool _usePreimage; + private readonly bool _flatInTrie; + private readonly bool _separateStorageTop; + + public PersistenceReader(IColumnDbSnapshot db, RocksdbPersistence mainDb) + { + _usePreimage = mainDb._configuration.UsePreimage; + _flatInTrie = mainDb._configuration.FlatInTrie; + _separateStorageTop = mainDb._configuration.SeparateStorageTop; + _db = db; + _mainDb = mainDb; + CurrentState = ReadCurrentState(db.GetColumn(FlatDbColumns.Metadata)); + if (_flatInTrie) + { + _state = _db.GetColumn(FlatDbColumns.StateNodes); + _storage = _db.GetColumn(FlatDbColumns.StorageNodes); + } + else + { + _state = _db.GetColumn(FlatDbColumns.State); + _storage = _db.GetColumn(FlatDbColumns.Storage); + } + _stateNodes = _db.GetColumn(FlatDbColumns.StateNodes); + _stateTopNodes = _db.GetColumn(FlatDbColumns.StateTopNodes); + _storageNodes = _db.GetColumn(FlatDbColumns.StorageNodes); + _storageTopNodes = _db.GetColumn(FlatDbColumns.StorageTopNodes); + } + + public StateId CurrentState { get; } + + public void Dispose() + { + _db.Dispose(); + } + + /* + private Decompressor RentDecompressor() + { + Decompressor? decompressor = _decompressor; + if (decompressor is null) return _mainDb.CreateDecompressor(); + if (Interlocked.CompareExchange(ref _decompressor, null, decompressor) == decompressor) return decompressor; + return _mainDb.CreateDecompressor(); + } + + private void ReturnDecompressor(Decompressor decompressor) + { + if (Interlocked.CompareExchange(ref _decompressor, decompressor, null) == null) + { + return; + } + decompressor.Dispose(); + } + */ + + public bool TryGetAccount(Address address, out Account? acc) + { + Span value = _state.GetSpan(_mainDb.EncodeAccountKey(stackalloc byte[StateKeyPrefixLength], address)); + try + { + if (address == FlatWorldStateScope.DebugAddress) + { + Console.Error.WriteLine($"Get {address}, got {value.ToHexString()}"); + } + if (value.IsNullOrEmpty()) + { + acc = null; + return true; + } + + var ctx = new Rlp.ValueDecoderContext(value); + acc = _mainDb._accountDecoder.Decode(ref ctx); + return true; + } + finally + { + _state.DangerousReleaseMemory(value); + } + } + + public bool TryGetSlot(Address address, in UInt256 index, out byte[] valueBytes) + { + ReadOnlySpan theKey = _mainDb.EncodeStorageKey(stackalloc byte[StorageKeyLength], address, index); + Span value = _storage.GetSpan(theKey); + try + { + if (value.IsNullOrEmpty()) + { + valueBytes = null; + return true; + } + + valueBytes = value.ToArray(); + return true; + } + finally + { + _storage.DangerousReleaseMemory(value); + } + } + + public byte[]? TryLoadRlp(Hash256? address, in TreePath path, Hash256 hash, ReadFlags flags) + { + if (address is null) + { + if (path.Length <= StateNodesTopThreshold) + { + return _stateTopNodes.Get(EncodeStateTopNodeKey(stackalloc byte[StateNodesTopKeyLength], in path)); + } + else + { + return _stateNodes.Get(EncodeStateNodeKey(stackalloc byte[StateNodesKeyLength], in path)); + } + } + else + { + if (_separateStorageTop && path.Length <= StorageNodesTopThreshold) + { + return _storageTopNodes.Get(EncodeStorageNodeTopKey(stackalloc byte[StorageNodesTopKeyLength], address, in path)); + } + else + { + return _storageNodes.Get(EncodeStorageNodeKey(stackalloc byte[StorageNodesKeyLength], address, in path)); + } + } + } + + public byte[]? GetAccountRaw(Hash256 addrHash) + { + return GetAccountRaw(addrHash.ValueHash256); + } + + private byte[]? GetAccountRaw(in ValueHash256 accountHash) + { + if (_usePreimage) throw new InvalidOperationException("Raw operation not available in preimage mode"); + return _state.GetSpan(accountHash.Bytes[..StateKeyPrefixLength]).ToArray(); + } + + public byte[]? GetStorageRaw(Hash256? addrHash, Hash256 slotHash) + { + if (_usePreimage) throw new InvalidOperationException("Raw operation not available in preimage mode"); + Span keySpan = stackalloc byte[StorageKeyLength]; + ReadOnlySpan storageKey = _mainDb.EncodeStorageKeyHashed(keySpan, addrHash.ValueHash256, slotHash.ValueHash256); + return _storage.Get(storageKey); + } + } +} diff --git a/src/Nethermind/Nethermind.State/Flat/Persistence/zstddictionary.bin b/src/Nethermind/Nethermind.State/Flat/Persistence/zstddictionary.bin new file mode 100644 index 00000000000..ddbdc07779a Binary files /dev/null and b/src/Nethermind/Nethermind.State/Flat/Persistence/zstddictionary.bin differ diff --git a/src/Nethermind/Nethermind.State/Flat/ReadonlyReaderRepository.cs b/src/Nethermind/Nethermind.State/Flat/ReadonlyReaderRepository.cs new file mode 100644 index 00000000000..98866a22199 --- /dev/null +++ b/src/Nethermind/Nethermind.State/Flat/ReadonlyReaderRepository.cs @@ -0,0 +1,56 @@ +// SPDX-FileCopyrightText: 2025 Demerzel Solutions Limited +// SPDX-License-Identifier: LGPL-3.0-only + +using System.Collections.Concurrent; +using Nethermind.Core.Collections; +using Nethermind.Core.Utils; + +namespace Nethermind.State.Flat; + +public class ReadonlyReaderRepository +{ + private ConcurrentDictionary> _sharedReader = new(); + private readonly IFlatDiffRepository _flatDiffRepository; + + public ReadonlyReaderRepository(IFlatDiffRepository flatDiffRepository) + { + flatDiffRepository.ReorgBoundaryReached += (sender, reached) => ClearAllReader(); + _flatDiffRepository = flatDiffRepository; + } + + private void ClearAllReader() + { + // Readers take up a persistence snapshot which which eventually slow down the db. So we need to clear them + // on persist, that way a new snapshot will be used later. + using ArrayPoolListRef toRemoves = new ArrayPoolListRef(_sharedReader.Count, _sharedReader.Keys); + + foreach (var stateId in toRemoves) + { + if (_sharedReader.TryRemove(stateId, out RefCountingDisposableBox snapshotBundle)) + { + snapshotBundle.Dispose(); + } + } + } + + public RefCountingDisposableBox? GatherReadOnlyReaderAtBaseBlock(StateId baseBlock) + { + if (_sharedReader.TryGetValue(baseBlock, out var snapshotBundle) && snapshotBundle.TryAcquire()) + { + return snapshotBundle; + } + + SnapshotBundle? bundle = _flatDiffRepository.GatherReaderAtBaseBlock(baseBlock, IFlatDiffRepository.SnapshotBundleUsage.StateReader); + if (bundle is null) return null; + + RefCountingDisposableBox newReader = new RefCountingDisposableBox(bundle); + newReader.AcquireLease(); + if (!_sharedReader.TryAdd(baseBlock, newReader)) + { + newReader.Dispose(); + } + + return newReader; + } + +} diff --git a/src/Nethermind/Nethermind.State/Flat/ScopeProvider/AbstractMinimalTrieStore.cs b/src/Nethermind/Nethermind.State/Flat/ScopeProvider/AbstractMinimalTrieStore.cs new file mode 100644 index 00000000000..f28a27448df --- /dev/null +++ b/src/Nethermind/Nethermind.State/Flat/ScopeProvider/AbstractMinimalTrieStore.cs @@ -0,0 +1,52 @@ +// SPDX-FileCopyrightText: 2025 Demerzel Solutions Limited +// SPDX-License-Identifier: LGPL-3.0-only + +using System; +using Nethermind.Core; +using Nethermind.Core.Crypto; +using Nethermind.Trie; +using Nethermind.Trie.Pruning; + +namespace Nethermind.State.Flat.ScopeProvider; + +public abstract class AbstractMinimalTrieStore: IScopedTrieStore +{ + public abstract TrieNode FindCachedOrUnknown(in TreePath path, Hash256 hash); + + public abstract byte[]? TryLoadRlp(in TreePath path, Hash256 hash, ReadFlags flags = ReadFlags.None); + public abstract ICommitter BeginCommit(TrieNode? root, WriteFlags writeFlags = WriteFlags.None); + + + public byte[]? LoadRlp(in TreePath path, Hash256 hash, ReadFlags flags = ReadFlags.None) + { + byte[]? value = TryLoadRlp(path, hash, flags); + if (value is null) + { + throw new TrieNodeException($"Missing trie node. {path}:{hash}", path, hash); + } + + return value; + } + + public virtual ITrieNodeResolver GetStorageTrieNodeResolver(Hash256? address) => throw new UnsupportedOperationException("Get trie node resolver not supported"); + + public INodeStorage.KeyScheme Scheme => INodeStorage.KeyScheme.HalfPath; + + public bool IsPersisted(in TreePath path, in ValueHash256 keccak) => throw new UnsupportedOperationException("Persisted check not supported"); + + public abstract class AbstractMinimalCommitter(ConcurrencyQuota quota) : ICommitter + { + public void Dispose() + { + } + + public abstract TrieNode CommitNode(ref TreePath path, TrieNode node); + + bool ICommitter.TryRequestConcurrentQuota() => quota.TryRequestConcurrencyQuota(); + void ICommitter.ReturnConcurrencyQuota() => quota.ReturnConcurrencyQuota(); + } + + public class UnsupportedOperationException(string message) : Exception(message) + { + } +} diff --git a/src/Nethermind/Nethermind.State/Flat/ScopeProvider/ConcurrencyQuota.cs b/src/Nethermind/Nethermind.State/Flat/ScopeProvider/ConcurrencyQuota.cs new file mode 100644 index 00000000000..70c31a9c1b3 --- /dev/null +++ b/src/Nethermind/Nethermind.State/Flat/ScopeProvider/ConcurrencyQuota.cs @@ -0,0 +1,25 @@ +// SPDX-FileCopyrightText: 2025 Demerzel Solutions Limited +// SPDX-License-Identifier: LGPL-3.0-only + +using System; +using System.Threading; + +namespace Nethermind.State.Flat.ScopeProvider; + +public class ConcurrencyQuota() +{ + private int _concurrency = Environment.ProcessorCount; + + public bool TryRequestConcurrencyQuota() + { + if (Interlocked.Decrement(ref _concurrency) >= 0) + { + return true; + } + + ReturnConcurrencyQuota(); + return false; + } + + public void ReturnConcurrencyQuota() => Interlocked.Increment(ref _concurrency); +} diff --git a/src/Nethermind/Nethermind.State/Flat/ScopeProvider/FlatScopeProvider.cs b/src/Nethermind/Nethermind.State/Flat/ScopeProvider/FlatScopeProvider.cs new file mode 100644 index 00000000000..6b5e43ffc18 --- /dev/null +++ b/src/Nethermind/Nethermind.State/Flat/ScopeProvider/FlatScopeProvider.cs @@ -0,0 +1,73 @@ +// SPDX-FileCopyrightText: 2025 Demerzel Solutions Limited +// SPDX-License-Identifier: LGPL-3.0-only + +using Autofac.Features.AttributeFilters; +using Nethermind.Core; +using Nethermind.Db; +using Nethermind.Evm.State; +using Nethermind.Logging; +using Org.BouncyCastle.Bcpg; + +namespace Nethermind.State.Flat.ScopeProvider; + +public class FlatScopeProvider : IWorldStateScopeProvider +{ + private readonly IFlatDiffRepository _flatDiffRepository; + private readonly ILogManager _logManager; + private readonly TrieStoreScopeProvider.KeyValueWithBatchingBackedCodeDb _codeDb; + private readonly bool _isReadOnly; + private readonly FlatDiffRepository.Configuration _configuration; + private readonly ITrieWarmer _trieWarmer; + private readonly ResourcePool _resourcePool; + private readonly IFlatDiffRepository.SnapshotBundleUsage _usage; + + public FlatScopeProvider( + [KeyFilter(DbNames.Code)] IDb codeDb, + IFlatDiffRepository flatDiffRepository, + FlatDiffRepository.Configuration configuration, + ITrieWarmer trieWarmer, + ResourcePool resourcePool, + IFlatDiffRepository.SnapshotBundleUsage usage, + ILogManager logManager, + bool isReadOnly = false) + { + _flatDiffRepository = flatDiffRepository; + _configuration = configuration; + _trieWarmer = trieWarmer; + _resourcePool = resourcePool; + _usage = usage; + _logManager = logManager; + _codeDb = new TrieStoreScopeProvider.KeyValueWithBatchingBackedCodeDb(codeDb); + _isReadOnly = isReadOnly; + } + + public bool HasRoot(BlockHeader? baseBlock) + { + return _flatDiffRepository.HasStateForBlock(new StateId(baseBlock)); + } + + public IWorldStateScopeProvider.IScope BeginScope(BlockHeader? baseBlock) + { + StateId currentState = new StateId(baseBlock); + SnapshotBundle snapshotBundle = _flatDiffRepository.GatherReaderAtBaseBlock(currentState, usage: _usage); + if (_trieWarmer is NoopTrieWarmer) snapshotBundle.SetPrewarmer(); + + ITrieWarmer warmer = _trieWarmer; + if (_configuration.DisableTrieWarmer) + { + warmer = new NoopTrieWarmer(); + } + + return new FlatWorldStateScope( + currentState, + snapshotBundle, + _codeDb, + _flatDiffRepository, + _configuration, + warmer, + _resourcePool, + _logManager, + _isReadOnly + ); + } +} diff --git a/src/Nethermind/Nethermind.State/Flat/ScopeProvider/FlatStorageTree.cs b/src/Nethermind/Nethermind.State/Flat/ScopeProvider/FlatStorageTree.cs new file mode 100644 index 00000000000..505607d6493 --- /dev/null +++ b/src/Nethermind/Nethermind.State/Flat/ScopeProvider/FlatStorageTree.cs @@ -0,0 +1,202 @@ +// SPDX-FileCopyrightText: 2025 Demerzel Solutions Limited +// SPDX-License-Identifier: LGPL-3.0-only + +using System; +using Nethermind.Core; +using Nethermind.Core.Collections; +using Nethermind.Core.Crypto; +using Nethermind.Core.Extensions; +using Nethermind.Evm.State; +using Nethermind.Int256; +using Nethermind.Logging; +using Nethermind.Trie; + +namespace Nethermind.State.Flat.ScopeProvider; + +public class FlatStorageTree : IWorldStateScopeProvider.IStorageTree +{ + private readonly StorageTree _tree; + private readonly StorageTree _warmupStorageTree; + private readonly Address _address; + private readonly FlatDiffRepository.Configuration _config; + private readonly ITrieWarmer _trieCacheWarmer; + private readonly FlatWorldStateScope _scope; + private readonly SnapshotBundle _bundle; + private readonly Hash256 _addressHash; + private readonly StorageTrieStoreAdapter _storageTrieAdapter; + private readonly StorageTrieStoreAdapter _warmerStorageTrieAdapter; + + // This number is the idx of the snapshot in the SnapshotBundle where a clear for this account was found. + // This is passed to TryGetSlot which prevent it from reading before self destruct. + private int _selfDestructKnownStateIdx; + + public FlatStorageTree( + FlatWorldStateScope scope, + ITrieWarmer trieCacheWarmer, + SnapshotBundle bundle, + FlatDiffRepository.Configuration config, + ConcurrencyQuota concurrencyQuota, + Hash256 storageRoot, + Address address, + ILogManager logManager) + { + _scope = scope; + _trieCacheWarmer = trieCacheWarmer; + _bundle = bundle; + _address = address; + _addressHash = address.ToAccountPath.ToHash256(); + _selfDestructKnownStateIdx = bundle.DetermineSelfDestructSnapshotIdx(address); + + _storageTrieAdapter = new StorageTrieStoreAdapter(bundle, concurrencyQuota, _addressHash, _selfDestructKnownStateIdx, + isTrieWarmer: false); + _warmerStorageTrieAdapter = new StorageTrieStoreAdapter(bundle, concurrencyQuota, _addressHash, _selfDestructKnownStateIdx, + isTrieWarmer: true); + + _tree = new StorageTree(_storageTrieAdapter, storageRoot, logManager); + _tree.RootHash = storageRoot; + + _warmupStorageTree = new StorageTree(_warmerStorageTrieAdapter, logManager); + _warmupStorageTree.RootHash = storageRoot; + + _config = config; + + // In case its all write. + // TODO: Check hint set is working or not. + _trieCacheWarmer.PushJob(_scope, null, this, 0, _scope.HintSequenceId); + } + + public Hash256 RootHash => _tree.RootHash; + public byte[] Get(in UInt256 index) + { + if (!_config.ReadWithTrie && TryGet(index, out var value)) + { + if (value == null) value = State.StorageTree.ZeroBytes; + + if (_config.VerifyWithTrie) + { + var treeValue = _tree.Get(index); + if (!Bytes.AreEqual(treeValue, value)) + { + throw new Exception($"Get slot got wrong value. Address {_address}, {_tree.RootHash}, {index}. Tree: {treeValue?.ToHexString()} vs Flat: {value?.ToHexString()}. Self destruct it {_selfDestructKnownStateIdx}"); + } + } + + HintGet(index, value); + return value; + } + else + { + value = _tree.Get(index); + HintGet(index, value); + return value; + } + } + + public void HintGet(in UInt256 index, byte[]? value) + { + // Note: VERY hot code. + // 90% of the read goes through prewarmer, not actually go through this class, meaning this method is called + // a lot. Unlike with account, setting the setted slot have a measurable net negative impact on performance. + // Trying to set this value async through trie warmer proved to be hard to pull of and result in random invalid + // block. + WarmUpSlot(index); + } + + public void HintSet(in UInt256 index) + { + WarmUpSlot(index); + } + + private void WarmUpSlot(UInt256 index) + { + _trieCacheWarmer.PushJob(_scope, null, this, index, _scope.HintSequenceId); + } + + // Called by trie warmer. + public bool WarUpStorageTrie(UInt256 index, int sequenceId) + { + if (_scope.HintSequenceId != sequenceId) return false; + + if (_bundle.ShouldPrewarm(_address, index)) + { + // Note: storage tree root not changed after write batch. Also not cleared. So the result is not correct. + // this is just to warm up the nodes. + ValueHash256 key = ValueKeccak.Zero; + StorageTree.ComputeKeyWithLookup(index, key.BytesAsSpan); + _ = _warmupStorageTree.Get(key.BytesAsSpan, keepChildRef: true); + return true; + } + + return false; + } + + private bool TryGet(in UInt256 index, out byte[]? value) + { + return _bundle.TryGetSlot(_address, index, _selfDestructKnownStateIdx, out value); + } + + public byte[] Get(in ValueHash256 hash) + { + throw new Exception("Not supported"); + } + + private void Set(UInt256 slot, byte[] value) + { + _bundle.SetChangedSlot(_address, slot, value); + } + + public void SelfDestruct() + { + _bundle.Clear(_address, _addressHash); + _selfDestructKnownStateIdx = _bundle.DetermineSelfDestructSnapshotIdx(_address); + + // Technically, they wont actually matter as the trie will traverse the existing path anyway and on self destruct + // it will just get blocked on root, so this is more of an optimization. + _storageTrieAdapter.SelfDestructKnownStateIdx = _selfDestructKnownStateIdx; + _warmerStorageTrieAdapter.SelfDestructKnownStateIdx = _selfDestructKnownStateIdx; + } + + public void CommitTree() + { + _tree.Commit(); + } + + public IWorldStateScopeProvider.IStorageWriteBatch CreateWriteBatch(int estimatedEntries, Action onRootUpdated) + { + TrieStoreScopeProvider.StorageTreeBulkWriteBatch storageTreeBulkWriteBatch = + new TrieStoreScopeProvider.StorageTreeBulkWriteBatch( + estimatedEntries, + _tree, + onRootUpdated, + _address, + commit: true); + + return new StorageTreeBulkWriteBatch( + storageTreeBulkWriteBatch, + this + ); + } + + private class StorageTreeBulkWriteBatch( + TrieStoreScopeProvider.StorageTreeBulkWriteBatch storageTreeBulkWriteBatch, + FlatStorageTree storageTree) : IWorldStateScopeProvider.IStorageWriteBatch + { + public void Set(in UInt256 index, byte[] value) + { + storageTreeBulkWriteBatch.Set(in index, value); + storageTree.Set(index, value); + } + + public void Clear() + { + storageTreeBulkWriteBatch.Clear(); + storageTree.SelfDestruct(); + } + + public void Dispose() + { + storageTreeBulkWriteBatch.Dispose(); + } + } +} + diff --git a/src/Nethermind/Nethermind.State/Flat/ScopeProvider/FlatWorldStateManager.cs b/src/Nethermind/Nethermind.State/Flat/ScopeProvider/FlatWorldStateManager.cs new file mode 100644 index 00000000000..5b8cb6123e4 --- /dev/null +++ b/src/Nethermind/Nethermind.State/Flat/ScopeProvider/FlatWorldStateManager.cs @@ -0,0 +1,129 @@ +// SPDX-FileCopyrightText: 2025 Demerzel Solutions Limited +// SPDX-License-Identifier: LGPL-3.0-only + +using System; +using System.Threading; +using Autofac.Features.AttributeFilters; +using Nethermind.Config; +using Nethermind.Core; +using Nethermind.Db; +using Nethermind.Evm.State; +using Nethermind.Logging; +using Nethermind.State.Flat.Persistence; +using Nethermind.State.SnapServer; +using Nethermind.Trie; +using Nethermind.Trie.Pruning; + +namespace Nethermind.State.Flat.ScopeProvider; + +public class FlatWorldStateManager : IWorldStateManager +{ + private readonly IFlatDiffRepository _flatDiffRepository; + private readonly FlatStateReader _flatStateReader; + private readonly IProcessExitSource _exitSource; + private readonly IDb _codeDb; + private readonly ILogManager _logManager; + private readonly FlatDiffRepository.Configuration _configuration; + private readonly FlatScopeProvider _mainWorldState; + private readonly ResourcePool _resourcePool; + + public FlatWorldStateManager( + IFlatDiffRepository flatDiffRepository, + FlatDiffRepository.Configuration configuration, + FlatStateReader flatStateReader, + TrieWarmer trieWarmer, + IProcessExitSource exitSource, + [KeyFilter(DbNames.Code)] IDb codeDb, + ResourcePool resourcePool, + ILogManager logManager + ) + { + _flatDiffRepository = flatDiffRepository; + _flatStateReader = flatStateReader; + _codeDb = codeDb; + _logManager = logManager; + _configuration = configuration; + _exitSource = exitSource; + _resourcePool = resourcePool; + _mainWorldState = new FlatScopeProvider( + codeDb, + flatDiffRepository, + configuration, + trieWarmer, + resourcePool, + IFlatDiffRepository.SnapshotBundleUsage.MainBlockProcessing, + logManager); + } + + public IWorldStateScopeProvider GlobalWorldState => _mainWorldState; + public IStateReader GlobalStateReader => _flatStateReader; + public ISnapServer? SnapServer => null; + public IReadOnlyKeyValueStore? HashServer => null; + public IWorldStateScopeProvider CreateResettableWorldState() + { + return new FlatScopeProvider( + _codeDb, + _flatDiffRepository, + _configuration, + new NoopTrieWarmer(), + _resourcePool, + IFlatDiffRepository.SnapshotBundleUsage.ReadOnlyProcessingEnv, + _logManager, + isReadOnly: true); + } + + event EventHandler? IWorldStateManager.ReorgBoundaryReached + { + add => _flatDiffRepository.ReorgBoundaryReached += value; + remove => _flatDiffRepository.ReorgBoundaryReached -= value; + } + + public IOverridableWorldScope CreateOverridableWorldScope() + { + var scopeProvider = new FlatScopeProvider( + _codeDb, + _flatDiffRepository, + _configuration, + new NoopTrieWarmer(), + _resourcePool, + IFlatDiffRepository.SnapshotBundleUsage.ReadOnlyProcessingEnv, + _logManager, + isReadOnly: true); + return new FakeOverridableWorldScope(scopeProvider, _flatStateReader); + } + + public bool VerifyTrie(BlockHeader stateAtBlock, CancellationToken cancellationToken) + { + using IPersistence.IPersistenceReader reader = _flatDiffRepository.LeaseReader(); + FlatVerifyTrieVisitor trieVisitor = new FlatVerifyTrieVisitor(_codeDb, reader, _logManager, cancellationToken); + + // Just a bit hacky due to mistmatch with best persisted state and actually persisted state. + StateId? stateId = _flatDiffRepository.FindLatestAvailableState(); + + // StateId? stateId = _flatDiffRepository.FindStateIdForStateRoot(stateAtBlock.StateRoot); + _flatStateReader.RunTreeVisitor(trieVisitor, stateId.Value.stateRoot.ToHash256(), new VisitingOptions() + { + }); + + if (trieVisitor.Stats.MismatchedAccount > 0 || trieVisitor.Stats.MismatchedSlot > 0) + { + _exitSource.Exit(10); + } + + return true; + } + + public void FlushCache(CancellationToken cancellationToken) + { + _flatDiffRepository.FlushCache(cancellationToken); + } + + public class FakeOverridableWorldScope(IWorldStateScopeProvider worldState, IStateReader stateReader) : IOverridableWorldScope + { + public IWorldStateScopeProvider WorldState => worldState; + public IStateReader GlobalStateReader => stateReader; + public void ResetOverrides() + { + } + } +} diff --git a/src/Nethermind/Nethermind.State/Flat/ScopeProvider/FlatWorldStateScope.cs b/src/Nethermind/Nethermind.State/Flat/ScopeProvider/FlatWorldStateScope.cs new file mode 100644 index 00000000000..db9ad6651b1 --- /dev/null +++ b/src/Nethermind/Nethermind.State/Flat/ScopeProvider/FlatWorldStateScope.cs @@ -0,0 +1,330 @@ +// SPDX-FileCopyrightText: 2025 Demerzel Solutions Limited +// SPDX-License-Identifier: LGPL-3.0-only + +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Threading; +using System.Threading.Tasks; +using Nethermind.Core; +using Nethermind.Core.Collections; +using Nethermind.Core.Crypto; +using Nethermind.Core.Extensions; +using Nethermind.Evm.State; +using Nethermind.Int256; +using Nethermind.Logging; +using Nethermind.Trie; +using Prometheus; + +namespace Nethermind.State.Flat.ScopeProvider; + +public class FlatWorldStateScope : IWorldStateScopeProvider.IScope +{ + // internal static Address DebugAddress = new Address("0x2c2b2df915e31d27e7a24c7c3cf9b114208a45e0"); + internal static Address DebugAddress = new Address("0x6ffedc1562918c07ae49b0ba210e6d80c7d61eab"); + internal static UInt256 DebugSlot = UInt256.Parse("0"); + + private static Histogram _snapshotBundleTimes = DevMetric.Factory.CreateHistogram("flat_write_batch", "aha", new HistogramConfiguration() + { + LabelNames = new[] { "type" }, + // Buckets = Histogram.PowersOfTenDividedBuckets(2, 12, 5) + Buckets = [1] + }); + + private readonly SnapshotBundle _snapshotBundle; + private readonly IWorldStateScopeProvider.ICodeDb _codeDb; + private readonly IFlatDiffRepository _flatDiffRepository; + private readonly Dictionary _storages = new(); + private readonly StateTree _stateTree; + private readonly PatriciaTree _warmupStateTree; + private readonly ILogManager _logManager; + private readonly bool _isReadOnly; + private readonly FlatDiffRepository.Configuration _configuration; + private readonly ConcurrencyQuota _concurrencyQuota; + private readonly ITrieWarmer _warmer; + + // The sequence id is for stopping trie warmer for doing work while committing. Incrementing this value invalidates + // tasks within the trie warmers's ring buffer. + private int _hintSequenceId = 0; + private StateId _currentStateId; + private readonly ResourcePool _resourcePool; + + public FlatWorldStateScope( + StateId currentStateId, + SnapshotBundle snapshotBundle, + IWorldStateScopeProvider.ICodeDb codeDb, + IFlatDiffRepository flatDiffRepository, + FlatDiffRepository.Configuration configuration, + ITrieWarmer trieCacheWarmer, + ResourcePool resourcePool, + ILogManager logManager, + bool isReadOnly = false) + { + _currentStateId = currentStateId; + _snapshotBundle = snapshotBundle; + _codeDb = codeDb; + _flatDiffRepository = flatDiffRepository; + _resourcePool = resourcePool; + + _concurrencyQuota = new ConcurrencyQuota(); // Used during tree commit. + _stateTree = new StateTree( + new StateTrieStoreAdapter(snapshotBundle, _concurrencyQuota, isTrieWarmer: false), + logManager + ); + _stateTree.RootHash = currentStateId.stateRoot.ToCommitment(); + _warmupStateTree = new PatriciaTree( + new StateTrieStoreAdapter(snapshotBundle, _concurrencyQuota, isTrieWarmer: true), + logManager + ); + _warmupStateTree.RootHash = currentStateId.stateRoot.ToCommitment(); + + _configuration = configuration; + _logManager = logManager; + _warmer = trieCacheWarmer; + _warmer.OnNewScope(); + _isReadOnly = isReadOnly; + } + + public void Dispose() => _snapshotBundle.Dispose(); + public Hash256 RootHash => _stateTree.RootHash; + public void UpdateRootHash() => _stateTree.UpdateRootHash(); + + public Account? Get(Address address) + { + if (!_configuration.ReadWithTrie && _snapshotBundle.TryGetAccount(address, out Account account)) + { + HintGet(address, account); + + if (address == DebugAddress) + { + Account? accTrie = _stateTree.Get(address); + Console.Error.WriteLine($"Address Get {account}. Tree {accTrie}"); + } + + if (_configuration.VerifyWithTrie) + { + Account? accTrie = _stateTree.Get(address); + if (accTrie != account) + { + throw new Exception($"Incorrect account {address}, account hash {address.ToAccountPath}, trie: {accTrie} vs flat: {account}"); + } + } + + return account; + } + else + { + account = _stateTree.Get(address); + HintGet(address, account); + return account; + } + } + + public void HintGet(Address address, Account? account) + { + _warmer.PushJob(this, address, null, null, _hintSequenceId); + + // during storage root update, the account will get re-fetched then updated. + _snapshotBundle.SetAccount(address, account); + } + + public void HintSet(Address address) + { + _warmer.PushJob(this, address, null, null, _hintSequenceId); + } + + public IWorldStateScopeProvider.ICodeDb CodeDb => _codeDb; + public int HintSequenceId => _hintSequenceId; // Called by FlatStorageTree + + public bool WarmUpStateTrie(Address address, int sequenceId) + { + if (_hintSequenceId != sequenceId) return false; + + try + { + if (_snapshotBundle.ShouldPrewarm(address, null)) + { + // Note: tree root not changed after write batch. Also not cleared. So the result is not correct. + // this is just for warming up + _ = _warmupStateTree.Get(address.ToAccountPath.Bytes, keepChildRef: true); + } + } + catch (AbstractMinimalTrieStore.UnsupportedOperationException) + { + // So there is this highly confusing case where patriciatree attempted to set storage nodes as persisted + // if its parent is persisted. No idea what is the case, but in this case, we really dont care. + } + + return true; + } + + public IWorldStateScopeProvider.IStorageTree CreateStorageTree(Address address) => CreateStorageTreeImpl(address); + + private FlatStorageTree CreateStorageTreeImpl(Address address) + { + ref FlatStorageTree storage = ref CollectionsMarshal.GetValueRefOrAddDefault(_storages, address, out bool exists); + if (exists) + { + return storage; + } + + Hash256 storageRoot = Get(address)?.StorageRoot ?? Keccak.EmptyTreeHash; + storage = new FlatStorageTree( + this, + _warmer, + _snapshotBundle, + _configuration, + _concurrencyQuota, + storageRoot, + address, + _logManager); + + return storage; + } + + public IWorldStateScopeProvider.IWorldStateWriteBatch StartWriteBatch(int estimatedAccountNum) + { + // Invalidates trie node warmer tasks at this point. Write batch already do things in parallel. + return new WriteBatch(this, estimatedAccountNum, _logManager.GetClassLogger()); + } + + public void Commit(long blockNumber) + { + StateId newStateId = new StateId(blockNumber, RootHash); + if (!_isReadOnly) + { + long sw = Stopwatch.GetTimestamp(); + // Commit will copy the trie nodes from the tree to the bundle. + using ArrayPoolList commitTask = new ArrayPoolList(_storages.Count); + + commitTask.Add(Task.Factory.StartNew(() => + { + sw = Stopwatch.GetTimestamp(); + // Commit will copy the trie nodes from the tree to the bundle. + _stateTree.Commit(); + _snapshotBundleTimes.WithLabels("statetree_commit").Observe(Stopwatch.GetTimestamp() - sw); + })); + + foreach (KeyValuePair storage in _storages) + { + commitTask.Add(Task.Factory.StartNew((ctx) => + { + FlatStorageTree st = (FlatStorageTree)ctx; + st.CommitTree(); + _concurrencyQuota.ReturnConcurrencyQuota(); + }, storage.Value)); + } + + Task.WaitAll(commitTask.AsSpan()); + _snapshotBundleTimes.WithLabels("storage_commit_wait").Observe(Stopwatch.GetTimestamp() - sw); + } + + _storages.Clear(); + + bool shouldAddSnapshot = !_isReadOnly && _currentStateId != newStateId; + + (Snapshot newSnapshot, CachedResource cachedResource) = _snapshotBundle.CollectAndApplySnapshot(_currentStateId, newStateId, shouldAddSnapshot); + + if (shouldAddSnapshot) + { + if (_currentStateId != newStateId) + { + _flatDiffRepository.AddSnapshot(newSnapshot, cachedResource); + } + } + + _currentStateId = newStateId; + } + + private class WriteBatch( + FlatWorldStateScope scope, + int estimatedAccountCount, + ILogger logger + ) : IWorldStateScopeProvider.IWorldStateWriteBatch + { + private readonly Dictionary _dirtyAccounts = new(estimatedAccountCount); + private readonly ConcurrentQueue<(AddressAsKey, Hash256)> _dirtyStorageTree = new(); + + public event EventHandler? OnAccountUpdated; + + public void Set(Address key, Account? account) + { + if (key == DebugAddress) Console.Error.WriteLine($"Address Set {account}"); + _dirtyAccounts[key] = account; + scope._snapshotBundle.SetAccount(key, account); + + if (account == null) + { + // This may not get called by the storage write batch as the worldstate does not try to update storage + // at all if the end account is null. This is not a problem for trie, but is a problem for flat. + scope.CreateStorageTreeImpl(key).SelfDestruct(); + } + } + + public IWorldStateScopeProvider.IStorageWriteBatch CreateStorageWriteBatch(Address address, int estimatedEntries) + { + return scope + .CreateStorageTreeImpl(address) + .CreateWriteBatch( + estimatedEntries: estimatedEntries, + onRootUpdated: (address, newRoot) => MarkDirty(address, newRoot)); + } + + private void MarkDirty(AddressAsKey address, Hash256 storageTreeRootHash) + { + _dirtyStorageTree.Enqueue((address, storageTreeRootHash)); + } + + public void Dispose() + { + try + { + long sw = Stopwatch.GetTimestamp(); + while (_dirtyStorageTree.TryDequeue(out var entry)) + { + (AddressAsKey key, Hash256 storageRoot) = entry; + if (!_dirtyAccounts.TryGetValue(key, out var account)) account = scope.Get(key); + if (account == null && storageRoot == Keccak.EmptyTreeHash) continue; + account ??= ThrowNullAccount(key); + account = account!.WithChangedStorageRoot(storageRoot); + // if (key == DebugAddress) Console.Error.WriteLine($"Address root update {account}"); + scope._snapshotBundle.SetAccount(key, account); + _dirtyAccounts[key] = account; + OnAccountUpdated?.Invoke(key, new IWorldStateScopeProvider.AccountUpdated(key, account)); + if (logger.IsTrace) Trace(key, storageRoot, account); + } + _snapshotBundleTimes.WithLabels("dirtystorage_dequeue").Observe(Stopwatch.GetTimestamp() - sw); + + using (var stateSetter = scope._stateTree.BeginSet(_dirtyAccounts.Count)) + { + sw = Stopwatch.GetTimestamp(); + foreach (var kv in _dirtyAccounts) + { + stateSetter.Set(kv.Key, kv.Value); + } + _snapshotBundleTimes.WithLabels("account_set").Observe(Stopwatch.GetTimestamp() - sw); + sw = Stopwatch.GetTimestamp(); + } + _snapshotBundleTimes.WithLabels("account_set_dispose").Observe(Stopwatch.GetTimestamp() - sw); + } + finally + { + _dirtyAccounts.Clear(); + + Interlocked.Increment(ref scope._hintSequenceId); + } + + [MethodImpl(MethodImplOptions.NoInlining)] + void Trace(Address address, Hash256 storageRoot, Account? account) + => logger.Trace($"Update {address} S {account?.StorageRoot} -> {storageRoot}"); + + [DoesNotReturn, StackTraceHidden] + static Account ThrowNullAccount(Address address) + => throw new InvalidOperationException($"Account {address} is null when updating storage hash"); + } + } +} diff --git a/src/Nethermind/Nethermind.State/Flat/ScopeProvider/StateTrieStoreAdapter.cs b/src/Nethermind/Nethermind.State/Flat/ScopeProvider/StateTrieStoreAdapter.cs new file mode 100644 index 00000000000..ac23933808f --- /dev/null +++ b/src/Nethermind/Nethermind.State/Flat/ScopeProvider/StateTrieStoreAdapter.cs @@ -0,0 +1,75 @@ +// SPDX-FileCopyrightText: 2025 Demerzel Solutions Limited +// SPDX-License-Identifier: LGPL-3.0-only + +using Nethermind.Core; +using Nethermind.Core.Crypto; +using Nethermind.Trie; +using Nethermind.Trie.Pruning; + +namespace Nethermind.State.Flat.ScopeProvider; + +internal class StateTrieStoreAdapter( + SnapshotBundle bundle, + ConcurrencyQuota concurrencyQuota, + bool isTrieWarmer +) : AbstractMinimalTrieStore +{ + public override TrieNode FindCachedOrUnknown(in TreePath path, Hash256 hash) + { + return bundle.FindStateNodeOrUnknown(path, hash, isTrieWarmer); + } + + public override byte[]? TryLoadRlp(in TreePath path, Hash256 hash, ReadFlags flags = ReadFlags.None) => bundle.TryLoadRlp(null, path, hash, flags, isTrieWarmer); + + public override ICommitter BeginCommit(TrieNode? root, WriteFlags writeFlags = WriteFlags.None) => new Committer(bundle, concurrencyQuota); + + public override ITrieNodeResolver GetStorageTrieNodeResolver(Hash256? address) + { + // Used in trie visitor and weird very edge case that cuts the whole thing to peaces + return new StorageTrieStoreAdapter(bundle, concurrencyQuota, address, -1, isTrieWarmer); + } + + private class Committer(SnapshotBundle bundle, ConcurrencyQuota concurrencyQuota) : AbstractMinimalCommitter(concurrencyQuota) + { + public override TrieNode CommitNode(ref TreePath path, TrieNode node) + { + bundle.SetStateNode(path, node); + return node; + } + } +} + +internal class StorageTrieStoreAdapter( + SnapshotBundle bundle, + ConcurrencyQuota concurrencyQuota, + Hash256AsKey addressHash, + int selfDestructKnownStateIdx, + bool isTrieWarmer +): AbstractMinimalTrieStore +{ + internal int SelfDestructKnownStateIdx = selfDestructKnownStateIdx; + + public override TrieNode FindCachedOrUnknown(in TreePath path, Hash256 hash) + { + return bundle.FindStorageNodeOrUnknown(addressHash, path, hash, SelfDestructKnownStateIdx, isTrieWarmer); + } + + public override byte[]? TryLoadRlp(in TreePath path, Hash256 hash, ReadFlags flags = ReadFlags.None) + { + return bundle.TryLoadRlp(addressHash, in path, hash, flags, isTrieWarmer); + } + + public override ICommitter BeginCommit(TrieNode? root, WriteFlags writeFlags = WriteFlags.None) + { + return new Committer(bundle, addressHash, concurrencyQuota); + } + + private class Committer(SnapshotBundle bundle, Hash256AsKey addressHash, ConcurrencyQuota concurrencyQuota) : AbstractMinimalCommitter(concurrencyQuota) + { + public override TrieNode CommitNode(ref TreePath path, TrieNode node) + { + bundle.SetStorageNode(addressHash, path, node); + return node; + } + } +} diff --git a/src/Nethermind/Nethermind.State/Flat/ScopeProvider/TrieStoreTrieCacheWarmer.cs b/src/Nethermind/Nethermind.State/Flat/ScopeProvider/TrieStoreTrieCacheWarmer.cs new file mode 100644 index 00000000000..6f58badc914 --- /dev/null +++ b/src/Nethermind/Nethermind.State/Flat/ScopeProvider/TrieStoreTrieCacheWarmer.cs @@ -0,0 +1,253 @@ +// SPDX-FileCopyrightText: 2025 Demerzel Solutions Limited +// SPDX-License-Identifier: LGPL-3.0-only + +using System; +using System.Collections.Concurrent; +using System.Runtime.CompilerServices; +using System.Threading; +using System.Threading.Tasks; +using Nethermind.Config; +using Nethermind.Core; +using Nethermind.Core.Collections; +using Nethermind.Int256; +using Nethermind.Logging; +using Nethermind.Trie; +using Prometheus; + +namespace Nethermind.State.Flat.ScopeProvider; + +public interface ITrieWarmer +{ + public void PushJob( + FlatWorldStateScope scope, + Address? path, + FlatStorageTree? storageTree, + in UInt256? index, + int sequenceId); + + void OnNewScope(); +} + +public class NoopTrieWarmer : ITrieWarmer +{ + public void PushJob(FlatWorldStateScope scope, Address? path, FlatStorageTree? storageTree, in UInt256? index, int sequenceId) + { + } + + public void OnNewScope() + { + } +} + +public sealed class TrieWarmer : ITrieWarmer +{ + private SpmcRingBuffer _jobBuffer = new SpmcRingBuffer(256); + + // If path is not null, its an address warmup. + // if storage tree is not null, its a storage warmup. + // So this ideally need to be under 64 byte in size so that it fits within cache line + private record struct Job( + object scopeOrStorageTree, + Address? path, + UInt256 index, + int sequenceId); + + Task? _warmerJob = null; + private static Counter _trieWarmEr = DevMetric.Factory.CreateCounter("triestore_trie_warmer", "hit rate", "type"); + private static Counter.Child _bufferFull = _trieWarmEr.WithLabels("buffer_full"); + private ConcurrentStack _awaitingWorkers = new ConcurrentStack(); + private WarmerWorkers? _mainWarmer = null; + + private bool TryDequeue(out Job job) + { + return _jobBuffer.TryDequeue(out job); + } + + public TrieWarmer(IProcessExitSource processExitSource, ILogManager logManager) + { + int processorCount = Environment.ProcessorCount; + _warmerJob = Task.Run(async () => + { + using ArrayPoolList tasks = new ArrayPoolList(processorCount); + for (int i = 0; i < processorCount; i++) + { + bool isMain = i == 0; + var worker = new WarmerWorkers(this, isMain); + tasks.Add(Task.Run(() => + { + worker.Run(processExitSource.Token); + })); + } + + await Task.WhenAll(tasks);; + }); + } + + private static void HandleJob(Job job, bool isMain) + { + (object scopeOrStorageTree, + Address? address, + UInt256 index, + int sequenceId) = job; + + try + { + if (scopeOrStorageTree is FlatWorldStateScope scope) + { + if (scope.WarmUpStateTrie(address, sequenceId)) + { + _trieWarmEr.WithLabels("state").Inc(); + } + else + { + _trieWarmEr.WithLabels("state_skip").Inc(); + } + } + else + { + FlatStorageTree storageTree = (FlatStorageTree)scopeOrStorageTree; + if (storageTree.WarUpStorageTrie(index, sequenceId)) + { + _trieWarmEr.WithLabels("storage").Inc(); + } + else + { + _trieWarmEr.WithLabels("storage_skip").Inc(); + } + } + } + catch (TrieNodeException) + { + _trieWarmEr.WithLabels("err_trienode").Inc(); + // It can be missing when the warmer lags so much behind that the node is now gone. + } + catch (ObjectDisposedException) + { + _trieWarmEr.WithLabels("err_disposed").Inc(); + // Yea... this need to be fixed. + } + catch (NullReferenceException) + { + _trieWarmEr.WithLabels("err_null").Inc(); + // Uhh.... + } + } + + private class WarmerWorkers(TrieWarmer mainWarmer, bool isMain) + { + private ManualResetEventSlim _resetEvent = new ManualResetEventSlim(); + private SpinWait _spinWait = new SpinWait(); + + public void Run(CancellationToken cancellationToken) + { + try + { + while (true) + { + if (cancellationToken.IsCancellationRequested) break; + + if (mainWarmer.TryDequeue(out var job)) + { + _spinWait.Reset(); + mainWarmer.MaybeWakeOpOtherWorker(); + + HandleJob(job, isMain); + } + else + { + if (_spinWait.NextSpinWillYield) + { + if (!isMain) + { + _trieWarmEr.WithLabels("wait_not_main").Inc(); + _resetEvent.Reset(); + mainWarmer.QueueWorker(this); + + _resetEvent.Wait(1, cancellationToken); + } + else + { + _resetEvent.Reset(); + mainWarmer.MainWarmerIdle(this); + + _resetEvent.Wait(1, cancellationToken); + } + + _spinWait.Reset(); + } + else + { + _spinWait.SpinOnce(); + } + } + } + } + catch (OperationCanceledException) + { + } + catch (Exception ex) + { + Console.Error.WriteLine("Error in warmup job " + ex); + } + } + + public void WakeUp() + { + _resetEvent.Set(); + } + } + + private void MainWarmerIdle(WarmerWorkers warmerWorkers) + { + _mainWarmer = warmerWorkers; + } + + private void MaybeWakeOpOtherWorker() + { + if (_jobBuffer.EstimatedJobCount > 0 && _awaitingWorkers.TryPop(out WarmerWorkers otherWorker)) otherWorker.WakeUp(); + } + + private void QueueWorker(WarmerWorkers worker) + { + _awaitingWorkers.Push(worker); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void PushJob( + FlatWorldStateScope scope, + Address? path, + FlatStorageTree? storageTree, + in UInt256? index, + int sequenceId) + { + if (path is not null) + { + if (!_jobBuffer.TryEnqueue(new Job(scope, path, index.GetValueOrDefault(), sequenceId))) + { + _bufferFull.Inc(); + return; + } + } + else + { + if (!_jobBuffer.TryEnqueue(new Job(storageTree, path, index.GetValueOrDefault(), sequenceId))) + { + _bufferFull.Inc(); + return; + } + } + + WarmerWorkers? mainWarmer = _mainWarmer; + if (mainWarmer is not null) + { + mainWarmer.WakeUp(); + _mainWarmer = null; + } + } + + public void OnNewScope() + { + _mainWarmer?.WakeUp(); + _mainWarmer = null; + } +} diff --git a/src/Nethermind/Nethermind.State/Flat/Snapshot.cs b/src/Nethermind/Nethermind.State/Flat/Snapshot.cs new file mode 100644 index 00000000000..cde39b49ae4 --- /dev/null +++ b/src/Nethermind/Nethermind.State/Flat/Snapshot.cs @@ -0,0 +1,147 @@ +// SPDX-FileCopyrightText: 2025 Demerzel Solutions Limited +// SPDX-License-Identifier: LGPL-3.0-only + +using System; +using System.Collections; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Linq; +using System.Runtime.InteropServices; +using Microsoft.Extensions.ObjectPool; +using Nethermind.Core; +using Nethermind.Core.Collections; +using Nethermind.Core.Crypto; +using Nethermind.Core.Utils; +using Nethermind.Int256; +using Nethermind.Trie; + +namespace Nethermind.State.Flat; + +/// +/// Snapshot are written keys between state From to state To +/// +/// +/// +/// +/// +public class Snapshot( + StateId from, + StateId to, + SnapshotContent content, + ObjectPool pool +) : RefCountingDisposable +{ + private Dictionary? _memory = null; // The memory changes, so we use this as a smoewhat estimate so it make some seense + public Dictionary EstimateMemory() => _memory ??= content.EstimateMemory(); + + public StateId From => from; + public StateId To => to; + public IEnumerable> Accounts => content.Accounts; + public IEnumerable> SelfDestructedStorageAddresses => content.SelfDestructedStorageAddresses; + public IEnumerable> Storages => content.Storages; + public IEnumerable> StorageNodes => content.StorageNodes; + public IEnumerable<(Hash256AsKey, TreePath)> StorageTrieNodeKeys => content.StorageNodes.Keys; + public IEnumerable> StateNodes => content.StateNodes; + public IEnumerable StateNodeKeys => content.StateNodes.Keys; + public int AccountsCount => content.Accounts.Count; + public int StoragesCount => content.Storages.Count; + public int TrieNodesCount => content.StorageNodes.Count; + public long DebugLease => _leases.Value; + + public bool TryGetAccount(AddressAsKey key, out Account acc) + { + return content.Accounts.TryGetValue(key, out acc); + } + + public bool HasSelfDestruct(Address address) + { + return content.SelfDestructedStorageAddresses.TryGetValue(address, out var _); + } + + public bool TryGetStorage(Address address, in UInt256 index, out byte[] value) + { + return content.Storages.TryGetValue((address, index), out value); + } + + public bool TryGetStateNode(in TreePath path, out TrieNode node) + { + return content.StateNodes.TryGetValue(path, out node); + } + + public bool TryGetStorageNode(Hash256 address, in TreePath path, out TrieNode node) + { + return content.StorageNodes.TryGetValue((address, path), out node); + } + + protected override void CleanUp() + { + pool.Return(content); + } + + public bool TryAcquire() + { + return TryAcquireLease(); + } +} + +public record SnapshotContent( + // They dont actually need to be concurrent, but its makes commit fast by just passing the whole content. + ConcurrentDictionary Accounts, + ConcurrentDictionary<(AddressAsKey, UInt256), byte[]?> Storages, + + // Bool is true if this is a new account also + ConcurrentDictionary SelfDestructedStorageAddresses, + + // Use of a separate dictionary just for state have a small but measurable impact + ConcurrentDictionary StateNodes, + ConcurrentDictionary<(Hash256AsKey, TreePath), TrieNode> StorageNodes +) { + public void Reset() + { + Accounts.NoResizeClear(); + Storages.NoResizeClear(); + SelfDestructedStorageAddresses.NoResizeClear(); + StateNodes.NoResizeClear(); + StorageNodes.NoResizeClear(); + } + + public Dictionary EstimateMemory() + { + Dictionary result = new Dictionary(){ + { MemoryType.Account, Accounts.Count }, + { MemoryType.Storage, Storages.Count }, + { MemoryType.StorageBytes, Storages.Sum((kv) => kv.Value?.Length ?? 0) }, + { MemoryType.SelfDestructedAddress, SelfDestructedStorageAddresses.Count }, + { MemoryType.StateNodes, StateNodes.Count }, + { MemoryType.StateNodesBytes, StateNodes.Sum((kv) => kv.Value.GetMemorySize(false)) }, + { MemoryType.StorageNodes, StorageNodes.Count }, + { MemoryType.StorageNodesBytes, StorageNodes.Sum((kv) => kv.Value.GetMemorySize(false)) }, + }; + + // I'm just winging it here. + result[MemoryType.TotalBytes] + = result[MemoryType.Account] * 40 + + result[MemoryType.Storage] * 48 + + result[MemoryType.StorageBytes] + + result[MemoryType.SelfDestructedAddress] * 40 + + result[MemoryType.StateNodes] * 40 + + result[MemoryType.StateNodesBytes] + + result[MemoryType.StorageNodes] * 48 + + result[MemoryType.StorageNodesBytes]; + + return result; + } +} + +public enum MemoryType +{ + Account, + Storage, + StorageBytes, + SelfDestructedAddress, + StateNodes, + StateNodesBytes, + StorageNodes, + StorageNodesBytes, + TotalBytes +} diff --git a/src/Nethermind/Nethermind.State/Flat/SnapshotBundle.cs b/src/Nethermind/Nethermind.State/Flat/SnapshotBundle.cs new file mode 100644 index 00000000000..1bbf829393a --- /dev/null +++ b/src/Nethermind/Nethermind.State/Flat/SnapshotBundle.cs @@ -0,0 +1,779 @@ +// SPDX-FileCopyrightText: 2025 Demerzel Solutions Limited +// SPDX-License-Identifier: LGPL-3.0-only + +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Diagnostics; +using Nethermind.Core; +using Nethermind.Core.Collections; +using Nethermind.Core.Crypto; +using Nethermind.Core.Extensions; +using Nethermind.Int256; +using Nethermind.State.Flat.Persistence; +using Nethermind.State.Flat.ScopeProvider; +using Nethermind.Trie; +using Prometheus; + +namespace Nethermind.State.Flat; + +/// +/// A bundle of and a layer of write buffer backed by a . +/// +public class SnapshotBundle : IDisposable +{ + private SnapshotContent _currentPooledContent; + // These maps are direct reference from members in _currentPooledContent. + private ConcurrentDictionary _changedAccounts; + private ConcurrentDictionary _changedStateNodes; // Bulkset can get nodes concurrently + private ConcurrentDictionary<(Hash256AsKey, TreePath), TrieNode> _changedStorageNodes; // Bulkset can get nodes concurrently + private ConcurrentDictionary<(AddressAsKey, UInt256), byte[]> _changedSlots; // Bulkset can get nodes concurrently + private ConcurrentDictionary _selfDestructedAccountAddresses; + + // The cached resource holds some items that is pooled. + // Notably it holds loaded caches from trie warmer. + private CachedResource _cachedResource; + + private readonly bool _forStateReader; + + public int SnapshotCount => _snapshots.Count; + + internal ArrayPoolList _snapshots; + private readonly IPersistence.IPersistenceReader _persistenceReader; + private readonly TrieNodeCache _trieNodeCache; + private bool _isPrewarmer; + private bool _isDisposed; + private readonly ResourcePool _resourcePool; + + private static Gauge _activeSnapshotBundle = DevMetric.Factory.CreateGauge("snapshot_bundle_active", "active", "usage"); + private static Counter _creeatedSnapshotBundle = DevMetric.Factory.CreateCounter("snapshot_bundle_created", "created", "usage"); + private static Counter _snapshotBundleEvents = DevMetric.Factory.CreateCounter("snapshot_bundle_evens", "event", "type", "is_prewarmer"); + private Counter.Child _nodeGetChanged; + private Counter.Child _nodeGetSnapshots; + private Counter.Child _nodeGetTrieCache; + private Counter.Child _nodeGetMiss; + private Counter.Child _nodeGetSelfDestruct; + + private static Histogram _snapshotBundleTimes = DevMetric.Factory.CreateHistogram("snapshot_bundle_times", "aha", new HistogramConfiguration() + { + LabelNames = new[] { "type", "is_prewarmer" }, + Buckets = Histogram.PowersOfTenDividedBuckets(1, 12, 5) + }); + private Histogram.Child _accountPersistenceRead; + private Histogram.Child _slotPersistenceRead; + private Histogram.Child _accountPersistenceEmptyRead; + private Histogram.Child _slotPersistenceEmptyRead; + private Histogram.Child _loadRlpRead; + private Histogram.Child _loadRlpReadTrieWarmer; + private Histogram.Child _loadStorageRlpRead; + private Histogram.Child _loadStorageRlpReadTrieWarmer; + private Histogram.Child _findStateNode; + private Histogram.Child _findStorageNodeLoadedNodes; + private Histogram.Child _findStorageNodeChangedNodes; + private Histogram.Child _findStorageNodeSnapshots; + private Histogram.Child _findStorageNodeNodeCache; + private Histogram.Child _findStorageNodeNodeCacheBefore; + private Histogram.Child _findStateNodeTrieWarmer; + private Histogram.Child _findStorageNode; + private Histogram.Child _findStorageNodeTrieWarmer; + private Histogram.Child _setStateNodesTime; + private Histogram.Child _setStorageNodesTime; + private Histogram.Child _setSlotTime; + private Histogram.Child _setSlotToZeroTime; + private Histogram.Child _setAccountTime; + + private Counter.Child _accountGet; + private Counter.Child _slotGet; + + private IFlatDiffRepository.SnapshotBundleUsage _usage; + + public SnapshotBundle(ArrayPoolList snapshots, + IPersistence.IPersistenceReader persistenceReader, + TrieNodeCache trieNodeCache, + ResourcePool resourcePool, + IFlatDiffRepository.SnapshotBundleUsage usage, + bool isPrewarmer = false) + { + _snapshots = snapshots; + _persistenceReader = persistenceReader; + _trieNodeCache = trieNodeCache; + _resourcePool = resourcePool; + _isPrewarmer = isPrewarmer; + _forStateReader = usage == IFlatDiffRepository.SnapshotBundleUsage.StateReader; + _usage = usage; + _activeSnapshotBundle.WithLabels(_usage.ToString()).Inc(); + _creeatedSnapshotBundle.WithLabels(_usage.ToString()).Inc(); + + SetupMetric(); + + if (!_forStateReader) + { + _currentPooledContent = resourcePool.GetSnapshotContent(usage); + _cachedResource = resourcePool.GetCachedResource(usage); + + ExpandCurrentPooledContent(); + } + } + + private void ExpandCurrentPooledContent() + { + _changedAccounts = _currentPooledContent.Accounts; + _changedSlots = _currentPooledContent.Storages; + _changedStorageNodes = _currentPooledContent.StorageNodes; + _changedStateNodes = _currentPooledContent.StateNodes; + _selfDestructedAccountAddresses = _currentPooledContent.SelfDestructedStorageAddresses; + } + + public void SetPrewarmer() + { + _isPrewarmer = true; + SetupMetric(); + } + + private void SetupMetric() + { + _nodeGetChanged = _snapshotBundleEvents.WithLabels("node_get_changed", _isPrewarmer.ToString()); + _nodeGetSnapshots = _snapshotBundleEvents.WithLabels("node_get_snapshots", _isPrewarmer.ToString()); + _nodeGetTrieCache = _snapshotBundleEvents.WithLabels("node_get_trie_cache", _isPrewarmer.ToString()); + _nodeGetSelfDestruct = _snapshotBundleEvents.WithLabels("node_get_self_destruct", _isPrewarmer.ToString()); + _nodeGetMiss = _snapshotBundleEvents.WithLabels("node_get_miss", _isPrewarmer.ToString()); + _accountGet = _snapshotBundleEvents.WithLabels("account_get", _isPrewarmer.ToString()); + _slotGet = _snapshotBundleEvents.WithLabels("slot_get", _isPrewarmer.ToString()); + + _accountPersistenceRead = _snapshotBundleTimes.WithLabels("account_persistence", _isPrewarmer.ToString()); + _slotPersistenceRead = _snapshotBundleTimes.WithLabels("slot_persistence", _isPrewarmer.ToString()); + _accountPersistenceEmptyRead = _snapshotBundleTimes.WithLabels("empty_account_persistence", _isPrewarmer.ToString()); + _slotPersistenceEmptyRead = _snapshotBundleTimes.WithLabels("empty_slot_persistence", _isPrewarmer.ToString()); + _loadRlpRead = _snapshotBundleTimes.WithLabels("rlp_read", _isPrewarmer.ToString()); + _loadRlpReadTrieWarmer = _snapshotBundleTimes.WithLabels("rlp_read_trie_warmer", _isPrewarmer.ToString()); + _loadStorageRlpRead = _snapshotBundleTimes.WithLabels("storage_rlp_read", _isPrewarmer.ToString()); + _loadStorageRlpReadTrieWarmer = _snapshotBundleTimes.WithLabels("storage_rlp_read_trie_warmer", _isPrewarmer.ToString()); + _findStateNode = _snapshotBundleTimes.WithLabels("find_state_node", _isPrewarmer.ToString()); + + _findStorageNodeLoadedNodes = _snapshotBundleTimes.WithLabels("find_storage_node_loaded_nodes", _isPrewarmer.ToString()); + _findStorageNodeChangedNodes = _snapshotBundleTimes.WithLabels("find_storage_node_changed_nodes", _isPrewarmer.ToString());; + _findStorageNodeSnapshots = _snapshotBundleTimes.WithLabels("find_storage_node_snapshots", _isPrewarmer.ToString());; + _findStorageNodeNodeCache = _snapshotBundleTimes.WithLabels("find_storage_node_node_cache", _isPrewarmer.ToString());; + _findStorageNodeNodeCacheBefore = _snapshotBundleTimes.WithLabels("find_storage_node_node_cache_before", _isPrewarmer.ToString());; + + _findStateNodeTrieWarmer = _snapshotBundleTimes.WithLabels("find_state_node_trie_warmer", _isPrewarmer.ToString()); + _findStorageNode = _snapshotBundleTimes.WithLabels("find_storage_node", _isPrewarmer.ToString()); + _findStorageNodeTrieWarmer = _snapshotBundleTimes.WithLabels("find_storage_node_trie_warmer", _isPrewarmer.ToString()); + _setSlotTime = _snapshotBundleTimes.WithLabels("set_slot", _isPrewarmer.ToString()); + _setSlotToZeroTime = _snapshotBundleTimes.WithLabels("set_slot_zero", _isPrewarmer.ToString()); + _setAccountTime = _snapshotBundleTimes.WithLabels("set_account", _isPrewarmer.ToString()); + + _setStateNodesTime = _snapshotBundleTimes.WithLabels("set_state_nodes", _isPrewarmer.ToString()); + _setStorageNodesTime = _snapshotBundleTimes.WithLabels("set_storage_nodes", _isPrewarmer.ToString()); + } + + public bool TryGetAccount(Address address, out Account? acc) + { + return DoTryGetAccount(address, false, out acc); + } + + private bool DoTryGetAccount(Address address, bool excludeChanged, out Account? acc) + { + if (_isDisposed) + { + acc = null; + return false; + } + + _accountGet.Inc(); + if (!_forStateReader && !excludeChanged) + { + if (_changedAccounts.TryGetValue(address, out acc)) return true; + } + + AddressAsKey key = address; + + for (int i = _snapshots.Count - 1; i >= 0; i--) + { + if (_snapshots[i].TryGetAccount(key, out acc)) + { + return true; + } + } + + long sw = Stopwatch.GetTimestamp(); + if (_persistenceReader.TryGetAccount(address, out acc)) + { + if (acc is null) + { + _accountPersistenceEmptyRead.Observe(Stopwatch.GetTimestamp() - sw); + } + else + { + _accountPersistenceRead.Observe(Stopwatch.GetTimestamp() - sw); + } + return true; + } + + return false; + } + + public int DetermineSelfDestructSnapshotIdx(Address address) + { + if (_selfDestructedAccountAddresses.ContainsKey(address)) + { + return _snapshots.Count; + } + + for (int i = _snapshots.Count - 1; i >= 0; i--) + { + if (_snapshots[i].HasSelfDestruct(address)) + { + return i; + } + } + + return -1; + } + + public bool TryGetSlot(Address address, in UInt256 index, int selfDestructStateIdx, out byte[] value) + { + if (_isDisposed) + { + value = null; + return false; + } + + _slotGet.Inc(); + + if (!_forStateReader) + { + if (_changedSlots.TryGetValue((address, index), out value)) + { + return true; + } + } + + if (selfDestructStateIdx == _snapshots.Count) + { + _nodeGetSelfDestruct.Inc(); + value = null; + return true; + } + + for (int i = _snapshots.Count - 1; i >= 0; i--) + { + if (_snapshots[i].TryGetStorage(address, index, out value)) return true; + + if (i <= selfDestructStateIdx) + { + value = null; + return true; + } + } + + long sw = Stopwatch.GetTimestamp(); + if (_persistenceReader.TryGetSlot(address, index, out value)) + { + if (value is null || value.Length == 0 || Bytes.AreEqual(value, StorageTree.ZeroBytes)) + { + _slotPersistenceEmptyRead.Observe(Stopwatch.GetTimestamp() - sw); + } + else + { + _slotPersistenceRead.Observe(Stopwatch.GetTimestamp() - sw); + } + return true; + } + + return false; + } + + public void SetChangedSlot(AddressAsKey address, in UInt256 index, byte[] value) + { + if (_forStateReader) throw new InvalidOperationException("Read only snapshot bundle"); + long sw = Stopwatch.GetTimestamp(); + // Note: Hot path + _changedSlots[(address, index)] = value; + if (value is null || Bytes.AreEqual(value, StorageTree.ZeroBytes)) + { + _setSlotToZeroTime.Observe(Stopwatch.GetTimestamp() - sw); + } + else + { + _setSlotTime.Observe(Stopwatch.GetTimestamp() - sw); + } + } + + public TrieNode FindStateNodeOrUnknown(in TreePath path, Hash256 hash, bool isTrieWarmer) + { + TrieNode node; + if (_forStateReader) + { + if (DoFindStateNodeExternal(path, hash, out node)) + { + return node; + } + return new TrieNode(NodeType.Unknown, hash); + } + + long sw = Stopwatch.GetTimestamp(); + + if (!isTrieWarmer) + { + // _changedStateNodes is really hot, so we dont touch it during prewarmer. + if (_changedStateNodes.TryGetValue(path, out node)) + { + Nethermind.Trie.Pruning.Metrics.LoadedFromCacheNodesCount++; + _nodeGetChanged.Inc(); + return node; + } + } + + if (_cachedResource.TrieWarmerLoadedNodes.TryGetValue(path, out node) && node.Keccak == hash) + { + Nethermind.Trie.Pruning.Metrics.LoadedFromCacheNodesCount++; + + // if (!isTrieWarmer) _changedStateNodes.TryAdd(path, node); + _nodeGetChanged.Inc(); + return node; + } + + if (!DoFindStateNodeExternal(path, hash, out node)) + { + // The map to holds the unknown nodes is different for trie warmer and the main tries. This prevent + // random invalid block. + node = _cachedResource.TrieWarmerLoadedNodes.GetOrAdd(path, new TrieNode(NodeType.Unknown, hash)); + } + else + { + _cachedResource.TrieWarmerLoadedNodes.AddOrUpdate(path, + static (path, trieNode) => trieNode, + static (treePath, trieNode, newNode) => newNode, + node); + } + + if (isTrieWarmer) + { + _findStateNodeTrieWarmer.Observe(Stopwatch.GetTimestamp() - sw); + } + else + { + _findStateNode.Observe(Stopwatch.GetTimestamp() - sw); + } + + return node; + } + + private bool DoFindStateNodeExternal(in TreePath path, Hash256 hash, out TrieNode node) + { + if (_isDisposed) + { + node = null; + return false; + } + + if (_trieNodeCache.TryGet(null, path, hash, out node)) + { + Nethermind.Trie.Pruning.Metrics.LoadedFromCacheNodesCount++; + _nodeGetTrieCache.Inc(); + return true; + } + + for (int i = _snapshots.Count - 1; i >= 0; i--) + { + if (_snapshots[i].TryGetStateNode(path, out node)) + { + Nethermind.Trie.Pruning.Metrics.LoadedFromCacheNodesCount++; + _nodeGetSnapshots.Inc(); + return true; + } + } + + _nodeGetMiss.Inc(); + return false; + } + + public TrieNode FindStorageNodeOrUnknown(Hash256AsKey address, in TreePath path, Hash256 hash, + int selfDestructStateIdx, bool isTrieWarmer) + { + long sw = Stopwatch.GetTimestamp(); + var res = DoFindStorageNodeOrUnknown(address, path, hash, selfDestructStateIdx, isTrieWarmer); + + if (isTrieWarmer) + { + _findStorageNodeTrieWarmer.Observe(Stopwatch.GetTimestamp() - sw); + } + else + { + _findStorageNode.Observe(Stopwatch.GetTimestamp() - sw); + } + + return res; + } + + private TrieNode DoFindStorageNodeOrUnknown(Hash256AsKey address, in TreePath path, Hash256 hash, int selfDestructStateIdx, bool isTrieWarmer) + { + if (_isDisposed) + { + return new TrieNode(NodeType.Unknown, hash); + } + + TrieNode node; + long sw = Stopwatch.GetTimestamp(); + + if (_forStateReader) + { + if (DoTryFindStorageNodeExternal(address, path, hash, selfDestructStateIdx, isTrieWarmer, sw, out node)) + { + return node; + } + return new TrieNode(NodeType.Unknown, hash); + } + + if (!isTrieWarmer) + { + if (_changedStorageNodes.TryGetValue((address, path), out node)) + { + if (!isTrieWarmer) _findStorageNodeChangedNodes.Observe(Stopwatch.GetTimestamp() - sw); + Nethermind.Trie.Pruning.Metrics.LoadedFromCacheNodesCount++; + return node; + } + } + + if (_cachedResource.LoadedStorageNodes.TryGetValue((address, path), out node) && node.Keccak == hash) + { + Nethermind.Trie.Pruning.Metrics.LoadedFromCacheNodesCount++; + if (!isTrieWarmer) _findStorageNodeLoadedNodes.Observe(Stopwatch.GetTimestamp() - sw); + // if (!isTrieWarmer) _changedStorageNodes.TryAdd((address, path), node); + return node; + } + + if (!DoTryFindStorageNodeExternal(address, path, hash, selfDestructStateIdx, isTrieWarmer, sw, out node)) + { + node = _cachedResource.LoadedStorageNodes.GetOrAdd((address, path), new TrieNode(NodeType.Unknown, hash)); + } + else + { + _cachedResource.LoadedStorageNodes.AddOrUpdate((address, path), + static (key, param) => param, + static (key, originalValue, param) => param, + node); + } + + return node; + } + + private bool DoTryFindStorageNodeExternal(Hash256AsKey address, in TreePath path, Hash256 hash, int selfDestructStateIdx, bool isTrieWarmer, long sw, out TrieNode node) + { + if (selfDestructStateIdx == -1) + { + // If no self destruct idx, check node cache first + if (_trieNodeCache.TryGet(address, path, hash, out node)) + { + if (!isTrieWarmer) _findStorageNodeNodeCache.Observe(Stopwatch.GetTimestamp() - sw); + Nethermind.Trie.Pruning.Metrics.LoadedFromCacheNodesCount++; + _nodeGetTrieCache.Inc(); + return true; + } + } + + for (int i = _snapshots.Count - 1; i >= 0 && i >= selfDestructStateIdx; i--) + { + if (_snapshots[i].TryGetStorageNode(address, path, out node)) + { + if (!isTrieWarmer) _findStorageNodeSnapshots.Observe(Stopwatch.GetTimestamp() - sw); + Nethermind.Trie.Pruning.Metrics.LoadedFromCacheNodesCount++; + _nodeGetSnapshots.Inc(); + return true; + } + } + + if (selfDestructStateIdx != -1) + { + // If there is a self destruct, there is no need to check further, return true + Nethermind.Trie.Pruning.Metrics.LoadedFromCacheNodesCount++; + _nodeGetSelfDestruct.Inc(); + node = null; + return true; + } + + _nodeGetMiss.Inc(); + node = null; + return false; + } + + public byte[]? TryLoadRlp(Hash256? address, in TreePath path, Hash256 hash, ReadFlags flags, bool isTrieWarmer) + { + if (_isDisposed) return null; + Nethermind.Trie.Pruning.Metrics.LoadedFromDbNodesCount++; + long sw = Stopwatch.GetTimestamp(); + var res = _persistenceReader.TryLoadRlp(address, path, hash, flags); + if (isTrieWarmer) + { + if (address is null) + { + _loadRlpReadTrieWarmer.Observe(Stopwatch.GetTimestamp() - sw); + } + else + { + _loadStorageRlpReadTrieWarmer.Observe(Stopwatch.GetTimestamp() - sw); + } + } + else + { + if (address is null) + { + _loadRlpRead.Observe(Stopwatch.GetTimestamp() - sw); + } + else + { + _loadStorageRlpRead.Observe(Stopwatch.GetTimestamp() - sw); + } + } + return res; + } + + // This is called only during trie commit + public void SetStateNode(in TreePath path, TrieNode newNode) + { + if (_forStateReader) throw new InvalidOperationException("Read only snapshot bundle"); + if (_isDisposed) return; + if (!newNode.IsSealed) throw new Exception("Node must be sealed for setting"); + + long sw = Stopwatch.GetTimestamp(); + // Note: Hot path + _changedStateNodes[path] = newNode; + _setStateNodesTime.Observe(Stopwatch.GetTimestamp() - sw); + } + + // This is called only during trie commit + public void SetStorageNode(Hash256AsKey addr, in TreePath path, TrieNode newNode) + { + if (_forStateReader) throw new InvalidOperationException("Read only snapshot bundle"); + if (_isDisposed) return; + if (!newNode.IsSealed) throw new Exception("Node must be sealed for setting"); + + long sw = Stopwatch.GetTimestamp(); + // Note: Hot path + _changedStorageNodes[(addr, path)] = newNode; + _setStorageNodesTime.Observe(Stopwatch.GetTimestamp() - sw); + } + + public void SetAccount(AddressAsKey addr, Account? account) + { + if (addr == FlatWorldStateScope.DebugAddress) + { + Console.Error.WriteLine($"set to {account}"); + } + long sw = Stopwatch.GetTimestamp(); + _changedAccounts[addr] = account; + _setAccountTime.Observe(Stopwatch.GetTimestamp() - sw); + } + + public bool ShouldPrewarm(Address address, UInt256? slot) + { + return _cachedResource.PrewarmedAddresses.TryAdd((address, slot), true); + } + + public (Snapshot, CachedResource) CollectAndApplySnapshot(StateId from, StateId to, bool returnSnapshot = true) + { + if (_forStateReader) throw new InvalidOperationException("Read only snapshot bundle"); + + // When assembling the snapshot, we straight up pass the _currentPooledContent into the new snapshot + // This is because copying the values have a measurable impact on overall performance. + var snapshot = new Snapshot( + from: from, + to: to, + content: _currentPooledContent, + pool: _resourcePool.GetSnapshotPool(_usage)); + + snapshot.AcquireLease(); // For this SnapshotBundle. + _snapshots.Add(snapshot); // Now later reads are correct + + // Invalidate cached resources + if (returnSnapshot) + { + CachedResource cachedResource = _cachedResource; + _cachedResource = _resourcePool.GetCachedResource(_usage); + + // Make and apply new snapshot content. + _currentPooledContent = _resourcePool.GetSnapshotContent(_usage); + ExpandCurrentPooledContent(); + + return (snapshot, cachedResource); + } + else + { + snapshot.Dispose(); // Revert the lease before + + _cachedResource.Clear(); + _currentPooledContent = _resourcePool.GetSnapshotContent(_usage); + + return (null, null); + } + } + + public Snapshot CompactToKnownState() + { + // TODO: Get this out of here. It feels weird. + if (_snapshots.Count == 0) + return new Snapshot( + new StateId(-1, ValueKeccak.EmptyTreeHash), new StateId(-1, ValueKeccak.EmptyTreeHash), + content: _resourcePool.GetSnapshotContent(_usage), + pool: _resourcePool.GetSnapshotPool(IFlatDiffRepository.SnapshotBundleUsage.Compactor)); + + SnapshotContent content = _resourcePool.GetSnapshotContent(IFlatDiffRepository.SnapshotBundleUsage.Compactor); + + ConcurrentDictionary accounts = content.Accounts; + ConcurrentDictionary<(AddressAsKey, UInt256), byte[]> storages = content.Storages; + ConcurrentDictionary selfDestructedStorageAddresses = content.SelfDestructedStorageAddresses; + ConcurrentDictionary<(Hash256AsKey, TreePath), TrieNode> storageNodes = content.StorageNodes; + ConcurrentDictionary stateNodes = content.StateNodes; + + if (_snapshots.Count == 1) return _snapshots[0]; + + StateId to = _snapshots[^1].To; + StateId from = _snapshots[0].From; + HashSet
addressToClear = new HashSet
(); + HashSet addressHashToClear = new HashSet(); + + + for (int i = 0; i < _snapshots.Count; i++) + { + var knownState = _snapshots[i]; + foreach (var knownStateAccount in knownState.Accounts) + { + Address address = knownStateAccount.Key; + accounts[address] = knownStateAccount.Value; + } + + addressToClear.Clear(); + addressHashToClear.Clear(); + + foreach (KeyValuePair addrK in knownState.SelfDestructedStorageAddresses) + { + var address = addrK.Key; + var isNewAccount = addrK.Value; + if (!isNewAccount) + { + selfDestructedStorageAddresses[address] = false; + addressToClear.Add(address); + addressHashToClear.Add(address.Value.ToAccountPath.ToCommitment()); + } + else + { + // Note, if its already false, we should not set it to true + selfDestructedStorageAddresses.TryAdd(address, true); + } + } + + if (addressToClear.Count > 0) + { + // Clear + foreach (var kv in storages) + { + if (addressToClear.Contains(kv.Key.Item1)) + { + storages.Remove(kv.Key, out _); + } + } + + foreach (var kv in storageNodes) + { + if (addressHashToClear.Contains(kv.Key.Item1)) + { + storageNodes.Remove(kv.Key, out _); + } + } + } + + foreach (var knownStateStorage in knownState.Storages) + { + storages[knownStateStorage.Key] = knownStateStorage.Value; + } + + foreach (var kv in knownState.StateNodes) + { + stateNodes[kv.Key] = kv.Value; + } + + foreach (var kv in knownState.StorageNodes) + { + storageNodes[kv.Key] = kv.Value; + } + } + + return new Snapshot( + from, + to, + content: content, + pool: _resourcePool.GetSnapshotPool(usage: IFlatDiffRepository.SnapshotBundleUsage.Compactor)); + } + + public void Dispose() + { + if (_isDisposed) return; + _isDisposed = true; + foreach (Snapshot snapshot in _snapshots) + { + snapshot.Dispose(); + } + + // Null them in case unexpected mutation from trie warmer + _snapshots = null; + _changedSlots = null; + _changedAccounts = null; + _changedStorageNodes = null; + _selfDestructedAccountAddresses = null; + + _persistenceReader.Dispose(); + + if (!_forStateReader) + { + _resourcePool.ReturnSnapshotContent(_usage, _currentPooledContent); + _resourcePool.ReturnCachedResource(_usage, _cachedResource); + } + + _activeSnapshotBundle.WithLabels(_usage.ToString()).Dec(); + } + + // Also called SelfDestruct + public void Clear(Address address, Hash256AsKey addressHash) + { + if (_forStateReader) throw new InvalidOperationException("Read only snapshot bundle"); + bool isNewAccount = false; + if (DoTryGetAccount(address, excludeChanged: true, out Account? account)) + { + // So... a clear is always sent even on new account. This makes is a minor optimization as + // it skip persistence, but probably need to make sure it does not send it at all in the first place. + isNewAccount = account == null; + if (address == FlatWorldStateScope.DebugAddress) + { + Console.Error.WriteLine($"The clear newness is {isNewAccount}"); + } + } + else + { + if (address == FlatWorldStateScope.DebugAddress) + { + Console.Error.WriteLine("The clear is not new"); + } + } + _selfDestructedAccountAddresses.TryAdd(address, isNewAccount); + + if (!isNewAccount) + { + foreach (var kv in _changedStorageNodes) + { + if (kv.Key.Item1.Value == addressHash) + { + _changedStorageNodes.TryRemove(kv.Key, out TrieNode _); + } + } + + foreach (var kv in _changedSlots) + { + if (kv.Key.Item1.Value == address) + { + _changedSlots.TryRemove(kv.Key, out byte[] _); + } + } + } + } +} diff --git a/src/Nethermind/Nethermind.State/Flat/StateId.cs b/src/Nethermind/Nethermind.State/Flat/StateId.cs new file mode 100644 index 00000000000..9108928fc58 --- /dev/null +++ b/src/Nethermind/Nethermind.State/Flat/StateId.cs @@ -0,0 +1,23 @@ +// SPDX-FileCopyrightText: 2025 Demerzel Solutions Limited +// SPDX-License-Identifier: LGPL-3.0-only + +using System; +using Nethermind.Core; +using Nethermind.Core.Crypto; +using Nethermind.Crypto; + +namespace Nethermind.State.Flat; + +public readonly record struct StateId(long blockNumber, ValueHash256 stateRoot) : IComparable +{ + public StateId(BlockHeader? header) : this(header?.Number ?? -1, header?.StateRoot ?? Keccak.EmptyTreeHash) + { + } + + public int CompareTo(StateId other) + { + var blockNumberComparison = blockNumber.CompareTo(other.blockNumber); + if (blockNumberComparison != 0) return blockNumberComparison; + return stateRoot.CompareTo(other.stateRoot); + } +} diff --git a/src/Nethermind/Nethermind.State/Flat/TrieNodeCache.cs b/src/Nethermind/Nethermind.State/Flat/TrieNodeCache.cs new file mode 100644 index 00000000000..0c0f7d33b28 --- /dev/null +++ b/src/Nethermind/Nethermind.State/Flat/TrieNodeCache.cs @@ -0,0 +1,232 @@ +// SPDX-FileCopyrightText: 2025 Demerzel Solutions Limited +// SPDX-License-Identifier: LGPL-3.0-only + +using System; +using System.Collections.Concurrent; +using System.Runtime.CompilerServices; +using Nethermind.Core.Crypto; +using Nethermind.Db; +using Nethermind.Logging; +using Nethermind.Trie; + +namespace Nethermind.State.Flat; + +/// +/// The trienode cache is a populated on the latest snapshot instead of on the last one. +/// This has a slightly better hit rate and uses less memory overall. +/// +public class TrieNodeCache +{ + // Not the nonblocking variant as it use slightly less memory + private ConcurrentDictionary[] _cacheShards; + private long[] _shardMemoryUsages; + private int _shardCount = 256; + private long _estimatedMemoryUsage = 0; + private int _nextShardToClear = 0; + private long _maxCacheMemoryThreshold; + private readonly ILogger _logger; + + public TrieNodeCache(long maxCacheMemoryThreshold, ILogManager logManager) + { + _cacheShards = new ConcurrentDictionary[_shardCount]; + for (int i = 0; i < _shardCount; i++) + { + _cacheShards[i] = new ConcurrentDictionary(); + } + _shardMemoryUsages = new long[_shardCount]; + _maxCacheMemoryThreshold = maxCacheMemoryThreshold; + _logger = logManager.GetClassLogger(); + } + + public bool TryGet(Hash256? address, TreePath path, Hash256 hash, out TrieNode node) + { + Key key = new Key(address, path); + int shardIdx = GetShardIdx(key); + + if (_cacheShards[shardIdx].TryGetValue(key, out var maybeNode)) + { + if (maybeNode.Keccak != hash) + { + // TODO: Double check if this is ever expected? + } + else + { + Nethermind.Trie.Pruning.Metrics.LoadedFromCacheNodesCount++; + node = maybeNode; + return true; + } + } + + node = null; + return false; + } + + public void Add(Snapshot snapshot, CachedResource cachedResource) + { + if (_maxCacheMemoryThreshold == 0) + { + // Note: still need to be done + // this was explicitly skipped during block processing to make commit run faster, + // but if this is not done, it will ccause OOM. + foreach (var kv in cachedResource.TrieWarmerLoadedNodes) + { + kv.Value.PrunePersistedRecursively(1); + } + + foreach (var kv in cachedResource.LoadedStorageNodes) + { + kv.Value.PrunePersistedRecursively(1); + } + + foreach (var kv in snapshot.StateNodes) + { + kv.Value.PrunePersistedRecursively(1); + } + + foreach (var kv in snapshot.StorageNodes) + { + kv.Value.PrunePersistedRecursively(1); + } + + return; + } + + void AddtoCache(Key key, TrieNode newNode) + { + int shardIdx = GetShardIdx(key); + if (_cacheShards[shardIdx].TryRemove(key, out var node)) + { + node.PrunePersistedRecursively(1); + long memory = node.GetMemorySize(false); + _shardMemoryUsages[shardIdx] -= memory; + _estimatedMemoryUsage -= memory; + } + + node = newNode; + if (_cacheShards[shardIdx].TryAdd(key, node)) + { + long memory = node.GetMemorySize(false); + _shardMemoryUsages[shardIdx] += memory; + _estimatedMemoryUsage += memory; + } + } + + foreach (var kv in cachedResource.TrieWarmerLoadedNodes) + { + kv.Value.PrunePersistedRecursively(1); + Key key = new Key(null, kv.Key); + if (!snapshot.TryGetStateNode(kv.Key, out _)) + { + AddtoCache(key, kv.Value); + } + } + + foreach (var kv in cachedResource.LoadedStorageNodes) + { + if (kv.Value is null) continue; + kv.Value.PrunePersistedRecursively(1); + Key key = new Key(kv.Key.Item1.Value, kv.Key.Item2); + if (!snapshot.TryGetStorageNode(kv.Key.Item1, kv.Key.Item2, out _)) + { + AddtoCache(key, kv.Value); + } + } + + foreach (var kv in snapshot.StateNodes) + { + kv.Value.PrunePersistedRecursively(1); + Key key = new Key(null, kv.Key); + AddtoCache(key, kv.Value); + } + + foreach (var kv in snapshot.StorageNodes) + { + kv.Value.PrunePersistedRecursively(1); + Key key = new Key(kv.Key.Item1.Value, kv.Key.Item2); + AddtoCache(key, kv.Value); + } + + long prevMemory = _estimatedMemoryUsage; + bool wasPruned = false; + // TODO: Make 16 parameter configurable. + while (snapshot.To.blockNumber % 16 == 0 && _estimatedMemoryUsage > _maxCacheMemoryThreshold) + { + wasPruned = true; + int shardToClear = _nextShardToClear; + + foreach (var kv in _cacheShards[shardToClear]) + { + var node = kv.Value; + node.PrunePersistedRecursively(1); + } + + _cacheShards[shardToClear].Clear(); + _shardMemoryUsages[shardToClear] = 0; + long recalculatedTotalMemory = 0; + foreach (var shardMemoryUsage in _shardMemoryUsages) + { + recalculatedTotalMemory += shardMemoryUsage; + } + + _estimatedMemoryUsage = recalculatedTotalMemory; + + _nextShardToClear += 1; + _nextShardToClear %= _shardCount; + } + + if (wasPruned) + { + _logger.Info($"Pruning trie cache from {prevMemory} to {_estimatedMemoryUsage}"); + } + + Nethermind.Trie.Pruning.Metrics.MemoryUsedByCache = _estimatedMemoryUsage; + } + + private int GetShardIdx(Key key) + { + // Separate by tree partition so that when pruned, whole partition is removed. This is because it is + // more efficient to load nodes from the same partition. + if (key.Address is null) + { + return key.Path.Path.Bytes[0]; + } + + return key.Address.Bytes[0]; + } + + public readonly struct Key : IEquatable + { + internal const long MemoryUsage = 8 + 36 + 8; // (address (probably shared), path, keccak pointer (shared with TrieNode)) + public readonly Hash256? Address; + // Direct member rather than property for large struct, so members are called directly, + // rather than struct copy through the property. Could also return a ref through property. + public readonly TreePath Path; + public Key(Hash256? address, in TreePath path) + { + Address = address; + Path = path; + } + + [SkipLocalsInit] + public override int GetHashCode() + { + var addressHash = Address != default ? Address.GetHashCode() : 1; + return HashCode.Combine(addressHash, Path.GetHashCode()); + } + + public bool Equals(Key other) + { + return other.Path == Path && other.Address == Address; + } + + public override bool Equals(object? obj) + { + return obj is Key other && Equals(other); + } + + public override string ToString() + { + return $"A:{Address} P:{Path}"; + } + } +} diff --git a/src/Nethermind/Nethermind.State/Healing/HealingStateTree.cs b/src/Nethermind/Nethermind.State/Healing/HealingStateTree.cs index 6f75c096a3f..f3b68f5f638 100644 --- a/src/Nethermind/Nethermind.State/Healing/HealingStateTree.cs +++ b/src/Nethermind/Nethermind.State/Healing/HealingStateTree.cs @@ -24,11 +24,11 @@ public HealingStateTree(ITrieStore? store, INodeStorage nodeStorage, Lazy Get(ReadOnlySpan rawKey, Hash256? rootHash = null) + public override ReadOnlySpan Get(ReadOnlySpan rawKey, Hash256? rootHash = null, bool cachedOnly = false, bool keepChildRef = false) { try { - return base.Get(rawKey, rootHash); + return base.Get(rawKey, rootHash, cachedOnly, keepChildRef: keepChildRef); } catch (MissingTrieNodeException e) { diff --git a/src/Nethermind/Nethermind.State/Healing/HealingStorageTree.cs b/src/Nethermind/Nethermind.State/Healing/HealingStorageTree.cs index 5f11c0bd326..91b87ef08eb 100644 --- a/src/Nethermind/Nethermind.State/Healing/HealingStorageTree.cs +++ b/src/Nethermind/Nethermind.State/Healing/HealingStorageTree.cs @@ -27,7 +27,7 @@ public HealingStorageTree(IScopedTrieStore? trieStore, INodeStorage nodeStorage, _recovery = recovery; } - public override ReadOnlySpan Get(ReadOnlySpan rawKey, Hash256? rootHash = null) + public override ReadOnlySpan Get(ReadOnlySpan rawKey, Hash256? rootHash = null, bool cachedOnly = false, bool keepChildRef = false) { try { @@ -38,7 +38,7 @@ public override ReadOnlySpan Get(ReadOnlySpan rawKey, Hash256? rootH Hash256 fullPath = new Hash256(rawKey); if (Recover(e.Path, e.Hash, fullPath)) { - return base.Get(rawKey, rootHash); + return base.Get(rawKey, rootHash, cachedOnly: cachedOnly, keepChildRef: keepChildRef); } throw; diff --git a/src/Nethermind/Nethermind.State/Nethermind.State.csproj b/src/Nethermind/Nethermind.State/Nethermind.State.csproj index 3933c3c7040..4de88153337 100644 --- a/src/Nethermind/Nethermind.State/Nethermind.State.csproj +++ b/src/Nethermind/Nethermind.State/Nethermind.State.csproj @@ -10,4 +10,12 @@ + + + + + + + + diff --git a/src/Nethermind/Nethermind.State/PartialStorageProviderBase.cs b/src/Nethermind/Nethermind.State/PartialStorageProviderBase.cs index 6a2b85a04d1..9f401a5bf56 100644 --- a/src/Nethermind/Nethermind.State/PartialStorageProviderBase.cs +++ b/src/Nethermind/Nethermind.State/PartialStorageProviderBase.cs @@ -47,9 +47,10 @@ public ReadOnlySpan Get(in StorageCell storageCell) /// /// Storage location /// Value to store - public void Set(in StorageCell storageCell, byte[] newValue) + /// Return true if this is the first update for this cell + public virtual bool Set(in StorageCell storageCell, byte[] newValue) { - PushUpdate(in storageCell, newValue); + return PushUpdate(in storageCell, newValue); } /// @@ -211,11 +212,14 @@ protected bool TryGetCachedValue(in StorageCell storageCell, out byte[]? bytes) /// /// Storage location /// Value to set - private void PushUpdate(in StorageCell cell, byte[] value) + /// Return true if this is the first update for this cell + private bool PushUpdate(in StorageCell cell, byte[] value) { StackList stack = SetupRegistry(cell); + bool isNewStack = stack.Count == 0; stack.Push(_changes.Count); _changes.Add(new Change(in cell, value, ChangeType.Update)); + return isNewStack; } /// diff --git a/src/Nethermind/Nethermind.State/PersistentStorageProvider.cs b/src/Nethermind/Nethermind.State/PersistentStorageProvider.cs index d9ee9de5680..2ba69bd6c5a 100644 --- a/src/Nethermind/Nethermind.State/PersistentStorageProvider.cs +++ b/src/Nethermind/Nethermind.State/PersistentStorageProvider.cs @@ -21,6 +21,7 @@ using Nethermind.Evm.Tracing.State; using Nethermind.Logging; using Nethermind.Int256; +using Nethermind.State.Flat.ScopeProvider; using Nethermind.Trie; namespace Nethermind.State; @@ -110,6 +111,22 @@ public byte[] GetOriginal(in StorageCell storageCell) return value; } + public override bool Set(in StorageCell storageCell, byte[] newValue) + { + if (base.Set(storageCell, newValue)) + { + if (TryGetCachedValue(storageCell, out _)) + { + // GetOrCreateStorage(storageCell.Address).HintSet(storageCell); + } + + return true; + } + + return false; + } + + public Hash256 GetStorageRoot(Address address) { return GetOrCreateStorage(address).StorageRoot; @@ -284,6 +301,8 @@ void UpdateRootHashesMultiThread() writeBatch.CreateStorageWriteBatch(kv.Key, kv.Value.EstimatedChanges) )) .ToPooledList(_storages.Count); + // Sort by decreasing changes. Slightly better parallelism. + storages.Sort((it1, it2) => it1.ContractState.EstimatedChanges.CompareTo(it2.ContractState.EstimatedChanges) * -1); ParallelUnbalancedWork.For( 0, @@ -345,6 +364,7 @@ private PerContractState GetOrCreateStorage(Address address) public void WarmUp(in StorageCell storageCell, bool isEmpty) { + GetOrCreateStorage(storageCell.Address).HintSet(storageCell); if (isEmpty) { } @@ -389,6 +409,11 @@ public override void ClearStorage(Address address) { base.ClearStorage(address); + if (address == FlatWorldStateScope.DebugAddress) + { + Console.Error.WriteLine($"Clear on world state for debug address {address}"); + } + _toUpdateRoots.TryAdd(address, true); PerContractState state = GetOrCreateStorage(address); @@ -402,38 +427,6 @@ private sealed class DefaultableDictionary() public int EstimatedSize => _dictionary.Count + (_missingAreDefault ? 1 : 0); public bool HasClear => _missingAreDefault; public int Capacity => _dictionary.Capacity; - /* - * -<<<<<<< HEAD - ======= - // Note: These all run in about 0.4ms. So the little overhead like attempting to sort the tasks - // may make it worse. Always check on mainnet. - - using ArrayPoolListRef commitTask = new(_storages.Count); - foreach (PerContractState storage in _storages.Values) - { - storage.EnsureStorageTree(); // Cannot be called concurrently - if (blockCommitter.TryRequestConcurrencyQuota()) - { - commitTask.Add(Task.Factory.StartNew((ctx) => - { - PerContractState st = (PerContractState)ctx; - st.Commit(); - st.Return(); - blockCommitter.ReturnConcurrencyQuota(); - }, storage)); - } - else - { - storage.Commit(); - storage.Return(); - } - } - - Task.WaitAll(commitTask.AsSpan()); - - >>>>>>> origin/master - */ public void Reset() { @@ -637,6 +630,12 @@ public void RemoveStorageTree() _backend = null; } + public void HintSet(in StorageCell storageCell) + { + EnsureStorageTree(); + _backend.HintSet(storageCell.Index); + } + internal static PerContractState Rent(Address address, PersistentStorageProvider persistentStorageProvider) => Pool.Rent(address, persistentStorageProvider); diff --git a/src/Nethermind/Nethermind.State/PrewarmerScopeProvider.cs b/src/Nethermind/Nethermind.State/PrewarmerScopeProvider.cs index d88567071d1..8f5500a25e0 100644 --- a/src/Nethermind/Nethermind.State/PrewarmerScopeProvider.cs +++ b/src/Nethermind/Nethermind.State/PrewarmerScopeProvider.cs @@ -3,11 +3,13 @@ using System; using System.Collections.Concurrent; +using System.Diagnostics; using Nethermind.Core; using Nethermind.Core.Crypto; -using Nethermind.Db; using Nethermind.Evm.State; using Nethermind.Int256; +using Prometheus; +using Metrics = Nethermind.Db.Metrics; namespace Nethermind.State; @@ -17,6 +19,14 @@ public class PrewarmerScopeProvider( bool populatePreBlockCache = true ) : IWorldStateScopeProvider, IPreBlockCaches { + internal static Histogram _timer = DevMetric.Factory.CreateHistogram("prewarmer_dirty_get", "timer", + new HistogramConfiguration() + { + LabelNames = ["part", "is_prewarmer"], + // Buckets = Histogram.PowersOfTenDividedBuckets(5, 10, 5) + Buckets = [1] + }); + public bool HasRoot(BlockHeader? baseBlock) => baseProvider.HasRoot(baseBlock); public IWorldStateScopeProvider.IScope BeginScope(BlockHeader? baseBlock) => new ScopeWrapper(baseProvider.BeginScope(baseBlock), preBlockCaches, populatePreBlockCache); @@ -30,9 +40,20 @@ private sealed class ScopeWrapper( bool populatePreBlockCache) : IWorldStateScopeProvider.IScope { + private Histogram.Child _addressGetHit = _timer.WithLabels("address_hit", populatePreBlockCache.ToString()); + private Histogram.Child _addressGetMiss = _timer.WithLabels("address_miss", populatePreBlockCache.ToString()); + private Histogram.Child _addressGetHint = _timer.WithLabels("address_get_hint", populatePreBlockCache.ToString()); + private Histogram.Child _addressSetHint = _timer.WithLabels("address_set_hint", populatePreBlockCache.ToString()); + private Histogram.Child _disposeTime = _timer.WithLabels("dispose", populatePreBlockCache.ToString()); + ConcurrentDictionary preBlockCache = preBlockCaches.StateCache; - public void Dispose() => baseScope.Dispose(); + public void Dispose() + { + long sw = Stopwatch.GetTimestamp(); + baseScope.Dispose(); + _disposeTime.Observe(Stopwatch.GetTimestamp() - sw); + } public IWorldStateScopeProvider.ICodeDb CodeDb => baseScope.CodeDb; @@ -62,6 +83,7 @@ public void UpdateRootHash() public Account? Get(Address address) { AddressAsKey addressAsKey = address; + long sw = Stopwatch.GetTimestamp(); if (populatePreBlockCache) { long priorReads = Metrics.ThreadLocalStateTreeReads; @@ -69,26 +91,47 @@ public void UpdateRootHash() if (Metrics.ThreadLocalStateTreeReads == priorReads) { + _addressGetHit.Observe(Stopwatch.GetTimestamp() - sw); Metrics.IncrementStateTreeCacheHits(); } + else + { + _addressGetMiss.Observe(Stopwatch.GetTimestamp() - sw); + } return account; } else { if (preBlockCache?.TryGetValue(addressAsKey, out Account? account) ?? false) { + _addressGetHit.Observe(Stopwatch.GetTimestamp() - sw); + sw = Stopwatch.GetTimestamp(); baseScope.HintGet(address, account); + _addressGetHint.Observe(Stopwatch.GetTimestamp() - sw); Metrics.IncrementStateTreeCacheHits(); } else { account = GetFromBaseTree(addressAsKey); + _addressGetMiss.Observe(Stopwatch.GetTimestamp() - sw); } return account; } } - public void HintGet(Address address, Account? account) => baseScope.HintGet(address, account); + public void HintGet(Address address, Account? account) + { + long sw = Stopwatch.GetTimestamp(); + baseScope.HintGet(address, account); + _addressGetHint.Observe(Stopwatch.GetTimestamp() - sw); + } + + public void HintSet(Address address) + { + long sw = Stopwatch.GetTimestamp(); + baseScope.HintSet(address); + _addressSetHint.Observe(Stopwatch.GetTimestamp() - sw); + } private Account? GetFromBaseTree(AddressAsKey address) { @@ -103,11 +146,16 @@ private sealed class StorageTreeWrapper( bool populatePreBlockCache ) : IWorldStateScopeProvider.IStorageTree { + private Histogram.Child _slotGetHit = _timer.WithLabels("slot_get_hit", populatePreBlockCache.ToString()); + private Histogram.Child _slotGetHint = _timer.WithLabels("slot_get_hint", populatePreBlockCache.ToString()); + private Histogram.Child _slotSetHint = _timer.WithLabels("slot_set_hint", populatePreBlockCache.ToString()); + private Histogram.Child _slotGetMiss = _timer.WithLabels("slot_get_miss", populatePreBlockCache.ToString()); public Hash256 RootHash => baseStorageTree.RootHash; public byte[] Get(in UInt256 index) { StorageCell storageCell = new StorageCell(address, in index); // TODO: Make the dictionary use UInt256 directly + long sw = Stopwatch.GetTimestamp(); if (populatePreBlockCache) { long priorReads = Db.Metrics.ThreadLocalStorageTreeReads; @@ -116,27 +164,48 @@ public byte[] Get(in UInt256 index) if (Db.Metrics.ThreadLocalStorageTreeReads == priorReads) { + _slotGetHit.Observe(Stopwatch.GetTimestamp() - sw); // Read from Concurrent Cache Db.Metrics.IncrementStorageTreeCache(); } + else + { + _slotGetMiss.Observe(Stopwatch.GetTimestamp() - sw); + } return value; } else { if (preBlockCache?.TryGetValue(storageCell, out byte[] value) ?? false) { + long sw2 = Stopwatch.GetTimestamp(); + _slotGetHit.Observe(sw2 - sw); baseStorageTree.HintGet(index, value); + _slotGetHint.Observe(Stopwatch.GetTimestamp() - sw2); Db.Metrics.IncrementStorageTreeCache(); } else { value = LoadFromTreeStorage(storageCell); + _slotGetMiss.Observe(Stopwatch.GetTimestamp() - sw); } return value; } } - public void HintGet(in UInt256 index, byte[]? value) => baseStorageTree.HintGet(in index, value); + public void HintGet(in UInt256 index, byte[]? value) + { + long sw = Stopwatch.GetTimestamp(); + baseStorageTree.HintGet(in index, value); + _slotSetHint.Observe(Stopwatch.GetTimestamp() - sw); + } + + public void HintSet(in UInt256 index) + { + long sw = Stopwatch.GetTimestamp(); + baseStorageTree.HintSet(in index); + _slotSetHint.Observe(Stopwatch.GetTimestamp() - sw); + } private byte[] LoadFromTreeStorage(StorageCell storageCell) { diff --git a/src/Nethermind/Nethermind.State/ResourcePool.cs b/src/Nethermind/Nethermind.State/ResourcePool.cs new file mode 100644 index 00000000000..df1a7e072dd --- /dev/null +++ b/src/Nethermind/Nethermind.State/ResourcePool.cs @@ -0,0 +1,101 @@ +// SPDX-FileCopyrightText: 2025 Demerzel Solutions Limited +// SPDX-License-Identifier: LGPL-3.0-only + +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using Microsoft.Extensions.ObjectPool; +using Nethermind.Core; +using Nethermind.Core.Crypto; +using Nethermind.Int256; +using Nethermind.State.Flat; +using Nethermind.Trie; +using Prometheus; + +namespace Nethermind.State; + +public class ResourcePool +{ + + private Dictionary> _snapshotPools = new() + { + { IFlatDiffRepository.SnapshotBundleUsage.MainBlockProcessing , new DefaultObjectPool(new SnapshotContentPolicy(IFlatDiffRepository.SnapshotBundleUsage.MainBlockProcessing)) }, + { IFlatDiffRepository.SnapshotBundleUsage.ReadOnlyProcessingEnv , new DefaultObjectPool(new SnapshotContentPolicy(IFlatDiffRepository.SnapshotBundleUsage.ReadOnlyProcessingEnv)) }, + { IFlatDiffRepository.SnapshotBundleUsage.StateReader , new DefaultObjectPool(new SnapshotContentPolicy(IFlatDiffRepository.SnapshotBundleUsage.StateReader)) }, + { IFlatDiffRepository.SnapshotBundleUsage.Compactor , new DefaultObjectPool(new SnapshotContentPolicy(IFlatDiffRepository.SnapshotBundleUsage.Compactor)) }, + }; + + private Dictionary> _cachedResourcePools = new() + { + { IFlatDiffRepository.SnapshotBundleUsage.MainBlockProcessing , new DefaultObjectPool(new CachedResourcePolicy()) }, + { IFlatDiffRepository.SnapshotBundleUsage.ReadOnlyProcessingEnv , new DefaultObjectPool(new CachedResourcePolicy()) }, + { IFlatDiffRepository.SnapshotBundleUsage.StateReader , new DefaultObjectPool(new CachedResourcePolicy()) }, + { IFlatDiffRepository.SnapshotBundleUsage.Compactor , new DefaultObjectPool(new CachedResourcePolicy()) } + }; + + private static Counter _createdSnapshotContent = DevMetric.Factory.CreateCounter("resourcepool_created_snapshot_content", "created snapshot content", "compacted"); + + private class SnapshotContentPolicy(IFlatDiffRepository.SnapshotBundleUsage usage) : IPooledObjectPolicy + { + public SnapshotContent Create() + { + _createdSnapshotContent.WithLabels(usage.ToString()).Inc(); + return new SnapshotContent( + Accounts: new ConcurrentDictionary(), + Storages: new ConcurrentDictionary<(AddressAsKey, UInt256), byte[]?>(), + SelfDestructedStorageAddresses: new ConcurrentDictionary(), + StateNodes: new ConcurrentDictionary(), + StorageNodes: new ConcurrentDictionary<(Hash256AsKey, TreePath), TrieNode>() + ); + } + + public bool Return(SnapshotContent obj) + { + obj.Reset(); + return true; + } + } + + private class CachedResourcePolicy() : IPooledObjectPolicy + { + public CachedResource Create() + { + return new CachedResource( + new ConcurrentDictionary(), + new ConcurrentDictionary<(Hash256AsKey, TreePath), TrieNode>(), + new ConcurrentDictionary<(AddressAsKey, UInt256?), bool>() + ); + } + + public bool Return(CachedResource obj) + { + obj.Clear(); + return true; + } + } + + public SnapshotContent GetSnapshotContent(IFlatDiffRepository.SnapshotBundleUsage usage) + { + return _snapshotPools[usage].Get(); + } + + public void ReturnSnapshotContent(IFlatDiffRepository.SnapshotBundleUsage usage, SnapshotContent snapshotContent) + { + _snapshotPools[usage].Return(snapshotContent); + } + + public ObjectPool GetSnapshotPool(IFlatDiffRepository.SnapshotBundleUsage usage) + { + return _snapshotPools[usage]; + } + + public CachedResource GetCachedResource(IFlatDiffRepository.SnapshotBundleUsage usage) + { + return _cachedResourcePools[usage].Get(); + } + + public void ReturnCachedResource(IFlatDiffRepository.SnapshotBundleUsage usage, CachedResource cachedResource) + { + _cachedResourcePools[usage].Return(cachedResource); + } +} diff --git a/src/Nethermind/Nethermind.State/SpmcRingBuffer.cs b/src/Nethermind/Nethermind.State/SpmcRingBuffer.cs new file mode 100644 index 00000000000..49d85e79224 --- /dev/null +++ b/src/Nethermind/Nethermind.State/SpmcRingBuffer.cs @@ -0,0 +1,149 @@ +// SPDX-FileCopyrightText: 2025 Demerzel Solutions Limited +// SPDX-License-Identifier: LGPL-3.0-only + +using System; +using System.Runtime.CompilerServices; +using System.Threading; + +namespace Nethermind.State; + +/// +/// ChatGPT generated single producer multiple consumer ring buffer +/// TODO: Check if using full fledge LMAX Distruptor is worth it. +/// +public sealed class SpmcRingBuffer +{ + private readonly T[] _entries; + private readonly long[] _sequences; + private readonly int _mask; + private readonly int _capacity; + + public long EstimatedJobCount + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get + { + // Single producer only ever increments _tail + long tail = Volatile.Read(ref _tail); + + // Multiple consumers only ever increment _head + long head = Volatile.Read(ref _head); + + long count = tail - head; + return count < 0 ? 0 : count; // clamp just in case of a race + } + } + + // --- head (consumers) + padding to avoid false sharing with _tail --- + private long _head; +#pragma warning disable CS0169 // Field is never used + private long _headPad1, _headPad2, _headPad3, _headPad4, _headPad5, _headPad6, _headPad7; + + // --- tail (producer) + padding --- + private long _tail; + private long _tailPad1, _tailPad2, _tailPad3, _tailPad4, _tailPad5, _tailPad6, _tailPad7; +#pragma warning restore CS0169 // Field is never used + + public SpmcRingBuffer(int capacityPowerOfTwo) + { + if (capacityPowerOfTwo <= 0) + throw new ArgumentOutOfRangeException(nameof(capacityPowerOfTwo)); + + // must be a power of two + if ((capacityPowerOfTwo & (capacityPowerOfTwo - 1)) != 0) + throw new ArgumentException("Capacity must be power of two.", nameof(capacityPowerOfTwo)); + + _capacity = capacityPowerOfTwo; + _mask = capacityPowerOfTwo - 1; + _entries = new T[capacityPowerOfTwo]; + _sequences = new long[capacityPowerOfTwo]; + + // LMAX / Vyukov-style: + // initial state: seq[i] = i, head = 0, tail = 0 + // - producer at tail = 0 expects seq[0] == 0 to claim + // - after publishing item 0: seq[0] = 1 (head+1) + for (int i = 0; i < capacityPowerOfTwo; i++) + _sequences[i] = i; + } + + /// + /// Approximate number of items in the buffer. + /// + public long EstimatedCount => + Volatile.Read(ref _tail) - Volatile.Read(ref _head); + + /// + /// Single producer: enqueue one item if there is space. + /// Returns false if the ring is full. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool TryEnqueue(in T item) + { + // Single producer: no CAS needed on tail. + long tail = _tail; + int index = (int)(tail & _mask); + + // Slot is free only if its sequence equals the current tail. + long seq = Volatile.Read(ref _sequences[index]); + if (seq != tail) + return false; // not yet consumed -> buffer full + + // Write payload first. + _entries[index] = item; + + // Publish: + // seq = tail + 1 means "item for head == tail is now visible". + // Volatile.Write gives us the release fence so consumer + // sees the payload after seeing seq. + Volatile.Write(ref _sequences[index], tail + 1); + + // Advance tail (only producer touches this). + _tail = tail + 1; + + return true; + } + + /// + /// Multiple consumers: try to dequeue one item. + /// Returns false if buffer appears empty at time of check. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool TryDequeue(out T item) + { + while (true) + { + long head = Volatile.Read(ref _head); + int index = (int)(head & _mask); + + long seq = Volatile.Read(ref _sequences[index]); + long expectedSeq = head + 1; + + // Not yet published? + if (seq < expectedSeq) + { + item = default!; + return false; + } + + // Slot is ready for this consumer, try to claim head. + if (seq == expectedSeq && + Interlocked.CompareExchange(ref _head, head + 1, head) == head) + { + // We own this slot now. + item = _entries[index]; + + // Mark slot as free for the next wrap: + // when tail reaches head + _capacity, this slot + // will again have seq == tail. + Volatile.Write( + ref _sequences[index], + head + _capacity + ); + + return true; + } + + // Lost race to another consumer, retry with updated head. + } + } +} diff --git a/src/Nethermind/Nethermind.State/StateProvider.cs b/src/Nethermind/Nethermind.State/StateProvider.cs index 97ef6d2dc46..1bf1826fed4 100644 --- a/src/Nethermind/Nethermind.State/StateProvider.cs +++ b/src/Nethermind/Nethermind.State/StateProvider.cs @@ -22,6 +22,7 @@ using Nethermind.Evm.Tracing.State; using Nethermind.Int256; using Nethermind.Logging; +using Nethermind.State.Flat.ScopeProvider; using Metrics = Nethermind.Db.Metrics; using static Nethermind.State.StateProvider; @@ -181,6 +182,11 @@ private void SetNewBalance(Address address, in UInt256 balanceChange, IReleaseSp { _needsStateRootUpdate = true; + if (address == FlatWorldStateScope.DebugAddress) + { + Console.Error.WriteLine($"Balance change {balanceChange}, {isSubtracting}"); + } + Account GetThroughCacheCheckExists() { Account result = GetThroughCache(address); @@ -327,6 +333,10 @@ public byte[] GetCode(Address address) public void DeleteAccount(Address address) { + if (address == FlatWorldStateScope.DebugAddress) + { + Console.Error.WriteLine($"delete address {Environment.StackTrace}"); + } _needsStateRootUpdate = true; PushDelete(address); } @@ -778,7 +788,13 @@ private void PushTouch(Address address, Account account, IReleaseSpec releaseSpe } private void PushDelete(Address address) - => Push(address, null, ChangeType.Delete); + { + Push(address, null, ChangeType.Delete); + if (address == FlatWorldStateScope.DebugAddress) + { + Console.Error.WriteLine("Delete account"); + } + } private void Push(Address address, Account? touchedAccount, ChangeType changeType) { @@ -789,6 +805,10 @@ private void Push(Address address, Account? touchedAccount, ChangeType changeTyp return; } + if (address == FlatWorldStateScope.DebugAddress) + { + Console.Error.WriteLine($"Push {changeType}, {touchedAccount}"); + } stack.Push(_changes.Count); _changes.Add(new Change(address, touchedAccount, changeType)); } @@ -804,6 +824,10 @@ private void PushRecreateEmpty(Address address, Account account, StackList { stack.Push(_changes.Count); _changes.Add(new Change(address, account, ChangeType.RecreateEmpty)); + if (address == FlatWorldStateScope.DebugAddress) + { + Console.Error.WriteLine($"Push recreate empty {account}"); + } } private StackList SetupCache(Address address) diff --git a/src/Nethermind/Nethermind.State/StateTree.cs b/src/Nethermind/Nethermind.State/StateTree.cs index cc3a384659b..63013b58c78 100644 --- a/src/Nethermind/Nethermind.State/StateTree.cs +++ b/src/Nethermind/Nethermind.State/StateTree.cs @@ -68,8 +68,14 @@ public bool TryGetStruct(Address address, out AccountStruct account, Hash256? ro return bytes.IsEmpty ? null : _decoder.Decode(bytes); } + private Address importantAddress = new Address("0xf664829682daf488be5318dae198a69ce27fb31b"); + public void Set(Address address, Account? account) { + if (address == importantAddress) + { + Console.Error.WriteLine($"Set {address} to {account}"); + } KeccakCache.ComputeTo(address.Bytes, out ValueHash256 keccak); Set(keccak.BytesAsSpan, account is null ? null : account.IsTotallyEmpty ? EmptyAccountRlp : Rlp.Encode(account)); } diff --git a/src/Nethermind/Nethermind.State/StorageTree.cs b/src/Nethermind/Nethermind.State/StorageTree.cs index 088fa41fa35..0a2d8ba082f 100644 --- a/src/Nethermind/Nethermind.State/StorageTree.cs +++ b/src/Nethermind/Nethermind.State/StorageTree.cs @@ -21,7 +21,7 @@ namespace Nethermind.State { public class StorageTree : PatriciaTree, IWorldStateScopeProvider.IStorageTree { - private const int LookupSize = 1024; + public const int LookupSize = 1024; private static readonly FrozenDictionary Lookup = CreateLookup(); public static readonly byte[] ZeroBytes = [0]; @@ -153,6 +153,10 @@ public byte[] Get(in ValueHash256 hash) return GetArray(hash.Bytes, null); } + public void HintSet(in UInt256 index) + { + } + [SkipLocalsInit] public void Set(in UInt256 index, byte[] value) { diff --git a/src/Nethermind/Nethermind.State/TrieStoreScopeProvider.cs b/src/Nethermind/Nethermind.State/TrieStoreScopeProvider.cs index 6f0a26a2b21..9eff78cccf6 100644 --- a/src/Nethermind/Nethermind.State/TrieStoreScopeProvider.cs +++ b/src/Nethermind/Nethermind.State/TrieStoreScopeProvider.cs @@ -142,6 +142,10 @@ public void Commit(long blockNumber) _storages.Clear(); } + public void HintSet(Address address) + { + } + internal StorageTree LookupStorageTree(Address address) { if (_storages.TryGetValue(address, out var storageTree)) @@ -182,7 +186,8 @@ public void Set(Address key, Account? account) public IWorldStateScopeProvider.IStorageWriteBatch CreateStorageWriteBatch(Address address, int estimatedEntries) { - return new StorageTreeBulkWriteBatch(estimatedEntries, scope.LookupStorageTree(address), this, address); + return new StorageTreeBulkWriteBatch(estimatedEntries, scope.LookupStorageTree(address), + (address, rootHash) => MarkDirty(address, rootHash), address); } public void MarkDirty(AddressAsKey address, Hash256 storageTreeRootHash) @@ -225,10 +230,15 @@ static Account ThrowNullAccount(Address address) } } - private class StorageTreeBulkWriteBatch(int estimatedEntries, StorageTree storageTree, WorldStateWriteBatch worldStateWriteBatch, AddressAsKey address) : IWorldStateScopeProvider.IStorageWriteBatch + public class StorageTreeBulkWriteBatch( + int estimatedEntries, + StorageTree storageTree, + Action onRootUpdated, + AddressAsKey address, + bool commit = false) : IWorldStateScopeProvider.IStorageWriteBatch { // Slight optimization on small contract as the index hash can be precalculated in some case. - private const int MIN_ENTRIES_TO_BATCH = 16; + public const int MIN_ENTRIES_TO_BATCH = 16; private bool _hasSelfDestruct; private bool _wasSetCalled = false; @@ -286,13 +296,20 @@ public void Dispose() if (hasSet) { - storageTree.UpdateRootHash(_bulkWrite?.Count > 64); - worldStateWriteBatch.MarkDirty(address, storageTree.RootHash); + if (commit) + { + storageTree.Commit(); + } + else + { + storageTree.UpdateRootHash(_bulkWrite?.Count > 64); + } + onRootUpdated(address, storageTree.RootHash); } } } - private class KeyValueWithBatchingBackedCodeDb(IKeyValueStoreWithBatching codeDb) : IWorldStateScopeProvider.ICodeDb + internal class KeyValueWithBatchingBackedCodeDb(IKeyValueStoreWithBatching codeDb) : IWorldStateScopeProvider.ICodeDb { public byte[]? GetCode(in ValueHash256 codeHash) { diff --git a/src/Nethermind/Nethermind.State/WorldState.cs b/src/Nethermind/Nethermind.State/WorldState.cs index 3ea6da61759..3ee252ed3ef 100644 --- a/src/Nethermind/Nethermind.State/WorldState.cs +++ b/src/Nethermind/Nethermind.State/WorldState.cs @@ -15,6 +15,8 @@ using Nethermind.Evm.Tracing.State; using Nethermind.Int256; using Nethermind.Logging; +using Nethermind.Trie.Pruning; +using Prometheus; [assembly: InternalsVisibleTo("Ethereum.Test.Base")] [assembly: InternalsVisibleTo("Ethereum.Blockchain.Test")] @@ -35,6 +37,9 @@ public class WorldState : IWorldState private bool _isInScope; private readonly ILogger _logger; + private bool isPrewarmer; + private Counter _timeCounter = DevMetric.Factory.CreateCounter("time_counter", "time_counter", "part", "is_prewarmer"); + public Hash256 StateRoot { get @@ -53,6 +58,11 @@ public WorldState( _persistentStorageProvider = new PersistentStorageProvider(_stateProvider, logManager); _transientStorageProvider = new TransientStorageProvider(logManager); _logger = logManager.GetClassLogger(); + isPrewarmer = false; + if (scopeProvider is PrewarmerScopeProvider psp) + { + isPrewarmer = !psp.IsWarmWorldState; + } } [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -226,10 +236,14 @@ public void DecrementNonce(Address address, UInt256 delta) public void CommitTree(long blockNumber) { + long sw = Stopwatch.GetTimestamp(); DebugGuardInScope(); _stateProvider.UpdateStateRootIfNeeded(); + _timeCounter.WithLabels("update_stateroot_if_needed", isPrewarmer.ToString()).Inc(Stopwatch.GetTimestamp() - sw); + sw = Stopwatch.GetTimestamp(); _currentScope.Commit(blockNumber); _persistentStorageProvider.ClearStorageMap(); + _timeCounter.WithLabels("commit_tree", isPrewarmer.ToString()).Inc(Stopwatch.GetTimestamp() - sw); } public UInt256 GetNonce(Address address) @@ -247,7 +261,9 @@ public IDisposable BeginScope(BlockHeader? baseBlock) if (_logger.IsTrace) _logger.Trace($"Beginning WorldState scope with baseblock {baseBlock?.ToString(BlockHeader.Format.Short) ?? "null"} with stateroot {baseBlock?.StateRoot?.ToString() ?? "null"}."); + long sw = Stopwatch.GetTimestamp(); _currentScope = ScopeProvider.BeginScope(baseBlock); + _timeCounter.WithLabels("begin_scope", isPrewarmer.ToString()).Inc(Stopwatch.GetTimestamp() - sw); _stateProvider.SetScope(_currentScope); _persistentStorageProvider.SetBackendScope(_currentScope); @@ -255,7 +271,9 @@ public IDisposable BeginScope(BlockHeader? baseBlock) { Reset(); _stateProvider.SetScope(null); + long sw = Stopwatch.GetTimestamp(); _currentScope.Dispose(); + _timeCounter.WithLabels("dispose_scope", isPrewarmer.ToString()).Inc(Stopwatch.GetTimestamp() - sw); _currentScope = null; _isInScope = false; if (_logger.IsTrace) _logger.Trace($"WorldState scope for baseblock {baseBlock?.ToString(BlockHeader.Format.Short) ?? "null"} closed"); @@ -321,17 +339,30 @@ public bool HasStateForBlock(BlockHeader? header) public void Commit(IReleaseSpec releaseSpec, IWorldStateTracer tracer, bool isGenesis = false, bool commitRoots = true) { DebugGuardInScope(); + + long sw = Stopwatch.GetTimestamp(); _transientStorageProvider.Commit(tracer); _persistentStorageProvider.Commit(tracer); _stateProvider.Commit(releaseSpec, tracer, commitRoots, isGenesis); + _timeCounter.WithLabels("commit", isPrewarmer.ToString()).Inc(Stopwatch.GetTimestamp() - sw); if (commitRoots) { - using IWorldStateScopeProvider.IWorldStateWriteBatch writeBatch = _currentScope.StartWriteBatch(_stateProvider.ChangedAccountCount); - writeBatch.OnAccountUpdated += (_, updatedAccount) => _stateProvider.SetState(updatedAccount.Address, updatedAccount.Account); - _persistentStorageProvider.FlushToTree(writeBatch); - _stateProvider.FlushToTree(writeBatch); + sw = Stopwatch.GetTimestamp(); + using (IWorldStateScopeProvider.IWorldStateWriteBatch writeBatch = + _currentScope.StartWriteBatch(_stateProvider.ChangedAccountCount)) + { + writeBatch.OnAccountUpdated += (_, updatedAccount) => _stateProvider.SetState(updatedAccount.Address, updatedAccount.Account); + _persistentStorageProvider.FlushToTree(writeBatch); + _timeCounter.WithLabels("flush_storage", isPrewarmer.ToString()).Inc(Stopwatch.GetTimestamp() - sw); + sw = Stopwatch.GetTimestamp(); + _stateProvider.FlushToTree(writeBatch); + _timeCounter.WithLabels("flush", isPrewarmer.ToString()).Inc(Stopwatch.GetTimestamp() - sw); + sw = Stopwatch.GetTimestamp(); + } + _timeCounter.WithLabels("write_batch_dispose", isPrewarmer.ToString()).Inc(Stopwatch.GetTimestamp() - sw); } + } public Snapshot TakeSnapshot(bool newTransactionStart = false) diff --git a/src/Nethermind/Nethermind.Trie/Nethermind.Trie.csproj b/src/Nethermind/Nethermind.Trie/Nethermind.Trie.csproj index c459591b43c..5f375087cee 100644 --- a/src/Nethermind/Nethermind.Trie/Nethermind.Trie.csproj +++ b/src/Nethermind/Nethermind.Trie/Nethermind.Trie.csproj @@ -15,4 +15,8 @@ + + + + diff --git a/src/Nethermind/Nethermind.Trie/PatriciaTree.cs b/src/Nethermind/Nethermind.Trie/PatriciaTree.cs index 4f04300e3d3..0dd158c3619 100644 --- a/src/Nethermind/Nethermind.Trie/PatriciaTree.cs +++ b/src/Nethermind/Nethermind.Trie/PatriciaTree.cs @@ -124,7 +124,7 @@ public PatriciaTree( _bufferPool = bufferPool; } - public void Commit(bool skipRoot = false, WriteFlags writeFlags = WriteFlags.None) + public void Commit(bool skipRoot = false, WriteFlags writeFlags = WriteFlags.None, bool noConcurrent = false) { if (!_allowCommits) { @@ -139,6 +139,8 @@ public void Commit(bool skipRoot = false, WriteFlags writeFlags = WriteFlags.Non _ => -1 }; + if (noConcurrent) maxLevelForConcurrentCommit = -1; + _writeBeforeCommit = 0; using ICommitter committer = TrieStore.BeginCommit(RootRef, writeFlags); @@ -327,7 +329,7 @@ private void SetRootHash(Hash256? value, bool resetObjects) [SkipLocalsInit] [DebuggerStepThrough] - public virtual ReadOnlySpan Get(ReadOnlySpan rawKey, Hash256? rootHash = null) + public virtual ReadOnlySpan Get(ReadOnlySpan rawKey, Hash256? rootHash = null, bool cachedOnly = false, bool keepChildRef = false) { byte[]? array = null; try @@ -348,7 +350,7 @@ public virtual ReadOnlySpan Get(ReadOnlySpan rawKey, Hash256? rootHa root = TrieStore.FindCachedOrUnknown(emptyPath, rootHash); } - SpanSource result = GetNew(nibbles, ref emptyPath, root, isNodeRead: false); + SpanSource result = GetNew(nibbles, ref emptyPath, root, isNodeRead: false, cachedOnly: cachedOnly, keepChildRef: keepChildRef); return result.IsNull ? ReadOnlySpan.Empty : result.Span; } @@ -374,7 +376,7 @@ public virtual ReadOnlySpan Get(ReadOnlySpan rawKey, Hash256? rootHa { root = TrieStore.FindCachedOrUnknown(emptyPath, rootHash); } - SpanSource result = GetNew(nibbles, ref emptyPath, root, isNodeRead: true); + SpanSource result = GetNew(nibbles, ref emptyPath, root, isNodeRead: true, cachedOnly: false); return result.ToArray(); } catch (TrieException e) @@ -403,7 +405,7 @@ public virtual ReadOnlySpan Get(ReadOnlySpan rawKey, Hash256? rootHa { root = TrieStore.FindCachedOrUnknown(emptyPath, rootHash); } - SpanSource result = GetNew(nibbles, ref emptyPath, root, isNodeRead: true); + SpanSource result = GetNew(nibbles, ref emptyPath, root, isNodeRead: true, cachedOnly: false); return result.ToArray() ?? []; } @@ -820,7 +822,7 @@ private record struct TraverseStack public TrieNode? OriginalChild; } - private SpanSource GetNew(Span remainingKey, ref TreePath path, TrieNode? node, bool isNodeRead) + private SpanSource GetNew(Span remainingKey, ref TreePath path, TrieNode? node, bool isNodeRead, bool cachedOnly, bool keepChildRef = false) { int originalPathLength = path.Length; @@ -834,6 +836,11 @@ private SpanSource GetNew(Span remainingKey, ref TreePath path, TrieNode? return default; } + if (cachedOnly && node.NodeType == NodeType.Unknown && node.FullRlp.IsNull) + { + return default; + } + node.ResolveNode(TrieStore, path); if (isNodeRead && remainingKey.Length == 0) @@ -856,7 +863,7 @@ private SpanSource GetNew(Span remainingKey, ref TreePath path, TrieNode? // Continue traversal to the child of the extension path.AppendMut(node.Key); - TrieNode? extensionChild = node.GetChildWithChildPath(TrieStore, ref path, 0); + TrieNode? extensionChild = node.GetChildWithChildPath(TrieStore, ref path, 0, keepChildRef: keepChildRef); remainingKey = remainingKey[node!.Key.Length..]; node = extensionChild; @@ -869,7 +876,7 @@ private SpanSource GetNew(Span remainingKey, ref TreePath path, TrieNode? int nib = remainingKey[0]; path.AppendMut(nib); - TrieNode? child = node.GetChildWithChildPath(TrieStore, ref path, nib); + TrieNode? child = node.GetChildWithChildPath(TrieStore, ref path, nib, keepChildRef: keepChildRef); // Continue loop with child as current node node = child; diff --git a/src/Nethermind/Nethermind.Trie/Pruning/TreePath.cs b/src/Nethermind/Nethermind.Trie/Pruning/TreePath.cs index 9714b92ddbc..f5abb541d47 100644 --- a/src/Nethermind/Nethermind.Trie/Pruning/TreePath.cs +++ b/src/Nethermind/Nethermind.Trie/Pruning/TreePath.cs @@ -2,6 +2,7 @@ // SPDX-License-Identifier: LGPL-3.0-only using System; +using System.Buffers.Binary; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Runtime.CompilerServices; @@ -22,7 +23,7 @@ namespace Nethermind.Trie; /// [Todo("check if its worth it to change the length to byte, or if it actually make things slower.")] [Todo("check if its worth it to not clear byte during TruncateMut, but will need proper comparator, span copy, etc.")] -public struct TreePath : IEquatable +public struct TreePath : IEquatable, IComparable { public const int MemorySize = 36; public ValueHash256 Path; @@ -315,6 +316,8 @@ public readonly int CompareTo(in TreePath otherTree) return Length.CompareTo(otherTree.Length); } + int IComparable.CompareTo(TreePath otherTree) => CompareTo(in otherTree); + /// /// Compare with otherTree, as if this TreePath was truncated to `length`. /// diff --git a/src/Nethermind/Nethermind.Trie/TrieNode.cs b/src/Nethermind/Nethermind.Trie/TrieNode.cs index 7225f0ab073..9f1e63d08fb 100644 --- a/src/Nethermind/Nethermind.Trie/TrieNode.cs +++ b/src/Nethermind/Nethermind.Trie/TrieNode.cs @@ -703,7 +703,7 @@ public int AppendChildPath(ref TreePath currentPath, int childIndex) return childNode; } - public TrieNode? GetChildWithChildPath(ITrieNodeResolver tree, ref TreePath childPath, int childIndex) + public TrieNode? GetChildWithChildPath(ITrieNodeResolver tree, ref TreePath childPath, int childIndex, bool keepChildRef = false) { /* extensions store value before the child while branches store children before the value * so just to treat them in the same way we update index on extensions @@ -735,7 +735,7 @@ public int AppendChildPath(ref TreePath currentPath, int childIndex) // Don't unresolve nodes with path length <= 4; there should be relatively few and they should fit // in RAM, but they are hit quite a lot, and don't have very good data locality. // That said, in practice, it does nothing notable, except for significantly improving benchmark score. - if (child?.IsPersisted == true && childPath.Length > 4 && childPath.Length % 2 == 0) + if (child?.IsPersisted == true && !keepChildRef && childPath.Length > 4 && childPath.Length % 2 == 0) { UnresolveChild(childIndex); }