Skip to content

Commit

Permalink
EIP-2935: Save historical block hashes in state (#6925)
Browse files Browse the repository at this point in the history
Co-authored-by: MarekM25 <marekm2504@gmail.com>
  • Loading branch information
tanishqjasoria and MarekM25 committed May 7, 2024
1 parent 7f750d3 commit f679833
Show file tree
Hide file tree
Showing 56 changed files with 476 additions and 69 deletions.
4 changes: 3 additions & 1 deletion src/Nethermind/Ethereum.Test.Base/BlockchainTestBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
using System.Threading;
using System.Threading.Tasks;
using Nethermind.Blockchain;
using Nethermind.Blockchain.Blocks;
using Nethermind.Blockchain.Find;
using Nethermind.Blockchain.Receipts;
using Nethermind.Consensus;
Expand Down Expand Up @@ -142,7 +143,7 @@ protected async Task<EthereumTestResult> RunTest(BlockchainTest test, Stopwatch?
IStateReader stateReader = new StateReader(trieStore, codeDb, _logManager);

IReceiptStorage receiptStorage = NullReceiptStorage.Instance;
IBlockhashProvider blockhashProvider = new BlockhashProvider(blockTree, _logManager);
IBlockhashProvider blockhashProvider = new BlockhashProvider(blockTree, specProvider, stateProvider, _logManager);
ITxValidator txValidator = new TxValidator(TestBlockchainIds.ChainId);
IHeaderValidator headerValidator = new HeaderValidator(blockTree, Sealer, specProvider, _logManager);
IUnclesValidator unclesValidator = new UnclesValidator(blockTree, headerValidator, _logManager);
Expand All @@ -166,6 +167,7 @@ protected async Task<EthereumTestResult> RunTest(BlockchainTest test, Stopwatch?
stateProvider,
receiptStorage,
NullWitnessCollector.Instance,
new BlockhashStore(blockTree, specProvider, stateProvider),
_logManager);

IBlockchainProcessor blockchainProcessor = new BlockchainProcessor(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
using Nethermind.AccountAbstraction.Executor;
using Nethermind.AccountAbstraction.Source;
using Nethermind.Blockchain;
using Nethermind.Blockchain.Blocks;
using Nethermind.Blockchain.Contracts.Json;
using Nethermind.Consensus;
using Nethermind.Consensus.Comparers;
Expand Down Expand Up @@ -190,6 +191,7 @@ protected override BlockProcessor CreateBlockProcessor()
State,
ReceiptStorage,
NullWitnessCollector.Instance,
new BlockhashStore(BlockTree, SpecProvider, State),
LogManager);

AbiParameterConverter.RegisterFactory(new AbiTypeFactory(new AbiTuple<UserOperationAbi>()));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
using System.Threading.Tasks;
using System.Threading;
using FluentAssertions;
using Nethermind.Blockchain.Blocks;
using Nethermind.Consensus.Processing;
using Nethermind.Consensus.Rewards;
using Nethermind.Core.Test.Blockchain;
Expand All @@ -49,6 +50,7 @@ public void Prepared_block_contains_author_field()
stateProvider,
NullReceiptStorage.Instance,
NullWitnessCollector.Instance,
Substitute.For<IBlockhashStore>(),
LimboLogs.Instance);

BlockHeader header = Build.A.BlockHeader.WithAuthor(TestItem.AddressD).TestObject;
Expand Down Expand Up @@ -80,6 +82,7 @@ public void Can_store_a_witness()
stateProvider,
NullReceiptStorage.Instance,
witnessCollector,
Substitute.For<IBlockhashStore>(),
LimboLogs.Instance);

BlockHeader header = Build.A.BlockHeader.WithAuthor(TestItem.AddressD).TestObject;
Expand Down Expand Up @@ -109,6 +112,7 @@ public void Recovers_state_on_cancel()
stateProvider,
NullReceiptStorage.Instance,
NullWitnessCollector.Instance,
Substitute.For<IBlockhashStore>(),
LimboLogs.Instance);

