Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
130 commits
Select commit Hold shift + click to select a range
9070ca2
add to release spec
Marchhill Aug 11, 2025
ea3f4d2
add hash to header
Marchhill Aug 11, 2025
5fae29d
add to block body
Marchhill Aug 11, 2025
273905e
BAL validation
Marchhill Aug 11, 2025
0246d85
add to constuctor
Marchhill Aug 11, 2025
db756e6
set BAL at end of processing
Marchhill Aug 12, 2025
8c6f4e2
constants, ssz containers
Marchhill Aug 12, 2025
dbed6d2
add block access tracer
Marchhill Aug 15, 2025
7d7cb29
construct BAL in tracer
Marchhill Aug 15, 2025
ec347c1
call byte encode, move BAL to core
Marchhill Aug 15, 2025
675485c
block access tracer improvements & tests
Marchhill Aug 18, 2025
65bcb30
add to execution payload
Marchhill Aug 18, 2025
4cd315b
update structures
Marchhill Aug 19, 2025
6f9aabf
only record last change of each tx
Marchhill Aug 19, 2025
d884b36
fix test
Marchhill Aug 19, 2025
0eb0736
change storage tracing
Marchhill Aug 19, 2025
603d56a
RLP encode bal
Marchhill Aug 19, 2025
1726b9b
prewarm addresses
Marchhill Aug 19, 2025
8ac6add
use bal as state provider
Marchhill Aug 19, 2025
190f2e4
reset changes using BAL
Marchhill Aug 21, 2025
f09890c
only trace when EIP enabled
Marchhill Aug 21, 2025
f93a43c
handle withdrawals
Marchhill Aug 21, 2025
2bf2cef
handle system contracts
Marchhill Aug 21, 2025
ce00892
fix compile
Marchhill Aug 21, 2025
9d1d9ff
add block processing test
Marchhill Aug 22, 2025
2f6829d
move bal decoding to suggested block validation
Marchhill Aug 22, 2025
23dc197
decoding debug logs
Marchhill Aug 22, 2025
496b686
empty rlp decode for bal
Marchhill Aug 22, 2025
15bbab1
move RLP decoding
Marchhill Aug 22, 2025
7c31e87
don't encode/decode for testing
Marchhill Aug 22, 2025
9015f70
init beacon block roots address
Marchhill Aug 22, 2025
8ac8939
setup execution requests contracts
Marchhill Aug 22, 2025
4a507b6
start implementing rlp
Marchhill Aug 26, 2025
87dc02c
account changes rlp+
Marchhill Aug 26, 2025
a9f0e93
bal encoding / decoding
Marchhill Aug 27, 2025
d12d0b5
change storagekey to bytes, use rlp in test
Marchhill Aug 27, 2025
3223680
always start new sequence rlp
Marchhill Aug 27, 2025
7009dee
fix bal encoding length
Marchhill Aug 27, 2025
f30d1b5
fix all rlp lengths
Marchhill Aug 27, 2025
c49c33c
fix storagechange and slotchanges encodings
Marchhill Aug 29, 2025
19bafac
use encodearray
Marchhill Aug 29, 2025
143ff10
encoding tests and more fixes
Marchhill Aug 29, 2025
795c35d
storage read encoding, complete bal encoding
Marchhill Aug 29, 2025
8ac9f46
tidy
Marchhill Aug 29, 2025
5f96184
more tidying
Marchhill Aug 29, 2025
3cd7a1a
use specific types
Marchhill Aug 29, 2025
9a7429b
check length is 32 bytes
Marchhill Aug 29, 2025
4b1aec5
check against maximums
Marchhill Aug 29, 2025
723eaca
fix decoding & test
Marchhill Sep 1, 2025
511bd95
equatable in tests, tidy up structs
Marchhill Sep 1, 2025
26be914
formatting
Marchhill Sep 1, 2025
0ad29d0
Merge remote-tracking branch 'upstream/master' into feature/block-lev…
Marchhill Sep 1, 2025
7d40f7e
refactor test to use testblockchain
Marchhill Sep 1, 2025
b4237c1
tidy tests
Marchhill Sep 1, 2025
e9bc323
fix test
Marchhill Sep 1, 2025
158359f
improve test
Marchhill Sep 2, 2025
0343916
add withdrawal to the test
Marchhill Sep 2, 2025
45923a0
eip 7002 & 7251
Marchhill Sep 2, 2025
e00506c
system contracts, storage changes
Marchhill Sep 3, 2025
7495932
work out hashes
Marchhill Sep 4, 2025
11547d4
simplify storage changes
Marchhill Sep 4, 2025
194fdb5
tidy
Marchhill Sep 4, 2025
df41d5d
simplify more
Marchhill Sep 4, 2025
64d1262
tidy storage reads
Marchhill Sep 4, 2025
fc16d59
tidy 4788
Marchhill Sep 4, 2025
ebb187a
use sorted lists and sets for BAL
Marchhill Sep 4, 2025
cafee52
separate test constants
Marchhill Sep 5, 2025
965f7be
add code change to test
Marchhill Sep 5, 2025
8c2503c
don't record zero nonce change
Marchhill Sep 5, 2025
bb4d82f
refactor from tracer to inside bal
Marchhill Sep 5, 2025
c2bf7a2
add traced and wrapped worldstate
Marchhill Sep 8, 2025
46c55bc
track state changes
Marchhill Sep 8, 2025
6407a68
reset BAL on new scope
Marchhill Sep 8, 2025
5ec4ff5
worldstate improvements
Marchhill Sep 9, 2025
b457091
fix test
Marchhill Sep 9, 2025
f253413
fix encoding test
Marchhill Sep 9, 2025
371478b
optimise incrementnonce
Marchhill Sep 9, 2025
f917e8c
refactor and remove tracer code
Marchhill Sep 9, 2025
cd60b07
optimise adding to and subtracting from balance
Marchhill Sep 10, 2025
9af3112
ignore old code
Marchhill Sep 10, 2025
fb14ec5
check BAL ordering
Marchhill Sep 10, 2025
6eb7232
tidy
Marchhill Sep 10, 2025
f0e7d59
make bal a journal
Marchhill Sep 11, 2025
595b62c
add reverting transaction test
Marchhill Sep 11, 2025
7295799
only construct BAL when enabled
Marchhill Sep 11, 2025
2c5a9e9
add IIndexedChange
Marchhill Sep 11, 2025
e7e5ed2
Merge remote-tracking branch 'upstream/master' into feature/block-lev…
Marchhill Sep 15, 2025
c3d5970
Merge remote-tracking branch 'upstream/master' into feature/block-lev…
Marchhill Sep 15, 2025
aceb9e0
check for null traced access worldstate
Marchhill Sep 15, 2025
5b016d0
fix hash calc
Marchhill Sep 16, 2025
95a5da7
disable in chainspec params test
Marchhill Sep 16, 2025
f05fcd7
bal snapshot fix
Marchhill Sep 16, 2025
31fdc5d
fix hash match check
Marchhill Sep 16, 2025
f3df8ba
remove comments
Marchhill Sep 16, 2025
4bd3960
Merge remote-tracking branch 'upstream/master' into feature/block-lev…
Marchhill Sep 16, 2025
ad7d5c8
null hash for empty access lsit
Marchhill Sep 16, 2025
e2643aa
amsterdam fork
Marchhill Sep 17, 2025
cf9629e
add bal to block & header decoder, store encoded bal in block, update…
Marchhill Sep 18, 2025
894f5c8
string encoding
Marchhill Sep 18, 2025
bdd2448
fix decoding
Marchhill Sep 23, 2025
ef6f37b
improve encoding test
Marchhill Sep 23, 2025
dc8122c
fix encoding, add tests
Marchhill Sep 24, 2025
50ab9c1
refactor bal encoding test
Marchhill Sep 25, 2025
d067106
code changes test
Marchhill Sep 25, 2025
e3d9fb5
don't use hash of storage index
Marchhill Sep 25, 2025
5394826
don't add redundant reads
Marchhill Sep 25, 2025
92c7d5a
add new fields to blockchain tests
Marchhill Sep 26, 2025
eacf617
Merge remote-tracking branch 'upstream/master' into feature/block-lev…
Marchhill Sep 26, 2025
1d6e8da
tidy code
Marchhill Sep 26, 2025
2d3a412
add blockchain engine test support
Marchhill Sep 30, 2025
2665f0e
tidy test base
Marchhill Sep 30, 2025
19694c2
fix engine test loading, different versions
Marchhill Sep 30, 2025
d6fc96d
only set hash if bals enabled
Marchhill Sep 30, 2025
93e823f
fix loading older engine tests
Marchhill Sep 30, 2025
ce700c2
fix BAL decoding in execution payload
Marchhill Oct 1, 2025
94cda6a
formatting
Marchhill Oct 1, 2025
57437f6
fix normal blockchain tests
Marchhill Oct 1, 2025
b3725c3
set to empty block access list hash if not enabled
Marchhill Oct 1, 2025
31b2424
move to newpayloadv5
Marchhill Oct 2, 2025
878b787
trace get account balance
Marchhill Oct 2, 2025
89bf5f9
codehash
Marchhill Oct 2, 2025
c046b92
create account & selfdestruct edge cases
Marchhill Oct 3, 2025
f936546
try only setting null hash when enabled
Marchhill Oct 6, 2025
be3fd21
undo
Marchhill Oct 6, 2025
455efa7
try only setting null hash when enabled
Marchhill Oct 6, 2025
c77d2ee
undo
Marchhill Oct 6, 2025
29537bb
set genesis bal hash in chainspec loader
Marchhill Oct 6, 2025
f0863b4
always set bal hash
Marchhill Oct 6, 2025
6ed885f
add to chainspecbasedprovider
Marchhill Oct 6, 2025
cdcf39b
set to empty hash if genesis
Marchhill Oct 6, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/Nethermind/Ethereum.Test.Base/BlockchainTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ public class BlockchainTest : EthereumTest

