diff --git a/src/Nethermind/Nethermind.Blockchain.Test/Receipts/PersistentReceiptStorageTests.cs b/src/Nethermind/Nethermind.Blockchain.Test/Receipts/PersistentReceiptStorageTests.cs index b9134750e5a..3186373e6ac 100644 --- a/src/Nethermind/Nethermind.Blockchain.Test/Receipts/PersistentReceiptStorageTests.cs +++ b/src/Nethermind/Nethermind.Blockchain.Test/Receipts/PersistentReceiptStorageTests.cs @@ -8,6 +8,8 @@ using Nethermind.Blockchain.Receipts; using Nethermind.Core; using Nethermind.Core.Crypto; +using Nethermind.Core.Extensions; +using Nethermind.Core.Test; using Nethermind.Core.Test.Builders; using Nethermind.Crypto; using Nethermind.Db; @@ -24,12 +26,13 @@ namespace Nethermind.Blockchain.Test.Receipts [TestFixture(false)] public class PersistentReceiptStorageTests { - private MemColumnsDb _receiptsDb = null!; + private TestMemColumnsDb _receiptsDb = null!; private ReceiptsRecovery _receiptsRecovery; private IBlockTree _blockTree; private readonly bool _useCompactReceipts; private ReceiptConfig _receiptConfig; private PersistentReceiptStorage _storage; + private ReceiptArrayStorageDecoder _decoder; public PersistentReceiptStorageTests(bool useCompactReceipts) { @@ -43,7 +46,7 @@ public void SetUp() EthereumEcdsa ethereumEcdsa = new(specProvider.ChainId, LimboLogs.Instance); _receiptConfig = new ReceiptConfig(); _receiptsRecovery = new(ethereumEcdsa, specProvider); - _receiptsDb = new MemColumnsDb(); + _receiptsDb = new TestMemColumnsDb(); _receiptsDb.GetColumnDb(ReceiptsColumns.Blocks).Set(Keccak.Zero, Array.Empty()); _blockTree = Substitute.For(); CreateStorage(); @@ -51,13 +54,14 @@ public void SetUp() private void CreateStorage() { + _decoder = new ReceiptArrayStorageDecoder(_useCompactReceipts); _storage = new PersistentReceiptStorage( _receiptsDb, MainnetSpecProvider.Instance, _receiptsRecovery, _blockTree, _receiptConfig, - new ReceiptArrayStorageDecoder(_useCompactReceipts) + _decoder ) { MigratedBlockNumber = 0 }; } @@ -100,6 +104,53 @@ public void Adds_and_retrieves_receipts_for_block() _storage.Get(block).Should().BeEquivalentTo(receipts); } + [Test] + public void Adds_should_prefix_key_with_blockNumber() + { + var (block, receipts) = InsertBlock(); + + Span blockNumPrefixed = stackalloc byte[40]; + block.Number.ToBigEndianByteArray().CopyTo(blockNumPrefixed); // TODO: We don't need to create an array here... + block.Hash!.Bytes.CopyTo(blockNumPrefixed[8..]); + + _receiptsDb.GetColumnDb(ReceiptsColumns.Blocks)[blockNumPrefixed].Should().NotBeNull(); + } + + [Test] + public void Adds_should_attempt_hash_key_first_if_inserted_with_hashkey() + { + var (block, receipts) = PrepareBlock(); + + using NettyRlpStream rlpStream = _decoder.EncodeToNewNettyStream(receipts, RlpBehaviors.Storage); + _receiptsDb.GetColumnDb(ReceiptsColumns.Blocks)[block.Hash.Bytes] = rlpStream.AsSpan().ToArray(); + + CreateStorage(); + _storage.Get(block); + + Span blockNumPrefixed = stackalloc byte[40]; + block.Number.ToBigEndianByteArray().CopyTo(blockNumPrefixed); // TODO: We don't need to create an array here... + block.Hash!.Bytes.CopyTo(blockNumPrefixed[8..]); + + TestMemDb blocksDb = (TestMemDb)_receiptsDb.GetColumnDb(ReceiptsColumns.Blocks); + blocksDb.KeyWasRead(blockNumPrefixed.ToArray(), 0); + blocksDb.KeyWasRead(block.Hash.Bytes, 1); + } + + [Test] + public void Should_be_able_to_get_block_with_hash_address() + { + var (block, receipts) = PrepareBlock(); + + Span blockNumPrefixed = stackalloc byte[40]; + block.Number.ToBigEndianByteArray().CopyTo(blockNumPrefixed); // TODO: We don't need to create an array here... + block.Hash!.Bytes.CopyTo(blockNumPrefixed[8..]); + + using NettyRlpStream rlpStream = _decoder.EncodeToNewNettyStream(receipts, RlpBehaviors.Storage); + _receiptsDb.GetColumnDb(ReceiptsColumns.Blocks)[block.Hash.Bytes] = rlpStream.AsSpan().ToArray(); + + _storage.Get(block).Length.Should().Be(receipts.Length); + } + [Test, Timeout(Timeout.MaxTestTime)] public void Should_not_cache_empty_non_processed_blocks() { @@ -167,14 +218,14 @@ public void Should_handle_inserting_null_receipts() [Test, Timeout(Timeout.MaxTestTime)] public void HasBlock_should_returnFalseForMissingHash() { - _storage.HasBlock(Keccak.Compute("missing-value")).Should().BeFalse(); + _storage.HasBlock(0, Keccak.Compute("missing-value")).Should().BeFalse(); } [Test, Timeout(Timeout.MaxTestTime)] public void HasBlock_should_returnTrueForKnownHash() { var (block, _) = InsertBlock(); - _storage.HasBlock(block.Hash!).Should().BeTrue(); + _storage.HasBlock(block.Number, block.Hash!).Should().BeTrue(); } [Test, Timeout(Timeout.MaxTestTime)] @@ -307,7 +358,7 @@ public void When_NewHeadBlock_ClearOldTxIndex() ); } - private (Block block, TxReceipt[] receipts) InsertBlock(Block? block = null, bool isFinalized = false, long? headNumber = null) + private (Block block, TxReceipt[] receipts) PrepareBlock(Block? block = null, bool isFinalized = false, long? headNumber = null) { block ??= Build.A.Block .WithNumber(1) @@ -335,6 +386,12 @@ public void When_NewHeadBlock_ClearOldTxIndex() _blockTree.FindBestSuggestedHeader().Returns(farHead); } var receipts = new[] { Build.A.Receipt.WithCalculatedBloom().TestObject }; + return (block, receipts); + } + + private (Block block, TxReceipt[] receipts) InsertBlock(Block? block = null, bool isFinalized = false, long? headNumber = null) + { + (block, TxReceipt[] receipts) = PrepareBlock(block, isFinalized, headNumber); _storage.Insert(block, receipts); _receiptsRecovery.TryRecover(block, receipts); diff --git a/src/Nethermind/Nethermind.Blockchain/Nethermind.Blockchain.csproj b/src/Nethermind/Nethermind.Blockchain/Nethermind.Blockchain.csproj index b2614ecd0d1..c4448729dd7 100644 --- a/src/Nethermind/Nethermind.Blockchain/Nethermind.Blockchain.csproj +++ b/src/Nethermind/Nethermind.Blockchain/Nethermind.Blockchain.csproj @@ -5,6 +5,7 @@ latest annotations true + true diff --git a/src/Nethermind/Nethermind.Blockchain/Receipts/IReceiptStorage.cs b/src/Nethermind/Nethermind.Blockchain/Receipts/IReceiptStorage.cs index 2e034bd98dd..e2ce64e5785 100644 --- a/src/Nethermind/Nethermind.Blockchain/Receipts/IReceiptStorage.cs +++ b/src/Nethermind/Nethermind.Blockchain/Receipts/IReceiptStorage.cs @@ -13,7 +13,7 @@ public interface IReceiptStorage : IReceiptFinder void Insert(Block block, TxReceipt[]? txReceipts, bool ensureCanonical); long? LowestInsertedReceiptBlockNumber { get; set; } long MigratedBlockNumber { get; set; } - bool HasBlock(Keccak hash); + bool HasBlock(long blockNumber, Keccak hash); void EnsureCanonical(Block block); } } diff --git a/src/Nethermind/Nethermind.Blockchain/Receipts/InMemoryReceiptStorage.cs b/src/Nethermind/Nethermind.Blockchain/Receipts/InMemoryReceiptStorage.cs index 680c0354861..da8b503dd32 100644 --- a/src/Nethermind/Nethermind.Blockchain/Receipts/InMemoryReceiptStorage.cs +++ b/src/Nethermind/Nethermind.Blockchain/Receipts/InMemoryReceiptStorage.cs @@ -68,7 +68,7 @@ public void Insert(Block block, TxReceipt[] txReceipts, bool ensureCanonical = t } } - public bool HasBlock(Keccak hash) + public bool HasBlock(long blockNumber, Keccak hash) { return _receipts.ContainsKey(hash); } diff --git a/src/Nethermind/Nethermind.Blockchain/Receipts/NullReceiptStorage.cs b/src/Nethermind/Nethermind.Blockchain/Receipts/NullReceiptStorage.cs index d8b808134cf..9c96a9d4880 100644 --- a/src/Nethermind/Nethermind.Blockchain/Receipts/NullReceiptStorage.cs +++ b/src/Nethermind/Nethermind.Blockchain/Receipts/NullReceiptStorage.cs @@ -43,7 +43,7 @@ public event EventHandler ReceiptsInserted remove { } } - public bool HasBlock(Keccak hash) + public bool HasBlock(long blockNumber, Keccak hash) { return false; } diff --git a/src/Nethermind/Nethermind.Blockchain/Receipts/PersistentReceiptStorage.cs b/src/Nethermind/Nethermind.Blockchain/Receipts/PersistentReceiptStorage.cs index 7f87bbae4e3..6f0b0636893 100644 --- a/src/Nethermind/Nethermind.Blockchain/Receipts/PersistentReceiptStorage.cs +++ b/src/Nethermind/Nethermind.Blockchain/Receipts/PersistentReceiptStorage.cs @@ -2,7 +2,9 @@ // SPDX-License-Identifier: LGPL-3.0-only using System; +using System.Collections.Generic; using System.IO; +using System.Linq; using System.Threading.Tasks; using Nethermind.Core; using Nethermind.Core.Caching; @@ -28,6 +30,7 @@ public class PersistentReceiptStorage : IReceiptStorage private readonly ReceiptArrayStorageDecoder _storageDecoder = ReceiptArrayStorageDecoder.Instance; private readonly IBlockTree _blockTree; private readonly IReceiptConfig _receiptConfig; + private readonly bool _legacyHashKey; private const int CacheSize = 64; private readonly LruCache _receiptsCache = new(CacheSize, CacheSize, "receipts"); @@ -56,6 +59,9 @@ public PersistentReceiptStorage( _lowestInsertedReceiptBlock = lowestBytes is null ? (long?)null : new RlpStream(lowestBytes).DecodeLong(); _migratedBlockNumber = Get(MigrationBlockNumberKey, long.MaxValue); + KeyValuePair? firstValue = _blocksDb.GetAll().FirstOrDefault(); + _legacyHashKey = firstValue.HasValue && firstValue.Value.Key != null && firstValue.Value.Key.Length == Keccak.Size; + _blockTree.BlockAddedToMain += BlockTreeOnBlockAddedToMain; } @@ -139,7 +145,8 @@ public TxReceipt[] Get(Block block) return receipts ?? Array.Empty(); } - Span receiptsData = _blocksDb.GetSpan(blockHash); + Span receiptsData = GetReceiptData(block.Number, blockHash); + try { if (receiptsData.IsNullOrEmpty()) @@ -162,6 +169,48 @@ public TxReceipt[] Get(Block block) } } + private unsafe Span GetReceiptData(long blockNumber, Keccak blockHash) + { + if (_legacyHashKey) + { + Span receiptsData = _blocksDb.GetSpan(blockHash); + if (receiptsData != null) + { + return receiptsData; + } + + Span blockNumPrefixed = stackalloc byte[40]; + GetBlockNumPrefixedKey(blockNumber, blockHash, blockNumPrefixed); + +#pragma warning disable CS9080 + receiptsData = _blocksDb.GetSpan(blockNumPrefixed); +#pragma warning restore CS9080 + + return receiptsData; + } + else + { + Span blockNumPrefixed = stackalloc byte[40]; + GetBlockNumPrefixedKey(blockNumber, blockHash, blockNumPrefixed); + + Span receiptsData = _blocksDb.GetSpan(blockNumPrefixed); + if (receiptsData.IsNull()) + { + receiptsData = _blocksDb.GetSpan(blockHash); + } + +#pragma warning disable CS9080 + return receiptsData; +#pragma warning restore CS9080 + } + } + + private static void GetBlockNumPrefixedKey(long blockNumber, Keccak blockHash, Span output) + { + blockNumber.WriteBigEndian(output); + blockHash!.Bytes.CopyTo(output[8..]); + } + public TxReceipt[] Get(Keccak blockHash) { Block? block = _blockTree.FindBlock(blockHash); @@ -180,8 +229,7 @@ public bool TryGetReceiptsIterator(long blockNumber, Keccak blockHash, out Recei } var result = CanGetReceiptsByHash(blockNumber); - var receiptsData = _blocksDb.GetSpan(blockHash); - + Span receiptsData = GetReceiptData(blockNumber, blockHash); Func recoveryContextFactory = () => null; @@ -220,7 +268,10 @@ public void Insert(Block block, TxReceipt[]? txReceipts, bool ensureCanonical = using (NettyRlpStream stream = _storageDecoder.EncodeToNewNettyStream(txReceipts, behaviors)) { - _blocksDb.Set(block.Hash!, stream.AsSpan()); + Span blockNumPrefixed = stackalloc byte[40]; + GetBlockNumPrefixedKey(blockNumber, block.Hash!, blockNumPrefixed); + + _blocksDb.Set(blockNumPrefixed, stream.AsSpan()); } if (blockNumber < MigratedBlockNumber) @@ -264,9 +315,26 @@ internal void ClearCache() _receiptsCache.Clear(); } - public bool HasBlock(Keccak hash) + public bool HasBlock(long blockNumber, Keccak blockHash) { - return _receiptsCache.Contains(hash) || _blocksDb.KeyExists(hash); + if (_receiptsCache.Contains(blockHash)) return true; + + if (_legacyHashKey) + { + if (_blocksDb.KeyExists(blockHash)) return true; + + Span blockNumPrefixed = stackalloc byte[40]; + GetBlockNumPrefixedKey(blockNumber, blockHash, blockNumPrefixed); + + return _blocksDb.KeyExists(blockNumPrefixed); + } + else + { + Span blockNumPrefixed = stackalloc byte[40]; + GetBlockNumPrefixedKey(blockNumber, blockHash, blockNumPrefixed); + + return _blocksDb.KeyExists(blockNumPrefixed) || _blocksDb.KeyExists(blockHash); + } } public void EnsureCanonical(Block block) diff --git a/src/Nethermind/Nethermind.Core.Test/TestMemColumnDb.cs b/src/Nethermind/Nethermind.Core.Test/TestMemColumnDb.cs new file mode 100644 index 00000000000..3df52be8c98 --- /dev/null +++ b/src/Nethermind/Nethermind.Core.Test/TestMemColumnDb.cs @@ -0,0 +1,32 @@ +// SPDX-FileCopyrightText: 2023 Demerzel Solutions Limited +// SPDX-License-Identifier: LGPL-3.0-only + +using System.Collections.Generic; +using Nethermind.Db; + +namespace Nethermind.Core.Test; + +public class TestMemColumnsDb : TestMemDb, IColumnsDb +{ + private readonly IDictionary _columnDbs = new Dictionary(); + + public TestMemColumnsDb() + { + } + + public TestMemColumnsDb(params TKey[] keys) + { + foreach (var key in keys) + { + GetColumnDb(key); + } + } + + public IDbWithSpan GetColumnDb(TKey key) => !_columnDbs.TryGetValue(key, out var db) ? _columnDbs[key] = new TestMemDb() : db; + public IEnumerable ColumnKeys => _columnDbs.Keys; + + public IReadOnlyDb CreateReadOnly(bool createInMemWriteStore) + { + return new ReadOnlyColumnsDb(this, createInMemWriteStore); + } +} diff --git a/src/Nethermind/Nethermind.Core/Extensions/Int64Extensions.cs b/src/Nethermind/Nethermind.Core/Extensions/Int64Extensions.cs index e470e4ef914..0bf1a3c7c66 100644 --- a/src/Nethermind/Nethermind.Core/Extensions/Int64Extensions.cs +++ b/src/Nethermind/Nethermind.Core/Extensions/Int64Extensions.cs @@ -126,6 +126,11 @@ public static byte[] ToBigEndianByteArray(this long value) return bytes; } + public static void WriteBigEndian(this long value, Span output) + { + BinaryPrimitives.WriteInt64BigEndian(output, value); + } + [ThreadStatic] private static byte[]? t_byteBuffer64; private static byte[] GetByteBuffer64() => t_byteBuffer64 ??= new byte[8]; diff --git a/src/Nethermind/Nethermind.Db.Rocks/ColumnDb.cs b/src/Nethermind/Nethermind.Db.Rocks/ColumnDb.cs index 3bebbc3c727..2e716da6d2a 100644 --- a/src/Nethermind/Nethermind.Db.Rocks/ColumnDb.cs +++ b/src/Nethermind/Nethermind.Db.Rocks/ColumnDb.cs @@ -51,7 +51,7 @@ public void Set(ReadOnlySpan key, byte[]? value, WriteFlags flags = WriteF public IEnumerable> GetAll(bool ordered = false) { - using Iterator iterator = _mainDb.CreateIterator(ordered, _columnFamily); + Iterator iterator = _mainDb.CreateIterator(ordered, _columnFamily); return _mainDb.GetAllCore(iterator); } diff --git a/src/Nethermind/Nethermind.Db.Rocks/DbOnTheRocks.cs b/src/Nethermind/Nethermind.Db.Rocks/DbOnTheRocks.cs index 1d8dbd20691..7465a7ee667 100644 --- a/src/Nethermind/Nethermind.Db.Rocks/DbOnTheRocks.cs +++ b/src/Nethermind/Nethermind.Db.Rocks/DbOnTheRocks.cs @@ -593,79 +593,89 @@ internal IEnumerable GetAllValuesCore(Iterator iterator) { try { - iterator.SeekToFirst(); - } - catch (RocksDbSharpException e) - { - CreateMarkerIfCorrupt(e); - throw; - } - - while (iterator.Valid()) - { - yield return iterator.Value(); try { - iterator.Next(); + iterator.SeekToFirst(); } catch (RocksDbSharpException e) { CreateMarkerIfCorrupt(e); throw; } - } - try - { - iterator.Dispose(); + while (iterator.Valid()) + { + yield return iterator.Value(); + try + { + iterator.Next(); + } + catch (RocksDbSharpException e) + { + CreateMarkerIfCorrupt(e); + throw; + } + } } - catch (RocksDbSharpException e) + finally { - CreateMarkerIfCorrupt(e); - throw; + try + { + iterator.Dispose(); + } + catch (RocksDbSharpException e) + { + CreateMarkerIfCorrupt(e); + throw; + } } } public IEnumerable> GetAllCore(Iterator iterator) { - if (_isDisposing) - { - throw new ObjectDisposedException($"Attempted to read form a disposed database {Name}"); - } - try { - iterator.SeekToFirst(); - } - catch (RocksDbSharpException e) - { - CreateMarkerIfCorrupt(e); - throw; - } - - while (iterator.Valid()) - { - yield return new KeyValuePair(iterator.Key(), iterator.Value()); + if (_isDisposing) + { + throw new ObjectDisposedException($"Attempted to read form a disposed database {Name}"); + } try { - iterator.Next(); + iterator.SeekToFirst(); } catch (RocksDbSharpException e) { CreateMarkerIfCorrupt(e); throw; } - } - try - { - iterator.Dispose(); + while (iterator.Valid()) + { + yield return new KeyValuePair(iterator.Key(), iterator.Value()); + + try + { + iterator.Next(); + } + catch (RocksDbSharpException e) + { + CreateMarkerIfCorrupt(e); + throw; + } + } } - catch (RocksDbSharpException e) + finally { - CreateMarkerIfCorrupt(e); - throw; + try + { + iterator.Dispose(); + } + catch (RocksDbSharpException e) + { + CreateMarkerIfCorrupt(e); + throw; + } } } diff --git a/src/Nethermind/Nethermind.Db.Test/DbOnTheRocksTests.cs b/src/Nethermind/Nethermind.Db.Test/DbOnTheRocksTests.cs index 96879227c90..ab1dfbba893 100644 --- a/src/Nethermind/Nethermind.Db.Test/DbOnTheRocksTests.cs +++ b/src/Nethermind/Nethermind.Db.Test/DbOnTheRocksTests.cs @@ -25,11 +25,25 @@ namespace Nethermind.Db.Test [Parallelizable(ParallelScope.None)] public class DbOnTheRocksTests { + private const string DbPath = "blocks"; + + [SetUp] + public void Setup() + { + Directory.CreateDirectory(DbPath); + } + + [TearDown] + public void TearDown() + { + Directory.Delete(DbPath, true); + } + [Test] public void Smoke_test() { IDbConfig config = new DbConfig(); - DbOnTheRocks db = new("blocks", GetRocksDbSettings("blocks", "Blocks"), config, LimboLogs.Instance); + DbOnTheRocks db = new(DbPath, GetRocksDbSettings(DbPath, "Blocks"), config, LimboLogs.Instance); db[new byte[] { 1, 2, 3 }] = new byte[] { 4, 5, 6 }; Assert.That(db[new byte[] { 1, 2, 3 }], Is.EqualTo(new byte[] { 4, 5, 6 })); @@ -44,7 +58,7 @@ public void Smoke_test() public void Smoke_test_span() { IDbConfig config = new DbConfig(); - DbOnTheRocks db = new("blocks", GetRocksDbSettings("blocks", "Blocks"), config, LimboLogs.Instance); + DbOnTheRocks db = new(DbPath, GetRocksDbSettings(DbPath, "Blocks"), config, LimboLogs.Instance); byte[] key = new byte[] { 1, 2, 3 }; byte[] value = new byte[] { 4, 5, 6 }; db.PutSpan(key, value); @@ -70,6 +84,32 @@ public void Can_get_all_on_empty() } } + [Test] + public void Smoke_test_iterator() + { + IDbConfig config = new DbConfig(); + DbOnTheRocks db = new(DbPath, GetRocksDbSettings(DbPath, "Blocks"), config, LimboLogs.Instance); + db[new byte[] { 1, 2, 3 }] = new byte[] { 4, 5, 6 }; + + KeyValuePair[] allValues = db.GetAll().ToArray()!; + allValues[0].Key.Should().BeEquivalentTo(new byte[] { 1, 2, 3 }); + allValues[0].Value.Should().BeEquivalentTo(new byte[] { 4, 5, 6 }); + } + + [Test] + public void Columns_db_smoke_test_iterator() + { + IDbConfig config = new DbConfig(); + using ColumnsDb columnsDb = new(DbPath, GetRocksDbSettings(DbPath, "Blocks"), config, + LimboLogs.Instance, new List() { ReceiptsColumns.Blocks }); + IDbWithSpan? db = columnsDb.GetColumnDb(ReceiptsColumns.Blocks); + db[new byte[] { 1, 2, 3 }] = new byte[] { 4, 5, 6 }; + + KeyValuePair[] allValues = db.GetAll().ToArray()!; + allValues[0].Key.Should().BeEquivalentTo(new byte[] { 1, 2, 3 }); + allValues[0].Value.Should().BeEquivalentTo(new byte[] { 4, 5, 6 }); + } + [Test] public async Task Dispose_while_writing_does_not_cause_access_violation_exception() { @@ -194,7 +234,7 @@ public void Test_columndb_put_and_get_span_correctly_store_value() try { IDbConfig config = new DbConfig(); - using ColumnsDb columnDb = new(path, GetRocksDbSettings("blocks", "Blocks"), config, + using ColumnsDb columnDb = new(path, GetRocksDbSettings(DbPath, "Blocks"), config, LimboLogs.Instance, new List() { ReceiptsColumns.Blocks }); using IDbWithSpan db = columnDb.GetColumnDb(ReceiptsColumns.Blocks); diff --git a/src/Nethermind/Nethermind.Db/DbExtensions.cs b/src/Nethermind/Nethermind.Db/DbExtensions.cs index b83a28b4a0c..c1a4eb925aa 100644 --- a/src/Nethermind/Nethermind.Db/DbExtensions.cs +++ b/src/Nethermind/Nethermind.Db/DbExtensions.cs @@ -48,6 +48,18 @@ public static void Set(this IDb db, Keccak key, Span value) } } + public static void Set(this IDb db, ReadOnlySpan key, ReadOnlySpan value) + { + if (db is IDbWithSpan dbWithSpan) + { + dbWithSpan.PutSpan(key, value); + } + else + { + db[key] = value.ToArray(); + } + } + public static KeyValuePair[] MultiGet(this IDb db, IEnumerable keys) { var k = keys.Select(k => k.Bytes).ToArray(); diff --git a/src/Nethermind/Nethermind.Facade/Filters/LogFinder.cs b/src/Nethermind/Nethermind.Facade/Filters/LogFinder.cs index c8242746471..91968829bff 100644 --- a/src/Nethermind/Nethermind.Facade/Filters/LogFinder.cs +++ b/src/Nethermind/Nethermind.Facade/Filters/LogFinder.cs @@ -64,13 +64,13 @@ BlockHeader FindHeader(BlockParameter blockParameter, string name, bool headLimi } cancellationToken.ThrowIfCancellationRequested(); - if (fromBlock.Number != 0 && fromBlock.ReceiptsRoot != Keccak.EmptyTreeHash && !_receiptStorage.HasBlock(fromBlock.Hash!)) + if (fromBlock.Number != 0 && fromBlock.ReceiptsRoot != Keccak.EmptyTreeHash && !_receiptStorage.HasBlock(fromBlock.Number, fromBlock.Hash!)) { throw new ResourceNotFoundException($"Receipt not available for 'From' block '{fromBlock.Number}'."); } cancellationToken.ThrowIfCancellationRequested(); - if (toBlock.Number != 0 && toBlock.ReceiptsRoot != Keccak.EmptyTreeHash && !_receiptStorage.HasBlock(toBlock.Hash!)) + if (toBlock.Number != 0 && toBlock.ReceiptsRoot != Keccak.EmptyTreeHash && !_receiptStorage.HasBlock(toBlock.Number, toBlock.Hash!)) { throw new ResourceNotFoundException($"Receipt not available for 'To' block '{toBlock.Number}'."); } diff --git a/src/Nethermind/Nethermind.Runner.Test/Ethereum/Steps/Migrations/ReceiptMigrationTests.cs b/src/Nethermind/Nethermind.Runner.Test/Ethereum/Steps/Migrations/ReceiptMigrationTests.cs index bba61f8110b..f7e74f275e0 100644 --- a/src/Nethermind/Nethermind.Runner.Test/Ethereum/Steps/Migrations/ReceiptMigrationTests.cs +++ b/src/Nethermind/Nethermind.Runner.Test/Ethereum/Steps/Migrations/ReceiptMigrationTests.cs @@ -121,9 +121,9 @@ public long MigratedBlockNumber set => _outStorage.MigratedBlockNumber = value; } - public bool HasBlock(Keccak hash) + public bool HasBlock(long blockNumber, Keccak hash) { - return _outStorage.HasBlock(hash); + return _outStorage.HasBlock(blockNumber, hash); } public void EnsureCanonical(Block block)