BlockHeader header = Build.A.BlockHeader.WithNumber(1).WithAuthor(TestItem.AddressD).TestObject;
Expand Down
128 changes: 103 additions & 25 deletions src/Nethermind/Nethermind.Blockchain.Test/BlockhashProviderTests.cs
Original file line number Diff line number Diff line change
@@ -1,17 +1,41 @@
// SPDX-FileCopyrightText: 2022 Demerzel Solutions Limited
// SPDX-License-Identifier: LGPL-3.0-only

using Nethermind.Blockchain.Blocks;
using Nethermind.Blockchain.Find;
using Nethermind.Core;
using Nethermind.Core.Crypto;
using Nethermind.Core.Specs;
using Nethermind.Core.Test.Builders;
using Nethermind.Db;
using Nethermind.Logging;
using Nethermind.Specs;
using Nethermind.Specs.Forks;
using Nethermind.Specs.Test;
using Nethermind.State;
using Nethermind.Trie.Pruning;
using NUnit.Framework;

namespace Nethermind.Blockchain.Test
{
[TestFixture]
[TestFixture, Parallelizable(ParallelScope.All)]
public class BlockhashProviderTests
{
private static IWorldState CreateWorldState()
{
var trieStore = new TrieStore(new MemDb(), LimboLogs.Instance);
var worldState = new WorldState(trieStore, new MemDb(), LimboLogs.Instance);
worldState.CreateAccount(Eip2935Constants.BlockHashHistoryAddress, 0, 1);
worldState.Commit(Frontier.Instance);
return worldState;
}
private static BlockhashProvider CreateBlockHashProvider(IBlockFinder tree, IReleaseSpec spec)
{
IWorldState worldState = CreateWorldState();
BlockhashProvider provider = new(tree, new TestSpecProvider(spec), worldState, LimboLogs.Instance);
return provider;
}

[Test, Timeout(Timeout.MaxTestTime)]
public void Can_get_parent_only_headers()
{
Expand All @@ -21,10 +45,10 @@ public void Can_get_parent_only_headers()

BlockTree tree = Build.A.BlockTree(genesis).OfHeadersOnly.OfChainLength(chainLength).TestObject;

BlockhashProvider provider = new(tree, LimboLogs.Instance);
BlockhashProvider provider = CreateBlockHashProvider(tree, Frontier.Instance);
BlockHeader? head = tree.FindHeader(chainLength - 1, BlockTreeLookupOptions.None);
Block current = Build.A.Block.WithParent(head!).TestObject;
Hash256 result = provider.GetBlockhash(current.Header, chainLength - 1);
Hash256? result = provider.GetBlockhash(current.Header, chainLength - 1);
Assert.That(result, Is.EqualTo(head?.Hash));
}

Expand All @@ -36,10 +60,10 @@ public void Can_lookup_up_to_256_before_with_headers_only()
Block genesis = Build.A.Block.Genesis.TestObject;
BlockTree tree = Build.A.BlockTree(genesis).OfHeadersOnly.OfChainLength(chainLength).TestObject;

BlockhashProvider provider = new(tree, LimboLogs.Instance);
BlockhashProvider provider = CreateBlockHashProvider(tree, Frontier.Instance);
BlockHeader head = tree.FindHeader(chainLength - 1, BlockTreeLookupOptions.None)!;
Block current = Build.A.Block.WithParent(head).TestObject;
Hash256 result = provider.GetBlockhash(current.Header, chainLength - 256);
Hash256? result = provider.GetBlockhash(current.Header, chainLength - 256);
Assert.That(result, Is.EqualTo(tree.FindHeader(256, BlockTreeLookupOptions.None)!.Hash));
}

Expand All @@ -51,10 +75,10 @@ public void Can_lookup_up_to_256_before_with_headers_only_and_competing_branches
Block genesis = Build.A.Block.Genesis.TestObject;
BlockTree tree = Build.A.BlockTree(genesis).OfHeadersOnly.OfChainLength(out Block headBlock, chainLength).OfChainLength(out Block _, chainLength, 1).TestObject;

BlockhashProvider provider = new(tree, LimboLogs.Instance);
BlockhashProvider provider = CreateBlockHashProvider(tree, Frontier.Instance);
Block current = Build.A.Block.WithParent(headBlock).TestObject;
long lookupNumber = chainLength - 256;
Hash256 result = provider.GetBlockhash(current.Header, lookupNumber);
Hash256? result = provider.GetBlockhash(current.Header, lookupNumber);
Assert.NotNull(result);
}