public TestBlockJson[]? Blocks { get; set; }
public TestBlockHeaderJson? GenesisBlockHeader { get; set; }
public TestEngineNewPayloadsJson[]? EngineNewPayloads { get; set; }

public Dictionary<Address, AccountState>? Pre { get; set; }
public Dictionary<Address, AccountState>? PostState { get; set; }
Expand Down
149 changes: 101 additions & 48 deletions src/Nethermind/Ethereum.Test.Base/BlockchainTestBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@
using System.Threading;
using System.Threading.Tasks;
using Autofac;
using Nethermind.Api;
using Nethermind.Blockchain;
using Nethermind.Blockchain.Find;
using Nethermind.Config;
Expand All @@ -30,26 +29,25 @@
using Nethermind.Specs;
using Nethermind.Specs.Forks;
using Nethermind.Specs.Test;
using Nethermind.State;
using Nethermind.Evm.State;
using Nethermind.Init.Modules;
using Nethermind.TxPool;
using NUnit.Framework;
using Nethermind.Merge.Plugin.Data;
using Nethermind.Merge.Plugin;
using Nethermind.JsonRpc;

namespace Ethereum.Test.Base;

public abstract class BlockchainTestBase
{
private static ILogger _logger;
private static ILogManager _logManager = new TestLogManager(LogLevel.Warn);
private static ISealValidator Sealer { get; }
private static readonly ILogger _logger;
private static readonly ILogManager _logManager = new TestLogManager(LogLevel.Warn);
private static DifficultyCalculatorWrapper DifficultyCalculator { get; }

static BlockchainTestBase()
{
DifficultyCalculator = new DifficultyCalculatorWrapper();
Sealer = new EthashSealValidator(_logManager, DifficultyCalculator, new CryptoRandom(), new Ethash(_logManager), Timestamper.Default); // temporarily keep reusing the same one as otherwise it would recreate cache for each test

_logManager ??= LimboLogs.Instance;
_logger = _logManager.GetClassLogger();
}
Expand Down Expand Up @@ -129,6 +127,7 @@ protected async Task<EthereumTestResult> RunTest(BlockchainTest test, Stopwatch?
// configProvider.GetConfig<IBlocksConfig>().PreWarmStateOnBlockProcessing = false;
await using IContainer container = new ContainerBuilder()
.AddModule(new TestNethermindModule(configProvider))
.AddModule(new TestMergeModule(configProvider))
.AddSingleton(specProvider)
.AddSingleton(_logManager)
.AddSingleton(rewardCalculator)
Expand All @@ -141,6 +140,7 @@ protected async Task<EthereumTestResult> RunTest(BlockchainTest test, Stopwatch?
IBlockchainProcessor blockchainProcessor = mainBlockProcessingContext.BlockchainProcessor;
IBlockTree blockTree = container.Resolve<IBlockTree>();
IBlockValidator blockValidator = container.Resolve<IBlockValidator>();
IEngineRpcModule engineRpcModule = container.Resolve<IEngineRpcModule>();
blockchainProcessor.Start();

BlockHeader parentHeader;
Expand All @@ -154,6 +154,7 @@ protected async Task<EthereumTestResult> RunTest(BlockchainTest test, Stopwatch?
test.GenesisRlp ??= Rlp.Encode(new Block(JsonToEthereumTest.Convert(test.GenesisBlockHeader)));

Block genesisBlock = Rlp.Decode<Block>(test.GenesisRlp.Bytes);
// Console.WriteLine(genesisBlock.ToString(Block.Format.Full));
Assert.That(genesisBlock.Header.Hash, Is.EqualTo(new Hash256(test.GenesisBlockHeader.Hash)));

ManualResetEvent genesisProcessed = new(false);
Expand All @@ -162,7 +163,7 @@ protected async Task<EthereumTestResult> RunTest(BlockchainTest test, Stopwatch?
{
if (args.Block.Number == 0)
{
Assert.That(stateProvider.HasStateForBlock(genesisBlock.Header), Is.EqualTo(true));
Assert.That(stateProvider.HasStateForBlock(genesisBlock.Header), Is.True);
genesisProcessed.Set();
}
};
Expand All @@ -172,6 +173,76 @@ protected async Task<EthereumTestResult> RunTest(BlockchainTest test, Stopwatch?
parentHeader = genesisBlock.Header;
}

if (test.Blocks is not null)
{
// blockchain test
parentHeader = SuggestBlocks(test, failOnInvalidRlp, blockValidator, blockTree, parentHeader);
}
else if (test.EngineNewPayloads is not null)
{
(ExecutionPayload, string[]?, string[]?, string?)[] payloads = [.. JsonToEthereumTest.Convert(test.EngineNewPayloads)];

// blockchain test engine
foreach ((ExecutionPayload executionPayload, string[]? blobVersionedHashes, string[]? validationError, string? newPayloadVersion) in payloads)
{
ResultWrapper<PayloadStatusV1> res;
byte[]?[] hashes = blobVersionedHashes is null ? null : [.. blobVersionedHashes.Select(x => Bytes.FromHexString(x))];

switch (newPayloadVersion ?? "5")
{
case "1":
res = await engineRpcModule.engine_newPayloadV1(executionPayload);
break;
case "2":
res = await engineRpcModule.engine_newPayloadV2(executionPayload);
break;
case "3":
res = await engineRpcModule.engine_newPayloadV3((ExecutionPayloadV3)executionPayload, [], executionPayload.ParentBeaconBlockRoot);
break;
case "4":
res = await engineRpcModule.engine_newPayloadV4((ExecutionPayloadV3)executionPayload, hashes, executionPayload.ParentBeaconBlockRoot, []);
break;
case "5":
res = await engineRpcModule.engine_newPayloadV4((ExecutionPayloadV3)executionPayload, hashes, executionPayload.ParentBeaconBlockRoot, []);
break;
default:
Assert.Fail("Invalid blockchain engine test, version not recognised.");
break;
}
}
}
else
{
Assert.Fail("Invalid blockchain test, did not contain blocks or new payloads.");
}

await blockchainProcessor.StopAsync(true);
stopwatch?.Stop();

IBlockCachePreWarmer? preWarmer = container.Resolve<MainProcessingContext>().LifetimeScope.ResolveOptional<IBlockCachePreWarmer>();

// Caches are cleared async, which is a problem as read for the MainWorldState with prewarmer is not correct if its not cleared.
preWarmer?.ClearCaches();

Block? headBlock = blockTree.RetrieveHeadBlock();
List<string> differences;
using (stateProvider.BeginScope(headBlock.Header))
{
differences = RunAssertions(test, blockTree.RetrieveHeadBlock(), stateProvider);
}

Assert.That(differences, Is.Empty, "differences");

return new EthereumTestResult
(
test.Name,
null,
differences.Count == 0
);
}

private static BlockHeader SuggestBlocks(BlockchainTest test, bool failOnInvalidRlp, IBlockValidator blockValidator, IBlockTree blockTree, BlockHeader parentHeader)
{
List<(Block Block, string ExpectedException)> correctRlp = DecodeRlps(test, failOnInvalidRlp);
for (int i = 0; i < correctRlp.Count; i++)
{
Expand Down Expand Up @@ -211,43 +282,22 @@ protected async Task<EthereumTestResult> RunTest(BlockchainTest test, Stopwatch?
parentHeader = correctRlp[i].Block.Header;
}

await blockchainProcessor.StopAsync(true);
stopwatch?.Stop();

IBlockCachePreWarmer? preWarmer = container.Resolve<MainProcessingContext>().LifetimeScope.ResolveOptional<IBlockCachePreWarmer>();
if (preWarmer is not null)
{
// Caches are cleared async, which is a problem as read for the MainWorldState with prewarmer is not correct if its not cleared.
preWarmer.ClearCaches();
}

Block? headBlock = blockTree.RetrieveHeadBlock();
List<string> differences;
using (stateProvider.BeginScope(headBlock.Header))
{
differences = RunAssertions(test, blockTree.RetrieveHeadBlock(), stateProvider);
}

Assert.That(differences.Count, Is.Zero, "differences");

return new EthereumTestResult
(
test.Name,
null,
differences.Count == 0
);
return parentHeader;
}

private List<(Block Block, string ExpectedException)> DecodeRlps(BlockchainTest test, bool failOnInvalidRlp)
private static List<(Block Block, string ExpectedException)> DecodeRlps(BlockchainTest test, bool failOnInvalidRlp)
{
List<(Block Block, string ExpectedException)> correctRlp = new();
List<(Block Block, string ExpectedException)> correctRlp = [];
for (int i = 0; i < test.Blocks.Length; i++)
{
TestBlockJson testBlockJson = test.Blocks[i];
try
{
var rlpContext = Bytes.FromHexString(testBlockJson.Rlp).AsRlpStream();
RlpStream rlpContext = Bytes.FromHexString(testBlockJson.Rlp).AsRlpStream();
Block suggestedBlock = Rlp.Decode<Block>(rlpContext);
// Console.WriteLine("suggested block:");
// Console.WriteLine(suggestedBlock.BlockAccessList);
// Hash256 tmp = new(ValueKeccak.Compute(Rlp.Encode(suggestedBlock.BlockAccessList!.Value).Bytes).Bytes);
suggestedBlock.Header.SealEngineType =
test.SealEngineUsed ? SealEngineType.Ethash : SealEngineType.None;

Expand Down Expand Up @@ -288,17 +338,20 @@ protected async Task<EthereumTestResult> RunTest(BlockchainTest test, Stopwatch?

if (correctRlp.Count == 0)
{
Assert.That(test.GenesisBlockHeader, Is.Not.Null);
Assert.That(test.LastBlockHash, Is.EqualTo(new Hash256(test.GenesisBlockHeader.Hash)));
using (Assert.EnterMultipleScope())
{
Assert.That(test.GenesisBlockHeader, Is.Not.Null);
Assert.That(test.LastBlockHash, Is.EqualTo(new Hash256(test.GenesisBlockHeader.Hash)));
}
}

return correctRlp;
}

private void InitializeTestState(BlockchainTest test, IWorldState stateProvider, ISpecProvider specProvider)
private static void InitializeTestState(BlockchainTest test, IWorldState stateProvider, ISpecProvider specProvider)
{
foreach (KeyValuePair<Address, AccountState> accountState in
((IEnumerable<KeyValuePair<Address, AccountState>>)test.Pre ?? Array.Empty<KeyValuePair<Address, AccountState>>()))
(IEnumerable<KeyValuePair<Address, AccountState>>)test.Pre ?? Array.Empty<KeyValuePair<Address, AccountState>>())
{
foreach (KeyValuePair<UInt256, byte[]> storageItem in accountState.Value.Storage)
{
Expand All @@ -316,18 +369,18 @@ private void InitializeTestState(BlockchainTest test, IWorldState stateProvider,
stateProvider.Reset();
}

private List<string> RunAssertions(BlockchainTest test, Block headBlock, IWorldState stateProvider)
private static List<string> RunAssertions(BlockchainTest test, Block headBlock, IWorldState stateProvider)
{
if (test.PostStateRoot is not null)
{
return test.PostStateRoot != stateProvider.StateRoot ? new List<string> { "state root mismatch" } : Enumerable.Empty<string>().ToList();
return test.PostStateRoot != stateProvider.StateRoot ? ["state root mismatch"] : [];
}

TestBlockHeaderJson testHeaderJson = (test.Blocks?
.Where(b => b.BlockHeader is not null)
.SingleOrDefault(b => new Hash256(b.BlockHeader.Hash) == headBlock.Hash)?.BlockHeader) ?? test.GenesisBlockHeader;
BlockHeader testHeader = JsonToEthereumTest.Convert(testHeaderJson);
List<string> differences = new();
List<string> differences = [];

IEnumerable<KeyValuePair<Address, AccountState>> deletedAccounts = test.Pre?
.Where(pre => !(test.PostState?.ContainsKey(pre.Key) ?? false)) ?? Array.Empty<KeyValuePair<Address, AccountState>>();
Expand All @@ -351,8 +404,8 @@ private List<string> RunAssertions(BlockchainTest test, Block headBlock, IWorldS
}

bool accountExists = stateProvider.AccountExists(acountAddress);
UInt256? balance = accountExists ? stateProvider.GetBalance(acountAddress) : (UInt256?)null;
UInt256? nonce = accountExists ? stateProvider.GetNonce(acountAddress) : (UInt256?)null;
UInt256? balance = accountExists ? stateProvider.GetBalance(acountAddress) : null;
UInt256? nonce = accountExists ? stateProvider.GetNonce(acountAddress) : null;

if (accountState.Balance != balance)
{
Expand All @@ -364,7 +417,7 @@ private List<string> RunAssertions(BlockchainTest test, Block headBlock, IWorldS
differences.Add($"{acountAddress} nonce exp: {accountState.Nonce}, actual: {nonce}");
}

byte[] code = accountExists ? stateProvider.GetCode(acountAddress) : new byte[0];
byte[] code = accountExists ? stateProvider.GetCode(acountAddress) : [];
if (!Bytes.AreEqual(accountState.Code, code))
{
differences.Add($"{acountAddress} code exp: {accountState.Code?.Length}, actual: {code?.Length}");
Expand All @@ -377,10 +430,10 @@ private List<string> RunAssertions(BlockchainTest test, Block headBlock, IWorldS

differencesBefore = differences.Count;

KeyValuePair<UInt256, byte[]>[] clearedStorages = new KeyValuePair<UInt256, byte[]>[0];
KeyValuePair<UInt256, byte[]>[] clearedStorages = [];
if (test.Pre.ContainsKey(acountAddress))
{
clearedStorages = test.Pre[acountAddress].Storage.Where(s => !accountState.Storage.ContainsKey(s.Key)).ToArray();
clearedStorages = [.. test.Pre[acountAddress].Storage.Where(s => !accountState.Storage.ContainsKey(s.Key))];
}

foreach (KeyValuePair<UInt256, byte[]> clearedStorage in clearedStorages)
Expand Down
1 change: 1 addition & 0 deletions src/Nethermind/Ethereum.Test.Base/BlockchainTestJson.cs
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ public class BlockchainTestJson

public TestBlockJson[]? Blocks { get; set; }
public TestBlockHeaderJson? GenesisBlockHeader { get; set; }
public TestEngineNewPayloadsJson[]? EngineNewPayloads { get; set; }

public Dictionary<Address, AccountState>? Pre { get; set; }
public Dictionary<Address, AccountState>? PostState { get; set; }
Expand Down
Loading