Expand All @@ -66,12 +90,12 @@ public void Can_lookup_up_to_256_before_soon_after_fast_sync()
Block genesis = Build.A.Block.Genesis.TestObject;
BlockTree tree = Build.A.BlockTree(genesis).OfHeadersOnly.OfChainLength(out Block headBlock, chainLength).OfChainLength(out Block _, chainLength, 1).TestObject;

BlockhashProvider provider = new(tree, LimboLogs.Instance);
BlockhashProvider provider = CreateBlockHashProvider(tree, Frontier.Instance);
Block current = Build.A.Block.WithParent(headBlock).TestObject;
tree.SuggestBlock(current);
tree.UpdateMainChain(current);
long lookupNumber = chainLength - 256;
Hash256 result = provider.GetBlockhash(current.Header, lookupNumber);
Hash256? result = provider.GetBlockhash(current.Header, lookupNumber);
Assert.NotNull(result);
}

Expand All @@ -83,7 +107,7 @@ public void Can_lookup_up_to_256_before_some_blocks_after_fast_sync()
Block genesis = Build.A.Block.Genesis.TestObject;
BlockTree tree = Build.A.BlockTree(genesis).OfHeadersOnly.OfChainLength(out Block headBlock, chainLength).OfChainLength(out Block _, chainLength, 1).TestObject;

BlockhashProvider provider = new(tree, LimboLogs.Instance);
BlockhashProvider provider = CreateBlockHashProvider(tree, Frontier.Instance);

Block current = Build.A.Block.WithParent(headBlock).TestObject;
for (int i = 0; i < 6; i++)
Expand All @@ -94,7 +118,7 @@ public void Can_lookup_up_to_256_before_some_blocks_after_fast_sync()
}

long lookupNumber = current.Number - 256;
Hash256 result = provider.GetBlockhash(current.Header, lookupNumber);
Hash256? result = provider.GetBlockhash(current.Header, lookupNumber);
Assert.NotNull(result);
}

Expand All @@ -113,9 +137,9 @@ public void Can_handle_non_main_chain_in_fast_sync()
current = Build.A.Block.WithParent(current).TestObject;
}

BlockhashProvider provider = new(tree, LimboLogs.Instance);
BlockhashProvider provider = CreateBlockHashProvider(tree, Frontier.Instance);

Hash256 result = provider.GetBlockhash(current.Header, 509);
Hash256? result = provider.GetBlockhash(current.Header, 509);
Assert.NotNull(result);
}

Expand All @@ -128,10 +152,10 @@ public void Can_get_parent_hash()

BlockTree tree = Build.A.BlockTree(genesis).OfChainLength(chainLength).TestObject;

BlockhashProvider provider = new(tree, LimboLogs.Instance);
BlockhashProvider provider = CreateBlockHashProvider(tree, Frontier.Instance);
BlockHeader head = tree.FindHeader(chainLength - 1, BlockTreeLookupOptions.None)!;
Block current = Build.A.Block.WithParent(head).TestObject;
Hash256 result = provider.GetBlockhash(current.Header, chainLength - 1);
Hash256? result = provider.GetBlockhash(current.Header, chainLength - 1);
Assert.That(result, Is.EqualTo(head.Hash));
}

Expand All @@ -143,10 +167,10 @@ public void Cannot_ask_for_self()
Block genesis = Build.A.Block.Genesis.TestObject;
BlockTree tree = Build.A.BlockTree(genesis).OfChainLength(chainLength).TestObject;

BlockhashProvider provider = new(tree, LimboLogs.Instance);
BlockhashProvider provider = CreateBlockHashProvider(tree, Frontier.Instance);
BlockHeader head = tree.FindHeader(chainLength - 1, BlockTreeLookupOptions.None)!;
Block current = Build.A.Block.WithParent(head).TestObject;
Hash256 result = provider.GetBlockhash(current.Header, chainLength);
Hash256? result = provider.GetBlockhash(current.Header, chainLength);
Assert.Null(result);
}

Expand All @@ -158,10 +182,10 @@ public void Cannot_ask_about_future()
Block genesis = Build.A.Block.Genesis.TestObject;
BlockTree tree = Build.A.BlockTree(genesis).OfChainLength(chainLength).TestObject;

BlockhashProvider provider = new(tree, LimboLogs.Instance);
BlockhashProvider provider = CreateBlockHashProvider(tree, Frontier.Instance);
BlockHeader head = tree.FindHeader(chainLength - 1, BlockTreeLookupOptions.None)!;
Block current = Build.A.Block.WithParent(head).TestObject;
Hash256 result = provider.GetBlockhash(current.Header, chainLength + 1);
Hash256? result = provider.GetBlockhash(current.Header, chainLength + 1);
Assert.Null(result);
}

Expand All @@ -173,10 +197,10 @@ public void Can_lookup_up_to_256_before()
Block genesis = Build.A.Block.Genesis.TestObject;
BlockTree tree = Build.A.BlockTree(genesis).OfChainLength(chainLength).TestObject;

BlockhashProvider provider = new(tree, LimboLogs.Instance);
BlockhashProvider provider = CreateBlockHashProvider(tree, Frontier.Instance);
BlockHeader head = tree.FindHeader(chainLength - 1, BlockTreeLookupOptions.None)!;
Block current = Build.A.Block.WithParent(head).TestObject;
Hash256 result = provider.GetBlockhash(current.Header, chainLength - 256);
Hash256? result = provider.GetBlockhash(current.Header, chainLength - 256);
Assert.That(result, Is.EqualTo(tree.FindHeader(256, BlockTreeLookupOptions.None)!.Hash));
}

Expand All @@ -188,10 +212,10 @@ public void No_lookup_more_than_256_before()
Block genesis = Build.A.Block.Genesis.TestObject;
BlockTree tree = Build.A.BlockTree(genesis).OfChainLength(chainLength).TestObject;

BlockhashProvider provider = new(tree, LimboLogs.Instance);
BlockhashProvider provider = CreateBlockHashProvider(tree, Frontier.Instance);
BlockHeader head = tree.FindHeader(chainLength - 1, BlockTreeLookupOptions.None)!;
Block current = Build.A.Block.WithParent(head).TestObject;
Hash256 result = provider.GetBlockhash(current.Header, chainLength - 257);
Hash256? result = provider.GetBlockhash(current.Header, chainLength - 257);
Assert.Null(result);
}

Expand All @@ -203,11 +227,65 @@ public void UInt_256_overflow()
Block genesis = Build.A.Block.Genesis.TestObject;
BlockTree tree = Build.A.BlockTree(genesis).OfChainLength(chainLength).TestObject;

BlockhashProvider provider = new(tree, LimboLogs.Instance);
BlockhashProvider provider = CreateBlockHashProvider(tree, Frontier.Instance);
BlockHeader head = tree.FindHeader(chainLength - 1, BlockTreeLookupOptions.None)!;
Block current = Build.A.Block.WithParent(head).TestObject;
Hash256 result = provider.GetBlockhash(current.Header, 127);
Hash256? result = provider.GetBlockhash(current.Header, 127);
Assert.That(result, Is.EqualTo(head.Hash));
}

[Timeout(Timeout.MaxTestTime)]
[TestCase(1)]
[TestCase(512)]
[TestCase(8192)]
[TestCase(8193)]
public void Eip2935_init_block_history_and_then_get_hash(int chainLength)
{
Block genesis = Build.A.Block.Genesis.TestObject;
BlockTree tree = Build.A.BlockTree(genesis).OfHeadersOnly.OfChainLength(chainLength).TestObject;

BlockHeader? head = tree.FindHeader(chainLength - 1, BlockTreeLookupOptions.None);
// number = chainLength
Block current = Build.A.Block.WithParent(head!).TestObject;
tree.SuggestHeader(current.Header);

IWorldState worldState = CreateWorldState();
var specToUse = new OverridableReleaseSpec(Prague.Instance)
{
Eip2935TransitionTimestamp = current.Timestamp
};
var specProvider = new CustomSpecProvider(
(new ForkActivation(0, genesis.Timestamp), Frontier.Instance),
(new ForkActivation(0, current.Timestamp), specToUse));
BlockhashProvider provider = new(tree, specProvider, worldState, LimboLogs.Instance);
BlockhashStore store = new(tree, specProvider, worldState);

store.ApplyHistoryBlockHashes(current.Header);
worldState.Commit(specToUse);

Hash256? result = provider.GetBlockhash(current.Header, chainLength - 1);
Assert.That(result, Is.EqualTo(head?.Hash));
AssertGenesisHash(provider, current.Header, genesis.Hash!);

head = current.Header;
// number = chainLength + 1
current = Build.A.Block.WithParent(head!).TestObject;
tree.SuggestHeader(current.Header);

store.ApplyHistoryBlockHashes(current.Header);
result = provider.GetBlockhash(current.Header, chainLength);
Assert.That(result, Is.EqualTo(head?.Hash));

AssertGenesisHash(provider, current.Header, genesis.Hash!);
}

private static void AssertGenesisHash(BlockhashProvider provider, BlockHeader currentHeader, Hash256 genesisHash)
{
Hash256? result = provider.GetBlockhash(currentHeader, 0);
if (currentHeader.Number > Eip2935Constants.RingBufferSize)
Assert.That(result, Is.Null);
else
Assert.That(result, Is.EqualTo(genesisHash));
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

using System.Threading;
using FluentAssertions;
using Nethermind.Blockchain.Blocks;
using Nethermind.Blockchain.Receipts;
using Nethermind.Config;
using Nethermind.Consensus.Processing;
Expand Down Expand Up @@ -55,7 +56,7 @@ public void Test()
dbProvider.RegisteredDbs[DbNames.Code],
LimboLogs.Instance);
StateReader stateReader = new(trieStore, dbProvider.GetDb<IDb>(DbNames.State), LimboLogs.Instance);
BlockhashProvider blockhashProvider = new(blockTree, LimboLogs.Instance);
BlockhashProvider blockhashProvider = new(blockTree, specProvider, stateProvider, LimboLogs.Instance);
VirtualMachine virtualMachine = new(
blockhashProvider,
specProvider,
Expand All @@ -73,6 +74,7 @@ public void Test()
stateProvider,
NullReceiptStorage.Instance,
NullWitnessCollector.Instance,
new BlockhashStore(blockTree, specProvider, stateProvider),
LimboLogs.Instance);
BlockchainProcessor blockchainProcessor = new(
blockTree,
Expand Down
4 changes: 3 additions & 1 deletion src/Nethermind/Nethermind.Blockchain.Test/ReorgTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

using System.Collections.Generic;
using FluentAssertions;
using Nethermind.Blockchain.Blocks;
using Nethermind.Blockchain.Receipts;
using Nethermind.Consensus.Comparers;
using Nethermind.Consensus.Processing;
Expand Down Expand Up @@ -56,7 +57,7 @@ public void Setup()
new TxValidator(specProvider.ChainId),
LimboLogs.Instance,
transactionComparerProvider.GetDefaultComparer());
BlockhashProvider blockhashProvider = new(_blockTree, LimboLogs.Instance);
BlockhashProvider blockhashProvider = new(_blockTree, specProvider, stateProvider, LimboLogs.Instance);
VirtualMachine virtualMachine = new(
blockhashProvider,
specProvider,
Expand All @@ -75,6 +76,7 @@ public void Setup()
stateProvider,
NullReceiptStorage.Instance,
new WitnessCollector(memDbProvider.StateDb, LimboLogs.Instance),
new BlockhashStore(_blockTree, MainnetSpecProvider.Instance, stateProvider),
LimboLogs.Instance);
_blockchainProcessor = new BlockchainProcessor(
_blockTree,
Expand Down
Loading

0 comments on commit f679833

Please sign in to comment.